diff --git a/api/next/67546.txt b/api/next/67546.txt new file mode 100644 index 00000000000000..1ef3bcaa08e178 --- /dev/null +++ b/api/next/67546.txt @@ -0,0 +1,8 @@ +pkg database/sql, func ConvertAssign(driver.ScanContext, interface{}, driver.Value) error #67546 +pkg database/sql/driver, type RowsColumnScanner interface { Close, Columns, Next, NextRow, ScanColumn } #67546 +pkg database/sql/driver, type RowsColumnScanner interface, Close() error #67546 +pkg database/sql/driver, type RowsColumnScanner interface, Columns() []string #67546 +pkg database/sql/driver, type RowsColumnScanner interface, Next([]Value) error #67546 +pkg database/sql/driver, type RowsColumnScanner interface, NextRow() error #67546 +pkg database/sql/driver, type RowsColumnScanner interface, ScanColumn(ScanContext, int, interface{}) error #67546 +pkg database/sql/driver, type ScanContext struct #67546 diff --git a/api/next/79287.txt b/api/next/79287.txt new file mode 100644 index 00000000000000..3e334d3159cb6d --- /dev/null +++ b/api/next/79287.txt @@ -0,0 +1,3 @@ +pkg go/types, method (*TypeList) String() string #79287 +pkg go/types, method (*TypeParamList) String() string #79287 +pkg go/types, method (Instance) String() string #79287 diff --git a/doc/next/6-stdlib/99-minor/database/sql/67546.md b/doc/next/6-stdlib/99-minor/database/sql/67546.md new file mode 100644 index 00000000000000..53bf69eb5fcbdb --- /dev/null +++ b/doc/next/6-stdlib/99-minor/database/sql/67546.md @@ -0,0 +1,2 @@ +The new [ConvertAssign] function gives database drivers access +to the type conversions performed by [Rows.Scan]. diff --git a/doc/next/6-stdlib/99-minor/database/sql/driver/67546.md b/doc/next/6-stdlib/99-minor/database/sql/driver/67546.md new file mode 100644 index 00000000000000..f618ede8bfee92 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/database/sql/driver/67546.md @@ -0,0 +1,2 @@ +Drivers may implement the new [RowsColumnScanner] interface +to scan directly into user-provided destinations. diff --git a/doc/next/6-stdlib/99-minor/go/types/79287.md b/doc/next/6-stdlib/99-minor/go/types/79287.md new file mode 100644 index 00000000000000..8ec275e7f67751 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/go/types/79287.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go index 502f658e985fca..dee509218965f8 100644 --- a/src/cmd/compile/doc.go +++ b/src/cmd/compile/doc.go @@ -205,7 +205,7 @@ all other compiler directives are of the form // // Examples: // -// //line foo.go:10 the filename is foo.go, and the line number is 10 for the next line +// //line foo.go:10 the (relative) filename is foo.go, and the line number is 10 for the next line // //line ../foo.go:10 relative filenames are resolved against the directive's source directory // //line C:foo.go:10 colons are permitted in filenames, here the filename is C:foo.go, and the line is 10 // //line a:100 :10 blanks are permitted in filenames, here the filename is " a:100 " (excluding quotes) diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go index 320836c8c4f449..57cde66fb5601c 100644 --- a/src/cmd/compile/internal/ir/symtab.go +++ b/src/cmd/compile/internal/ir/symtab.go @@ -39,8 +39,8 @@ type symsStruct struct { InterfaceSwitch *obj.LSym MallocGC *obj.LSym MallocGCTiny *obj.LSym - MallocGCSmallNoScan [11]*obj.LSym - MallocGCSmallScanNoHeader [11]*obj.LSym + MallocGCSmallNoScan [8]*obj.LSym + MallocGCSmallScanNoHeader [8]*obj.LSym Memmove *obj.LSym Memequal *obj.LSym Msanread *obj.LSym diff --git a/src/cmd/compile/internal/ssa/stmtlines_test.go b/src/cmd/compile/internal/ssa/stmtlines_test.go index 02bdfca92504e0..acab3c9cb114d0 100644 --- a/src/cmd/compile/internal/ssa/stmtlines_test.go +++ b/src/cmd/compile/internal/ssa/stmtlines_test.go @@ -103,7 +103,8 @@ func TestStmtLines(t *testing.T) { if pkgname == "runtime" { continue } - if pkgname == "crypto/internal/fips140/nistec/fiat" { + if pkgname == "crypto/internal/fips140/nistec/fiat" || + pkgname == "crypto/internal/fips140/nistec" { continue // golang.org/issue/49372 } if e.Val(dwarf.AttrStmtList) == nil { diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 9fb79bc97758a9..9a4970a579085d 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -806,7 +806,7 @@ func (s *state) specializedMallocSym(size int64, hasPointers bool) *obj.LSym { if !s.sizeSpecializedMallocEnabled() { return nil } - const specializedMallocMax = 128 // This must match the constant in mkmalloc. + const specializedMallocMax = 80 // This must match the constant in mkmalloc. if size > specializedMallocMax { return nil } diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index e086f56f4ca6f2..1f5d44caa9ded3 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -232,7 +232,7 @@ func TestParseFile(t *testing.T) { var tooLarge int = PosMax + 1 func TestLineDirectives(t *testing.T) { - // valid line directives lead to a syntax error after them + // valid line directives lead to this syntax error after them const valid = "syntax error: package statement must be first" const filename = "directives.go" @@ -271,8 +271,8 @@ func TestLineDirectives(t *testing.T) { {fmt.Sprintf("//line foo:10:%d\n", tooLarge), fmt.Sprintf("invalid column number: %d", tooLarge), filename, 1, 15}, // effect of valid //line directives on lines - {"//line foo:123\n foo", valid, "foo", 123, 0}, - {"//line foo:123\n foo", valid, " foo", 123, 0}, + {"//line foo:123\n", valid, "foo", 123, 0}, + {"//line foo:123\n", valid, " foo", 123, 0}, {"//line foo:123\n//line bar:345\nfoo", valid, "bar", 345, 0}, {"//line :x:1\n", valid, ":x", 1, 0}, {"//line foo ::1\n", valid, "foo :", 1, 0}, @@ -322,7 +322,7 @@ func TestLineDirectives(t *testing.T) { {fmt.Sprintf("/*line foo:10:%d*/", tooLarge), fmt.Sprintf("invalid column number: %d", tooLarge), filename, 1, 15}, // effect of valid /*line directives on lines - {"/*line foo:123*/ foo", valid, "foo", 123, 0}, + {"/*line foo:123*/", valid, "foo", 123, 0}, {"/*line foo:123*/\n//line bar:345\nfoo", valid, "bar", 345, 0}, {"/*line :x:1*/", valid, ":x", 1, 0}, {"/*line foo ::1*/", valid, "foo :", 1, 0}, @@ -342,6 +342,14 @@ func TestLineDirectives(t *testing.T) { {"/*line :10:20*/", valid, filename, 10, 20}, {"//line bar:1\n/*line :10*/", valid, "", 10, 0}, {"//line bar:1\n/*line :10:20*/", valid, "bar", 10, 20}, + + // effect of cleaning filenames + {"//line C:foo:123\n", valid, filepath.Clean("C:foo"), 123, 0}, + {"//line /src/a/a.go:123\n", valid, filepath.Clean("/src/a/a.go"), 123, 0}, + {"//line foo/../bar:1\n", valid, filepath.Clean("foo/../bar"), 1, 0}, + {"/*line C:foo:123*/", valid, filepath.Clean("C:foo"), 123, 0}, + {"/*line /src/a/a.go:123*/", valid, filepath.Clean("/src/a/a.go"), 123, 0}, + {"/*line foo/../bar:1*/", valid, filepath.Clean("foo/../bar"), 1, 0}, } { base := NewFileBase(filename) _, err := Parse(base, strings.NewReader(test.src), nil, nil, 0) @@ -372,102 +380,40 @@ func TestLineDirectives(t *testing.T) { } func TestLineDirectivesWithDir(t *testing.T) { - const valid = "syntax error: package statement must be first" - srcFile := filepath.Join("dir", "directives.go") - - check := func(src, want string) { - t.Helper() - base := NewFileBase(srcFile) - _, err := Parse(base, strings.NewReader(src), nil, nil, 0) - if err == nil { - t.Errorf("%s: no error reported", src) - return - } - perr, ok := err.(Error) - if !ok { - t.Errorf("%s: got %v; want parser error", src, err) - return - } - if perr.Msg != valid { - t.Errorf("%s: got msg = %q; want %q", src, perr.Msg, valid) - return - } - if got := perr.Pos.RelFilename(); got != want { - t.Errorf("%s: got filename = %q; want %q", src, got, want) - } + const dir = "dir" + filename := filepath.Join(dir, "directives.go") + + type test struct{ src, filename string } + relPaths := []test{ + {"//line foo:1\n", filepath.Join(dir, "foo")}, + {"//line ./foo:1\n", filepath.Join(dir, "foo")}, + {"//line ../foo:1\n", "foo"}, + {"//line sub/foo:1\n", filepath.Join(dir, "sub", "foo")}, + {"/*line foo:1*/", filepath.Join(dir, "foo")}, + {"//line bar:1\n//line :2:1\n", filepath.Join(dir, "bar")}, } - for _, test := range []struct { - src string - filename string - }{ - {"//line foo:1\n x", filepath.Join("dir", "foo")}, - {"//line ./foo:1\n x", filepath.Join("dir", "foo")}, - {"//line ../foo:1\n x", "foo"}, - {"//line sub/foo:1\n x", filepath.Join("dir", "sub", "foo")}, - {"/*line foo:1*/ x", filepath.Join("dir", "foo")}, - {"//line bar:1\n//line :2:1\n x", filepath.Join("dir", "bar")}, - } { - check(test.src, test.filename) - } - - var absCases []struct { - src, filename string - } + var absPaths []test if runtime.GOOS == "windows" { - absCases = append(absCases, struct{ src, filename string }{ - "//line c:\\bar:1\n x", "c:\\bar", - }) + absPaths = []test{ + {"//line c:\\bar:1\n", "c:\\bar"}, + } } else { - absCases = append(absCases, - struct{ src, filename string }{"//line /abs/foo:1\n x", "/abs/foo"}, - struct{ src, filename string }{"//line /src/a/a.go:1\n x", "/src/a/a.go"}, - ) - } - for _, test := range absCases { - check(test.src, test.filename) + absPaths = []test{ + {"//line /abs/foo:1\n", "/abs/foo"}, + {"//line /src/a/a.go:1\n", "/src/a/a.go"}, + } } -} -func TestLineDirectivesPaths(t *testing.T) { - const valid = "syntax error: package statement must be first" - const filename = "directives.go" - - type tc struct { - src string - filename string - line uint - } - var cases []tc - cases = []tc{ - {"//line C:foo:123\n", "C:foo", 123}, - {"//line /src/a/a.go:123\n foo", filepath.Clean("/src/a/a.go"), 123}, - {"/*line C:foo:123*/", "C:foo", 123}, - {"/*line /src/a/a.go:123*/ foo", filepath.Clean("/src/a/a.go"), 123}, - {"//line foo/../bar:1\n x", "bar", 1}, - } - for _, test := range cases { + for _, test := range append(relPaths, absPaths...) { base := NewFileBase(filename) - _, err := Parse(base, strings.NewReader(test.src), nil, nil, 0) - if err == nil { - t.Errorf("%s: no error reported", test.src) - continue - } - perr, ok := err.(Error) - if !ok { - t.Errorf("%s: got %v; want parser error", test.src, err) - continue - } - if perr.Msg != valid { - t.Errorf("%s: got msg = %q; want %q", test.src, perr.Msg, valid) - continue + pkg, err := Parse(base, strings.NewReader(test.src+"package p"), nil, nil, 0) + if err != nil { + t.Error(err) } - if got := perr.Pos.RelFilename(); got != test.filename { + if got := pkg.Pos().RelFilename(); got != test.filename { t.Errorf("%s: got filename = %q; want %q", test.src, got, test.filename) } - if got := perr.Pos.RelLine(); got != test.line { - t.Errorf("%s: got line = %d; want %d", test.src, got, test.line) - } } } diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go index 901984814ec933..e6caf676b3a4c6 100644 --- a/src/cmd/compile/internal/types2/api.go +++ b/src/cmd/compile/internal/types2/api.go @@ -437,6 +437,10 @@ type Instance struct { Type Type } +func (inst Instance) String() string { + return fmt.Sprintf("%s%s", inst.TypeArgs, inst.Type) +} + // An Initializer describes a package-level variable, or a list of variables in case // of a multi-valued initialization expression, and the corresponding initialization // expression. diff --git a/src/cmd/compile/internal/types2/typelists.go b/src/cmd/compile/internal/types2/typelists.go index a2aba4a9a553c9..b0633022ae2015 100644 --- a/src/cmd/compile/internal/types2/typelists.go +++ b/src/cmd/compile/internal/types2/typelists.go @@ -4,6 +4,8 @@ package types2 +import "bytes" + // TypeParamList holds a list of type parameters. type TypeParamList struct{ tparams []*TypeParam } @@ -24,6 +26,19 @@ func (l *TypeParamList) list() []*TypeParam { return l.tparams } +func (l *TypeParamList) String() string { + var buf bytes.Buffer + buf.WriteByte('[') + for i, tparam := range l.tparams { + if i > 0 { + buf.WriteString(", ") + } + WriteType(&buf, tparam, nil) + } + buf.WriteByte(']') + return buf.String() +} + // TypeList holds a list of types. type TypeList struct{ types []Type } @@ -52,6 +67,19 @@ func (l *TypeList) list() []Type { return l.types } +func (l *TypeList) String() string { + var buf bytes.Buffer + buf.WriteByte('[') + for i, t := range l.types { + if i > 0 { + buf.WriteString(", ") + } + WriteType(&buf, t, nil) + } + buf.WriteByte(']') + return buf.String() +} + // ---------------------------------------------------------------------------- // Implementation diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index de2c7be2e833f9..78d55bec559987 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -924,6 +924,7 @@ func runInstall(pkg string, ch chan struct{}) { "-D", "GOARCH_" + goarch, "-D", "GOOS_GOARCH_" + goos + "_" + goarch, "-p", pkg, + "-std", } if goarch == "mips" || goarch == "mipsle" { // Define GOMIPS_value from gomips. diff --git a/src/cmd/go.mod b/src/cmd/go.mod index a5bbc6b69013a6..df4fba11865b84 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -9,9 +9,9 @@ require ( golang.org/x/mod v0.36.1-0.20260513122029-343ee60345a1 golang.org/x/sync v0.20.0 golang.org/x/sys v0.44.0 - golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa + golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 golang.org/x/term v0.39.0 - golang.org/x/tools v0.44.1-0.20260414062052-55fb96ff894f + golang.org/x/tools v0.45.1-0.20260520205638-b38156a7a9f5 ) require ( diff --git a/src/cmd/go.sum b/src/cmd/go.sum index 6eb3c889306465..b8531a556a60ab 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -16,13 +16,13 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa h1:efT73AJZfAAUV7SOip6pWGkwJDzIGiKBZGVzHYa+ve4= -golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa/go.mod h1:kHjTxDEnAu6/Nl9lDkzjWpR+bmKfxeiRuSDlsMb70gE= +golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= +golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= -golang.org/x/tools v0.44.1-0.20260414062052-55fb96ff894f h1:OsDhJTPRMdqueEUhZ6K1sdC07K6rj9i4RYTQGF6zSHA= -golang.org/x/tools v0.44.1-0.20260414062052-55fb96ff894f/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.45.1-0.20260520205638-b38156a7a9f5 h1:jqdNq3qAaJT9zQL5Cbq/TRYEdoLZmystI2hoCyAsAuw= +golang.org/x/tools v0.45.1-0.20260520205638-b38156a7a9f5/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ= diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index 8c2ac0d46a8e1c..ab7cbe92210538 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -828,6 +828,9 @@ func TestAbstractOriginSanity(t *testing.T) { t.Skip("skipping test in short mode.") } + // TODO(go.dev/issue/79547): -l=4 builds are temporarily broken. + t.Skip("-l=4 builds are currently broken because they introduce an allocation in runtime.printfloat64") + mustHaveDWARF(t) abstractOriginSanity(t, "testdata/httptest", OptAllInl4) } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go index ed2284e6306a99..f80e393adbba00 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go @@ -10,6 +10,7 @@ import ( "fmt" "go/ast" "go/types" + "slices" "strings" "golang.org/x/tools/go/analysis" @@ -49,108 +50,88 @@ func init() { Analyzer.Flags.BoolVar(&whitelist, "whitelist", whitelist, "use composite white list; for testing only") } -// runUnkeyedLiteral checks if a composite literal is a struct literal with -// unkeyed fields. func run(pass *analysis.Pass) (any, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - nodeFilter := []ast.Node{ - (*ast.CompositeLit)(nil), - } - inspect.Preorder(nodeFilter, func(n ast.Node) { - cl := n.(*ast.CompositeLit) + for curLit := range inspect.Root().Preorder((*ast.CompositeLit)(nil)) { + complit := curLit.Node().(*ast.CompositeLit) + + // Skip empty or partly/fully keyed literals. + if len(complit.Elts) == 0 || + slices.ContainsFunc(complit.Elts, func(e ast.Expr) bool { return is[*ast.KeyValueExpr](e) }) { + continue + } - typ := pass.TypesInfo.Types[cl].Type + // Find struct type. + // (For a type parameter, choose an arbitrary term.) + typ := pass.TypesInfo.Types[complit].Type if typ == nil { - // cannot determine composite literals' type, skip it - return + continue // no type info } - typeName := typ.String() - if whitelist && unkeyedLiteral[typeName] { - // skip whitelisted types - return + terms, err := typeparams.NormalTerms(typ) + if err != nil || len(terms) == 0 { + continue // invalid or empty type } - var structuralTypes []types.Type - switch typ := types.Unalias(typ).(type) { - case *types.TypeParam: - terms, err := typeparams.StructuralTerms(typ) - if err != nil { - return // invalid type - } - for _, term := range terms { - structuralTypes = append(structuralTypes, term.Type()) - } - default: - structuralTypes = append(structuralTypes, typ) + t := terms[0].Type() + strct, ok := typeparams.Deref(t).Underlying().(*types.Struct) + if !ok { + continue // not a struct literal + } + if isSamePackageType(pass, t) { + continue // allow unkeyed literals for structs in same package } - for _, typ := range structuralTypes { - strct, ok := typeparams.Deref(typ).Underlying().(*types.Struct) - if !ok { - // skip non-struct composite literals - continue - } - if isLocalType(pass, typ) { - // allow unkeyed locally defined composite literal - continue - } + // Allow whitelisted types. + typeName := typ.String() + if whitelist && unkeyedLiteral[typeName] { + continue + } - // check if the struct contains an unkeyed field - allKeyValue := true - var suggestedFixAvailable = len(cl.Elts) == strct.NumFields() - var missingKeys []analysis.TextEdit - for i, e := range cl.Elts { - if _, ok := e.(*ast.KeyValueExpr); !ok { - allKeyValue = false - if i >= strct.NumFields() { - break - } - field := strct.Field(i) - if !field.Exported() { - // Adding unexported field names for structs not defined - // locally will not work. - suggestedFixAvailable = false - break - } - missingKeys = append(missingKeys, analysis.TextEdit{ - Pos: e.Pos(), - End: e.Pos(), - NewText: fmt.Appendf(nil, "%s: ", field.Name()), - }) + // If there is one value per field, + // offer to fill in the field names. + var fixes []analysis.SuggestedFix + if len(complit.Elts) == strct.NumFields() { + var edits []analysis.TextEdit + for i, elt := range complit.Elts { + field := strct.Field(i) + // We cannot fill in the name of an + // exported field from another package. + if !field.Exported() { + edits = nil + break } + edits = append(edits, analysis.TextEdit{ + Pos: elt.Pos(), + End: elt.Pos(), + NewText: fmt.Appendf(nil, "%s: ", field.Name()), + }) } - if allKeyValue { - // all the struct fields are keyed - continue - } - - diag := analysis.Diagnostic{ - Pos: cl.Pos(), - End: cl.End(), - Message: fmt.Sprintf("%s struct literal uses unkeyed fields", typeName), - } - if suggestedFixAvailable { - diag.SuggestedFixes = []analysis.SuggestedFix{{ + if edits != nil { + fixes = []analysis.SuggestedFix{{ Message: "Add field names to struct literal", - TextEdits: missingKeys, + TextEdits: edits, }} } - pass.Report(diag) - return } - }) + + pass.Report(analysis.Diagnostic{ + Pos: complit.Pos(), + End: complit.End(), + Message: fmt.Sprintf("%s struct literal uses unkeyed fields", typeName), + SuggestedFixes: fixes, + }) + } return nil, nil } -// isLocalType reports whether typ belongs to the same package as pass. -// TODO(adonovan): local means "internal to a function"; rename to isSamePackageType. -func isLocalType(pass *analysis.Pass, typ types.Type) bool { +// isSamePackageType reports whether typ belongs to the same package as pass. +func isSamePackageType(pass *analysis.Pass, typ types.Type) bool { switch x := types.Unalias(typ).(type) { case *types.Struct: // struct literals are local types return true case *types.Pointer: - return isLocalType(pass, x.Elem()) + return isSamePackageType(pass, x.Elem()) case interface{ Obj() *types.TypeName }: // *Named or *TypeParam (aliases were removed already) // names in package foo are local to foo_test too return x.Obj().Pkg() != nil && @@ -158,3 +139,8 @@ func isLocalType(pass *analysis.Pass, typ types.Type) bool { } return false } + +func is[T any](x any) bool { + _, ok := x.(T) + return ok +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go index f1465f73434fed..eb4373dafea1a3 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// The errorsas package defines an Analyzer that checks that the second argument to -// errors.As is a pointer to a type implementing error. +// Package errorsas defines an Analyzer that checks that the second argument to +// [errors.As] is a pointer to a type implementing error. package errorsas import ( @@ -19,7 +19,12 @@ import ( const Doc = `report passing non-pointer or non-error values to errors.As The errorsas analyzer reports calls to errors.As where the type -of the second argument is not a pointer to a type implementing error.` +of the second argument is not a pointer to a type implementing error. +For example: + + var unwrappedErr net.DNSError + errors.As(err, unwrappedErr) // should use &unwrappedErr, DNSError.Error has a pointer receiver +` var Analyzer = &analysis.Analyzer{ Name: "errorsas", diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/doc.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/doc.go index a3e98cb6572c7c..8b817a702900ba 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/doc.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/doc.go @@ -61,6 +61,12 @@ inliner machinery is capable of replacing f by a function literal, func(){...}(). However, the inline analyzer discards all such "literalizations" unconditionally, again on grounds of style.) +A call to a function F from its dedicated test (TestF) is not inlined, +since the purpose of the test is to exercise F itself, even when +it's a deprecated function to which other calls should be inlined. +This is not true for type aliases; see https://go.dev/issue/79271. +See further discussion in https://go.dev/issue/79272. + ## Constants Given a constant that is marked for inlining, like this one: diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go index efa2dcaf89ddb9..890f4adf6ed5f2 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go @@ -339,23 +339,19 @@ func (a *analyzer) inlineAlias(tn *types.TypeName, curId inspector.Cursor) { expr = curId.Node().(*ast.IndexListExpr) } - fieldType := curId - if fieldType.ParentEdgeKind() == edge.StarExpr_X { - fieldType = fieldType.Parent() - } - if fieldType.ParentEdgeKind() == edge.Field_Type { - field := fieldType.Parent().Node().(*ast.Field) - if len(field.Names) == 0 { - identicalName := false - if rhs, ok := alias.Rhs().(*types.Named); ok { - identicalName = alias.Obj().Name() == rhs.Obj().Name() - } - if !identicalName { - // Type is embedded, inlining the alias will cause - // the field name to be changed, which might break - // programs in terms of backwards compatibility. - return - } + // Reject inlining of a type alias used to declare an embedded + // struct field if doing so would change the field's name. + if v, ok := a.pass.TypesInfo.Defs[id].(*types.Var); ok && v.Embedded() { + identicalName := false + // TODO(adonovan): should we allow a pointer (type A = *pkg.A)? + if rhs, ok := alias.Rhs().(*types.Named); ok { + identicalName = alias.Obj().Name() == rhs.Obj().Name() + } + if !identicalName { + // Type is embedded, inlining the alias will cause + // the field name to be changed, which might break + // programs in terms of backwards compatibility. + return } } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go index ec0044b3074705..9df39fb23a7a96 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go @@ -19,14 +19,13 @@ import ( "golang.org/x/tools/internal/analysis/analyzerutil" typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" - "golang.org/x/tools/internal/goplsexport" "golang.org/x/tools/internal/refactor" "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" ) -var atomicTypesAnalyzer = &analysis.Analyzer{ +var AtomicTypesAnalyzer = &analysis.Analyzer{ Name: "atomictypes", Doc: analyzerutil.MustExtractDoc(doc, "atomictypes"), Requires: []*analysis.Analyzer{ @@ -37,11 +36,6 @@ var atomicTypesAnalyzer = &analysis.Analyzer{ URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#atomictypes", } -func init() { - // Export to gopls until this is a published modernizer. - goplsexport.AtomicTypesModernizer = atomicTypesAnalyzer -} - // TODO(mkalil): support the Pointer variants. // Consider the following function signatures for pointer loading: // func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go index e9a3a0d985c695..260dd50908c2e7 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go @@ -111,6 +111,31 @@ The any analyzer suggests replacing uses of the empty interface type, `interface{}`, with the `any` alias, which was introduced in Go 1.18. This is a purely stylistic change that makes code more readable. +# Analyzer embedlit + +embedlit: simplify references to embedded fields in composite literals + +The embedlit analyzer suggests removing redundant embedded field type specifiers +from composite literals. Go1.27 introduced the ability to directly initialize +fields promoted from embedded struct types without a nested literal. For +example, given the following structs: + + type T struct { + U + } + + type U struct { + x int + } + +A composite literal such as + + t := T{U: U{x: 1}} + +would become + + t := T{x: 1} + # Analyzer errorsastype errorsastype: replace errors.As with errors.AsType[T] @@ -142,6 +167,9 @@ The fmtappendf analyzer suggests replacing `[]byte(fmt.Sprintf(...))` with by Sprintf, making the code more efficient. The suggestion also applies to fmt.Sprint and fmt.Sprintln. +Since its fix is not a Pareto improvement, fmtappendf is disabled by default in +the `go fix` analyzer suite; see golang/go#77581. + # Analyzer forvar forvar: remove redundant re-declaration of loop variables @@ -424,6 +452,16 @@ It also handles variants using [strings.IndexByte] instead of Index, or the byte Fixes are offered only in cases in which there are no potential modifications of the idx, s, or substr expressions between their definition and use. +It also replaces [strings.SplitN](s, sep, 2)[0] and [strings.Split](s, sep)[0] with the "before" result of strings.Cut, when sep is a non-empty string constant: + + x := strings.SplitN(s, sep, 2)[0] + +is replaced by: + + x, _, _ := strings.Cut(s, sep) + +The fix is only offered when sep is a non-empty string literal. When sep is a variable or the empty string, the semantics differ (strings.Split(s, "")[0] returns the first character of s, but strings.Cut(s, "").before is ""), so no fix is suggested. + # Analyzer stringscutprefix stringscutprefix: replace HasPrefix/TrimPrefix with CutPrefix @@ -506,6 +544,9 @@ is replaced by: This avoids quadratic memory allocation and improves performance. +No diagnostics are issued in tests, where data sizes are often +small and asymptotic performance is not a security concern. + The analyzer requires that all references to s before the final uses are += operations. To avoid warning about trivial cases, at least one must appear within a loop. The variable s must be a local diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/embedlit.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/embedlit.go new file mode 100644 index 00000000000000..7d0b7e3b683f01 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/embedlit.go @@ -0,0 +1,406 @@ +// 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 modernize + +import ( + "bytes" + "fmt" + "go/ast" + "go/token" + "go/types" + "slices" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/edge" + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/analysis/analyzerutil" + typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" + "golang.org/x/tools/internal/astutil" + "golang.org/x/tools/internal/moreiters" + "golang.org/x/tools/internal/typesinternal/typeindex" + "golang.org/x/tools/internal/versions" +) + +var EmbedLitAnalyzer = &analysis.Analyzer{ + Name: "embedlit", + Doc: analyzerutil.MustExtractDoc(doc, "embedlit"), + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + typeindexanalyzer.Analyzer, + }, + Run: runEmbedLit, + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#embedlit", +} + +// Go1.27 introduced the ability to directly access embedded struct fields. +// The embedlit modernizer suggests two types of fixes that use this feature: +// 1. Removing redundant field type specifiers in embedded struct fields. +// 2. Moving embedded struct field assignments inside of the struct literal +// initialization. +func runEmbedLit(pass *analysis.Pass) (any, error) { + var ( + inspect = pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + index = pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index) + info = pass.TypesInfo + ) + for curLit := range inspect.Root().Preorder((*ast.CompositeLit)(nil)) { + if curLit.ParentEdgeKind() != edge.KeyValueExpr_Value { // non-nested comp lit + // TODO(mkalil): Figure out how to handle addition/removal of commas in + // the comp lit when we observe code where both patterns apply. (This will + // likely require a significant amount of work). For now, only apply edits + // from one pattern at a time. + if !embedlitUnnest(pass, info, curLit) { + err := embedlitCombine(pass, index, info, curLit) // calls pass.ReadFile + if err != nil { + return nil, err + } + } + } + } + return nil, nil +} + +// Pattern A: removing unneeded embedded field type specifier from the struct +// literal. +// T{U: U{f: v, ...}} => T{f: v, ...} +// It returns true if it reported a diagnostic with edits. +func embedlitUnnest(pass *analysis.Pass, info *types.Info, curLit inspector.Cursor) bool { + var ( + edits []analysis.TextEdit + names []string // names of the embedded field types that can be removed + lit = curLit.Node().(*ast.CompositeLit) + compLitType = info.TypeOf(lit) + ) + + // checkLit determines whether any of the fields in the given struct literal can + // be promoted, and calculates the corresponding edits. + var checkLit func(lit *ast.CompositeLit) + checkLit = func(lit *ast.CompositeLit) { + for _, elt := range lit.Elts { + // Can't promote an unkeyed field; would result in a syntax error. + if kv, ok := elt.(*ast.KeyValueExpr); ok { + if innerLit := isEmbeddedFieldLit(info, compLitType, kv); innerLit != nil { + // Emit edits to delete the unnecessary embedded field type specifier + // and its closing brace. + closingPos := innerLit.Rbrace + if len(innerLit.Elts) > 0 { + // Delete any inner trailing commas or white space. Extra trailing commas + // would result in invalid code. + closingPos = innerLit.Elts[len(innerLit.Elts)-1].End() + } + file := astutil.EnclosingFile(curLit) + // Enable modernizer only for Go1.27. + if !analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_27) { + return + } + // If any comments overlap with the range to delete, don't suggest a fix. + if !moreiters.Empty(astutil.Comments(file, kv.Pos(), innerLit.Lbrace+1)) || + !moreiters.Empty(astutil.Comments(file, closingPos, innerLit.Rbrace+1)) { + continue + } + edits = append(edits, []analysis.TextEdit{ + // T{U: U{f: v, ...}} + // ----- - + { + // Delete the key and the opening brace of the inner struct literal. + Pos: kv.Pos(), + End: innerLit.Lbrace + 1, + }, + { + // Delete the corresponding closing brace, including preceding + // white space or commas. Failing to delete trailing commas may + // result in invalid code. + Pos: closingPos, + End: innerLit.Rbrace + 1, + }, + }...) + names = append(names, kv.Key.(*ast.Ident).Name) + checkLit(innerLit) + } + } + } + } + checkLit(lit) + if len(edits) > 0 { + pass.Report(analysis.Diagnostic{ + Pos: curLit.Node().Pos(), + End: curLit.Node().End(), + Message: "embedded field type can be removed from struct literal", + SuggestedFixes: []analysis.SuggestedFix{ + { + Message: fmt.Sprintf("Remove embedded field type%s %s", cond(len(names) == 1, "", "s"), strings.Join(names, ", ")), + TextEdits: edits, + }, + }, + }) + return true + } + return false +} + +// Pattern B: moving embedded field assignments inside the struct literal +// initialization. +// t := T{...}; t.x = x => t := T{..., x: x} +// (or var t = ...) +func embedlitCombine(pass *analysis.Pass, index *typeindex.Index, info *types.Info, curLit inspector.Cursor) error { + compLit := curLit.Node().(*ast.CompositeLit) + if !moreiters.Every(slices.Values(compLit.Elts), func(e ast.Expr) bool { + return is[*ast.KeyValueExpr](e) + }) { + // Promoting additional embedded fields would result in mixing keyed and + // unkeyed fields, which isn't allowed. + return nil + } + var ( + // Ident for "t" in the assignment. + lhs *ast.Ident + // The cursor representing the statement that initializes the comp lit "t". + // We use its siblings to search for field assignments and verify that there + // are no intervening statements, in case those statements observe "t". + curStmt inspector.Cursor + ) + switch curLit.ParentEdgeKind() { + case edge.AssignStmt_Rhs: + assign := curLit.Parent().Node().(*ast.AssignStmt) + // TODO(mkalil): Handle lhs forms that aren't idents, i.e. x.y[i] = T{...}. + if id, ok := assign.Lhs[curLit.ParentEdgeIndex()].(*ast.Ident); ok { + lhs = id + curStmt = curLit.Parent() + } + case edge.ValueSpec_Values: + spec := curLit.Parent().Node().(*ast.ValueSpec) + lhs = spec.Names[curLit.ParentEdgeIndex()] + if decl, ok := moreiters.First(curLit.Enclosing((*ast.DeclStmt)(nil))); ok { + curStmt = decl + } + default: + return nil + } + + if lhs == nil || !curStmt.Valid() { + return nil + } + + var ( + tObj = info.ObjectOf(lhs) + // Marks the contiguous block of embedded field assign statements that will + // be moved into the struct initialization. + firstStmt, lastStmt inspector.Cursor + ) +stmtloop: + for { + var ok bool + curStmt, ok = curStmt.NextSibling() + if !ok { + break // end of (e.g.) block + } + // All embedded field value assignments must immediately follow the struct + // initialization. + assign, ok := curStmt.Node().(*ast.AssignStmt) + if !ok || len(assign.Lhs) != 1 || !(assign.Tok == token.ASSIGN || assign.Tok == token.DEFINE) { + // TODO(mkalil): handle multi-assignments like t.x, t.y = 1, 2 + break + } + expr := assign.Lhs[0] + sel, ok := expr.(*ast.SelectorExpr) + if !ok { + break + } + // Verify that sel.X refers to the same object as "t" + selXId, ok := sel.X.(*ast.Ident) + if !ok { + // TODO(mkalil): handle deeply nested expressions like t.B.x + break + } + obj := info.ObjectOf(selXId) + if obj != tObj { + break + } + rhsCur := curStmt.ChildAt(edge.AssignStmt_Rhs, 0) + if uses(index, rhsCur, tObj) { + break + } + for c := range rhsCur.Preorder((*ast.Ident)(nil)) { + id := c.Node().(*ast.Ident) + // If the rhs uses a value of t (e.g. t.x = t.y), don't suggest a fix because + // we can't evaluate t.y when constructing the new literal. + if info.ObjectOf(id) == tObj { + break stmtloop + } + // Note: we don't need to worry about expressions with side effects + // changing the behavior when moved inside the comp lit. The order of + // effects will be preserved because we preserve the order of the key + // value pairs inside the comp lit. + } + if !firstStmt.Valid() { + firstStmt = curStmt + } + lastStmt = curStmt + } + + if !firstStmt.Valid() { + return nil + } + + file := astutil.EnclosingFile(curLit) + // Enable modernizer only for Go1.27. + if !analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_27) { + return nil + } + + // Read file content to determine if the struct lit has a trailing comma + // after its last element. + tokFile := pass.Fset.File(compLit.Rbrace) + filename := tokFile.Name() + src, err := pass.ReadFile(filename) + if err != nil { + return err + } + + hasTrailingComma := false + if len(compLit.Elts) > 0 { + lastElt := compLit.Elts[len(compLit.Elts)-1] + lastEltOffset := tokFile.Offset(lastElt.End()) + rbraceOffset := tokFile.Offset(compLit.Rbrace) + hasTrailingComma = bytes.Contains(src[lastEltOffset:rbraceOffset], []byte(",")) + } + var edits []analysis.TextEdit + // Emit edits to move the field assignment into the struct lit while + // removing it from its current place. + // t := T{...}; t.x = v + // ----- --- - + // t := T{..., x: v} + + // Add a trailing comma before the closing brace of compLit if one doesn't + // exist, and delete the closing brace itself. + // t := T{...}; t.x = v + // - + // t := T{..., t.x = v + if len(compLit.Elts) > 0 && !hasTrailingComma { + edits = append(edits, analysis.TextEdit{ + Pos: compLit.Rbrace, + End: compLit.Rbrace + 1, + NewText: []byte(","), + }) + } else { + edits = append(edits, analysis.TextEdit{ + Pos: compLit.Rbrace, + End: compLit.Rbrace + 1, + }) + } + + // For each assignment: + // t.x = v + // -- --- + // x : v + curStmt = firstStmt + var prevStmt inspector.Cursor + for { + assign := curStmt.Node().(*ast.AssignStmt) + expr := assign.Lhs[0] + sel := expr.(*ast.SelectorExpr) + // Delete "t." + edits = append(edits, analysis.TextEdit{ + Pos: assign.Pos(), + End: sel.Sel.Pos(), + }) + // Replace "=" with ":" + edits = append(edits, analysis.TextEdit{ + Pos: expr.End(), + End: assign.TokPos + 1, + NewText: []byte(":"), + }) + + // Add a comma after the previous assignment if this is not the first one. + if prevStmt.Valid() { + edits = append(edits, analysis.TextEdit{ + Pos: prevStmt.Node().End(), + NewText: []byte(","), + }) + } + + // For the last assignment, add the closing brace of the struct lit. + if curStmt == lastStmt { + edits = append(edits, analysis.TextEdit{ + Pos: assign.End(), + NewText: []byte("}"), + }) + break + } + prevStmt = curStmt + curStmt, _ = curStmt.NextSibling() // can't fail because we break out of the loop when we hit lastStmt + } + + pass.Report(analysis.Diagnostic{ + Pos: curLit.Node().Pos(), + End: curLit.Node().End(), + Message: "embedded field assignment can be moved to struct literal", + SuggestedFixes: []analysis.SuggestedFix{ + { + Message: "Move embedded field assignment to struct literal", + TextEdits: edits, + }, + }, + }) + return nil +} + +// isEmbeddedFieldLit determines whether elt is a KeyValueExpr "T: T{...}" for +// an embedded field for which we can safely remove its type. +// If so, it returns the corresponding CompositeLit. +// If elt contains an unkeyed field or ambiguous type, it returns nil. +func isEmbeddedFieldLit(info *types.Info, topLevelType types.Type, kv *ast.KeyValueExpr) *ast.CompositeLit { + obj := keyedField(info, kv) + if obj == nil || !obj.Embedded() { + return nil + } + lit, ok := kv.Value.(*ast.CompositeLit) + if !ok || len(lit.Elts) == 0 { + // Skip if the struct literal is empty. + return nil + } + // We cannot remove this type if any of its nested composite elements have + // unkeyed fields or are ambiguous, so we check for those conditions before + // returning. + for _, elt := range lit.Elts { + kv, ok := elt.(*ast.KeyValueExpr) + if !ok { + return nil + } + obj := keyedField(info, kv) + if obj == nil { + return nil + } + k := kv.Key.(*ast.Ident) // can't fail + // Cannot promote an ambiguous type, for example: + // type T struct { A; B } + // type A struct { x int } + // type B struct { x int } + // _ = T{A: A{x: 1}} + // cannot be simplified to T{x: 1} because T has two different embedded fields called "x". + // We also reject composite literals with slice elements, as parentObj will be nil. + parentObj, _, _ := types.LookupFieldOrMethod(topLevelType, true, obj.Pkg(), k.Name) + if parentObj != obj { + return nil + } + } + return lit +} + +// keyedField reports whether the key of kv is an embedded field type. If so, it +// returns the type of the embedded field, otherwise it returns nil. +func keyedField(info *types.Info, kv *ast.KeyValueExpr) *types.Var { + k, ok := kv.Key.(*ast.Ident) + if !ok { + return nil + } + obj, ok := info.ObjectOf(k).(*types.Var) + if !ok || !obj.IsField() { + return nil + } + return obj +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go index 8603d54712125b..f52f202e22def9 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go @@ -17,14 +17,14 @@ import ( "golang.org/x/tools/internal/analysis/analyzerutil" typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" - "golang.org/x/tools/internal/goplsexport" + "golang.org/x/tools/internal/moreiters" "golang.org/x/tools/internal/refactor" "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" ) -var errorsastypeAnalyzer = &analysis.Analyzer{ +var ErrorsAsTypeAnalyzer = &analysis.Analyzer{ Name: "errorsastype", Doc: analyzerutil.MustExtractDoc(doc, "errorsastype"), URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#errorsastype", @@ -32,11 +32,6 @@ var errorsastypeAnalyzer = &analysis.Analyzer{ Run: errorsastype, } -func init() { - // Export to gopls until this is a published modernizer. - goplsexport.ErrorsAsTypeModernizer = errorsastypeAnalyzer -} - // errorsastype offers a fix to replace error.As with the newer // errors.AsType[T] following this pattern: // @@ -60,22 +55,15 @@ func init() { // // because the transformation in that case would be ungainly. // +// For the negated case (!errors.As), we use !ok instead. +// // Note that the cmd/vet suite includes the "errorsas" analyzer, which // detects actual mistakes in the use of errors.As. This logic does // not belong in errorsas because the problems it fixes are merely // stylistic. // // TODO(adonovan): support more cases: -// -// - Negative cases -// var myerr E -// if !errors.As(err, &myerr) { ... } -// => -// myerr, ok := errors.AsType[E](err) -// if !ok { ... } -// // - if myerr := new(E); errors.As(err, myerr); { ... } -// // - if errors.As(err, myerr) && othercond { ... } func errorsastype(pass *analysis.Pass) (any, error) { var ( @@ -89,7 +77,7 @@ func errorsastype(pass *analysis.Pass) (any, error) { continue // spread call: errors.As(pair()) } - v, curDeclStmt := canUseErrorsAsType(info, index, curCall) + v, curDeclStmt, curIfStmt := canUseErrorsAsType(info, index, curCall) if v == nil { continue } @@ -127,64 +115,82 @@ func errorsastype(pass *analysis.Pass) (any, error) { // Choose a name for the "ok" variable. // We generate a new name only if 'ok' is already declared at // curCall and it also used within the if-statement. - curIf := curCall.Parent() - ifScope := info.Scopes[curIf.Node().(*ast.IfStmt)] - okName := freshName(info, index, ifScope, v.Pos(), curCall, curIf, token.NoPos, "ok") + ifScope := info.Scopes[curIfStmt.Node().(*ast.IfStmt)] + negated := curCall.ParentEdgeKind() == edge.UnaryExpr_X // bool => Tok==NOT + okName := freshName(info, index, ifScope, v.Pos(), curCall, curIfStmt, token.NoPos, "ok") + // Because we reject any use of v outside the if statement, any use besides + // the argument in errors.As must lie inside the if statement. + usesV := moreiters.Len(index.Uses(v)) > 1 + + edits := append( + // delete "var myerr *MyErr" + refactor.DeleteStmt(pass.Fset.File(call.Fun.Pos()), curDeclStmt), + // if errors.As (err, &myerr) { ... } + // ------------- -------------- -------- ---- + // if myerr, ok := errors.AsType[*MyErr](err ); ok { ... } + analysis.TextEdit{ + // Insert "myerr, ok := " if myerr is used inside the if statement. + // Otherwise insert "_, ok := ". + Pos: call.Pos(), + End: call.Pos(), + NewText: fmt.Appendf(nil, "%s, %s := ", cond(usesV, v.Name(), "_"), okName), + }, + analysis.TextEdit{ + // replace As with AsType[T] + Pos: asIdent.Pos(), + End: asIdent.End(), + NewText: fmt.Appendf(nil, "AsType[%s]", errtype), + }, + analysis.TextEdit{ + // delete ", &myerr" + Pos: call.Args[0].End(), + End: call.Args[1].End(), + }, + analysis.TextEdit{ + // insert "; ok" for errors.AsType or "; !ok" for !errors.AsType + Pos: call.End(), + End: call.End(), + NewText: fmt.Appendf(nil, "; %s%s", cond(negated, "!", ""), okName), + }, + ) + if negated { + unaryExpr := curCall.Parent().Node().(*ast.UnaryExpr) + // delete "!" + edits = append(edits, analysis.TextEdit{ + Pos: unaryExpr.OpPos, + End: unaryExpr.X.Pos(), + }) + } pass.Report(analysis.Diagnostic{ Pos: call.Fun.Pos(), End: call.Fun.End(), Message: fmt.Sprintf("errors.As can be simplified using AsType[%s]", errtype), SuggestedFixes: []analysis.SuggestedFix{{ - Message: fmt.Sprintf("Replace errors.As with AsType[%s]", errtype), - TextEdits: append( - // delete "var myerr *MyErr" - refactor.DeleteStmt(pass.Fset.File(call.Fun.Pos()), curDeclStmt), - // if errors.As (err, &myerr) { ... } - // ------------- -------------- -------- ---- - // if myerr, ok := errors.AsType[*MyErr](err ); ok { ... } - analysis.TextEdit{ - // insert "myerr, ok := " - Pos: call.Pos(), - End: call.Pos(), - NewText: fmt.Appendf(nil, "%s, %s := ", v.Name(), okName), - }, - analysis.TextEdit{ - // replace As with AsType[T] - Pos: asIdent.Pos(), - End: asIdent.End(), - NewText: fmt.Appendf(nil, "AsType[%s]", errtype), - }, - analysis.TextEdit{ - // delete ", &myerr" - Pos: call.Args[0].End(), - End: call.Args[1].End(), - }, - analysis.TextEdit{ - // insert "; ok" - Pos: call.End(), - End: call.End(), - NewText: fmt.Appendf(nil, "; %s", okName), - }, - ), + Message: fmt.Sprintf("Replace errors.As with AsType[%s]", errtype), + TextEdits: edits, }}, }) } return nil, nil } -// canUseErrorsAsType reports whether curCall is a call to -// errors.As beneath an if statement, preceded by a -// declaration of the typed error var. The var must not be -// used outside the if statement. -func canUseErrorsAsType(info *types.Info, index *typeindex.Index, curCall inspector.Cursor) (_ *types.Var, _ inspector.Cursor) { - if curCall.ParentEdgeKind() != edge.IfStmt_Cond { - return // not beneath if statement +// canUseErrorsAsType reports whether curCall is a call to errors.As beneath an +// if statement, preceded by a declaration of the typed error var. The var must +// not be used outside the if statement. +// If the conditions are met, it returns the error var, the cursor for its +// DeclStmt, and the cursor for the IfStmt that contains the call to errors.As. +// Otherwise it returns a nil error var. +func canUseErrorsAsType(info *types.Info, index *typeindex.Index, curCall inspector.Cursor) (_ *types.Var, curDeclStmt, curIfStmt inspector.Cursor) { + curCond := curCall + if curCond.ParentEdgeKind() == edge.UnaryExpr_X { // if !errors.As(err, &v) + curCond = curCond.Parent() } - var ( - curIfStmt = curCall.Parent() - ifStmt = curIfStmt.Node().(*ast.IfStmt) - ) + if curCond.ParentEdgeKind() != edge.IfStmt_Cond { + return // not beneath if or unaryexpr + } + curIfStmt = curCond.Parent() + ifStmt := curIfStmt.Node().(*ast.IfStmt) if ifStmt.Init != nil { return // if statement already has an init part } @@ -228,5 +234,5 @@ func canUseErrorsAsType(info *types.Info, index *typeindex.Index, curCall inspec // ... // if errors.As(err, &v) { ... } // with no uses of v outside the IfStmt. - return v, curDecl.Parent() // DeclStmt + return v, curDecl.Parent(), curIfStmt // curDecl.Parent() is a DeclStmt } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go index 93aadf04a13fb5..9fd8657581ddd0 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go @@ -19,6 +19,7 @@ import ( typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" ) @@ -230,6 +231,7 @@ func minmax(pass *analysis.Pass) (any, error) { if compare, ok := ifStmt.Cond.(*ast.BinaryExpr); ok && ifStmt.Init == nil && isInequality(compare.Op) != 0 && + typesinternal.NoEffects(info, compare) && isAssignBlock(ifStmt.Body) { // a blank var has no type. if tLHS := info.TypeOf(ifStmt.Body.List[0].(*ast.AssignStmt).Lhs[0]); tLHS != nil && !maybeNaN(tLHS) { @@ -361,18 +363,18 @@ func canUseBuiltinMinMax(fn *types.Func, body *ast.BlockStmt) bool { return false } - return hasMinMaxLogic(body, fn.Name()) + return hasMinMaxLogic(body, fn.Name(), sig.Params().At(0).Name(), sig.Params().At(1).Name()) } // hasMinMaxLogic checks if the function body implements simple min/max logic. -func hasMinMaxLogic(body *ast.BlockStmt, funcName string) bool { +func hasMinMaxLogic(body *ast.BlockStmt, funcName, param0, param1 string) bool { // Pattern 1: Single if/else statement if len(body.List) == 1 { if ifStmt, ok := body.List[0].(*ast.IfStmt); ok { // Get the "false" result from the else block if elseBlock, ok := ifStmt.Else.(*ast.BlockStmt); ok && len(elseBlock.List) == 1 { if elseRet, ok := elseBlock.List[0].(*ast.ReturnStmt); ok && len(elseRet.Results) == 1 { - return checkMinMaxPattern(ifStmt, elseRet.Results[0], funcName) + return checkMinMaxPattern(ifStmt, elseRet.Results[0], funcName, param0, param1) } } } @@ -382,7 +384,7 @@ func hasMinMaxLogic(body *ast.BlockStmt, funcName string) bool { if len(body.List) == 2 { if ifStmt, ok := body.List[0].(*ast.IfStmt); ok && ifStmt.Else == nil { if retStmt, ok := body.List[1].(*ast.ReturnStmt); ok && len(retStmt.Results) == 1 { - return checkMinMaxPattern(ifStmt, retStmt.Results[0], funcName) + return checkMinMaxPattern(ifStmt, retStmt.Results[0], funcName, param0, param1) } } } @@ -394,7 +396,8 @@ func hasMinMaxLogic(body *ast.BlockStmt, funcName string) bool { // ifStmt: the if statement to check // falseResult: the expression returned when the condition is false // funcName: "min" or "max" -func checkMinMaxPattern(ifStmt *ast.IfStmt, falseResult ast.Expr, funcName string) bool { +// param0, param1: the two parameter names for the function. +func checkMinMaxPattern(ifStmt *ast.IfStmt, falseResult ast.Expr, funcName, param0, param1 string) bool { // Must have condition with comparison cmp, ok := ifStmt.Cond.(*ast.BinaryExpr) if !ok { @@ -417,10 +420,24 @@ func checkMinMaxPattern(ifStmt *ast.IfStmt, falseResult ast.Expr, funcName strin return false // Not a comparison operator } - t := thenRet.Results[0] // "true" result - f := falseResult // "false" result - x := cmp.X // left operand - y := cmp.Y // right operand + t := thenRet.Results[0] // "true" result + f := falseResult // "false" result + x, ok := cmp.X.(*ast.Ident) // left operand + if !ok { + return false // Not a basic min/max comparison + } + y, ok := cmp.Y.(*ast.Ident) // right operand + if !ok { + return false // Not a basic min/max comparison + } + + // Check that the min max algorithm uses the function's params + // Which param corresponds to which part of the operation doesn't matter, + // so we have to try both. + if !(param0 == x.Name && param1 == y.Name || + param0 == y.Name && param1 == x.Name) { + return false + } // Check operand order and adjust sign accordingly if astutil.EqualSyntax(t, x) && astutil.EqualSyntax(f, y) { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go index db3b86fbf4c8a8..80491273b5d5f9 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go @@ -17,7 +17,6 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/edge" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/internal/analysis/analyzerutil" "golang.org/x/tools/internal/refactor" @@ -35,24 +34,26 @@ var doc string // Suite lists all modernize analyzers. var Suite = []*analysis.Analyzer{ AnyAnalyzer, - atomicTypesAnalyzer, + AtomicTypesAnalyzer, // AppendClippedAnalyzer, // not nil-preserving! // BLoopAnalyzer, // may skew benchmark results, see golang/go#74967 - FmtAppendfAnalyzer, + EmbedLitAnalyzer, + ErrorsAsTypeAnalyzer, + // FmtAppendfAnalyzer, // makes code less clear, see golang/go#77581 ForVarAnalyzer, MapsLoopAnalyzer, MinMaxAnalyzer, NewExprAnalyzer, OmitZeroAnalyzer, - plusBuildAnalyzer, + PlusBuildAnalyzer, RangeIntAnalyzer, ReflectTypeForAnalyzer, - slicesbackwardAnalyzer, + slicesBackwardAnalyzer, SlicesContainsAnalyzer, // SlicesDeleteAnalyzer, // not nil-preserving! SlicesSortAnalyzer, - stditeratorsAnalyzer, - stringscutAnalyzer, + StdIteratorsAnalyzer, + StringsCutAnalyzer, StringsCutPrefixAnalyzer, StringsSeqAnalyzer, StringsBuilderAnalyzer, @@ -121,15 +122,6 @@ func within(pass *analysis.Pass, pkgs ...string) bool { moreiters.Contains(stdlib.Dependencies(pkgs...), path) } -// unparenEnclosing removes enclosing parens from cur in -// preparation for a call to [Cursor.ParentEdge]. -func unparenEnclosing(cur inspector.Cursor) inspector.Cursor { - for cur.ParentEdgeKind() == edge.ParenExpr_X { - cur = cur.Parent() - } - return cur -} - var ( builtinAny = types.Universe.Lookup("any") builtinAppend = types.Universe.Lookup("append") diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go index ff6c90551e9b91..574ce0a899051b 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go @@ -11,22 +11,16 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/internal/analysis/analyzerutil" - "golang.org/x/tools/internal/goplsexport" "golang.org/x/tools/internal/versions" ) -var plusBuildAnalyzer = &analysis.Analyzer{ +var PlusBuildAnalyzer = &analysis.Analyzer{ Name: "plusbuild", Doc: analyzerutil.MustExtractDoc(doc, "plusbuild"), URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#plusbuild", Run: plusbuild, } -func init() { - // Export to gopls until this is a published modernizer. - goplsexport.PlusBuildModernizer = plusBuildAnalyzer -} - func plusbuild(pass *analysis.Pass) (any, error) { check := func(f *ast.File) { // "//go:build" directives were added in go1.17, but diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go index 7c529c97da97d8..f7cb965f366aea 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go @@ -356,16 +356,9 @@ func isScalarLvalue(info *types.Info, curId inspector.Cursor) bool { // as it is always true for a variable even when that variable is // used only as an r-value. So we must inspect enclosing syntax. - cur := curId + cur := astutil.UnparenEnclosingCursor(curId) - // Strip enclosing parens. - ek := cur.ParentEdgeKind() - for ek == edge.ParenExpr_X { - cur = cur.Parent() - ek = cur.ParentEdgeKind() - } - - switch ek { + switch cur.ParentEdgeKind() { case edge.AssignStmt_Lhs: assign := cur.Parent().Node().(*ast.AssignStmt) if assign.Tok != token.DEFINE { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go index fb1f2e2efca591..959939b5a2a4c6 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go @@ -65,9 +65,9 @@ func reflecttypefor(pass *analysis.Pass) (any, error) { // Special cases for TypeOf((*T)(nil)).Elem(), and // TypeOf([]T(nil)).Elem(), needed when T is an interface type. if curCall.ParentEdgeKind() == edge.SelectorExpr_X { - curSel := unparenEnclosing(curCall).Parent() + curSel := astutil.UnparenEnclosingCursor(curCall).Parent() if curSel.ParentEdgeKind() == edge.CallExpr_Fun { - call2 := unparenEnclosing(curSel).Parent().Node().(*ast.CallExpr) // potentially .Elem() + call2 := astutil.UnparenEnclosingCursor(curSel).Parent().Node().(*ast.CallExpr) // potentially .Elem() obj := typeutil.Callee(info, call2) if typesinternal.IsMethodNamed(obj, "reflect", "Type", "Elem") { // reflect.TypeOf(expr).Elem() diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesbackward.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesbackward.go index 293a7c0c3fbd4e..305b5e5b149141 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesbackward.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesbackward.go @@ -9,6 +9,7 @@ import ( "go/ast" "go/token" "go/types" + "strings" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" @@ -23,7 +24,7 @@ import ( "golang.org/x/tools/internal/versions" ) -var slicesbackwardAnalyzer = &analysis.Analyzer{ +var slicesBackwardAnalyzer = &analysis.Analyzer{ Name: "slicesbackward", Doc: analyzerutil.MustExtractDoc(doc, "slicesbackward"), Requires: []*analysis.Analyzer{ @@ -36,7 +37,7 @@ var slicesbackwardAnalyzer = &analysis.Analyzer{ func init() { // Export to gopls until this is a published modernizer. - goplsexport.SlicesBackwardModernizer = slicesbackwardAnalyzer + goplsexport.SlicesBackwardModernizer = slicesBackwardAnalyzer } // slicesbackward offers a fix to replace a manually-written backward loop: @@ -147,18 +148,47 @@ func slicesbackward(pass *analysis.Pass) (any, error) { // s[i] — pure element accesses that can be replaced by the value var // other — index used for non-indexing purposes var ( - sliceIndexes []*ast.IndexExpr - otherUses int + // First assignment in the loop body of the form "name := s[i]"; or nil. + firstSliceIdxAssign *ast.AssignStmt + // List of s[i] expressions to replace by the value var (excludes firstSliceIdxAssign, which will be entirely removed). + sliceIdxsReplace []*ast.IndexExpr + // Total count of s[i] usages. + sliceIdxs int + // Non-indexing uses of i. + otherUses int ) for curUse := range index.Uses(indexObj) { if !bodyCur.Contains(curUse) { continue } // Is i in the Index position of an s[i] expression? + // If so, we also need to check whether s[i] is an lvalue. If we're + // mutating the slice or taking an element's address, a fix will not + // be offered. if curUse.ParentEdgeKind() == edge.IndexExpr_Index { - idxExpr := curUse.Parent().Node().(*ast.IndexExpr) + if isScalarLvalue(pass.TypesInfo, curUse.Parent()) { + continue nextLoop + } + idxCur := curUse.Parent() + idxExpr := idxCur.Node().(*ast.IndexExpr) if astutil.EqualSyntax(idxExpr.X, sliceExpr) { - sliceIndexes = append(sliceIndexes, idxExpr) + sliceIdxs++ + // If the current statement is the first in the body of the form + // "name := s[i]", save it so we can use "name" as the value + // variable in slices.Backward. We can also remove the entire assign + // statement. + if firstSliceIdxAssign == nil && idxCur.ParentEdgeKind() == edge.AssignStmt_Rhs { + assignStmt := idxCur.Parent().Node().(*ast.AssignStmt) + if len(assignStmt.Lhs) == 1 && assignStmt.Tok == token.DEFINE { + // The condition above implies that assignStmt.Lhs[0] is a valid + // identifier. + firstSliceIdxAssign = assignStmt + // We don't need to replace the index expr with the value variable + // name if we are going to remove the entire assignment. + continue + } + } + sliceIdxsReplace = append(sliceIdxsReplace, idxExpr) continue } } @@ -167,15 +197,18 @@ func slicesbackward(pass *analysis.Pass) (any, error) { // Build the suggested fix. // - // for i := len(s) - 1; i >= 0; i-- { ... s[i] ... } - // ---------------------------- ---- - // _, v := range slices.Backward(s) v + // for i := len(s) - 1; i >= 0; i-- { ... s[i] ... } + // -------------------------------- ---- + // for _, v := range slices.Backward(s) { ... v ... } sliceStr := astutil.Format(pass.Fset, sliceExpr) prefix, edits := refactor.AddImport(info, file, "slices", "slices", "Backward", loop.Pos()) - elemName := freshName(info, index, info.Scopes[loop], loop.Pos(), bodyCur, bodyCur, token.NoPos, "v") + elemName := chooseValueName(firstSliceIdxAssign, sliceStr) + elemName = freshName(info, index, info.Scopes[loop], loop.Pos(), bodyCur, bodyCur, token.NoPos, elemName) - // Replace each s[i] with elemName. - for _, sx := range sliceIndexes { + // Replace each s[i] with elemName (except for in the statement of the + // form "name := s[i]" where we might have gotten elemName from - we will + // delete this entire statement instead). + for _, sx := range sliceIdxsReplace { edits = append(edits, analysis.TextEdit{ Pos: sx.Pos(), End: sx.End(), @@ -183,17 +216,27 @@ func slicesbackward(pass *analysis.Pass) (any, error) { }) } - // Replace the loop header with a range over slices.Backward. - var header string - if otherUses == 0 && len(sliceIndexes) > 0 { + if firstSliceIdxAssign != nil { + edits = append(edits, analysis.TextEdit{ + Pos: firstSliceIdxAssign.Pos(), + End: firstSliceIdxAssign.End(), + }) + } + + // Replace the loop header with a range over slices.Backward. In + // well-typed code, at least one of the index or value variables must be + // referenced inside the loop body (otherUses + sliceIndexes > 0). + var vars string + if otherUses == 0 { // sliceIdxs > 0 // All uses of i are s[i]; drop the index variable. - header = fmt.Sprintf("_, %s := range %sBackward(%s)", - elemName, prefix, sliceStr) - } else { - // i is used for other purposes; keep both index and value. - header = fmt.Sprintf("%s, %s := range %sBackward(%s)", - indexIdent.Name, elemName, prefix, sliceStr) + vars = fmt.Sprintf("_, %s", elemName) + } else if sliceIdxs == 0 { // otherUses > 0 + // Index i is not used in any s[i] expressions; drop the value variable. + vars = indexIdent.Name + } else { // otherUses > 0 && sliceIdxs > 0, keep both variables. + vars = fmt.Sprintf("%s, %s", indexIdent.Name, elemName) } + header := fmt.Sprintf("%s := range %sBackward(%s)", vars, prefix, sliceStr) edits = append(edits, analysis.TextEdit{ Pos: loop.Init.Pos(), End: loop.Post.End(), @@ -213,3 +256,20 @@ func slicesbackward(pass *analysis.Pass) (any, error) { } return nil, nil } + +// chooseValueName uses a heuristic to generate a name for the value variable in +// the call to slices.Backward. +func chooseValueName(assign *ast.AssignStmt, sliceStr string) string { + if assign != nil { + return assign.Lhs[0].(*ast.Ident).Name + } + // Heuristic: remove plural s suffix from slice var + // if present, otherwise use first letter. + if token.IsIdentifier(sliceStr) && len(sliceStr) > 1 { + if single, ok := strings.CutSuffix(sliceStr, "s"); ok { + return single + } + return sliceStr[:1] // first letter (assuming ASCII) + } + return "v" +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go index c1d42188f61dae..ed75e05e9a92b3 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go @@ -19,6 +19,7 @@ import ( "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/refactor" "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" ) @@ -58,12 +59,8 @@ var SlicesContainsAnalyzer = &analysis.Analyzer{ // statement is "found = false" (or vice versa), the // loop becomes "found = [!]slices.Contains(...)". // -// It may change cardinality of effects of the "needle" expression. -// (Mostly this appears to be a desirable optimization, avoiding -// redundantly repeated evaluation.) -// -// TODO(adonovan): Add a check that needle/predicate expression from -// if-statement has no effects. Now the program behavior may change. +// It rejects candidates whose needle/predicate expression from the if-statement +// has side effects to avoid changes in program behavior. func slicescontains(pass *analysis.Pass) (any, error) { // Skip the analyzer in packages where its // fixes would create an import cycle. @@ -174,6 +171,11 @@ func slicescontains(pass *analysis.Pass) (any, error) { return } + // Reject if needle/predicate expression has side effects. + if !typesinternal.NoEffects(info, arg2) { + return + } + // Reject if the body, needle or predicate references either range variable. usesRangeVar := func(n ast.Node) bool { cur, ok := curRange.FindNode(n) diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go index 86e1c8fd421b6e..19532686387f71 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go @@ -17,12 +17,11 @@ import ( "golang.org/x/tools/internal/analysis/analyzerutil" typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" - "golang.org/x/tools/internal/goplsexport" "golang.org/x/tools/internal/stdlib" "golang.org/x/tools/internal/typesinternal/typeindex" ) -var stditeratorsAnalyzer = &analysis.Analyzer{ +var StdIteratorsAnalyzer = &analysis.Analyzer{ Name: "stditerators", Doc: analyzerutil.MustExtractDoc(doc, "stditerators"), Requires: []*analysis.Analyzer{ @@ -32,11 +31,6 @@ var stditeratorsAnalyzer = &analysis.Analyzer{ URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stditerators", } -func init() { - // Export to gopls until this is a published modernizer. - goplsexport.StdIteratorsModernizer = stditeratorsAnalyzer -} - // stditeratorsTable records std types that have legacy T.{Len,At} // iteration methods as well as a newer T.All method that returns an // iter.Seq. diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go index e89baa9b0e90a3..6aa9c881a32c4b 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go @@ -13,6 +13,7 @@ import ( "go/types" "maps" "slices" + "strings" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" @@ -47,6 +48,7 @@ func stringsbuilder(pass *analysis.Pass) (any, error) { var ( inspect = pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) index = pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index) + info = pass.TypesInfo ) // Gather all local string variables that appear on the @@ -55,7 +57,7 @@ func stringsbuilder(pass *analysis.Pass) (any, error) { for curAssign := range inspect.Root().Preorder((*ast.AssignStmt)(nil)) { assign := curAssign.Node().(*ast.AssignStmt) if assign.Tok == token.ADD_ASSIGN && is[*ast.Ident](assign.Lhs[0]) { - if v, ok := pass.TypesInfo.Uses[assign.Lhs[0].(*ast.Ident)].(*types.Var); ok && + if v, ok := info.Uses[assign.Lhs[0].(*ast.Ident)].(*types.Var); ok && v.Kind() == types.LocalVar && types.Identical(v.Type(), builtinString.Type()) { candidates[v] = true @@ -75,7 +77,7 @@ func stringsbuilder(pass *analysis.Pass) (any, error) { // Now check each candidate variable's decl and uses. nextcand: for _, v := range slices.SortedFunc(maps.Keys(candidates), lexicalOrder) { - var edits []analysis.TextEdit + var edits, postEdits []analysis.TextEdit // postEdits are emitted last // Check declaration of s has one of these forms: // @@ -100,6 +102,13 @@ nextcand: if file == lastEditFile && v.Pos() < lastEditEnd { continue } + filename := pass.Fset.File(file.FileStart).Name() + // Suppress diagnostics in test files, where suggested fixes may increase + // verbosity, and performance doesn't matter as much. + // See https://go.dev/issue/78613 + if strings.HasSuffix(filename, "_test.go") { + continue + } ek := def.ParentEdgeKind() if ek == edge.AssignStmt_Lhs && @@ -121,10 +130,10 @@ nextcand: // Add strings import. prefix, importEdits := refactor.AddImport( - pass.TypesInfo, astutil.EnclosingFile(def), "strings", "strings", "Builder", v.Pos()) + info, astutil.EnclosingFile(def), "strings", "strings", "Builder", v.Pos()) edits = append(edits, importEdits...) - if isEmptyString(pass.TypesInfo, assign.Rhs[0]) { + if isEmptyString(info, assign.Rhs[0]) { // s := "" // --------------------- // var s strings.Builder @@ -171,7 +180,7 @@ nextcand: // Add strings import. prefix, importEdits := refactor.AddImport( - pass.TypesInfo, astutil.EnclosingFile(def), "strings", "strings", "Builder", v.Pos()) + info, astutil.EnclosingFile(def), "strings", "strings", "Builder", v.Pos()) edits = append(edits, importEdits...) spec := def.Parent().Node().(*ast.ValueSpec) @@ -193,7 +202,7 @@ nextcand: NewText: fmt.Appendf(nil, " %sBuilder", prefix), }) - if len(spec.Values) > 0 && !isEmptyString(pass.TypesInfo, spec.Values[0]) { + if len(spec.Values) > 0 && !isEmptyString(info, spec.Values[0]) { if decl.Rparen.IsValid() { // var decl with explicit parens: // @@ -273,11 +282,8 @@ nextcand: ) for curUse := range index.Uses(v) { // Strip enclosing parens around Ident. + curUse = astutil.UnparenEnclosingCursor(curUse) ek := curUse.ParentEdgeKind() - for ek == edge.ParenExpr_X { - curUse = curUse.Parent() - ek = curUse.ParentEdgeKind() - } // intervening reports whether cur has an ancestor of // one of the given types that is within the scope of v. @@ -315,20 +321,21 @@ nextcand: // s += expr // ------------- - // s.WriteString(expr) - edits = append(edits, []analysis.TextEdit{ + edits = append(edits, analysis.TextEdit{ // replace " += " with ".WriteString(" - { - Pos: assign.Lhs[0].End(), - End: assign.Rhs[0].Pos(), - NewText: []byte(".WriteString("), - }, + Pos: assign.Lhs[0].End(), + End: assign.Rhs[0].Pos(), + NewText: []byte(".WriteString("), + }) + + // Delay inserting the closing parenthesis, in case it overlaps with a + // .String() edit, since it would need to come after. + postEdits = append(postEdits, analysis.TextEdit{ // insert ")" - { - Pos: assign.End(), - End: assign.End(), - NewText: []byte(")"), - }, - }...) + Pos: assign.End(), + End: assign.End(), + NewText: []byte(")"), + }) } else if ek == edge.UnaryExpr_X && curUse.Parent().Node().(*ast.UnaryExpr).Op == token.AND { @@ -357,6 +364,8 @@ nextcand: continue nextcand // no += in a loop; reject } + edits = append(edits, postEdits...) + lastEditFile = file lastEditEnd = edits[len(edits)-1].End diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go index 6192c56fa34041..ae93d4d3f26b9b 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go @@ -21,14 +21,13 @@ import ( "golang.org/x/tools/internal/analysis/analyzerutil" typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" - "golang.org/x/tools/internal/goplsexport" "golang.org/x/tools/internal/moreiters" "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" ) -var stringscutAnalyzer = &analysis.Analyzer{ +var StringsCutAnalyzer = &analysis.Analyzer{ Name: "stringscut", Doc: analyzerutil.MustExtractDoc(doc, "stringscut"), Requires: []*analysis.Analyzer{ @@ -39,11 +38,6 @@ var stringscutAnalyzer = &analysis.Analyzer{ URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringscut", } -func init() { - // Export to gopls until this is a published modernizer. - goplsexport.StringsCutModernizer = stringscutAnalyzer -} - // stringscut offers a fix to replace an occurrence of strings.Index{,Byte} with // strings.{Cut,Contains}, and similar fixes for functions in the bytes package. // Consider some candidate for replacement i := strings.Index(s, substr). @@ -92,7 +86,7 @@ func init() { // } // // If the condition involving `i` is equivalent to i >= 0, then we replace it with -// `if ok“. +// `if ok`. // If the condition is negated (e.g. equivalent to `i < 0`), we use `if !ok` instead. // If the slices of `s` match `s[:i]` or `s[i+len(substr):]` or their variants listed above, // then we replace them with before and after. @@ -124,6 +118,8 @@ func stringscut(pass *analysis.Pass) (any, error) { bytesIndexByte = index.Object("bytes", "IndexByte") ) + stringsplitCut(pass, index) + scopeFixCount := make(map[*types.Scope]int) // the number of times we have offered a fix within a given scope in the current pass for _, obj := range []types.Object{ @@ -149,11 +145,20 @@ func stringscut(pass *analysis.Pass) (any, error) { switch ek, idx := curCall.ParentEdge(); ek { case edge.ValueSpec_Values: // Have: var i = strings.Index(...) + // If the call occurs in a multi-value declaration or assignment, don't suggest a fix because it would produce invalid code (See golang/go#78643). + spec := curCall.Parent().Node().(*ast.ValueSpec) + if len(spec.Names) != 1 { + continue + } curName := curCall.Parent().ChildAt(edge.ValueSpec_Names, idx) iIdent = curName.Node().(*ast.Ident) case edge.AssignStmt_Rhs: // Have: i := strings.Index(...) // (Must be i's definition.) + assign := curCall.Parent().Node().(*ast.AssignStmt) + if len(assign.Lhs) != 1 { + continue + } curLhs := curCall.Parent().ChildAt(edge.AssignStmt_Lhs, idx) iIdent, _ = curLhs.Node().(*ast.Ident) // may be nil } @@ -367,6 +372,129 @@ func stringscut(pass *analysis.Pass) (any, error) { return nil, nil } +// stringsplitCut reports patterns where strings.Split or strings.SplitN with +// n=2 is immediately indexed at [0], which can be simplified to strings.Cut, +// when sep is a non-empty string constant. The transformation is +// semantics-preserving only for non-empty sep: strings.Split(s, "")[0] +// returns the first character of s, but strings.Cut(s, "").before is "". +// For variable sep the value is unknown at analysis time, so we conservatively +// skip those cases too. +// +// For example: +// +// x := strings.SplitN(s, ",", 2)[0] +// ------ -- +// x, _, _ := strings.Cut(s, ",") +// +// Requires Go 1.18 (when strings.Cut was added). +func stringsplitCut(pass *analysis.Pass, index *typeindex.Index) { + info := pass.TypesInfo + + stringsSplit := index.Object("strings", "Split") + stringsSplitN := index.Object("strings", "SplitN") + + for _, obj := range []types.Object{stringsSplit, stringsSplitN} { + for curCall := range index.Calls(obj) { + callExpr := curCall.Node().(*ast.CallExpr) + + // For SplitN, the third argument must be the integer constant 2. + if obj.Name() == "SplitN" && !isIntLiteral(info, callExpr.Args[2], 2) { + continue + } + + // Sep must be a non-empty constant string. + // strings.Split(s, "")[0] returns the first character of s, but + // strings.Cut(s, "").before is "", so the semantics differ for + // an empty sep. For a variable sep we cannot rule out "" at + // analysis time, so we conservatively skip those cases too. + sepTV := info.Types[callExpr.Args[1]] + if sepTV.Value == nil || constant.StringVal(sepTV.Value) == "" { + continue + } + + // The call must be the X of an IndexExpr. + if curCall.ParentEdgeKind() != edge.IndexExpr_X { + continue + } + parent := curCall.Parent() + indexExpr := parent.Node().(*ast.IndexExpr) + + // The index must be the integer constant 0. + if !isZeroIntConst(info, indexExpr.Index) { + continue + } + + // The IndexExpr must be the sole RHS of an assignment statement. + if parent.ParentEdgeKind() != edge.AssignStmt_Rhs { + continue + } + assign := parent.Parent().Node().(*ast.AssignStmt) + if assign.Tok != token.DEFINE || len(assign.Lhs) != 1 { + continue + } + + // The LHS must be a single non-blank identifier. + lhsIdent, ok := assign.Lhs[0].(*ast.Ident) + if !ok || lhsIdent.Name == "_" { + continue + } + + // strings.Cut requires Go 1.18. + if !analyzerutil.FileUsesGoVersion(pass, astutil.EnclosingFile(curCall), versions.Go1_18) { + continue + } + + // Build the fix. + // + // x := strings.SplitN(s, sep, 2)[0] + // --- ------ --- + // x, _, _ := strings.Cut(s, sep) + callFunIdent := typesinternal.UsedIdent(info, callExpr.Fun) + + var edits []analysis.TextEdit + + // LHS: insert ", _, _" after x + edits = append(edits, analysis.TextEdit{ + Pos: lhsIdent.End(), + End: lhsIdent.End(), + NewText: []byte(", _, _"), + }) + + // Function name: Split/SplitN → Cut + edits = append(edits, analysis.TextEdit{ + Pos: callFunIdent.Pos(), + End: callFunIdent.End(), + NewText: []byte("Cut"), + }) + + // For SplitN: remove the ", 2" third argument. + if obj.Name() == "SplitN" { + edits = append(edits, analysis.TextEdit{ + Pos: callExpr.Args[1].End(), // after sep + End: callExpr.Rparen, // before ) + }) + } + + // Remove the "[0]" index expression. + edits = append(edits, analysis.TextEdit{ + Pos: indexExpr.Lbrack, + End: indexExpr.End(), + }) + + pass.Report(analysis.Diagnostic{ + Pos: callExpr.Fun.Pos(), + End: callExpr.Fun.End(), + Message: fmt.Sprintf("strings.%s call can be simplified using strings.Cut", obj.Name()), + Category: "stringscut", + SuggestedFixes: []analysis.SuggestedFix{{ + Message: fmt.Sprintf("Simplify strings.%s call using strings.Cut", obj.Name()), + TextEdits: edits, + }}, + }) + } + } +} + // indexArgValid reports whether expr is a valid strings.Index(_, _) arg // for the transformation. An arg is valid iff it is: // - constant; @@ -387,10 +515,10 @@ func indexArgValid(info *types.Info, index *typeindex.Index, expr ast.Expr, afte case *ast.Ident: sObj := info.Uses[expr] sUses := index.Uses(sObj) - return !hasModifyingUses(info, sUses, afterPos) + return !hasModifyingUses(sUses, afterPos) default: // For now, skip instances where s or substr are not - // identifers, basic lits, or call expressions of the form + // identifiers, basic lits, or call expressions of the form // []byte(s). // TODO(mkalil): Handle s and substr being expressions like ptr.field[i]. // From adonovan: We'd need to analyze s and substr to see @@ -487,18 +615,15 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a // hasModifyingUses reports whether any of the uses involve potential // modifications. Uses involving assignments before the "afterPos" won't be // considered. -func hasModifyingUses(info *types.Info, uses iter.Seq[inspector.Cursor], afterPos token.Pos) bool { +func hasModifyingUses(uses iter.Seq[inspector.Cursor], afterPos token.Pos) bool { for curUse := range uses { ek := curUse.ParentEdgeKind() if ek == edge.AssignStmt_Lhs { if curUse.Node().Pos() <= afterPos { continue } - assign := curUse.Parent().Node().(*ast.AssignStmt) - if sameObject(info, assign.Lhs[0], curUse.Node().(*ast.Ident)) { - // Modifying use because we are reassigning the value of the object. - return true - } + // Any use on the LHS is a modifying use. + return true } else if ek == edge.UnaryExpr_X && curUse.Parent().Node().(*ast.UnaryExpr).Op == token.AND { // Modifying use because we might be passing the object by reference (an explicit &). diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go index 13065312732cdb..79859a536d03f0 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go @@ -416,7 +416,7 @@ func match(info *types.Info, arg ast.Expr, param *types.Var) bool { } // propagate propagates changes in wrapper (non-None) kind information backwards -// through through the wrapper.callers graph of well-formed forwarding calls. +// through the wrapper.callers graph of well-formed forwarding calls. func propagate(pass *analysis.Pass, w *wrapper, call *ast.CallExpr, kind Kind, res *Result) { // Check correct call forwarding. // diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/edge/edge.go b/src/cmd/vendor/golang.org/x/tools/go/ast/edge/edge.go index 4f6ccfd6e5e293..8dc4dd1502baa3 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/ast/edge/edge.go +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/edge/edge.go @@ -12,7 +12,7 @@ import ( "reflect" ) -// A Kind describes a field of an ast.Node struct. +// A Kind describes a field of an [ast.Node] struct. type Kind uint8 // String returns a description of the edge kind. @@ -41,21 +41,25 @@ func (k Kind) Get(n ast.Node, idx int) ast.Node { panic(fmt.Sprintf("%v.Get(%T): invalid node type", k, n)) } v := reflect.ValueOf(n).Elem().Field(fieldInfos[k].index) - if idx != -1 { - v = v.Index(idx) // asserts valid index - } else { - // (The type assertion below asserts that v is not a slice.) + + if v.Kind() == reflect.Slice { + v = v.Index(idx) // asserts valid idx + } else if idx != -1 { + panic(fmt.Sprintf("%v, Get(%T, %d): cannot index non-slice", v, n, idx)) } - return v.Interface().(ast.Node) // may be nil + + out, _ := v.Interface().(ast.Node) // may be nil + return out } +// Each [Kind] is named Type_Field, where Type is the +// [ast.Node] struct type and Field is the name of the field const ( Invalid Kind = iota // for nodes at the root of the traversal - // Kinds are sorted alphabetically. - // Numbering is not stable. - // Each is named Type_Field, where Type is the - // ast.Node struct type and Field is the name of the field + // As of Go1.26 these kinds are sorted alphabetically, but + // numbering must be stable, so any new addition of const should + // use a new value (be added at the end of the list). ArrayType_Elt ArrayType_Len diff --git a/src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go index 77aad553d5b93f..0d6d0bced0fae9 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go +++ b/src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -24,8 +24,10 @@ package objectpath import ( + "encoding/binary" "fmt" "go/types" + "slices" "strconv" "strings" @@ -124,7 +126,66 @@ func For(obj types.Object) (Path, error) { // An Encoder amortizes the cost of encoding the paths of multiple objects. // The zero value of an Encoder is ready to use. type Encoder struct { - scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects + pkgIndex map[*types.Package]*pkgIndex +} + +// A traversal encapsulates the state of a single traversal of the object/type graph. +type traversal struct { + pkg *types.Package + ix *pkgIndex // non-nil if we are building the index + + target types.Object // the sought symbol (if ix == nil) + found Path // the found path (if ix == nil) + + // These maps are used to short circuit cycles through + // interface methods, such as occur in the following example: + // + // type I interface { f() interface{I} } + // + // See golang/go#68046 for details. + seenTParamNames map[*types.TypeName]bool // global cycle breaking through type parameters + seenMethods map[*types.Func]bool // global cycle breaking through recursive interfaces +} + +// A pkgIndex holds a compressed index of objectpaths of all symbols +// (fields, methods, params) requiring search for an entire package. +// +// The first time a search for a given package is requested, we simply +// traverse the type graph for the target object, maintaining the +// current object path as a stack. If we find the target object, we +// save the path and terminate the main loop (but it's not worth +// breaking out of the current recursion). +// +// On the second search (a pkgIndex exists but its data is nil), we +// build an index of the traversal, which we use for all subsequent +// searches. +// +// The traversal index is encoded in the data field as a list of records, +// one per node, in preorder. Records are of two types: +// +// - A record for a package-level object consists of a pair +// (parent, nameIndex uvarint), where parent is zero and +// nameIndex is the index of the object's name in the sorted +// pkg.Scope().Names() slice. +// +// - A record for a nested node (a segment of an object path) +// consists of (parent uvarint, op byte, index uvarint), where +// parent is the index of the record for the parent node, +// op is the destructuring operator, and index (if op = [AFMTr]) +// is its integer operand. +// +// Since data[0] = 0 all nodes have positive offsets. In effect the +// encoding is a trie in which each node stores one path segment +// and points to the node for its prefix. +// +// TODO(adonovan): opt: evaluate an only 2-level tree with nodes for +// package-level objects and the-rest-of-the-path. One calculation +// suggested that it might be similar speed but 30% more compact. +type pkgIndex struct { + pkg *types.Package + data []byte // encoding of traversal; nil if not yet constructed + scopeNames []string // memo of pkg.Scope().Names() to avoid O(n) alloc/sort at lookup + offsets map[types.Object]uint32 // each object's node offset within encoded traversal data } // For returns the path to an object relative to its package, @@ -211,10 +272,9 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { if pkg == nil { return "", fmt.Errorf("predeclared %s has no path", obj) } - scope := pkg.Scope() // 2. package-level object? - if scope.Lookup(obj.Name()) == obj { + if pkg.Scope().Lookup(obj.Name()) == obj { // Only exported objects (and non-exported types) have a path. // Non-exported types may be referenced by other objects. if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() { @@ -232,19 +292,18 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { // have a path. return "", fmt.Errorf("no path for %v", obj) } + case *types.Const, // Only package-level constants have a path. *types.Label, // Labels are function-local. *types.PkgName: // PkgNames are file-local. return "", fmt.Errorf("no path for %v", obj) case *types.Var: - // Could be: - // - a field (obj.IsField()) - // - a func parameter or result - // - a local var. - // Sadly there is no way to distinguish - // a param/result from a local - // so we must proceed to the find. + // A var, if not package-level, must be a + // parameter (incl. receiver) or result, or a struct field. + if obj.Kind() == types.LocalVar { + return "", fmt.Errorf("no path for local %v", obj) + } case *types.Func: // A func, if not package-level, must be a method. @@ -261,89 +320,311 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { panic(obj) } - // 4. Search the API for the path to the var (field/param/result) or method. + // 4. Search the object/type graph for the path to + // the var (field/param/result) or method. + ix, ok := enc.pkgIndex[pkg] + if !ok { + // First search: don't build an index, just traverse. + // This avoids allocation in [For], whose Encoder + // lives for a single call. + ix = &pkgIndex{pkg: pkg} + + if enc.pkgIndex == nil { + enc.pkgIndex = make(map[*types.Package]*pkgIndex) + } + enc.pkgIndex[pkg] = ix // build the index next time + + f := traversal{pkg: pkg, target: obj} + f.traverse() + + if f.found != "" { + return f.found, nil + } + } else { + // Second search: build an index while traversing. + if ix.data == nil { + ix.offsets = make(map[types.Object]uint32) + ix.data = []byte{0} // offset 0 is sentinel + (&traversal{pkg: pkg, ix: ix}).traverse() + } + + // Second and later searches: consult the index. + if offset, ok := ix.offsets[obj]; ok { + return ix.path(offset), nil + } + } + + return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path()) +} + +// traverse performs a complete traversal of all symbols reachable from the package. +func (tr *traversal) traverse() { + scope := tr.pkg.Scope() + names := scope.Names() + if tr.ix != nil { + tr.ix.scopeNames = names + } + + empty := make([]byte, 0, 48) // initial space for stack (ix == nil) - // First inspect package-level named types. + // First inspect package-level type names. // In the presence of path aliases, these give // the best paths because non-types may // refer to types, but not the reverse. - empty := make([]byte, 0, 48) // initial space - objs := enc.scopeObjects(scope) - for _, o := range objs { - tname, ok := o.(*types.TypeName) - if !ok { - continue // handle non-types in second pass + for i, name := range names { + if tr.found != "" { + return // found (ix == nil) } - path := append(empty, o.Name()...) - path = append(path, opType) - - T := o.Type() - if alias, ok := T.(*types.Alias); ok { - if r := findTypeParam(obj, alias.TypeParams(), path, opTypeParam); r != nil { - return Path(r), nil - } - if r := find(obj, alias.Rhs(), append(path, opRhs)); r != nil { - return Path(r), nil - } + obj := scope.Lookup(name) + if _, ok := obj.(*types.TypeName); !ok { + continue // handle non-types in second pass + } - } else if tname.IsAlias() { - // legacy alias - if r := find(obj, T, path); r != nil { - return Path(r), nil - } + // emit (name, opType) + var path []byte + var offset uint32 + if tr.ix == nil { + path = append(empty, name...) + path = append(path, opType) + } else { + offset = tr.ix.emitPackageLevel(i) + tr.ix.offsets[obj] = offset + offset = tr.ix.emitPathSegment(offset, opType, -1) + } - } else if named, ok := T.(*types.Named); ok { - // defined (named) type - if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam); r != nil { - return Path(r), nil - } - if r := find(obj, named.Underlying(), append(path, opUnderlying)); r != nil { - return Path(r), nil - } + // A TypeName (for Named or Alias) may have type parameters. + switch t := obj.Type().(type) { + case *types.Alias: + tr.tparams(t.TypeParams(), path, offset, opTypeParam) + tr.typ(path, offset, opRhs, -1, t.Rhs()) + case *types.Named: + tr.tparams(t.TypeParams(), path, offset, opTypeParam) + tr.typ(path, offset, opUnderlying, -1, t.Underlying()) } } // Then inspect everything else: - // non-types, and declared methods of defined types. - for _, o := range objs { - path := append(empty, o.Name()...) - if _, ok := o.(*types.TypeName); !ok { - if o.Exported() { + // exported non-types, and declared methods of defined types. + for i, name := range names { + if tr.found != "" { + return // found (ix == nil) + } + + obj := scope.Lookup(name) + + if tname, ok := obj.(*types.TypeName); !ok { + if obj.Exported() { // exported non-type (const, var, func) - if r := find(obj, o.Type(), append(path, opType)); r != nil { - return Path(r), nil + var path []byte + var offset uint32 + if tr.ix == nil { + path = append(empty, name...) + } else { + offset = tr.ix.emitPackageLevel(i) + tr.ix.offsets[obj] = offset } + tr.typ(path, offset, opType, -1, obj.Type()) } - continue - } - // Inspect declared methods of defined types. - if T, ok := types.Unalias(o.Type()).(*types.Named); ok { - path = append(path, opType) + } else if T, ok := types.Unalias(tname.Type()).(*types.Named); ok { + // defined type + var path []byte + var offset uint32 + if tr.ix == nil { + path = append(empty, name...) + path = append(path, opType) + } else { + // Inv: map entry for obj was populated in first pass. + offset = tr.ix.emitPathSegment(tr.ix.offsets[obj], opType, -1) + } + + // Inspect declared methods of defined types. + // // The method index here is always with respect // to the underlying go/types data structures, // which ultimately derives from source order // and must be preserved by export data. for i := 0; i < T.NumMethods(); i++ { m := T.Method(i) - path2 := appendOpArg(path, opMethod, i) - if m == obj { - return Path(path2), nil // found declared method - } - if r := find(obj, m.Type(), append(path2, opType)); r != nil { - return Path(r), nil + tr.object(path, offset, opMethod, i, m) + } + } + } +} + +func (tr *traversal) visitType(path []byte, offset uint32, T types.Type) { + switch T := T.(type) { + case *types.Alias: + tr.typ(path, offset, opRhs, -1, T.Rhs()) + + case *types.Basic, *types.Named: + // Named types belonging to pkg were handled already, + // so T must belong to another package. No path. + return + + case *types.Pointer, *types.Slice, *types.Array, *types.Chan: + type hasElem interface{ Elem() types.Type } // note: includes Map + tr.typ(path, offset, opElem, -1, T.(hasElem).Elem()) + + case *types.Map: + tr.typ(path, offset, opKey, -1, T.Key()) + tr.typ(path, offset, opElem, -1, T.Elem()) + + case *types.Signature: + tr.tparams(T.RecvTypeParams(), path, offset, opRecvTypeParam) + tr.tparams(T.TypeParams(), path, offset, opTypeParam) + tr.typ(path, offset, opParams, -1, T.Params()) + tr.typ(path, offset, opResults, -1, T.Results()) + + case *types.Struct: + for i := 0; i < T.NumFields(); i++ { + tr.object(path, offset, opField, i, T.Field(i)) + } + + case *types.Tuple: + for i := 0; i < T.Len(); i++ { + tr.object(path, offset, opAt, i, T.At(i)) + } + + case *types.Interface: + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + if m.Pkg() != nil && m.Pkg() != tr.pkg { + continue // embedded method from another package + } + if !tr.seenMethods[m] { + if tr.seenMethods == nil { + tr.seenMethods = make(map[*types.Func]bool) } + tr.seenMethods[m] = true + tr.object(path, offset, opMethod, i, m) } } + + case *types.TypeParam: + tname := T.Obj() + if tname.Pkg() != nil && tname.Pkg() != tr.pkg { + return // type parameter from another package + } + if !tr.seenTParamNames[tname] { + if tr.seenTParamNames == nil { + tr.seenTParamNames = make(map[*types.TypeName]bool) + } + tr.seenTParamNames[tname] = true + tr.object(path, offset, opObj, -1, tname) + tr.typ(path, offset, opConstraint, -1, T.Constraint()) + } } +} - return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path()) +func (tr *traversal) tparams(list *types.TypeParamList, path []byte, offset uint32, op byte) { + for i := 0; i < list.Len(); i++ { + tr.typ(path, offset, op, i, list.At(i)) + } +} + +// typ descends the type graph edge (op, index), then proceeds to traverse type t. +func (tr *traversal) typ(path []byte, offset uint32, op byte, index int, t types.Type) { + if tr.ix == nil { + path = appendOpArg(path, op, index) + } else { + offset = tr.ix.emitPathSegment(offset, op, index) + } + tr.visitType(path, offset, t) +} + +// object descends the type graph edge (op, index), records object +// obj, then proceeds to traverse its type. +func (tr *traversal) object(path []byte, offset uint32, op byte, index int, obj types.Object) { + if tr.ix == nil { + path = appendOpArg(path, op, index) + if obj == tr.target && tr.found == "" { + tr.found = Path(path) + } + path = append(path, opType) + } else { + offset = tr.ix.emitPathSegment(offset, op, index) + if _, ok := tr.ix.offsets[obj]; !ok { + tr.ix.offsets[obj] = offset + } + offset = tr.ix.emitPathSegment(offset, opType, -1) + } + tr.visitType(path, offset, obj.Type()) +} + +// emitPackageLevel encodes a record for a package-level symbol, +// identified by its index in ix.scopeNames. +func (p *pkgIndex) emitPackageLevel(index int) uint32 { + off := uint32(len(p.data)) + p.data = append(p.data, 0) // zero varint => no parent + p.data = binary.AppendUvarint(p.data, uint64(index)) + return off +} + +// emitPathSegment emits a record for a non-initial object path segment. +func (p *pkgIndex) emitPathSegment(parent uint32, op byte, index int) uint32 { + off := uint32(len(p.data)) + p.data = binary.AppendUvarint(p.data, uint64(parent)) + p.data = append(p.data, op) + switch op { + case opAt, opField, opMethod, opTypeParam, opRecvTypeParam: + p.data = binary.AppendUvarint(p.data, uint64(index)) + } + return off +} + +// path returns the Path for the encoded node at the specified offset. +func (p *pkgIndex) path(offset uint32) Path { + var elems []string // path elements in reverse + for { + // Read parent index. + parent, n := binary.Uvarint(p.data[offset:]) + offset += uint32(n) + + if parent == 0 { + break // root (end of path) + } + + op := p.data[offset] + offset++ + + // The [AFMTr] operators have a numeric operand. + switch op { + case opAt, opField, opMethod, opTypeParam, opRecvTypeParam: + val, n := binary.Uvarint(p.data[offset:]) + offset += uint32(n) + elems = append(elems, strconv.Itoa(int(val))) + } + + elems = append(elems, string([]byte{op})) + + offset = uint32(parent) + } + idx, _ := binary.Uvarint(p.data[offset:]) + + // Convert index to Path string. + name := p.scopeNames[idx] + sz := len(name) + for _, elem := range elems { + sz += len(elem) + } + var buf strings.Builder + buf.Grow(sz) + buf.WriteString(name) + for _, elem := range slices.Backward(elems) { + buf.WriteString(elem) + } + return Path(buf.String()) } -func appendOpArg(path []byte, op byte, arg int) []byte { +// appendOpArg appends (op, index) to the object path. +// A negative index is ignored. +func appendOpArg(path []byte, op byte, index int) []byte { path = append(path, op) - path = strconv.AppendInt(path, int64(arg), 10) + if index >= 0 { + path = strconv.AppendInt(path, int64(index), 10) + } return path } @@ -442,138 +723,6 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { // panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named))) } -// find finds obj within type T, returning the path to it, or nil if not found. -// -// The seen map is used to short circuit cycles through type parameters. If -// nil, it will be allocated as necessary. -// -// The seenMethods map is used internally to short circuit cycles through -// interface methods, such as occur in the following example: -// -// type I interface { f() interface{I} } -// -// See golang/go#68046 for details. -func find(obj types.Object, T types.Type, path []byte) []byte { - return (&finder{obj: obj}).find(T, path) -} - -// finder closes over search state for a call to find. -type finder struct { - obj types.Object // the sought object - seenTParamNames map[*types.TypeName]bool // for cycle breaking through type parameters - seenMethods map[*types.Func]bool // for cycle breaking through recursive interfaces -} - -func (f *finder) find(T types.Type, path []byte) []byte { - switch T := T.(type) { - case *types.Alias: - return f.find(types.Unalias(T), path) - case *types.Basic, *types.Named: - // Named types belonging to pkg were handled already, - // so T must belong to another package. No path. - return nil - case *types.Pointer: - return f.find(T.Elem(), append(path, opElem)) - case *types.Slice: - return f.find(T.Elem(), append(path, opElem)) - case *types.Array: - return f.find(T.Elem(), append(path, opElem)) - case *types.Chan: - return f.find(T.Elem(), append(path, opElem)) - case *types.Map: - if r := f.find(T.Key(), append(path, opKey)); r != nil { - return r - } - return f.find(T.Elem(), append(path, opElem)) - case *types.Signature: - if r := f.findTypeParam(T.RecvTypeParams(), path, opRecvTypeParam); r != nil { - return r - } - if r := f.findTypeParam(T.TypeParams(), path, opTypeParam); r != nil { - return r - } - if r := f.find(T.Params(), append(path, opParams)); r != nil { - return r - } - return f.find(T.Results(), append(path, opResults)) - case *types.Struct: - for i := 0; i < T.NumFields(); i++ { - fld := T.Field(i) - path2 := appendOpArg(path, opField, i) - if fld == f.obj { - return path2 // found field var - } - if r := f.find(fld.Type(), append(path2, opType)); r != nil { - return r - } - } - return nil - case *types.Tuple: - for i := 0; i < T.Len(); i++ { - v := T.At(i) - path2 := appendOpArg(path, opAt, i) - if v == f.obj { - return path2 // found param/result var - } - if r := f.find(v.Type(), append(path2, opType)); r != nil { - return r - } - } - return nil - case *types.Interface: - for i := 0; i < T.NumMethods(); i++ { - m := T.Method(i) - if f.seenMethods[m] { - continue // break cycles (see TestIssue70418) - } - path2 := appendOpArg(path, opMethod, i) - if m == f.obj { - return path2 // found interface method - } - if f.seenMethods == nil { - f.seenMethods = make(map[*types.Func]bool) - } - f.seenMethods[m] = true - if r := f.find(m.Type(), append(path2, opType)); r != nil { - return r - } - } - return nil - case *types.TypeParam: - name := T.Obj() - if f.seenTParamNames[name] { - return nil - } - if name == f.obj { - return append(path, opObj) - } - if f.seenTParamNames == nil { - f.seenTParamNames = make(map[*types.TypeName]bool) - } - f.seenTParamNames[name] = true - if r := f.find(T.Constraint(), append(path, opConstraint)); r != nil { - return r - } - return nil - } - panic(T) -} - -func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte) []byte { - return (&finder{obj: obj}).findTypeParam(list, path, op) -} - -func (f *finder) findTypeParam(list *types.TypeParamList, path []byte, op byte) []byte { - for i := 0; i < list.Len(); i++ { - tparam := list.At(i) - path2 := appendOpArg(path, op, i) - if r := f.find(tparam, path2); r != nil { - return r - } - } - return nil -} - // Object returns the object denoted by path p within the package pkg. func Object(pkg *types.Package, p Path) (types.Object, error) { pathstr := string(p) @@ -708,7 +857,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) { } tparams := hasTypeParams.TypeParams() if n := tparams.Len(); index >= n { - return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) + return nil, fmt.Errorf("type parameter index %d out of range [0-%d)", index, n) } t = tparams.At(index) @@ -719,7 +868,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) { } rtparams := sig.RecvTypeParams() if n := rtparams.Len(); index >= n { - return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) + return nil, fmt.Errorf("receiver type parameter index %d out of range [0-%d)", index, n) } t = rtparams.At(index) @@ -794,23 +943,3 @@ func Object(pkg *types.Package, p Path) (types.Object, error) { return obj, nil // success } - -// scopeObjects is a memoization of scope objects. -// Callers must not modify the result. -func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object { - m := enc.scopeMemo - if m == nil { - m = make(map[*types.Scope][]types.Object) - enc.scopeMemo = m - } - objs, ok := m[scope] - if !ok { - names := scope.Names() // allocates and sorts - objs = make([]types.Object, len(names)) - for i, name := range names { - objs[i] = scope.Lookup(name) - } - m[scope] = objs - } - return objs -} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go b/src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go index 5ed4765c723d63..40a347214b5a38 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go @@ -8,6 +8,7 @@ import ( "go/ast" "go/token" "iter" + "sort" "strings" ) @@ -114,18 +115,25 @@ func Directives(g *ast.CommentGroup) (res []*Directive) { } // Comments returns an iterator over the comments overlapping the specified interval. +// Comments are sorted by position in the file, so we can use binary search. func Comments(file *ast.File, start, end token.Pos) iter.Seq[*ast.Comment] { - // TODO(adonovan): optimize use binary O(log n) instead of linear O(n) search. return func(yield func(*ast.Comment) bool) { - for _, cg := range file.Comments { - for _, co := range cg.List { + // Find the first comment group that overlaps the range. + i := sort.Search(len(file.Comments), func(i int) bool { + return file.Comments[i].End() >= start + }) + for _, cg := range file.Comments[i:] { + if cg.Pos() > end { + return + } + // Find the first comment in the group that overlaps the range. + j := sort.Search(len(cg.List), func(j int) bool { + return cg.List[j].End() >= start + }) + for _, co := range cg.List[j:] { if co.Pos() > end { return } - if co.End() < start { - continue - } - if !yield(co) { return } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/astutil/cursor.go b/src/cmd/vendor/golang.org/x/tools/internal/astutil/cursor.go new file mode 100644 index 00000000000000..74e75d1458a20c --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/astutil/cursor.go @@ -0,0 +1,38 @@ +// 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 astutil + +import ( + "go/ast" + + "golang.org/x/tools/go/ast/edge" + "golang.org/x/tools/go/ast/inspector" +) + +// UnparenCursor returns the cursor for an expression with any +// enclosing parentheses removed, similar to [ast.Unparen]. +// It is often prudent to call this before switching on the +// type of cur.Node(). +// +// See also [UnparenEnclosingCursor]. +func UnparenCursor(cur inspector.Cursor) inspector.Cursor { + for is[*ast.ParenExpr](cur) { + cur, _ = cur.FirstChild() + } + return cur +} + +// UnparenEnclosingCursor returns the first element of +// the [Cursor.Enclosing] sequence that is not itself enclosed +// in parens. It is often prudent to call this before switching on +// cur.ParentEdge(). +// +// See also [UnparenCursor]. +func UnparenEnclosingCursor(cur inspector.Cursor) inspector.Cursor { + for cur.ParentEdgeKind() == edge.ParenExpr_X { + cur = cur.Parent() + } + return cur +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/astutil/purge.go b/src/cmd/vendor/golang.org/x/tools/internal/astutil/purge.go index 81ac46a0c4eb78..d0a39415dcbb40 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/astutil/purge.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/astutil/purge.go @@ -12,9 +12,14 @@ import ( ) // PurgeFuncBodies returns a copy of src in which the contents of each -// outermost {...} region except struct and interface types have been -// deleted. This reduces the amount of work required to parse the -// top-level declarations. +// outermost {...} region have been deleted, except for struct and +// interface type bodies and the bodies of length-elided array +// literals ([...]T), whose element count is part of the type. It +// includes function bodies, function-literal bodies, and the bodies +// of slice, map, and explicitly-sized array composite literals (whose +// contents don't affect the type of the enclosing declaration). This +// reduces the amount of work required to parse the top-level +// declarations. // // PurgeFuncBodies does not preserve newlines or position information. // Also, if the input is invalid, parsing the output of @@ -22,11 +27,12 @@ import ( // on parser error recovery. func PurgeFuncBodies(src []byte) []byte { // Destroy the content of any {...}-bracketed regions that are - // not immediately preceded by a "struct" or "interface" - // token. That includes function bodies, composite literals, - // switch/select bodies, and all blocks of statements. - // This will lead to non-void functions that don't have return - // statements, which of course is a type error, but that's ok. + // not immediately preceded by a "struct" or "interface" token, + // and that are not the body of a length-elided array literal. + // That includes function bodies, switch/select bodies, and most + // composite literals; this will lead to non-void functions that + // don't have return statements, which of course is a type error, + // but that's ok. var out bytes.Buffer file := token.NewFileSet().AddFile("", -1, len(src)) @@ -34,7 +40,8 @@ func PurgeFuncBodies(src []byte) []byte { sc.Init(file, src, nil, 0) var prev token.Token var cursor int // last consumed src offset - var braces []token.Pos // stack of unclosed braces or -1 for struct/interface type + var braces []token.Pos // stack of unclosed braces, or -1 for a region we preserve + var ellipsis bool // saw "[...]" not yet consumed by a literal-body "{" for { pos, tok, _ := sc.Scan() if tok == token.EOF { @@ -44,9 +51,23 @@ func PurgeFuncBodies(src []byte) []byte { case token.COMMENT: // TODO(adonovan): opt: skip, to save an estimated 20% of time. + case token.SEMICOLON: + ellipsis = false + + case token.RBRACK: + // "...]" occurs only in the array-type prefix of a + // composite literal; variadic "..." is followed by + // a type or ")", never "]". + if prev == token.ELLIPSIS { + ellipsis = true + } + case token.LBRACE: if prev == token.STRUCT || prev == token.INTERFACE { - pos = -1 + pos = -1 // type body: preserve (don't consume ellipsis) + } else if ellipsis { + pos = -1 // [...]T literal body: preserve + ellipsis = false } braces = append(braces, pos) @@ -55,7 +76,7 @@ func PurgeFuncBodies(src []byte) []byte { top := braces[last] braces = braces[:last] if top < 0 { - // struct/interface type: leave alone + // preserve } else if len(braces) == 0 { // toplevel only // Delete {...} body. start := file.Offset(top) diff --git a/src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go b/src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go index b855a5600855f9..58fc8bb42718a7 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go @@ -254,3 +254,8 @@ func needsParens(parentNode ast.Node, old, new ast.Expr) bool { } return false } + +func is[T any](n any) bool { + _, ok := n.(T) + return ok +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/goplsexport/export.go b/src/cmd/vendor/golang.org/x/tools/internal/goplsexport/export.go index 57c15c414efd2a..414c9cb03ef22e 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/goplsexport/export.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/goplsexport/export.go @@ -9,11 +9,12 @@ package goplsexport import "golang.org/x/tools/go/analysis" var ( - ErrorsAsTypeModernizer *analysis.Analyzer // = modernize.errorsastypeAnalyzer + ErrorsAsTypeModernizer *analysis.Analyzer // = modernize.errorsastypeAnalyzer SlicesBackwardModernizer *analysis.Analyzer // = modernize.slicesbackwardAnalyzer - StdIteratorsModernizer *analysis.Analyzer // = modernize.stditeratorsAnalyzer - PlusBuildModernizer *analysis.Analyzer // = modernize.plusbuildAnalyzer - StringsCutModernizer *analysis.Analyzer // = modernize.stringscutAnalyzer - UnsafeFuncsModernizer *analysis.Analyzer // = modernize.unsafeFuncsAnalyzer - AtomicTypesModernizer *analysis.Analyzer // = modernize.atomicTypesAnalyzer + StdIteratorsModernizer *analysis.Analyzer // = modernize.stditeratorsAnalyzer + PlusBuildModernizer *analysis.Analyzer // = modernize.plusbuildAnalyzer + StringsCutModernizer *analysis.Analyzer // = modernize.stringscutAnalyzer + UnsafeFuncsModernizer *analysis.Analyzer // = modernize.unsafeFuncsAnalyzer + AtomicTypesModernizer *analysis.Analyzer // = modernize.atomicTypesAnalyzer + EmbedLitModernizer *analysis.Analyzer // = modernize.embedLitAnalyzer ) diff --git a/src/cmd/vendor/golang.org/x/tools/internal/moreiters/iters.go b/src/cmd/vendor/golang.org/x/tools/internal/moreiters/iters.go index 9e4aaf94855ade..3dc81c4dbfdf77 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/moreiters/iters.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/moreiters/iters.go @@ -53,3 +53,11 @@ func Len[T any](seq iter.Seq[T]) (n int) { } return } + +// Empty reports whether the sequence contains no elements. +func Empty[T any](seq iter.Seq[T]) bool { + for range seq { + return false + } + return true +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/refactor/delete.go b/src/cmd/vendor/golang.org/x/tools/internal/refactor/delete.go index 25547f04f0991e..b655b8a2ee3760 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/refactor/delete.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/refactor/delete.go @@ -439,7 +439,7 @@ Big: inStmtList = true case *ast.ForStmt: use(parent.For, parent.Body.Lbrace) - // special handling, as init;cond;post BlockStmt is not a statment list + // special handling, as init;cond;post BlockStmt is not a statement list if parent.Init != nil && parent.Cond != nil && stmt == parent.Init && lineOf(parent.Cond.Pos()) == lineOf(stmt.End()) { rightStmt = parent.Cond.Pos() } else if parent.Post != nil && parent.Cond != nil && stmt == parent.Post && lineOf(parent.Cond.End()) == lineOf(stmt.Pos()) { diff --git a/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go b/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go index 815c8c0bba0069..68e2844794b437 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go @@ -530,9 +530,7 @@ func analyzeTypeParams(_ logger, fset *token.FileSet, info *types.Info, decl *as // We don't care about most of the properties that matter for parameter references: // a type is immutable, cannot have its address taken, and does not undergo conversions. // TODO(jba): can we nevertheless combine this with the traversal in analyzeParams? - var stack []ast.Node - stack = append(stack, decl.Type) // for scope of function itself - ast.PreorderStack(decl.Body, stack, func(n ast.Node, stack []ast.Node) bool { + visit := func(n ast.Node, stack []ast.Node) bool { if id, ok := n.(*ast.Ident); ok { if v, ok := info.Uses[id].(*types.TypeName); ok { if pinfo, ok := paramInfos[v]; ok { @@ -543,7 +541,16 @@ func analyzeTypeParams(_ logger, fset *token.FileSet, info *types.Info, decl *as } } return true - }) + } + var stack []ast.Node + stack = append(stack, decl.Type) // for scope of function itself + if decl.Type.Params != nil { + ast.PreorderStack(decl.Type.Params, stack, visit) + } + if decl.Type.Results != nil { + ast.PreorderStack(decl.Type.Results, stack, visit) + } + ast.PreorderStack(decl.Body, stack, visit) return params } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go b/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go index cbcf7b3ff2934b..cf4c587176e5cd 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go @@ -2050,7 +2050,7 @@ func resolveEffects(logf logger, args []*argument, effects []int, sg substGraph) } } if !sg.has(argi) { - for j := 0; j < i; j++ { + for j := range i { argj := args[j] if argj.pure { continue diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/typeindex/typeindex.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/typeindex/typeindex.go index 222ada55a2550a..e9ea0c4cdc9256 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/typeindex/typeindex.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/typeindex/typeindex.go @@ -17,6 +17,7 @@ import ( "golang.org/x/tools/go/ast/edge" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typesinternal" ) @@ -217,10 +218,9 @@ func (ix *Index) Selection(path, typename, name string) types.Object { func (ix *Index) Calls(callee types.Object) iter.Seq[inspector.Cursor] { return func(yield func(inspector.Cursor) bool) { for cur := range ix.Uses(callee) { - ek := cur.ParentEdgeKind() - // The call may be of the form f() or x.f(), // optionally with parens; ascend from f to call. + // See logic in [typesinternal.UsedIdent], to which this is dual. // // It is tempting but wrong to use the first // CallExpr ancestor: we have to make sure the @@ -229,25 +229,20 @@ func (ix *Index) Calls(callee types.Object) iter.Seq[inspector.Cursor] { // Avoiding Enclosing is also significantly faster. // inverse unparen: f -> (f) - for ek == edge.ParenExpr_X { - cur = cur.Parent() - ek = cur.ParentEdgeKind() - } + cur = astutil.UnparenEnclosingCursor(cur) - // ascend selector: f -> x.f - if ek == edge.SelectorExpr_Sel { - cur = cur.Parent() - ek = cur.ParentEdgeKind() + // ascend selector (or qualified identifier): f -> x.f + if cur.ParentEdgeKind() == edge.SelectorExpr_Sel { + cur = astutil.UnparenEnclosingCursor(cur.Parent()) } - // inverse unparen again - for ek == edge.ParenExpr_X { - cur = cur.Parent() - ek = cur.ParentEdgeKind() + // ascend typeparams: f -> f[T]; f -> f[T1, T2] + if ek := cur.ParentEdgeKind(); ek == edge.IndexExpr_X || ek == edge.IndexListExpr_X { + cur = astutil.UnparenEnclosingCursor(cur.Parent()) } // ascend from f or x.f to call - if ek == edge.CallExpr_Fun { + if cur.ParentEdgeKind() == edge.CallExpr_Fun { curCall := cur.Parent() call := curCall.Node().(*ast.CallExpr) if typeutil.Callee(ix.info, call) == callee { diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 421f557745ff4f..b8a8f795ec2475 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -48,7 +48,7 @@ golang.org/x/sync/semaphore golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa +# golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 ## explicit; go 1.25.0 golang.org/x/telemetry golang.org/x/telemetry/counter @@ -73,7 +73,7 @@ golang.org/x/text/internal/tag golang.org/x/text/language golang.org/x/text/transform golang.org/x/text/unicode/norm -# golang.org/x/tools v0.44.1-0.20260414062052-55fb96ff894f +# golang.org/x/tools v0.45.1-0.20260520205638-b38156a7a9f5 ## explicit; go 1.25.0 golang.org/x/tools/cmd/bisect golang.org/x/tools/cover diff --git a/src/crypto/internal/fips140/bigmod/nat.go b/src/crypto/internal/fips140/bigmod/nat.go index 53f77502afa017..0233f12639c00c 100644 --- a/src/crypto/internal/fips140/bigmod/nat.go +++ b/src/crypto/internal/fips140/bigmod/nat.go @@ -142,6 +142,14 @@ func (x *Nat) Bits() []uint { return x.limbs } +// SetBits assigns x = y, where y is a slice of little-endian uint. x is resized +// to the length of y. +func (x *Nat) SetBits(y []uint) *Nat { + x.reset(len(y)) + copy(x.limbs, y) + return x +} + // Bytes returns x as a zero-extended big-endian byte slice. The size of the // slice will match the size of m. // diff --git a/src/crypto/internal/fips140/ecdsa/ecdsa.go b/src/crypto/internal/fips140/ecdsa/ecdsa.go index 5759c34c6a12bb..30f004ff6b884a 100644 --- a/src/crypto/internal/fips140/ecdsa/ecdsa.go +++ b/src/crypto/internal/fips140/ecdsa/ecdsa.go @@ -13,6 +13,7 @@ import ( "errors" "hash" "io" + "math/bits" "sync" ) @@ -54,7 +55,7 @@ const ( type Curve[P Point[P]] struct { curve curveID newPoint func() P - ordInverse func([]byte) ([]byte, error) + ordInverse func(*[4]uint64) N *bigmod.Modulus nMinus2 []byte } @@ -387,14 +388,11 @@ func signGeneric[P Point[P]](c *Curve[P], priv *PrivateKey, drbg *hmacDRBG, hash // inverse sets kInv to the inverse of k modulo the order of the curve. func inverse[P Point[P]](c *Curve[P], kInv, k *bigmod.Nat) { - if c.ordInverse != nil { - kBytes, err := c.ordInverse(k.Bytes(c.N)) - // Some platforms don't implement ordInverse, and always return an error. - if err == nil { - _, err := kInv.SetBytes(kBytes, c.N) - if err != nil { - panic("ecdsa: internal error: ordInverse produced an invalid value") - } + if c.ordInverse != nil && bits.UintSize == 64 { + if kb := k.Bits(); len(kb) == 4 { + k64 := [4]uint64{uint64(kb[0]), uint64(kb[1]), uint64(kb[2]), uint64(kb[3])} + c.ordInverse(&k64) + kInv.SetBits([]uint{uint(k64[0]), uint(k64[1]), uint(k64[2]), uint(k64[3])}) return } } diff --git a/src/crypto/internal/fips140/edwards25519/field/_asm/fe_amd64_asm.go b/src/crypto/internal/fips140/edwards25519/field/_asm/fe_amd64_asm.go index ecb713b3c42a68..e2fcf0b2462661 100644 --- a/src/crypto/internal/fips140/edwards25519/field/_asm/fe_amd64_asm.go +++ b/src/crypto/internal/fips140/edwards25519/field/_asm/fe_amd64_asm.go @@ -19,7 +19,6 @@ func main() { Package("crypto/internal/fips140/edwards25519/field") ConstraintExpr("!purego") feMul() - feSquare() Generate() } @@ -37,95 +36,6 @@ type uint128 struct { func (c uint128) String() string { return c.name } -func feSquare() { - TEXT("feSquare", NOSPLIT, "func(out, a *Element)") - Doc("feSquare sets out = a * a. It works like feSquareGeneric.") - Pragma("noescape") - - a := Dereference(Param("a")) - l0 := namedComponent{a.Field("l0"), "l0"} - l1 := namedComponent{a.Field("l1"), "l1"} - l2 := namedComponent{a.Field("l2"), "l2"} - l3 := namedComponent{a.Field("l3"), "l3"} - l4 := namedComponent{a.Field("l4"), "l4"} - - // r0 = l0×l0 + 19×2×(l1×l4 + l2×l3) - r0 := uint128{"r0", GP64(), GP64()} - mul64(r0, 1, l0, l0) - addMul64(r0, 38, l1, l4) - addMul64(r0, 38, l2, l3) - - // r1 = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3 - r1 := uint128{"r1", GP64(), GP64()} - mul64(r1, 2, l0, l1) - addMul64(r1, 38, l2, l4) - addMul64(r1, 19, l3, l3) - - // r2 = = 2×l0×l2 + l1×l1 + 19×2×l3×l4 - r2 := uint128{"r2", GP64(), GP64()} - mul64(r2, 2, l0, l2) - addMul64(r2, 1, l1, l1) - addMul64(r2, 38, l3, l4) - - // r3 = = 2×l0×l3 + 2×l1×l2 + 19×l4×l4 - r3 := uint128{"r3", GP64(), GP64()} - mul64(r3, 2, l0, l3) - addMul64(r3, 2, l1, l2) - addMul64(r3, 19, l4, l4) - - // r4 = = 2×l0×l4 + 2×l1×l3 + l2×l2 - r4 := uint128{"r4", GP64(), GP64()} - mul64(r4, 2, l0, l4) - addMul64(r4, 2, l1, l3) - addMul64(r4, 1, l2, l2) - - Comment("First reduction chain") - maskLow51Bits := GP64() - MOVQ(Imm((1<<51)-1), maskLow51Bits) - c0, r0lo := shiftRightBy51(&r0) - c1, r1lo := shiftRightBy51(&r1) - c2, r2lo := shiftRightBy51(&r2) - c3, r3lo := shiftRightBy51(&r3) - c4, r4lo := shiftRightBy51(&r4) - maskAndAdd(r0lo, maskLow51Bits, c4, 19) - maskAndAdd(r1lo, maskLow51Bits, c0, 1) - maskAndAdd(r2lo, maskLow51Bits, c1, 1) - maskAndAdd(r3lo, maskLow51Bits, c2, 1) - maskAndAdd(r4lo, maskLow51Bits, c3, 1) - - Comment("Second reduction chain (carryPropagate)") - // c0 = r0 >> 51 - MOVQ(r0lo, c0) - SHRQ(Imm(51), c0) - // c1 = r1 >> 51 - MOVQ(r1lo, c1) - SHRQ(Imm(51), c1) - // c2 = r2 >> 51 - MOVQ(r2lo, c2) - SHRQ(Imm(51), c2) - // c3 = r3 >> 51 - MOVQ(r3lo, c3) - SHRQ(Imm(51), c3) - // c4 = r4 >> 51 - MOVQ(r4lo, c4) - SHRQ(Imm(51), c4) - maskAndAdd(r0lo, maskLow51Bits, c4, 19) - maskAndAdd(r1lo, maskLow51Bits, c0, 1) - maskAndAdd(r2lo, maskLow51Bits, c1, 1) - maskAndAdd(r3lo, maskLow51Bits, c2, 1) - maskAndAdd(r4lo, maskLow51Bits, c3, 1) - - Comment("Store output") - out := Dereference(Param("out")) - Store(r0lo, out.Field("l0")) - Store(r1lo, out.Field("l1")) - Store(r2lo, out.Field("l2")) - Store(r3lo, out.Field("l3")) - Store(r4lo, out.Field("l4")) - - RET() -} - func feMul() { TEXT("feMul", NOSPLIT, "func(out, a, b *Element)") Doc("feMul sets out = a * b. It works like feMulGeneric.") diff --git a/src/crypto/internal/fips140/edwards25519/field/fe.go b/src/crypto/internal/fips140/edwards25519/field/fe.go index 5c5c22c637378c..45ec5c8dd4c4a2 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe.go +++ b/src/crypto/internal/fips140/edwards25519/field/fe.go @@ -115,65 +115,40 @@ func (v *Element) Negate(a *Element) *Element { // // If z == 0, Invert returns v = 0. func (v *Element) Invert(z *Element) *Element { - // Inversion is implemented as exponentiation with exponent p − 2. It uses the - // same sequence of 254 squarings and 11 multiplications as [Curve25519]. - var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element - - z2.Square(z) // 2 - t.Square(&z2) // 4 - t.Square(&t) // 8 - z9.Multiply(&t, z) // 9 - z11.Multiply(&z9, &z2) // 11 - t.Square(&z11) // 22 - z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0 - - t.Square(&z2_5_0) // 2^6 - 2^1 - for i := 0; i < 4; i++ { - t.Square(&t) // 2^10 - 2^5 - } - z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0 + // Inversion is implemented as exponentiation with exponent p − 2. + // It uses 254 squarings and 11 multiplications, grouping squarings to use SquareN. + var z11, t0, t1, t2, t Element - t.Square(&z2_10_0) // 2^11 - 2^1 - for i := 0; i < 9; i++ { - t.Square(&t) // 2^20 - 2^10 - } - z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0 + t1.Square(z) // 2 + t.Square(&t1) // 4 + t.Square(&t) // 8 + t2.Multiply(&t, z) // 9 + z11.Multiply(&t2, &t1) // 11 + t.Square(&z11) // 22 + t0.Multiply(&t, &t2) // 31 = 2^5 - 2^0 - t.Square(&z2_20_0) // 2^21 - 2^1 - for i := 0; i < 19; i++ { - t.Square(&t) // 2^40 - 2^20 - } - t.Multiply(&t, &z2_20_0) // 2^40 - 2^0 + t.SquareN(&t0, 5) // 2^10 - 2^5 + t2.Multiply(&t, &t0) // 2^10 - 1 - t.Square(&t) // 2^41 - 2^1 - for i := 0; i < 9; i++ { - t.Square(&t) // 2^50 - 2^10 - } - z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0 + t.SquareN(&t2, 5) // 2^15 - 2^5 + t0.Multiply(&t, &t0) // 2^15 - 1 - t.Square(&z2_50_0) // 2^51 - 2^1 - for i := 0; i < 49; i++ { - t.Square(&t) // 2^100 - 2^50 - } - z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0 + t.SquareN(&t0, 15) // 2^30 - 2^15 + t1.Multiply(&t, &t0) // 2^30 - 1 - t.Square(&z2_100_0) // 2^101 - 2^1 - for i := 0; i < 99; i++ { - t.Square(&t) // 2^200 - 2^100 - } - t.Multiply(&t, &z2_100_0) // 2^200 - 2^0 + t.SquareN(&t1, 30) // 2^60 - 2^30 + t0.Multiply(&t, &t1) // 2^60 - 1 - t.Square(&t) // 2^201 - 2^1 - for i := 0; i < 49; i++ { - t.Square(&t) // 2^250 - 2^50 - } - t.Multiply(&t, &z2_50_0) // 2^250 - 2^0 + t.SquareN(&t0, 60) // 2^120 - 2^60 + t1.Multiply(&t, &t0) // 2^120 - 1 - t.Square(&t) // 2^251 - 2^1 - t.Square(&t) // 2^252 - 2^2 - t.Square(&t) // 2^253 - 2^3 - t.Square(&t) // 2^254 - 2^4 - t.Square(&t) // 2^255 - 2^5 + t.SquareN(&t1, 120) // 2^240 - 2^120 + t.Multiply(&t, &t1) // 2^240 - 1 + + t.SquareN(&t, 10) // 2^250 - 2^10 + t.Multiply(&t, &t2) // 2^250 - 1 + + t.SquareN(&t, 5) // 2^255 - 2^5 return v.Multiply(&t, &z11) // 2^255 - 21 } @@ -311,6 +286,12 @@ func (v *Element) Square(x *Element) *Element { return v } +// SquareN sets v = x^(2^n), and returns v. n must be positive. +func (v *Element) SquareN(x *Element, n int) *Element { + feSquareN(v, x, n) + return v +} + // Mult32 sets v = x * y, and returns v. func (v *Element) Mult32(x *Element, y uint32) *Element { x0lo, x0hi := mul51(x.l0, y) @@ -340,51 +321,37 @@ func mul51(a uint64, b uint32) (lo uint64, hi uint64) { func (v *Element) Pow22523(x *Element) *Element { var t0, t1, t2 Element - t0.Square(x) // x^2 - t1.Square(&t0) // x^4 - t1.Square(&t1) // x^8 - t1.Multiply(x, &t1) // x^9 - t0.Multiply(&t0, &t1) // x^11 - t0.Square(&t0) // x^22 - t0.Multiply(&t1, &t0) // x^31 - t1.Square(&t0) // x^62 - for i := 1; i < 5; i++ { // x^992 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1 - t1.Square(&t0) // 2^11 - 2 - for i := 1; i < 10; i++ { // 2^20 - 2^10 - t1.Square(&t1) - } - t1.Multiply(&t1, &t0) // 2^20 - 1 - t2.Square(&t1) // 2^21 - 2 - for i := 1; i < 20; i++ { // 2^40 - 2^20 - t2.Square(&t2) - } - t1.Multiply(&t2, &t1) // 2^40 - 1 - t1.Square(&t1) // 2^41 - 2 - for i := 1; i < 10; i++ { // 2^50 - 2^10 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // 2^50 - 1 - t1.Square(&t0) // 2^51 - 2 - for i := 1; i < 50; i++ { // 2^100 - 2^50 - t1.Square(&t1) - } - t1.Multiply(&t1, &t0) // 2^100 - 1 - t2.Square(&t1) // 2^101 - 2 - for i := 1; i < 100; i++ { // 2^200 - 2^100 - t2.Square(&t2) - } - t1.Multiply(&t2, &t1) // 2^200 - 1 - t1.Square(&t1) // 2^201 - 2 - for i := 1; i < 50; i++ { // 2^250 - 2^50 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // 2^250 - 1 - t0.Square(&t0) // 2^251 - 2 - t0.Square(&t0) // 2^252 - 4 - return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3) + t0.Square(x) // x^2 + t1.Multiply(x, &t0) // x^3 + t0.Square(&t1) // x^6 + t0.Square(&t0) // x^12 + t0.Multiply(&t1, &t0) // x^15 + t0.Square(&t0) // x^30 + t0.Multiply(x, &t0) // x^31 = 2^5 - 1 + + t1.SquareN(&t0, 5) // 2^10 - 2^5 + t1.Multiply(&t1, &t0) // 2^10 - 1 + + t2.SquareN(&t1, 5) // 2^15 - 2^5 + t0.Multiply(&t2, &t0) // 2^15 - 1 + + t2.SquareN(&t0, 15) // 2^30 - 2^15 + t2.Multiply(&t2, &t0) // 2^30 - 1 + + t0.SquareN(&t2, 30) // 2^60 - 2^30 + t0.Multiply(&t0, &t2) // 2^60 - 1 + + t2.SquareN(&t0, 60) // 2^120 - 2^60 + t2.Multiply(&t2, &t0) // 2^120 - 1 + + t0.SquareN(&t2, 120) // 2^240 - 2^120 + t0.Multiply(&t0, &t2) // 2^240 - 1 + + t0.SquareN(&t0, 10) // 2^250 - 2^10 + t0.Multiply(&t0, &t1) // 2^250 - 1 + + t0.SquareN(&t0, 2) // 2^252 - 4 + return v.Multiply(&t0, x) // 2^252 - 3 } // sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion. diff --git a/src/crypto/internal/fips140/edwards25519/field/fe_alias_test.go b/src/crypto/internal/fips140/edwards25519/field/fe_alias_test.go index 0c81239458d04a..3473e53a80e6c8 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe_alias_test.go +++ b/src/crypto/internal/fips140/edwards25519/field/fe_alias_test.go @@ -96,6 +96,12 @@ func TestAliasing(t *testing.T) { {name: "Negate", oneArgF: (*Element).Negate}, {name: "Set", oneArgF: (*Element).Set}, {name: "Square", oneArgF: (*Element).Square}, + { + name: "SquareN", + oneArgF: func(v, x *Element) *Element { + return v.SquareN(x, 10) + }, + }, {name: "Pow22523", oneArgF: (*Element).Pow22523}, { name: "Mult32", diff --git a/src/crypto/internal/fips140/edwards25519/field/fe_amd64.go b/src/crypto/internal/fips140/edwards25519/field/fe_amd64.go index 00bf8f4479225b..1f3ce861c4dfea 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe_amd64.go +++ b/src/crypto/internal/fips140/edwards25519/field/fe_amd64.go @@ -8,8 +8,3 @@ package field // //go:noescape func feMul(out *Element, a *Element, b *Element) - -// feSquare sets out = a * a. It works like feSquareGeneric. -// -//go:noescape -func feSquare(out *Element, a *Element) diff --git a/src/crypto/internal/fips140/edwards25519/field/fe_amd64.s b/src/crypto/internal/fips140/edwards25519/field/fe_amd64.s index 5e06e242ed8e92..a24a2410391bff 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe_amd64.s +++ b/src/crypto/internal/fips140/edwards25519/field/fe_amd64.s @@ -229,170 +229,3 @@ TEXT ·feMul(SB), NOSPLIT, $0-24 MOVQ R13, 24(AX) MOVQ R15, 32(AX) RET - -// func feSquare(out *Element, a *Element) -TEXT ·feSquare(SB), NOSPLIT, $0-16 - MOVQ a+8(FP), CX - - // r0 = l0×l0 - MOVQ (CX), AX - MULQ (CX) - MOVQ AX, SI - MOVQ DX, BX - - // r0 += 38×l1×l4 - MOVQ 8(CX), DX - LEAQ (DX)(DX*8), AX - LEAQ (DX)(AX*2), AX - SHLQ $0x01, AX - MULQ 32(CX) - ADDQ AX, SI - ADCQ DX, BX - - // r0 += 38×l2×l3 - MOVQ 16(CX), DX - LEAQ (DX)(DX*8), AX - LEAQ (DX)(AX*2), AX - SHLQ $0x01, AX - MULQ 24(CX) - ADDQ AX, SI - ADCQ DX, BX - - // r1 = 2×l0×l1 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 8(CX) - MOVQ AX, R8 - MOVQ DX, DI - - // r1 += 38×l2×l4 - MOVQ 16(CX), DX - LEAQ (DX)(DX*8), AX - LEAQ (DX)(AX*2), AX - SHLQ $0x01, AX - MULQ 32(CX) - ADDQ AX, R8 - ADCQ DX, DI - - // r1 += 19×l3×l3 - MOVQ 24(CX), DX - LEAQ (DX)(DX*8), AX - LEAQ (DX)(AX*2), AX - MULQ 24(CX) - ADDQ AX, R8 - ADCQ DX, DI - - // r2 = 2×l0×l2 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 16(CX) - MOVQ AX, R10 - MOVQ DX, R9 - - // r2 += l1×l1 - MOVQ 8(CX), AX - MULQ 8(CX) - ADDQ AX, R10 - ADCQ DX, R9 - - // r2 += 38×l3×l4 - MOVQ 24(CX), DX - LEAQ (DX)(DX*8), AX - LEAQ (DX)(AX*2), AX - SHLQ $0x01, AX - MULQ 32(CX) - ADDQ AX, R10 - ADCQ DX, R9 - - // r3 = 2×l0×l3 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 24(CX) - MOVQ AX, R12 - MOVQ DX, R11 - - // r3 += 2×l1×l2 - MOVQ 8(CX), AX - SHLQ $0x01, AX - MULQ 16(CX) - ADDQ AX, R12 - ADCQ DX, R11 - - // r3 += 19×l4×l4 - MOVQ 32(CX), DX - LEAQ (DX)(DX*8), AX - LEAQ (DX)(AX*2), AX - MULQ 32(CX) - ADDQ AX, R12 - ADCQ DX, R11 - - // r4 = 2×l0×l4 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 32(CX) - MOVQ AX, R14 - MOVQ DX, R13 - - // r4 += 2×l1×l3 - MOVQ 8(CX), AX - SHLQ $0x01, AX - MULQ 24(CX) - ADDQ AX, R14 - ADCQ DX, R13 - - // r4 += l2×l2 - MOVQ 16(CX), AX - MULQ 16(CX) - ADDQ AX, R14 - ADCQ DX, R13 - - // First reduction chain - MOVQ $0x0007ffffffffffff, AX - SHLQ $0x0d, SI, BX - SHLQ $0x0d, R8, DI - SHLQ $0x0d, R10, R9 - SHLQ $0x0d, R12, R11 - SHLQ $0x0d, R14, R13 - ANDQ AX, SI - IMUL3Q $0x13, R13, R13 - ADDQ R13, SI - ANDQ AX, R8 - ADDQ BX, R8 - ANDQ AX, R10 - ADDQ DI, R10 - ANDQ AX, R12 - ADDQ R9, R12 - ANDQ AX, R14 - ADDQ R11, R14 - - // Second reduction chain (carryPropagate) - MOVQ SI, BX - SHRQ $0x33, BX - MOVQ R8, DI - SHRQ $0x33, DI - MOVQ R10, R9 - SHRQ $0x33, R9 - MOVQ R12, R11 - SHRQ $0x33, R11 - MOVQ R14, R13 - SHRQ $0x33, R13 - ANDQ AX, SI - IMUL3Q $0x13, R13, R13 - ADDQ R13, SI - ANDQ AX, R8 - ADDQ BX, R8 - ANDQ AX, R10 - ADDQ DI, R10 - ANDQ AX, R12 - ADDQ R9, R12 - ANDQ AX, R14 - ADDQ R11, R14 - - // Store output - MOVQ out+0(FP), AX - MOVQ SI, (AX) - MOVQ R8, 8(AX) - MOVQ R10, 16(AX) - MOVQ R12, 24(AX) - MOVQ R14, 32(AX) - RET diff --git a/src/crypto/internal/fips140/edwards25519/field/fe_amd64_noasm.go b/src/crypto/internal/fips140/edwards25519/field/fe_amd64_noasm.go index 4b81f25d1d0f53..a85eb109bbd3fd 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe_amd64_noasm.go +++ b/src/crypto/internal/fips140/edwards25519/field/fe_amd64_noasm.go @@ -7,5 +7,3 @@ package field func feMul(v, x, y *Element) { feMulGeneric(v, x, y) } - -func feSquare(v, x *Element) { feSquareGeneric(v, x) } diff --git a/src/crypto/internal/fips140/edwards25519/field/fe_generic.go b/src/crypto/internal/fips140/edwards25519/field/fe_generic.go index ef1f15a5dc0598..579eab6122f036 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe_generic.go +++ b/src/crypto/internal/fips140/edwards25519/field/fe_generic.go @@ -183,7 +183,7 @@ func feMulGeneric(v, a, b *Element) { v.l4 = rr4&maskLow51Bits + rr3>>51 } -func feSquareGeneric(v, a *Element) { +func feSquare(v, a *Element) { l0 := a.l0 l1 := a.l1 l2 := a.l2 @@ -256,6 +256,62 @@ func feSquareGeneric(v, a *Element) { v.l4 = rr4&maskLow51Bits + rr3>>51 } +// feSquareN squares a n times and writes the result to v. +// It uses local variables to keep limbs in registers. +func feSquareN(v, a *Element, n int) { + l0 := a.l0 + l1 := a.l1 + l2 := a.l2 + l3 := a.l3 + l4 := a.l4 + + for range n { + r0 := mul(l0, l0) + r0 = addMul38(r0, l1, l4) + r0 = addMul38(r0, l2, l3) + + r1 := mul(l0*2, l1) + r1 = addMul38(r1, l2, l4) + r1 = addMul19(r1, l3, l3) + + r2 := mul(l0*2, l2) + r2 = addMul(r2, l1, l1) + r2 = addMul38(r2, l3, l4) + + r3 := mul(l0*2, l3) + r3 = addMul(r3, l1*2, l2) + r3 = addMul19(r3, l4, l4) + + r4 := mul(l0*2, l4) + r4 = addMul(r4, l1*2, l3) + r4 = addMul(r4, l2, l2) + + c0 := shiftRightBy51(r0) + c1 := shiftRightBy51(r1) + c2 := shiftRightBy51(r2) + c3 := shiftRightBy51(r3) + c4 := shiftRightBy51(r4) + + rr0 := r0.lo&maskLow51Bits + mul19(c4) + rr1 := r1.lo&maskLow51Bits + c0 + rr2 := r2.lo&maskLow51Bits + c1 + rr3 := r3.lo&maskLow51Bits + c2 + rr4 := r4.lo&maskLow51Bits + c3 + + l0 = rr0&maskLow51Bits + mul19(rr4>>51) + l1 = rr1&maskLow51Bits + rr0>>51 + l2 = rr2&maskLow51Bits + rr1>>51 + l3 = rr3&maskLow51Bits + rr2>>51 + l4 = rr4&maskLow51Bits + rr3>>51 + } + + v.l0 = l0 + v.l1 = l1 + v.l2 = l2 + v.l3 = l3 + v.l4 = l4 +} + // carryPropagate brings the limbs below 52 bits by applying the reduction // identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry. func (v *Element) carryPropagate() *Element { diff --git a/src/crypto/internal/fips140/edwards25519/field/fe_test.go b/src/crypto/internal/fips140/edwards25519/field/fe_test.go index b268878912197b..a98c152b631477 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe_test.go +++ b/src/crypto/internal/fips140/edwards25519/field/fe_test.go @@ -489,22 +489,27 @@ func TestSqrtRatio(t *testing.T) { } } -func TestFeSquare(t *testing.T) { - asmLikeGeneric := func(a Element) bool { - t1 := a - t2 := a - - feSquareGeneric(&t1, &t1) - feSquare(&t2, &t2) - - if t1 != t2 { - t.Logf("got: %#v,\nexpected: %#v", t1, t2) +func TestSquareN(t *testing.T) { + squareNMatchesRepeatSquare := func(x Element) bool { + for _, n := range []int{1, 2, 5, 10, 15, 50, 120} { + got := new(Element).SquareN(&x, n) + want := new(Element).Set(&x) + for range n { + want.Square(want) + } + if got.Equal(want) != 1 { + t.Logf("SquareN(%d) mismatch", n) + return false + } + if !isInBounds(got) { + t.Logf("SquareN(%d) out of bounds", n) + return false + } } - - return t1 == t2 && isInBounds(&t2) + return true } - if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil { + if err := quick.Check(squareNMatchesRepeatSquare, quickCheckConfig(1024)); err != nil { t.Error(err) } } diff --git a/src/crypto/internal/fips140/nistec/_asm/go.mod b/src/crypto/internal/fips140/nistec/_asm/go.mod index 09daa240276170..3941288f122404 100644 --- a/src/crypto/internal/fips140/nistec/_asm/go.mod +++ b/src/crypto/internal/fips140/nistec/_asm/go.mod @@ -1,11 +1,11 @@ module crypto/internal/fips140/nistec/_asm -go 1.24 +go 1.26.0 require github.com/mmcloughlin/avo v0.6.0 require ( - golang.org/x/mod v0.20.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/tools v0.42.0 // indirect ) diff --git a/src/crypto/internal/fips140/nistec/_asm/go.sum b/src/crypto/internal/fips140/nistec/_asm/go.sum index 76af484b2eba35..fbfa9879c3a846 100644 --- a/src/crypto/internal/fips140/nistec/_asm/go.sum +++ b/src/crypto/internal/fips140/nistec/_asm/go.sum @@ -2,7 +2,13 @@ github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= diff --git a/src/crypto/internal/fips140/nistec/_asm/p256_asm.go b/src/crypto/internal/fips140/nistec/_asm/p256_asm.go index c32e7edf74a7a3..1d2e5270cb46f6 100644 --- a/src/crypto/internal/fips140/nistec/_asm/p256_asm.go +++ b/src/crypto/internal/fips140/nistec/_asm/p256_asm.go @@ -52,8 +52,6 @@ func main() { p256FromMont() p256Select() p256SelectAffine() - p256OrdMul() - p256OrdSqr() p256SubInternal() p256MulInternal() p256SqrInternal() @@ -832,583 +830,6 @@ func p256SelectAffine() { RET() } -// Implements: -// -// func p256OrdMul(res, in1, in2 *p256OrdElement) -func p256OrdMul() { - Implement("p256OrdMul") - Attributes(NOSPLIT) - - Load(Param("res"), res_ptr) - Load(Param("in1"), x_ptr) - Load(Param("in2"), y_ptr) - - Comment("x * y[0]") - MOVQ(Mem{Base: y_ptr}.Offset(8*0), t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*0), RAX) - MULQ(t0_v1) - MOVQ(RAX, acc0_v1) - MOVQ(RDX, acc1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*1), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc1_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, acc2_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*2), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, acc3_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*3), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, acc4_v1) - XORQ(acc5_v1, acc5_v1) - - Comment("First reduction step") - MOVQ(acc0_v1, RAX) - p256ordK0 := p256ordK0_DATA() - MULQ(p256ordK0) - MOVQ(RAX, t0_v1) - - p256ord := p256ord_DATA() - MOVQ(p256ord.Offset(0x00), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc0_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x08), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc1_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc1_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x10), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc2_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x18), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc3_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc3_v1) - ADCQ(RDX, acc4_v1) - ADCQ(Imm(0), acc5_v1) - - Comment("x * y[1]") - MOVQ(Mem{Base: y_ptr}.Offset(8*1), t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*0), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc1_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*1), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc2_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*2), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc3_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*3), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc4_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc4_v1) - ADCQ(RDX, acc5_v1) - ADCQ(Imm(0), acc0_v1) - - Comment("Second reduction step") - MOVQ(acc1_v1, RAX) - MULQ(p256ordK0) - MOVQ(RAX, t0_v1) - - MOVQ(p256ord.Offset(0x00), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc1_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x08), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc2_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x10), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc3_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x18), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc4_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc4_v1) - ADCQ(RDX, acc5_v1) - ADCQ(Imm(0), acc0_v1) - - Comment("x * y[2]") - MOVQ(Mem{Base: y_ptr}.Offset(8*2), t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*0), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*1), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc3_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*2), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc4_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc4_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*3), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc5_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc5_v1) - ADCQ(RDX, acc0_v1) - ADCQ(Imm(0), acc1_v1) - - Comment("Third reduction step") - MOVQ(acc2_v1, RAX) - MULQ(p256ordK0) - MOVQ(RAX, t0_v1) - - MOVQ(p256ord.Offset(0x00), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x08), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc3_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x10), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc4_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc4_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x18), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc5_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc5_v1) - ADCQ(RDX, acc0_v1) - ADCQ(Imm(0), acc1_v1) - - Comment("x * y[3]") - MOVQ(Mem{Base: y_ptr}.Offset(8*3), t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*0), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*1), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc4_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc4_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*2), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc5_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc5_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*3), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc0_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc0_v1) - ADCQ(RDX, acc1_v1) - ADCQ(Imm(0), acc2_v1) - - Comment("Last reduction step") - MOVQ(acc3_v1, RAX) - MULQ(p256ordK0) - MOVQ(RAX, t0_v1) - - MOVQ(p256ord.Offset(0x00), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x08), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc4_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc4_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x10), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc5_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc5_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x18), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc0_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc0_v1) - ADCQ(RDX, acc1_v1) - ADCQ(Imm(0), acc2_v1) - - Comment("Copy result [255:0]") - MOVQ(acc4_v1, x_ptr) - MOVQ(acc5_v1, acc3_v1) - MOVQ(acc0_v1, t0_v1) - MOVQ(acc1_v1, t1_v1) - - Comment("Subtract p256") - SUBQ(p256ord.Offset(0x00), acc4_v1) - SBBQ(p256ord.Offset(0x08), acc5_v1) - SBBQ(p256ord.Offset(0x10), acc0_v1) - SBBQ(p256ord.Offset(0x18), acc1_v1) - SBBQ(Imm(0), acc2_v1) - - CMOVQCS(x_ptr, acc4_v1) - CMOVQCS(acc3_v1, acc5_v1) - CMOVQCS(t0_v1, acc0_v1) - CMOVQCS(t1_v1, acc1_v1) - - MOVQ(acc4_v1, Mem{Base: res_ptr}.Offset(8*0)) - MOVQ(acc5_v1, Mem{Base: res_ptr}.Offset(8*1)) - MOVQ(acc0_v1, Mem{Base: res_ptr}.Offset(8*2)) - MOVQ(acc1_v1, Mem{Base: res_ptr}.Offset(8*3)) - - RET() -} - -// Implements: -// -// func p256OrdSqr(res, in *p256OrdElement, n int) -func p256OrdSqr() { - Implement("p256OrdSqr") - Attributes(NOSPLIT) - - Load(Param("res"), res_ptr) - Load(Param("in"), x_ptr) - Load(Param("n"), RBX) - - Label("ordSqrLoop") - - Comment("y[1:] * y[0]") - MOVQ(Mem{Base: x_ptr}.Offset(8*0), t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*1), RAX) - MULQ(t0_v1) - MOVQ(RAX, acc1_v1) - MOVQ(RDX, acc2_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*2), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, acc3_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*3), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, acc4_v1) - - Comment("y[2:] * y[1]") - MOVQ(Mem{Base: x_ptr}.Offset(8*1), t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*2), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*3), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc4_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc4_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, acc5_v1) - - Comment("y[3] * y[2]") - MOVQ(Mem{Base: x_ptr}.Offset(8*2), t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*3), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc5_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, y_ptr) - XORQ(t1_v1, t1_v1) - - Comment("*2") - ADDQ(acc1_v1, acc1_v1) - ADCQ(acc2_v1, acc2_v1) - ADCQ(acc3_v1, acc3_v1) - ADCQ(acc4_v1, acc4_v1) - ADCQ(acc5_v1, acc5_v1) - ADCQ(y_ptr, y_ptr) - ADCQ(Imm(0), t1_v1) - - Comment("Missing products") - MOVQ(Mem{Base: x_ptr}.Offset(8*0), RAX) - MULQ(RAX) - MOVQ(RAX, acc0_v1) - MOVQ(RDX, t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*1), RAX) - MULQ(RAX) - ADDQ(t0_v1, acc1_v1) - ADCQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*2), RAX) - MULQ(RAX) - ADDQ(t0_v1, acc3_v1) - ADCQ(RAX, acc4_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t0_v1) - - MOVQ(Mem{Base: x_ptr}.Offset(8*3), RAX) - MULQ(RAX) - ADDQ(t0_v1, acc5_v1) - ADCQ(RAX, y_ptr) - ADCQ(RDX, t1_v1) - MOVQ(t1_v1, x_ptr) - - Comment("First reduction step") - MOVQ(acc0_v1, RAX) - p256ordK0 := p256ordK0_DATA() - MULQ(p256ordK0) - MOVQ(RAX, t0_v1) - - p256ord := p256ord_DATA() - MOVQ(p256ord.Offset(0x00), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc0_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x08), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc1_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc1_v1) - - MOVQ(t0_v1, t1_v1) - ADCQ(RDX, acc2_v1) - ADCQ(Imm(0), t1_v1) - SUBQ(t0_v1, acc2_v1) - SBBQ(Imm(0), t1_v1) - - MOVQ(t0_v1, RAX) - MOVQ(t0_v1, RDX) - MOVQ(t0_v1, acc0_v1) - SHLQ(Imm(32), RAX) - SHRQ(Imm(32), RDX) - - ADDQ(t1_v1, acc3_v1) - ADCQ(Imm(0), acc0_v1) - SUBQ(RAX, acc3_v1) - SBBQ(RDX, acc0_v1) - - Comment("Second reduction step") - MOVQ(acc1_v1, RAX) - MULQ(p256ordK0) - MOVQ(RAX, t0_v1) - - MOVQ(p256ord.Offset(0x00), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc1_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x08), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc2_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc2_v1) - - MOVQ(t0_v1, t1_v1) - ADCQ(RDX, acc3_v1) - ADCQ(Imm(0), t1_v1) - SUBQ(t0_v1, acc3_v1) - SBBQ(Imm(0), t1_v1) - - MOVQ(t0_v1, RAX) - MOVQ(t0_v1, RDX) - MOVQ(t0_v1, acc1_v1) - SHLQ(Imm(32), RAX) - SHRQ(Imm(32), RDX) - - ADDQ(t1_v1, acc0_v1) - ADCQ(Imm(0), acc1_v1) - SUBQ(RAX, acc0_v1) - SBBQ(RDX, acc1_v1) - - Comment("Third reduction step") - MOVQ(acc2_v1, RAX) - MULQ(p256ordK0) - MOVQ(RAX, t0_v1) - - MOVQ(p256ord.Offset(0x00), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc2_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x08), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc3_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc3_v1) - - MOVQ(t0_v1, t1_v1) - ADCQ(RDX, acc0_v1) - ADCQ(Imm(0), t1_v1) - SUBQ(t0_v1, acc0_v1) - SBBQ(Imm(0), t1_v1) - - MOVQ(t0_v1, RAX) - MOVQ(t0_v1, RDX) - MOVQ(t0_v1, acc2_v1) - SHLQ(Imm(32), RAX) - SHRQ(Imm(32), RDX) - - ADDQ(t1_v1, acc1_v1) - ADCQ(Imm(0), acc2_v1) - SUBQ(RAX, acc1_v1) - SBBQ(RDX, acc2_v1) - - Comment("Last reduction step") - MOVQ(acc3_v1, RAX) - MULQ(p256ordK0) - MOVQ(RAX, t0_v1) - - MOVQ(p256ord.Offset(0x00), RAX) - MULQ(t0_v1) - ADDQ(RAX, acc3_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(p256ord.Offset(0x08), RAX) - MULQ(t0_v1) - ADDQ(t1_v1, acc0_v1) - ADCQ(Imm(0), RDX) - ADDQ(RAX, acc0_v1) - ADCQ(Imm(0), RDX) - MOVQ(RDX, t1_v1) - - MOVQ(t0_v1, t1_v1) - ADCQ(RDX, acc1_v1) - ADCQ(Imm(0), t1_v1) - SUBQ(t0_v1, acc1_v1) - SBBQ(Imm(0), t1_v1) - - MOVQ(t0_v1, RAX) - MOVQ(t0_v1, RDX) - MOVQ(t0_v1, acc3_v1) - SHLQ(Imm(32), RAX) - SHRQ(Imm(32), RDX) - - ADDQ(t1_v1, acc2_v1) - ADCQ(Imm(0), acc3_v1) - SUBQ(RAX, acc2_v1) - SBBQ(RDX, acc3_v1) - XORQ(t0_v1, t0_v1) - - Comment("Add bits [511:256] of the sqr result") - ADCQ(acc4_v1, acc0_v1) - ADCQ(acc5_v1, acc1_v1) - ADCQ(y_ptr, acc2_v1) - ADCQ(x_ptr, acc3_v1) - ADCQ(Imm(0), t0_v1) - - MOVQ(acc0_v1, acc4_v1) - MOVQ(acc1_v1, acc5_v1) - MOVQ(acc2_v1, y_ptr) - MOVQ(acc3_v1, t1_v1) - - Comment("Subtract p256") - SUBQ(p256ord.Offset(0x00), acc0_v1) - SBBQ(p256ord.Offset(0x08), acc1_v1) - SBBQ(p256ord.Offset(0x10), acc2_v1) - SBBQ(p256ord.Offset(0x18), acc3_v1) - SBBQ(Imm(0), t0_v1) - - CMOVQCS(acc4_v1, acc0_v1) - CMOVQCS(acc5_v1, acc1_v1) - CMOVQCS(y_ptr, acc2_v1) - CMOVQCS(t1_v1, acc3_v1) - - MOVQ(acc0_v1, Mem{Base: res_ptr}.Offset(8*0)) - MOVQ(acc1_v1, Mem{Base: res_ptr}.Offset(8*1)) - MOVQ(acc2_v1, Mem{Base: res_ptr}.Offset(8*2)) - MOVQ(acc3_v1, Mem{Base: res_ptr}.Offset(8*3)) - MOVQ(res_ptr, x_ptr) - DECQ(RBX) - JNE(LabelRef("ordSqrLoop")) - - RET() -} - // These variables have been versioned as they get redfined in the reference implementation. // This is done to produce a minimal semantic diff. var ( @@ -2595,7 +2016,7 @@ func p256PointDoubleAsm() { // #----------------------------DATA SECTION-----------------------------------## // Pointers for memoizing Data section symbols -var p256const0_ptr, p256const1_ptr, p256ordK0_ptr, p256ord_ptr, p256one_ptr *Mem +var p256const0_ptr, p256const1_ptr, p256one_ptr *Mem func p256const0_DATA() Mem { if p256const0_ptr != nil { @@ -2619,39 +2040,6 @@ func p256const1_DATA() Mem { return p256const1 } -func p256ordK0_DATA() Mem { - if p256ordK0_ptr != nil { - return *p256ordK0_ptr - } - - p256ordK0 := GLOBL("p256ordK0", 8) - p256ordK0_ptr = &p256ordK0 - DATA(0, U64(0xccd1c8aaee00bc4f)) - return p256ordK0 -} - -var p256ordConstants = [4]uint64{ - 0xf3b9cac2fc632551, - 0xbce6faada7179e84, - 0xffffffffffffffff, - 0xffffffff00000000, -} - -func p256ord_DATA() Mem { - if p256ord_ptr != nil { - return *p256ord_ptr - } - - p256ord := GLOBL("p256ord", 8) - p256ord_ptr = &p256ord - - for i, k := range p256ordConstants { - DATA(i*8, U64(k)) - } - - return p256ord -} - var p256oneConstants = [4]uint64{ 0x0000000000000001, 0xffffffff00000000, diff --git a/src/crypto/internal/fips140/nistec/p256.go b/src/crypto/internal/fips140/nistec/p256.go index 650bde4e73e0a7..25ecdad2a0051f 100644 --- a/src/crypto/internal/fips140/nistec/p256.go +++ b/src/crypto/internal/fips140/nistec/p256.go @@ -392,8 +392,8 @@ func (q *P256Point) Select(p1, p2 *P256Point, cond int) *P256Point { return q } -// p256OrdElement is a P-256 scalar field element in [0, ord(G)-1] in the -// Montgomery domain (with R 2²⁵⁶) as four uint64 limbs in little-endian order. +// p256OrdElement is a P-256 scalar field element in [0, ord(G)-1] +// as four uint64 limbs in little-endian order. type p256OrdElement [4]uint64 // SetBytes sets s to the big-endian value of x, reducing it as necessary. diff --git a/src/crypto/internal/fips140/nistec/p256_asm.go b/src/crypto/internal/fips140/nistec/p256_asm.go index f00e70d99d1d8c..22fc8c06f789ad 100644 --- a/src/crypto/internal/fips140/nistec/p256_asm.go +++ b/src/crypto/internal/fips140/nistec/p256_asm.go @@ -76,9 +76,10 @@ const p256CompressedLength = 1 + p256ElementLength // the curve, it returns nil and an error, and the receiver is unchanged. // Otherwise, it returns p. func (p *P256Point) SetBytes(b []byte) (*P256Point, error) { - // p256Mul operates in the Montgomery domain with R = 2²⁵⁶ mod p. Thus rr - // here is R in the Montgomery domain, or R×R mod p. See comment in - // P256OrdInverse about how this is used. + // This implementation operates in the Montgomery domain with R = 2²⁵⁶ mod + // p. Elements in the Montgomery domain take the form a×R and p256Mul + // calculates (a × b × R⁻¹) mod p. rr is R in the domain, or R×R mod p, thus + // p256Mul(e, RR) gives e×R, i.e. converts e into the Montgomery domain. rr := p256Element{0x0000000000000003, 0xfffffffbffffffff, 0xfffffffffffffffe, 0x00000004fffffffd} @@ -282,7 +283,7 @@ func p256Mul(res, in1, in2 *p256Element) //go:noescape func p256Sqr(res, in *p256Element, n int) -// Montgomery multiplication by R⁻¹, or 1 outside the domain. +// Montgomery multiplication by R⁻¹, or 1 outside the domain, as R⁻¹×R = 1. // Sets res = in * R⁻¹, bringing res out of the Montgomery domain. // //go:noescape @@ -365,8 +366,8 @@ func p256PointAddAsm(res, in1, in2 *P256Point) int //go:noescape func p256PointDoubleAsm(res, in *P256Point) -// p256OrdElement is a P-256 scalar field element in [0, ord(G)-1] in the -// Montgomery domain (with R 2²⁵⁶) as four uint64 limbs in little-endian order. +// p256OrdElement is a P-256 scalar field element in [0, ord(G)-1] +// as four uint64 limbs in little-endian order. type p256OrdElement [4]uint64 // p256OrdReduce ensures s is in the range [0, ord(G)-1]. diff --git a/src/crypto/internal/fips140/nistec/p256_asm_amd64.s b/src/crypto/internal/fips140/nistec/p256_asm_amd64.s index 64894891e98037..86053049c68bd6 100644 --- a/src/crypto/internal/fips140/nistec/p256_asm_amd64.s +++ b/src/crypto/internal/fips140/nistec/p256_asm_amd64.s @@ -640,509 +640,6 @@ loop_select_base: MOVOU X3, 48(DX) RET -// func p256OrdMul(res *p256OrdElement, in1 *p256OrdElement, in2 *p256OrdElement) -// Requires: CMOV -TEXT ·p256OrdMul(SB), NOSPLIT, $0-24 - MOVQ res+0(FP), DI - MOVQ in1+8(FP), SI - MOVQ in2+16(FP), CX - - // x * y[0] - MOVQ (CX), R14 - MOVQ (SI), AX - MULQ R14 - MOVQ AX, R8 - MOVQ DX, R9 - MOVQ 8(SI), AX - MULQ R14 - ADDQ AX, R9 - ADCQ $0x00, DX - MOVQ DX, R10 - MOVQ 16(SI), AX - MULQ R14 - ADDQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R11 - MOVQ 24(SI), AX - MULQ R14 - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R12 - XORQ R13, R13 - - // First reduction step - MOVQ R8, AX - MULQ p256ordK0<>+0(SB) - MOVQ AX, R14 - MOVQ p256ord<>+0(SB), AX - MULQ R14 - ADDQ AX, R8 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+8(SB), AX - MULQ R14 - ADDQ R15, R9 - ADCQ $0x00, DX - ADDQ AX, R9 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+16(SB), AX - MULQ R14 - ADDQ R15, R10 - ADCQ $0x00, DX - ADDQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+24(SB), AX - MULQ R14 - ADDQ R15, R11 - ADCQ $0x00, DX - ADDQ AX, R11 - ADCQ DX, R12 - ADCQ $0x00, R13 - - // x * y[1] - MOVQ 8(CX), R14 - MOVQ (SI), AX - MULQ R14 - ADDQ AX, R9 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 8(SI), AX - MULQ R14 - ADDQ R15, R10 - ADCQ $0x00, DX - ADDQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 16(SI), AX - MULQ R14 - ADDQ R15, R11 - ADCQ $0x00, DX - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 24(SI), AX - MULQ R14 - ADDQ R15, R12 - ADCQ $0x00, DX - ADDQ AX, R12 - ADCQ DX, R13 - ADCQ $0x00, R8 - - // Second reduction step - MOVQ R9, AX - MULQ p256ordK0<>+0(SB) - MOVQ AX, R14 - MOVQ p256ord<>+0(SB), AX - MULQ R14 - ADDQ AX, R9 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+8(SB), AX - MULQ R14 - ADDQ R15, R10 - ADCQ $0x00, DX - ADDQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+16(SB), AX - MULQ R14 - ADDQ R15, R11 - ADCQ $0x00, DX - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+24(SB), AX - MULQ R14 - ADDQ R15, R12 - ADCQ $0x00, DX - ADDQ AX, R12 - ADCQ DX, R13 - ADCQ $0x00, R8 - - // x * y[2] - MOVQ 16(CX), R14 - MOVQ (SI), AX - MULQ R14 - ADDQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 8(SI), AX - MULQ R14 - ADDQ R15, R11 - ADCQ $0x00, DX - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 16(SI), AX - MULQ R14 - ADDQ R15, R12 - ADCQ $0x00, DX - ADDQ AX, R12 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 24(SI), AX - MULQ R14 - ADDQ R15, R13 - ADCQ $0x00, DX - ADDQ AX, R13 - ADCQ DX, R8 - ADCQ $0x00, R9 - - // Third reduction step - MOVQ R10, AX - MULQ p256ordK0<>+0(SB) - MOVQ AX, R14 - MOVQ p256ord<>+0(SB), AX - MULQ R14 - ADDQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+8(SB), AX - MULQ R14 - ADDQ R15, R11 - ADCQ $0x00, DX - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+16(SB), AX - MULQ R14 - ADDQ R15, R12 - ADCQ $0x00, DX - ADDQ AX, R12 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+24(SB), AX - MULQ R14 - ADDQ R15, R13 - ADCQ $0x00, DX - ADDQ AX, R13 - ADCQ DX, R8 - ADCQ $0x00, R9 - - // x * y[3] - MOVQ 24(CX), R14 - MOVQ (SI), AX - MULQ R14 - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 8(SI), AX - MULQ R14 - ADDQ R15, R12 - ADCQ $0x00, DX - ADDQ AX, R12 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 16(SI), AX - MULQ R14 - ADDQ R15, R13 - ADCQ $0x00, DX - ADDQ AX, R13 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 24(SI), AX - MULQ R14 - ADDQ R15, R8 - ADCQ $0x00, DX - ADDQ AX, R8 - ADCQ DX, R9 - ADCQ $0x00, R10 - - // Last reduction step - MOVQ R11, AX - MULQ p256ordK0<>+0(SB) - MOVQ AX, R14 - MOVQ p256ord<>+0(SB), AX - MULQ R14 - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+8(SB), AX - MULQ R14 - ADDQ R15, R12 - ADCQ $0x00, DX - ADDQ AX, R12 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+16(SB), AX - MULQ R14 - ADDQ R15, R13 - ADCQ $0x00, DX - ADDQ AX, R13 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+24(SB), AX - MULQ R14 - ADDQ R15, R8 - ADCQ $0x00, DX - ADDQ AX, R8 - ADCQ DX, R9 - ADCQ $0x00, R10 - - // Copy result [255:0] - MOVQ R12, SI - MOVQ R13, R11 - MOVQ R8, R14 - MOVQ R9, R15 - - // Subtract p256 - SUBQ p256ord<>+0(SB), R12 - SBBQ p256ord<>+8(SB), R13 - SBBQ p256ord<>+16(SB), R8 - SBBQ p256ord<>+24(SB), R9 - SBBQ $0x00, R10 - CMOVQCS SI, R12 - CMOVQCS R11, R13 - CMOVQCS R14, R8 - CMOVQCS R15, R9 - MOVQ R12, (DI) - MOVQ R13, 8(DI) - MOVQ R8, 16(DI) - MOVQ R9, 24(DI) - RET - -DATA p256ordK0<>+0(SB)/8, $0xccd1c8aaee00bc4f -GLOBL p256ordK0<>(SB), RODATA, $8 - -DATA p256ord<>+0(SB)/8, $0xf3b9cac2fc632551 -DATA p256ord<>+8(SB)/8, $0xbce6faada7179e84 -DATA p256ord<>+16(SB)/8, $0xffffffffffffffff -DATA p256ord<>+24(SB)/8, $0xffffffff00000000 -GLOBL p256ord<>(SB), RODATA, $32 - -// func p256OrdSqr(res *p256OrdElement, in *p256OrdElement, n int) -// Requires: CMOV -TEXT ·p256OrdSqr(SB), NOSPLIT, $0-24 - MOVQ res+0(FP), DI - MOVQ in+8(FP), SI - MOVQ n+16(FP), BX - -ordSqrLoop: - // y[1:] * y[0] - MOVQ (SI), R14 - MOVQ 8(SI), AX - MULQ R14 - MOVQ AX, R9 - MOVQ DX, R10 - MOVQ 16(SI), AX - MULQ R14 - ADDQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R11 - MOVQ 24(SI), AX - MULQ R14 - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R12 - - // y[2:] * y[1] - MOVQ 8(SI), R14 - MOVQ 16(SI), AX - MULQ R14 - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ 24(SI), AX - MULQ R14 - ADDQ R15, R12 - ADCQ $0x00, DX - ADDQ AX, R12 - ADCQ $0x00, DX - MOVQ DX, R13 - - // y[3] * y[2] - MOVQ 16(SI), R14 - MOVQ 24(SI), AX - MULQ R14 - ADDQ AX, R13 - ADCQ $0x00, DX - MOVQ DX, CX - XORQ R15, R15 - - // *2 - ADDQ R9, R9 - ADCQ R10, R10 - ADCQ R11, R11 - ADCQ R12, R12 - ADCQ R13, R13 - ADCQ CX, CX - ADCQ $0x00, R15 - - // Missing products - MOVQ (SI), AX - MULQ AX - MOVQ AX, R8 - MOVQ DX, R14 - MOVQ 8(SI), AX - MULQ AX - ADDQ R14, R9 - ADCQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R14 - MOVQ 16(SI), AX - MULQ AX - ADDQ R14, R11 - ADCQ AX, R12 - ADCQ $0x00, DX - MOVQ DX, R14 - MOVQ 24(SI), AX - MULQ AX - ADDQ R14, R13 - ADCQ AX, CX - ADCQ DX, R15 - MOVQ R15, SI - - // First reduction step - MOVQ R8, AX - MULQ p256ordK0<>+0(SB) - MOVQ AX, R14 - MOVQ p256ord<>+0(SB), AX - MULQ R14 - ADDQ AX, R8 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+8(SB), AX - MULQ R14 - ADDQ R15, R9 - ADCQ $0x00, DX - ADDQ AX, R9 - MOVQ R14, R15 - ADCQ DX, R10 - ADCQ $0x00, R15 - SUBQ R14, R10 - SBBQ $0x00, R15 - MOVQ R14, AX - MOVQ R14, DX - MOVQ R14, R8 - SHLQ $0x20, AX - SHRQ $0x20, DX - ADDQ R15, R11 - ADCQ $0x00, R8 - SUBQ AX, R11 - SBBQ DX, R8 - - // Second reduction step - MOVQ R9, AX - MULQ p256ordK0<>+0(SB) - MOVQ AX, R14 - MOVQ p256ord<>+0(SB), AX - MULQ R14 - ADDQ AX, R9 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+8(SB), AX - MULQ R14 - ADDQ R15, R10 - ADCQ $0x00, DX - ADDQ AX, R10 - MOVQ R14, R15 - ADCQ DX, R11 - ADCQ $0x00, R15 - SUBQ R14, R11 - SBBQ $0x00, R15 - MOVQ R14, AX - MOVQ R14, DX - MOVQ R14, R9 - SHLQ $0x20, AX - SHRQ $0x20, DX - ADDQ R15, R8 - ADCQ $0x00, R9 - SUBQ AX, R8 - SBBQ DX, R9 - - // Third reduction step - MOVQ R10, AX - MULQ p256ordK0<>+0(SB) - MOVQ AX, R14 - MOVQ p256ord<>+0(SB), AX - MULQ R14 - ADDQ AX, R10 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+8(SB), AX - MULQ R14 - ADDQ R15, R11 - ADCQ $0x00, DX - ADDQ AX, R11 - MOVQ R14, R15 - ADCQ DX, R8 - ADCQ $0x00, R15 - SUBQ R14, R8 - SBBQ $0x00, R15 - MOVQ R14, AX - MOVQ R14, DX - MOVQ R14, R10 - SHLQ $0x20, AX - SHRQ $0x20, DX - ADDQ R15, R9 - ADCQ $0x00, R10 - SUBQ AX, R9 - SBBQ DX, R10 - - // Last reduction step - MOVQ R11, AX - MULQ p256ordK0<>+0(SB) - MOVQ AX, R14 - MOVQ p256ord<>+0(SB), AX - MULQ R14 - ADDQ AX, R11 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ p256ord<>+8(SB), AX - MULQ R14 - ADDQ R15, R8 - ADCQ $0x00, DX - ADDQ AX, R8 - ADCQ $0x00, DX - MOVQ DX, R15 - MOVQ R14, R15 - ADCQ DX, R9 - ADCQ $0x00, R15 - SUBQ R14, R9 - SBBQ $0x00, R15 - MOVQ R14, AX - MOVQ R14, DX - MOVQ R14, R11 - SHLQ $0x20, AX - SHRQ $0x20, DX - ADDQ R15, R10 - ADCQ $0x00, R11 - SUBQ AX, R10 - SBBQ DX, R11 - XORQ R14, R14 - - // Add bits [511:256] of the sqr result - ADCQ R12, R8 - ADCQ R13, R9 - ADCQ CX, R10 - ADCQ SI, R11 - ADCQ $0x00, R14 - MOVQ R8, R12 - MOVQ R9, R13 - MOVQ R10, CX - MOVQ R11, R15 - - // Subtract p256 - SUBQ p256ord<>+0(SB), R8 - SBBQ p256ord<>+8(SB), R9 - SBBQ p256ord<>+16(SB), R10 - SBBQ p256ord<>+24(SB), R11 - SBBQ $0x00, R14 - CMOVQCS R12, R8 - CMOVQCS R13, R9 - CMOVQCS CX, R10 - CMOVQCS R15, R11 - MOVQ R8, (DI) - MOVQ R9, 8(DI) - MOVQ R10, 16(DI) - MOVQ R11, 24(DI) - MOVQ DI, SI - DECQ BX - JNE ordSqrLoop - RET - // func p256SubInternal() // Requires: CMOV TEXT p256SubInternal(SB), NOSPLIT, $0 diff --git a/src/crypto/internal/fips140/nistec/p256_asm_arm64.s b/src/crypto/internal/fips140/nistec/p256_asm_arm64.s index 33da24508e2655..23bfe049f425a0 100644 --- a/src/crypto/internal/fips140/nistec/p256_asm_arm64.s +++ b/src/crypto/internal/fips140/nistec/p256_asm_arm64.s @@ -45,24 +45,14 @@ #define y2 R25 #define y3 R26 -#define const2 t2 -#define const3 t3 - DATA p256const0<>+0x00(SB)/8, $0x00000000ffffffff DATA p256const1<>+0x00(SB)/8, $0xffffffff00000001 -DATA p256ordK0<>+0x00(SB)/8, $0xccd1c8aaee00bc4f -DATA p256ord<>+0x00(SB)/8, $0xf3b9cac2fc632551 -DATA p256ord<>+0x08(SB)/8, $0xbce6faada7179e84 -DATA p256ord<>+0x10(SB)/8, $0xffffffffffffffff -DATA p256ord<>+0x18(SB)/8, $0xffffffff00000000 DATA p256one<>+0x00(SB)/8, $0x0000000000000001 DATA p256one<>+0x08(SB)/8, $0xffffffff00000000 DATA p256one<>+0x10(SB)/8, $0xffffffffffffffff DATA p256one<>+0x18(SB)/8, $0x00000000fffffffe GLOBL p256const0<>(SB), 8, $8 GLOBL p256const1<>(SB), 8, $8 -GLOBL p256ordK0<>(SB), 8, $8 -GLOBL p256ord<>(SB), 8, $32 GLOBL p256one<>(SB), 8, $32 /* ---------------------------------------*/ @@ -343,425 +333,10 @@ loop_select: STP (y2, y3), 3*16(res_ptr) RET /* ---------------------------------------*/ -// func p256OrdSqr(res, in *p256OrdElement, n int) -TEXT ·p256OrdSqr(SB),NOSPLIT,$0 - MOVD in+8(FP), a_ptr - MOVD n+16(FP), b_ptr - - MOVD p256ordK0<>(SB), hlp1 - LDP p256ord<>+0x00(SB), (const0, const1) - LDP p256ord<>+0x10(SB), (const2, const3) - - LDP 0*16(a_ptr), (x0, x1) - LDP 1*16(a_ptr), (x2, x3) - -ordSqrLoop: - SUB $1, b_ptr - - // x[1:] * x[0] - MUL x0, x1, acc1 - UMULH x0, x1, acc2 - - MUL x0, x2, t0 - ADDS t0, acc2, acc2 - UMULH x0, x2, acc3 - - MUL x0, x3, t0 - ADCS t0, acc3, acc3 - UMULH x0, x3, acc4 - ADC $0, acc4, acc4 - // x[2:] * x[1] - MUL x1, x2, t0 - ADDS t0, acc3 - UMULH x1, x2, t1 - ADCS t1, acc4 - ADC $0, ZR, acc5 - - MUL x1, x3, t0 - ADDS t0, acc4 - UMULH x1, x3, t1 - ADC t1, acc5 - // x[3] * x[2] - MUL x2, x3, t0 - ADDS t0, acc5 - UMULH x2, x3, acc6 - ADC $0, acc6 - - MOVD $0, acc7 - // *2 - ADDS acc1, acc1 - ADCS acc2, acc2 - ADCS acc3, acc3 - ADCS acc4, acc4 - ADCS acc5, acc5 - ADCS acc6, acc6 - ADC $0, acc7 - // Missing products - MUL x0, x0, acc0 - UMULH x0, x0, t0 - ADDS t0, acc1, acc1 - - MUL x1, x1, t0 - ADCS t0, acc2, acc2 - UMULH x1, x1, t1 - ADCS t1, acc3, acc3 - - MUL x2, x2, t0 - ADCS t0, acc4, acc4 - UMULH x2, x2, t1 - ADCS t1, acc5, acc5 - - MUL x3, x3, t0 - ADCS t0, acc6, acc6 - UMULH x3, x3, t1 - ADC t1, acc7, acc7 - // First reduction step - MUL acc0, hlp1, hlp0 - - MUL const0, hlp1, t0 - ADDS t0, acc0, acc0 - UMULH const0, hlp0, t1 - - MUL const1, hlp0, t0 - ADCS t0, acc1, acc1 - UMULH const1, hlp0, y0 - - MUL const2, hlp0, t0 - ADCS t0, acc2, acc2 - UMULH const2, hlp0, acc0 - - MUL const3, hlp0, t0 - ADCS t0, acc3, acc3 - - UMULH const3, hlp0, hlp0 - ADC $0, hlp0 - - ADDS t1, acc1, acc1 - ADCS y0, acc2, acc2 - ADCS acc0, acc3, acc3 - ADC $0, hlp0, acc0 - // Second reduction step - MUL acc1, hlp1, hlp0 - - MUL const0, hlp1, t0 - ADDS t0, acc1, acc1 - UMULH const0, hlp0, t1 - - MUL const1, hlp0, t0 - ADCS t0, acc2, acc2 - UMULH const1, hlp0, y0 - - MUL const2, hlp0, t0 - ADCS t0, acc3, acc3 - UMULH const2, hlp0, acc1 - - MUL const3, hlp0, t0 - ADCS t0, acc0, acc0 - - UMULH const3, hlp0, hlp0 - ADC $0, hlp0 - - ADDS t1, acc2, acc2 - ADCS y0, acc3, acc3 - ADCS acc1, acc0, acc0 - ADC $0, hlp0, acc1 - // Third reduction step - MUL acc2, hlp1, hlp0 - - MUL const0, hlp1, t0 - ADDS t0, acc2, acc2 - UMULH const0, hlp0, t1 - - MUL const1, hlp0, t0 - ADCS t0, acc3, acc3 - UMULH const1, hlp0, y0 - - MUL const2, hlp0, t0 - ADCS t0, acc0, acc0 - UMULH const2, hlp0, acc2 - - MUL const3, hlp0, t0 - ADCS t0, acc1, acc1 - - UMULH const3, hlp0, hlp0 - ADC $0, hlp0 - - ADDS t1, acc3, acc3 - ADCS y0, acc0, acc0 - ADCS acc2, acc1, acc1 - ADC $0, hlp0, acc2 - - // Last reduction step - MUL acc3, hlp1, hlp0 - - MUL const0, hlp1, t0 - ADDS t0, acc3, acc3 - UMULH const0, hlp0, t1 - - MUL const1, hlp0, t0 - ADCS t0, acc0, acc0 - UMULH const1, hlp0, y0 - - MUL const2, hlp0, t0 - ADCS t0, acc1, acc1 - UMULH const2, hlp0, acc3 - - MUL const3, hlp0, t0 - ADCS t0, acc2, acc2 - - UMULH const3, hlp0, hlp0 - ADC $0, acc7 - - ADDS t1, acc0, acc0 - ADCS y0, acc1, acc1 - ADCS acc3, acc2, acc2 - ADC $0, hlp0, acc3 - - ADDS acc4, acc0, acc0 - ADCS acc5, acc1, acc1 - ADCS acc6, acc2, acc2 - ADCS acc7, acc3, acc3 - ADC $0, ZR, acc4 - - SUBS const0, acc0, y0 - SBCS const1, acc1, y1 - SBCS const2, acc2, y2 - SBCS const3, acc3, y3 - SBCS $0, acc4, acc4 - - CSEL CS, y0, acc0, x0 - CSEL CS, y1, acc1, x1 - CSEL CS, y2, acc2, x2 - CSEL CS, y3, acc3, x3 - - CBNZ b_ptr, ordSqrLoop - - MOVD res+0(FP), res_ptr - STP (x0, x1), 0*16(res_ptr) - STP (x2, x3), 1*16(res_ptr) - - RET -/* ---------------------------------------*/ -// func p256OrdMul(res, in1, in2 *p256OrdElement) -TEXT ·p256OrdMul(SB),NOSPLIT,$0 - MOVD in1+8(FP), a_ptr - MOVD in2+16(FP), b_ptr - - MOVD p256ordK0<>(SB), hlp1 - LDP p256ord<>+0x00(SB), (const0, const1) - LDP p256ord<>+0x10(SB), (const2, const3) - - LDP 0*16(a_ptr), (x0, x1) - LDP 1*16(a_ptr), (x2, x3) - LDP 0*16(b_ptr), (y0, y1) - LDP 1*16(b_ptr), (y2, y3) - - // y[0] * x - MUL y0, x0, acc0 - UMULH y0, x0, acc1 - - MUL y0, x1, t0 - ADDS t0, acc1 - UMULH y0, x1, acc2 - - MUL y0, x2, t0 - ADCS t0, acc2 - UMULH y0, x2, acc3 - - MUL y0, x3, t0 - ADCS t0, acc3 - UMULH y0, x3, acc4 - ADC $0, acc4 - // First reduction step - MUL acc0, hlp1, hlp0 - - MUL const0, hlp1, t0 - ADDS t0, acc0, acc0 - UMULH const0, hlp0, t1 - - MUL const1, hlp0, t0 - ADCS t0, acc1, acc1 - UMULH const1, hlp0, y0 - - MUL const2, hlp0, t0 - ADCS t0, acc2, acc2 - UMULH const2, hlp0, acc0 - - MUL const3, hlp0, t0 - ADCS t0, acc3, acc3 - - UMULH const3, hlp0, hlp0 - ADC $0, acc4 - - ADDS t1, acc1, acc1 - ADCS y0, acc2, acc2 - ADCS acc0, acc3, acc3 - ADC $0, hlp0, acc0 - // y[1] * x - MUL y1, x0, t0 - ADDS t0, acc1 - UMULH y1, x0, t1 - - MUL y1, x1, t0 - ADCS t0, acc2 - UMULH y1, x1, hlp0 - - MUL y1, x2, t0 - ADCS t0, acc3 - UMULH y1, x2, y0 - - MUL y1, x3, t0 - ADCS t0, acc4 - UMULH y1, x3, y1 - ADC $0, ZR, acc5 - - ADDS t1, acc2 - ADCS hlp0, acc3 - ADCS y0, acc4 - ADC y1, acc5 - // Second reduction step - MUL acc1, hlp1, hlp0 - - MUL const0, hlp1, t0 - ADDS t0, acc1, acc1 - UMULH const0, hlp0, t1 - - MUL const1, hlp0, t0 - ADCS t0, acc2, acc2 - UMULH const1, hlp0, y0 - - MUL const2, hlp0, t0 - ADCS t0, acc3, acc3 - UMULH const2, hlp0, acc1 - - MUL const3, hlp0, t0 - ADCS t0, acc0, acc0 - - UMULH const3, hlp0, hlp0 - ADC $0, acc5 - - ADDS t1, acc2, acc2 - ADCS y0, acc3, acc3 - ADCS acc1, acc0, acc0 - ADC $0, hlp0, acc1 - // y[2] * x - MUL y2, x0, t0 - ADDS t0, acc2 - UMULH y2, x0, t1 - - MUL y2, x1, t0 - ADCS t0, acc3 - UMULH y2, x1, hlp0 - - MUL y2, x2, t0 - ADCS t0, acc4 - UMULH y2, x2, y0 - - MUL y2, x3, t0 - ADCS t0, acc5 - UMULH y2, x3, y1 - ADC $0, ZR, acc6 - - ADDS t1, acc3 - ADCS hlp0, acc4 - ADCS y0, acc5 - ADC y1, acc6 - // Third reduction step - MUL acc2, hlp1, hlp0 - - MUL const0, hlp1, t0 - ADDS t0, acc2, acc2 - UMULH const0, hlp0, t1 - - MUL const1, hlp0, t0 - ADCS t0, acc3, acc3 - UMULH const1, hlp0, y0 - - MUL const2, hlp0, t0 - ADCS t0, acc0, acc0 - UMULH const2, hlp0, acc2 - - MUL const3, hlp0, t0 - ADCS t0, acc1, acc1 - - UMULH const3, hlp0, hlp0 - ADC $0, acc6 - - ADDS t1, acc3, acc3 - ADCS y0, acc0, acc0 - ADCS acc2, acc1, acc1 - ADC $0, hlp0, acc2 - // y[3] * x - MUL y3, x0, t0 - ADDS t0, acc3 - UMULH y3, x0, t1 - - MUL y3, x1, t0 - ADCS t0, acc4 - UMULH y3, x1, hlp0 - - MUL y3, x2, t0 - ADCS t0, acc5 - UMULH y3, x2, y0 - - MUL y3, x3, t0 - ADCS t0, acc6 - UMULH y3, x3, y1 - ADC $0, ZR, acc7 - - ADDS t1, acc4 - ADCS hlp0, acc5 - ADCS y0, acc6 - ADC y1, acc7 - // Last reduction step - MUL acc3, hlp1, hlp0 - - MUL const0, hlp1, t0 - ADDS t0, acc3, acc3 - UMULH const0, hlp0, t1 - - MUL const1, hlp0, t0 - ADCS t0, acc0, acc0 - UMULH const1, hlp0, y0 - - MUL const2, hlp0, t0 - ADCS t0, acc1, acc1 - UMULH const2, hlp0, acc3 - - MUL const3, hlp0, t0 - ADCS t0, acc2, acc2 - - UMULH const3, hlp0, hlp0 - ADC $0, acc7 - - ADDS t1, acc0, acc0 - ADCS y0, acc1, acc1 - ADCS acc3, acc2, acc2 - ADC $0, hlp0, acc3 - - ADDS acc4, acc0, acc0 - ADCS acc5, acc1, acc1 - ADCS acc6, acc2, acc2 - ADCS acc7, acc3, acc3 - ADC $0, ZR, acc4 - - SUBS const0, acc0, t0 - SBCS const1, acc1, t1 - SBCS const2, acc2, t2 - SBCS const3, acc3, t3 - SBCS $0, acc4, acc4 - - CSEL CS, t0, acc0, acc0 - CSEL CS, t1, acc1, acc1 - CSEL CS, t2, acc2, acc2 - CSEL CS, t3, acc3, acc3 - - MOVD res+0(FP), res_ptr - STP (acc0, acc1), 0*16(res_ptr) - STP (acc2, acc3), 1*16(res_ptr) - - RET -/* ---------------------------------------*/ +// input: x0-x3, y0-y3 +// output: x0-x3 +// uses: const0, const1 +// clobbers: y0-y3, hlp0 TEXT p256SubInternal<>(SB),NOSPLIT,$0 SUBS x0, y0, acc0 SBCS x1, y1, acc1 diff --git a/src/crypto/internal/fips140/nistec/p256_ordinv.go b/src/crypto/internal/fips140/nistec/p256_ordinv.go index 156a873188c3ae..b8a51dedd09349 100644 --- a/src/crypto/internal/fips140/nistec/p256_ordinv.go +++ b/src/crypto/internal/fips140/nistec/p256_ordinv.go @@ -2,53 +2,30 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (amd64 || arm64) && !purego - package nistec -import "errors" - -// Montgomery multiplication modulo org(G). Sets res = in1 * in2 * R⁻¹. -// -//go:noescape -func p256OrdMul(res, in1, in2 *p256OrdElement) - -// Montgomery square modulo org(G), repeated n times (n >= 1). -// -//go:noescape -func p256OrdSqr(res, in *p256OrdElement, n int) - -func P256OrdInverse(k []byte) ([]byte, error) { - if len(k) != 32 { - return nil, errors.New("invalid scalar length") - } - - x := new(p256OrdElement) - p256OrdBigToLittle(x, (*[32]byte)(k)) - p256OrdReduce(x) +import "math/bits" +// P256OrdInverse sets k to the inverse of k modulo ord(G). k is encoded as four +// uint64 limbs in little-endian order, and must be reduced. If k is zero, the +// result is zero. +func P256OrdInverse(k *[4]uint64) { // Inversion is implemented as exponentiation by n - 2, per Fermat's little theorem. // // The sequence of 38 multiplications and 254 squarings is derived from // https://briansmith.org/ecc-inversion-addition-chains-01#p256_scalar_inversion - _1 := new(p256OrdElement) - _11 := new(p256OrdElement) - _101 := new(p256OrdElement) - _111 := new(p256OrdElement) - _1111 := new(p256OrdElement) - _10101 := new(p256OrdElement) - _101111 := new(p256OrdElement) - t := new(p256OrdElement) - - // This code operates in the Montgomery domain where R = 2²⁵⁶ mod n and n is - // the order of the scalar field. Elements in the Montgomery domain take the - // form a×R and p256OrdMul calculates (a × b × R⁻¹) mod n. RR is R in the - // domain, or R×R mod n, thus p256OrdMul(x, RR) gives x×R, i.e. converts x - // into the Montgomery domain. - RR := &p256OrdElement{0x83244c95be79eea2, 0x4699799c49bd6fa6, - 0x2845b2392b6bec59, 0x66e12d94f3d95620} + _1 := new(p256OrdMontElement) + _11 := new(p256OrdMontElement) + _101 := new(p256OrdMontElement) + _111 := new(p256OrdMontElement) + _1111 := new(p256OrdMontElement) + _10101 := new(p256OrdMontElement) + _101111 := new(p256OrdMontElement) + x := new(p256OrdMontElement) + t := new(p256OrdMontElement) - p256OrdMul(_1, x, RR) // _1 + j := (*p256OrdElement)(k) + p256OrdToMontgomery(_1, j) // _1 p256OrdSqr(x, _1, 1) // _10 p256OrdMul(_11, x, _1) // _11 p256OrdMul(_101, x, _11) // _101 @@ -79,7 +56,7 @@ func P256OrdInverse(k []byte) ([]byte, error) { 6, 2, 5, 6, 5, 4, 5, 5, 3, 10, 2, 5, 5, 3, 7, 6} - muls := []*p256OrdElement{ + muls := []*p256OrdMontElement{ _101111, _111, _11, _1111, _10101, _101, _101, _101, _111, _101111, _1111, _1, _1, _1111, _111, @@ -91,12 +68,499 @@ func P256OrdInverse(k []byte) ([]byte, error) { p256OrdMul(x, x, muls[i]) } - // Montgomery multiplication by R⁻¹, or 1 outside the domain as R⁻¹×R = 1, - // converts a Montgomery value out of the domain. - one := &p256OrdElement{1} - p256OrdMul(x, x, one) + p256OrdFromMontgomery(j, x) +} + +// The code below was generated by Fiat Cryptography v0.1.6-63-g92ee794c2, and +// then manually formatted and optimized. +// +// word-by-word-montgomery --lang Go --no-wide-int +// --relax-primitive-carry-to-bitwidth 32,64 --cmovznz-by-mul --static +// --package-case flatcase --private-function-case camelCase --private-type-case +// camelCase --no-prefix-fiat --package-name nistec p256Ord 64 +// 2^256-2^224+2^192-89188191075325690597107910205041859247 +// mul square from_montgomery to_montgomery +// +// https://mit-plv.github.io/fiat-crypto/ +// +// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, +// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +// p256OrdMontElement is a scalar field element in the Montgomery domain, as +// four uint64 limbs in little-endian order. It must be strictly less than +// ord(G) and in Montgomery form (with R 2²⁵⁶). +type p256OrdMontElement [4]uint64 + +// p256OrdMul multiplies two field elements in the Montgomery domain. +func p256OrdMul(out1 *p256OrdMontElement, arg1 *p256OrdMontElement, arg2 *p256OrdMontElement) { + x1 := arg1[1] + x2 := arg1[2] + x3 := arg1[3] + x4 := arg1[0] + x6, x5 := bits.Mul64(x4, arg2[3]) + x8, x7 := bits.Mul64(x4, arg2[2]) + x10, x9 := bits.Mul64(x4, arg2[1]) + x12, x11 := bits.Mul64(x4, arg2[0]) + x13, x14 := bits.Add64(x12, x9, 0) + x15, x16 := bits.Add64(x10, x7, x14) + x17, x18 := bits.Add64(x8, x5, x16) + x19 := x18 + x6 + _, x20 := bits.Mul64(x11, 0xccd1c8aaee00bc4f) + // x20 * 0xffffffff00000000 = x20 * (2^64 - 2^32) + x22, b22 := bits.Sub64(0, x20<<32, 0) + x23 := x20 - (x20 >> 32) - b22 + // x20 * 0xffffffffffffffff = x20 * (2^64 - 1) + x24, b24 := bits.Sub64(0, x20, 0) + x25 := x20 - b24 + x27, x26 := bits.Mul64(x20, 0xbce6faada7179e84) + x29, x28 := bits.Mul64(x20, 0xf3b9cac2fc632551) + x30, x31 := bits.Add64(x29, x26, 0) + x32, x33 := bits.Add64(x27, x24, x31) + x34, x35 := bits.Add64(x25, x22, x33) + x36 := x35 + x23 + _, x38 := bits.Add64(x11, x28, 0) + x39, x40 := bits.Add64(x13, x30, x38) + x41, x42 := bits.Add64(x15, x32, x40) + x43, x44 := bits.Add64(x17, x34, x42) + x45, x46 := bits.Add64(x19, x36, x44) + x48, x47 := bits.Mul64(x1, arg2[3]) + x50, x49 := bits.Mul64(x1, arg2[2]) + x52, x51 := bits.Mul64(x1, arg2[1]) + x54, x53 := bits.Mul64(x1, arg2[0]) + x55, x56 := bits.Add64(x54, x51, 0) + x57, x58 := bits.Add64(x52, x49, x56) + x59, x60 := bits.Add64(x50, x47, x58) + x61 := x60 + x48 + x62, x63 := bits.Add64(x39, x53, 0) + x64, x65 := bits.Add64(x41, x55, x63) + x66, x67 := bits.Add64(x43, x57, x65) + x68, x69 := bits.Add64(x45, x59, x67) + x70, x71 := bits.Add64(x46, x61, x69) + _, x72 := bits.Mul64(x62, 0xccd1c8aaee00bc4f) + x74, b74 := bits.Sub64(0, x72<<32, 0) + x75 := x72 - (x72 >> 32) - b74 + x76, b76 := bits.Sub64(0, x72, 0) + x77 := x72 - b76 + x79, x78 := bits.Mul64(x72, 0xbce6faada7179e84) + x81, x80 := bits.Mul64(x72, 0xf3b9cac2fc632551) + x82, x83 := bits.Add64(x81, x78, 0) + x84, x85 := bits.Add64(x79, x76, x83) + x86, x87 := bits.Add64(x77, x74, x85) + x88 := x87 + x75 + _, x90 := bits.Add64(x62, x80, 0) + x91, x92 := bits.Add64(x64, x82, x90) + x93, x94 := bits.Add64(x66, x84, x92) + x95, x96 := bits.Add64(x68, x86, x94) + x97, x98 := bits.Add64(x70, x88, x96) + x99 := x98 + x71 + x101, x100 := bits.Mul64(x2, arg2[3]) + x103, x102 := bits.Mul64(x2, arg2[2]) + x105, x104 := bits.Mul64(x2, arg2[1]) + x107, x106 := bits.Mul64(x2, arg2[0]) + x108, x109 := bits.Add64(x107, x104, 0) + x110, x111 := bits.Add64(x105, x102, x109) + x112, x113 := bits.Add64(x103, x100, x111) + x114 := x113 + x101 + x115, x116 := bits.Add64(x91, x106, 0) + x117, x118 := bits.Add64(x93, x108, x116) + x119, x120 := bits.Add64(x95, x110, x118) + x121, x122 := bits.Add64(x97, x112, x120) + x123, x124 := bits.Add64(x99, x114, x122) + _, x125 := bits.Mul64(x115, 0xccd1c8aaee00bc4f) + x127, b127 := bits.Sub64(0, x125<<32, 0) + x128 := x125 - (x125 >> 32) - b127 + x129, b129 := bits.Sub64(0, x125, 0) + x130 := x125 - b129 + x132, x131 := bits.Mul64(x125, 0xbce6faada7179e84) + x134, x133 := bits.Mul64(x125, 0xf3b9cac2fc632551) + x135, x136 := bits.Add64(x134, x131, 0) + x137, x138 := bits.Add64(x132, x129, x136) + x139, x140 := bits.Add64(x130, x127, x138) + x141 := x140 + x128 + _, x143 := bits.Add64(x115, x133, 0) + x144, x145 := bits.Add64(x117, x135, x143) + x146, x147 := bits.Add64(x119, x137, x145) + x148, x149 := bits.Add64(x121, x139, x147) + x150, x151 := bits.Add64(x123, x141, x149) + x152 := x151 + x124 + x154, x153 := bits.Mul64(x3, arg2[3]) + x156, x155 := bits.Mul64(x3, arg2[2]) + x158, x157 := bits.Mul64(x3, arg2[1]) + x160, x159 := bits.Mul64(x3, arg2[0]) + x161, x162 := bits.Add64(x160, x157, 0) + x163, x164 := bits.Add64(x158, x155, x162) + x165, x166 := bits.Add64(x156, x153, x164) + x167 := x166 + x154 + x168, x169 := bits.Add64(x144, x159, 0) + x170, x171 := bits.Add64(x146, x161, x169) + x172, x173 := bits.Add64(x148, x163, x171) + x174, x175 := bits.Add64(x150, x165, x173) + x176, x177 := bits.Add64(x152, x167, x175) + _, x178 := bits.Mul64(x168, 0xccd1c8aaee00bc4f) + x180, b180 := bits.Sub64(0, x178<<32, 0) + x181 := x178 - (x178 >> 32) - b180 + x182, b182 := bits.Sub64(0, x178, 0) + x183 := x178 - b182 + x185, x184 := bits.Mul64(x178, 0xbce6faada7179e84) + x187, x186 := bits.Mul64(x178, 0xf3b9cac2fc632551) + x188, x189 := bits.Add64(x187, x184, 0) + x190, x191 := bits.Add64(x185, x182, x189) + x192, x193 := bits.Add64(x183, x180, x191) + x194 := x193 + x181 + _, x196 := bits.Add64(x168, x186, 0) + x197, x198 := bits.Add64(x170, x188, x196) + x199, x200 := bits.Add64(x172, x190, x198) + x201, x202 := bits.Add64(x174, x192, x200) + x203, x204 := bits.Add64(x176, x194, x202) + x205 := x204 + x177 + x206, x207 := bits.Sub64(x197, 0xf3b9cac2fc632551, 0) + x208, x209 := bits.Sub64(x199, 0xbce6faada7179e84, x207) + x210, x211 := bits.Sub64(x201, 0xffffffffffffffff, x209) + x212, x213 := bits.Sub64(x203, 0xffffffff00000000, x211) + _, x215 := bits.Sub64(x205, 0, x213) + mask, _ := bits.Sub64(0, 0, x215) + out1[0] = x206&^mask | x197&mask + out1[1] = x208&^mask | x199&mask + out1[2] = x210&^mask | x201&mask + out1[3] = x212&^mask | x203&mask +} + +// p256OrdSqr squares n times a field element in the Montgomery domain. +func p256OrdSqr(out1 *p256OrdMontElement, arg1 *p256OrdMontElement, n int) { + x1 := arg1[1] + x2 := arg1[2] + x3 := arg1[3] + x4 := arg1[0] + for range n { + x6, x5 := bits.Mul64(x4, x3) + x8, x7 := bits.Mul64(x4, x2) + x10, x9 := bits.Mul64(x4, x1) + x12, x11 := bits.Mul64(x4, x4) + x13, x14 := bits.Add64(x12, x9, 0) + x15, x16 := bits.Add64(x10, x7, x14) + x17, x18 := bits.Add64(x8, x5, x16) + x19 := x18 + x6 + _, x20 := bits.Mul64(x11, 0xccd1c8aaee00bc4f) + x22, b22 := bits.Sub64(0, x20<<32, 0) + x23 := x20 - (x20 >> 32) - b22 + x24, b24 := bits.Sub64(0, x20, 0) + x25 := x20 - b24 + x27, x26 := bits.Mul64(x20, 0xbce6faada7179e84) + x29, x28 := bits.Mul64(x20, 0xf3b9cac2fc632551) + x30, x31 := bits.Add64(x29, x26, 0) + x32, x33 := bits.Add64(x27, x24, x31) + x34, x35 := bits.Add64(x25, x22, x33) + x36 := x35 + x23 + _, x38 := bits.Add64(x11, x28, 0) + x39, x40 := bits.Add64(x13, x30, x38) + x41, x42 := bits.Add64(x15, x32, x40) + x43, x44 := bits.Add64(x17, x34, x42) + x45, x46 := bits.Add64(x19, x36, x44) + x48, x47 := bits.Mul64(x1, x3) + x50, x49 := bits.Mul64(x1, x2) + x52, x51 := bits.Mul64(x1, x1) + x54, x53 := bits.Mul64(x1, x4) + x55, x56 := bits.Add64(x54, x51, 0) + x57, x58 := bits.Add64(x52, x49, x56) + x59, x60 := bits.Add64(x50, x47, x58) + x61 := x60 + x48 + x62, x63 := bits.Add64(x39, x53, 0) + x64, x65 := bits.Add64(x41, x55, x63) + x66, x67 := bits.Add64(x43, x57, x65) + x68, x69 := bits.Add64(x45, x59, x67) + x70, x71 := bits.Add64(x46, x61, x69) + _, x72 := bits.Mul64(x62, 0xccd1c8aaee00bc4f) + x74, b74 := bits.Sub64(0, x72<<32, 0) + x75 := x72 - (x72 >> 32) - b74 + x76, b76 := bits.Sub64(0, x72, 0) + x77 := x72 - b76 + x79, x78 := bits.Mul64(x72, 0xbce6faada7179e84) + x81, x80 := bits.Mul64(x72, 0xf3b9cac2fc632551) + x82, x83 := bits.Add64(x81, x78, 0) + x84, x85 := bits.Add64(x79, x76, x83) + x86, x87 := bits.Add64(x77, x74, x85) + x88 := x87 + x75 + _, x90 := bits.Add64(x62, x80, 0) + x91, x92 := bits.Add64(x64, x82, x90) + x93, x94 := bits.Add64(x66, x84, x92) + x95, x96 := bits.Add64(x68, x86, x94) + x97, x98 := bits.Add64(x70, x88, x96) + x99 := x98 + x71 + x101, x100 := bits.Mul64(x2, x3) + x103, x102 := bits.Mul64(x2, x2) + x105, x104 := bits.Mul64(x2, x1) + x107, x106 := bits.Mul64(x2, x4) + x108, x109 := bits.Add64(x107, x104, 0) + x110, x111 := bits.Add64(x105, x102, x109) + x112, x113 := bits.Add64(x103, x100, x111) + x114 := x113 + x101 + x115, x116 := bits.Add64(x91, x106, 0) + x117, x118 := bits.Add64(x93, x108, x116) + x119, x120 := bits.Add64(x95, x110, x118) + x121, x122 := bits.Add64(x97, x112, x120) + x123, x124 := bits.Add64(x99, x114, x122) + _, x125 := bits.Mul64(x115, 0xccd1c8aaee00bc4f) + x127, b127 := bits.Sub64(0, x125<<32, 0) + x128 := x125 - (x125 >> 32) - b127 + x129, b129 := bits.Sub64(0, x125, 0) + x130 := x125 - b129 + x132, x131 := bits.Mul64(x125, 0xbce6faada7179e84) + x134, x133 := bits.Mul64(x125, 0xf3b9cac2fc632551) + x135, x136 := bits.Add64(x134, x131, 0) + x137, x138 := bits.Add64(x132, x129, x136) + x139, x140 := bits.Add64(x130, x127, x138) + x141 := x140 + x128 + _, x143 := bits.Add64(x115, x133, 0) + x144, x145 := bits.Add64(x117, x135, x143) + x146, x147 := bits.Add64(x119, x137, x145) + x148, x149 := bits.Add64(x121, x139, x147) + x150, x151 := bits.Add64(x123, x141, x149) + x152 := x151 + x124 + x154, x153 := bits.Mul64(x3, x3) + x156, x155 := bits.Mul64(x3, x2) + x158, x157 := bits.Mul64(x3, x1) + x160, x159 := bits.Mul64(x3, x4) + x161, x162 := bits.Add64(x160, x157, 0) + x163, x164 := bits.Add64(x158, x155, x162) + x165, x166 := bits.Add64(x156, x153, x164) + x167 := x166 + x154 + x168, x169 := bits.Add64(x144, x159, 0) + x170, x171 := bits.Add64(x146, x161, x169) + x172, x173 := bits.Add64(x148, x163, x171) + x174, x175 := bits.Add64(x150, x165, x173) + x176, x177 := bits.Add64(x152, x167, x175) + _, x178 := bits.Mul64(x168, 0xccd1c8aaee00bc4f) + x180, b180 := bits.Sub64(0, x178<<32, 0) + x181 := x178 - (x178 >> 32) - b180 + x182, b182 := bits.Sub64(0, x178, 0) + x183 := x178 - b182 + x185, x184 := bits.Mul64(x178, 0xbce6faada7179e84) + x187, x186 := bits.Mul64(x178, 0xf3b9cac2fc632551) + x188, x189 := bits.Add64(x187, x184, 0) + x190, x191 := bits.Add64(x185, x182, x189) + x192, x193 := bits.Add64(x183, x180, x191) + x194 := x193 + x181 + _, x196 := bits.Add64(x168, x186, 0) + x197, x198 := bits.Add64(x170, x188, x196) + x199, x200 := bits.Add64(x172, x190, x198) + x201, x202 := bits.Add64(x174, x192, x200) + x203, x204 := bits.Add64(x176, x194, x202) + x205 := x204 + x177 + x206, x207 := bits.Sub64(x197, 0xf3b9cac2fc632551, 0) + x208, x209 := bits.Sub64(x199, 0xbce6faada7179e84, x207) + x210, x211 := bits.Sub64(x201, 0xffffffffffffffff, x209) + x212, x213 := bits.Sub64(x203, 0xffffffff00000000, x211) + _, x215 := bits.Sub64(x205, 0, x213) + mask, _ := bits.Sub64(0, 0, x215) + x4 = x206&^mask | x197&mask + x1 = x208&^mask | x199&mask + x2 = x210&^mask | x201&mask + x3 = x212&^mask | x203&mask + } + out1[0] = x4 + out1[1] = x1 + out1[2] = x2 + out1[3] = x3 +} + +// p256OrdFromMontgomery translates a field element out of the Montgomery domain. +func p256OrdFromMontgomery(out1 *p256OrdElement, arg1 *p256OrdMontElement) { + x1 := arg1[0] + _, x2 := bits.Mul64(x1, 0xccd1c8aaee00bc4f) + x5, x4 := bits.Mul64(x2, 0xffffffff00000000) + x7, x6 := bits.Mul64(x2, 0xffffffffffffffff) + x9, x8 := bits.Mul64(x2, 0xbce6faada7179e84) + x11, x10 := bits.Mul64(x2, 0xf3b9cac2fc632551) + x12, x13 := bits.Add64(x11, x8, 0) + x14, x15 := bits.Add64(x9, x6, x13) + x16, x17 := bits.Add64(x7, x4, x15) + _, x19 := bits.Add64(x1, x10, 0) + x20, x21 := bits.Add64(0, x12, x19) + x22, x23 := bits.Add64(0, x14, x21) + x24, x25 := bits.Add64(0, x16, x23) + x26, x27 := bits.Add64(x20, arg1[1], 0) + x28, x29 := bits.Add64(x22, 0, x27) + x30, x31 := bits.Add64(x24, 0, x29) + _, x32 := bits.Mul64(x26, 0xccd1c8aaee00bc4f) + x35, x34 := bits.Mul64(x32, 0xffffffff00000000) + x37, x36 := bits.Mul64(x32, 0xffffffffffffffff) + x39, x38 := bits.Mul64(x32, 0xbce6faada7179e84) + x41, x40 := bits.Mul64(x32, 0xf3b9cac2fc632551) + x42, x43 := bits.Add64(x41, x38, 0) + x44, x45 := bits.Add64(x39, x36, x43) + x46, x47 := bits.Add64(x37, x34, x45) + _, x49 := bits.Add64(x26, x40, 0) + x50, x51 := bits.Add64(x28, x42, x49) + x52, x53 := bits.Add64(x30, x44, x51) + x54, x55 := bits.Add64(x31+(x25+(x17+x5)), x46, x53) + x56, x57 := bits.Add64(x50, arg1[2], 0) + x58, x59 := bits.Add64(x52, 0, x57) + x60, x61 := bits.Add64(x54, 0, x59) + _, x62 := bits.Mul64(x56, 0xccd1c8aaee00bc4f) + x65, x64 := bits.Mul64(x62, 0xffffffff00000000) + x67, x66 := bits.Mul64(x62, 0xffffffffffffffff) + x69, x68 := bits.Mul64(x62, 0xbce6faada7179e84) + x71, x70 := bits.Mul64(x62, 0xf3b9cac2fc632551) + x72, x73 := bits.Add64(x71, x68, 0) + x74, x75 := bits.Add64(x69, x66, x73) + x76, x77 := bits.Add64(x67, x64, x75) + _, x79 := bits.Add64(x56, x70, 0) + x80, x81 := bits.Add64(x58, x72, x79) + x82, x83 := bits.Add64(x60, x74, x81) + x84, x85 := bits.Add64(x61+(x55+(x47+x35)), x76, x83) + x86, x87 := bits.Add64(x80, arg1[3], 0) + x88, x89 := bits.Add64(x82, 0, x87) + x90, x91 := bits.Add64(x84, 0, x89) + _, x92 := bits.Mul64(x86, 0xccd1c8aaee00bc4f) + x95, x94 := bits.Mul64(x92, 0xffffffff00000000) + x97, x96 := bits.Mul64(x92, 0xffffffffffffffff) + x99, x98 := bits.Mul64(x92, 0xbce6faada7179e84) + x101, x100 := bits.Mul64(x92, 0xf3b9cac2fc632551) + x102, x103 := bits.Add64(x101, x98, 0) + x104, x105 := bits.Add64(x99, x96, x103) + x106, x107 := bits.Add64(x97, x94, x105) + _, x109 := bits.Add64(x86, x100, 0) + x110, x111 := bits.Add64(x88, x102, x109) + x112, x113 := bits.Add64(x90, x104, x111) + x114, x115 := bits.Add64(x91+(x85+(x77+x65)), x106, x113) + x116 := x115 + (x107 + x95) + x117, x118 := bits.Sub64(x110, 0xf3b9cac2fc632551, 0) + x119, x120 := bits.Sub64(x112, 0xbce6faada7179e84, x118) + x121, x122 := bits.Sub64(x114, 0xffffffffffffffff, x120) + x123, x124 := bits.Sub64(x116, 0xffffffff00000000, x122) + mask, _ := bits.Sub64(0, 0, x124) + out1[0] = x117&^mask | x110&mask + out1[1] = x119&^mask | x112&mask + out1[2] = x121&^mask | x114&mask + out1[3] = x123&^mask | x116&mask +} - var xOut [32]byte - p256OrdLittleToBig(&xOut, x) - return xOut[:], nil +// p256OrdToMontgomery translates a field element into the Montgomery domain. +func p256OrdToMontgomery(out1 *p256OrdMontElement, arg1 *p256OrdElement) { + x1 := arg1[1] + x2 := arg1[2] + x3 := arg1[3] + x4 := arg1[0] + x6, x5 := bits.Mul64(x4, 0x66e12d94f3d95620) + x8, x7 := bits.Mul64(x4, 0x2845b2392b6bec59) + x10, x9 := bits.Mul64(x4, 0x4699799c49bd6fa6) + x12, x11 := bits.Mul64(x4, 0x83244c95be79eea2) + x13, x14 := bits.Add64(x12, x9, 0) + x15, x16 := bits.Add64(x10, x7, x14) + x17, x18 := bits.Add64(x8, x5, x16) + _, x19 := bits.Mul64(x11, 0xccd1c8aaee00bc4f) + x22, x21 := bits.Mul64(x19, 0xffffffff00000000) + x24, x23 := bits.Mul64(x19, 0xffffffffffffffff) + x26, x25 := bits.Mul64(x19, 0xbce6faada7179e84) + x28, x27 := bits.Mul64(x19, 0xf3b9cac2fc632551) + x29, x30 := bits.Add64(x28, x25, 0) + x31, x32 := bits.Add64(x26, x23, x30) + x33, x34 := bits.Add64(x24, x21, x32) + _, x36 := bits.Add64(x11, x27, 0) + x37, x38 := bits.Add64(x13, x29, x36) + x39, x40 := bits.Add64(x15, x31, x38) + x41, x42 := bits.Add64(x17, x33, x40) + x43, x44 := bits.Add64(x18+x6, x34+x22, x42) + x46, x45 := bits.Mul64(x1, 0x66e12d94f3d95620) + x48, x47 := bits.Mul64(x1, 0x2845b2392b6bec59) + x50, x49 := bits.Mul64(x1, 0x4699799c49bd6fa6) + x52, x51 := bits.Mul64(x1, 0x83244c95be79eea2) + x53, x54 := bits.Add64(x52, x49, 0) + x55, x56 := bits.Add64(x50, x47, x54) + x57, x58 := bits.Add64(x48, x45, x56) + x59, x60 := bits.Add64(x37, x51, 0) + x61, x62 := bits.Add64(x39, x53, x60) + x63, x64 := bits.Add64(x41, x55, x62) + x65, x66 := bits.Add64(x43, x57, x64) + _, x67 := bits.Mul64(x59, 0xccd1c8aaee00bc4f) + x70, x69 := bits.Mul64(x67, 0xffffffff00000000) + x72, x71 := bits.Mul64(x67, 0xffffffffffffffff) + x74, x73 := bits.Mul64(x67, 0xbce6faada7179e84) + x76, x75 := bits.Mul64(x67, 0xf3b9cac2fc632551) + x77, x78 := bits.Add64(x76, x73, 0) + x79, x80 := bits.Add64(x74, x71, x78) + x81, x82 := bits.Add64(x72, x69, x80) + _, x84 := bits.Add64(x59, x75, 0) + x85, x86 := bits.Add64(x61, x77, x84) + x87, x88 := bits.Add64(x63, x79, x86) + x89, x90 := bits.Add64(x65, x81, x88) + x91, x92 := bits.Add64(x66+x44+(x58+x46), x82+x70, x90) + x94, x93 := bits.Mul64(x2, 0x66e12d94f3d95620) + x96, x95 := bits.Mul64(x2, 0x2845b2392b6bec59) + x98, x97 := bits.Mul64(x2, 0x4699799c49bd6fa6) + x100, x99 := bits.Mul64(x2, 0x83244c95be79eea2) + x101, x102 := bits.Add64(x100, x97, 0) + x103, x104 := bits.Add64(x98, x95, x102) + x105, x106 := bits.Add64(x96, x93, x104) + x107, x108 := bits.Add64(x85, x99, 0) + x109, x110 := bits.Add64(x87, x101, x108) + x111, x112 := bits.Add64(x89, x103, x110) + x113, x114 := bits.Add64(x91, x105, x112) + _, x115 := bits.Mul64(x107, 0xccd1c8aaee00bc4f) + x118, x117 := bits.Mul64(x115, 0xffffffff00000000) + x120, x119 := bits.Mul64(x115, 0xffffffffffffffff) + x122, x121 := bits.Mul64(x115, 0xbce6faada7179e84) + x124, x123 := bits.Mul64(x115, 0xf3b9cac2fc632551) + x125, x126 := bits.Add64(x124, x121, 0) + x127, x128 := bits.Add64(x122, x119, x126) + x129, x130 := bits.Add64(x120, x117, x128) + _, x132 := bits.Add64(x107, x123, 0) + x133, x134 := bits.Add64(x109, x125, x132) + x135, x136 := bits.Add64(x111, x127, x134) + x137, x138 := bits.Add64(x113, x129, x136) + x139, x140 := bits.Add64(x114+x92+(x106+x94), x130+x118, x138) + x142, x141 := bits.Mul64(x3, 0x66e12d94f3d95620) + x144, x143 := bits.Mul64(x3, 0x2845b2392b6bec59) + x146, x145 := bits.Mul64(x3, 0x4699799c49bd6fa6) + x148, x147 := bits.Mul64(x3, 0x83244c95be79eea2) + x149, x150 := bits.Add64(x148, x145, 0) + x151, x152 := bits.Add64(x146, x143, x150) + x153, x154 := bits.Add64(x144, x141, x152) + x155, x156 := bits.Add64(x133, x147, 0) + x157, x158 := bits.Add64(x135, x149, x156) + x159, x160 := bits.Add64(x137, x151, x158) + x161, x162 := bits.Add64(x139, x153, x160) + _, x163 := bits.Mul64(x155, 0xccd1c8aaee00bc4f) + x166, x165 := bits.Mul64(x163, 0xffffffff00000000) + x168, x167 := bits.Mul64(x163, 0xffffffffffffffff) + x170, x169 := bits.Mul64(x163, 0xbce6faada7179e84) + x172, x171 := bits.Mul64(x163, 0xf3b9cac2fc632551) + x173, x174 := bits.Add64(x172, x169, 0) + x175, x176 := bits.Add64(x170, x167, x174) + x177, x178 := bits.Add64(x168, x165, x176) + _, x180 := bits.Add64(x155, x171, 0) + x181, x182 := bits.Add64(x157, x173, x180) + x183, x184 := bits.Add64(x159, x175, x182) + x185, x186 := bits.Add64(x161, x177, x184) + x187, x188 := bits.Add64(x162+x140+(x154+x142), x178+x166, x186) + x189, x190 := bits.Sub64(x181, 0xf3b9cac2fc632551, 0) + x191, x192 := bits.Sub64(x183, 0xbce6faada7179e84, x190) + x193, x194 := bits.Sub64(x185, 0xffffffffffffffff, x192) + x195, x196 := bits.Sub64(x187, 0xffffffff00000000, x194) + _, x198 := bits.Sub64(x188, 0, x196) + mask, _ := bits.Sub64(0, 0, x198) + out1[0] = x189&^mask | x181&mask + out1[1] = x191&^mask | x183&mask + out1[2] = x193&^mask | x185&mask + out1[3] = x195&^mask | x187&mask } diff --git a/src/crypto/internal/fips140/nistec/p256_ordinv_noasm.go b/src/crypto/internal/fips140/nistec/p256_ordinv_noasm.go deleted file mode 100644 index 9cbb1a89dbaf18..00000000000000 --- a/src/crypto/internal/fips140/nistec/p256_ordinv_noasm.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2022 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. - -//go:build (!amd64 && !arm64) || purego - -package nistec - -import "errors" - -func P256OrdInverse(k []byte) ([]byte, error) { - return nil, errors.New("unimplemented") -} diff --git a/src/crypto/internal/fips140test/nistec_ordinv_fips140v1.0_test.go b/src/crypto/internal/fips140test/nistec_ordinv_fips140v1.0_test.go new file mode 100644 index 00000000000000..06d057bdb84a32 --- /dev/null +++ b/src/crypto/internal/fips140test/nistec_ordinv_fips140v1.0_test.go @@ -0,0 +1,30 @@ +// 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. + +//go:build fips140v1.0 || fips140v1.26 + +package fipstest + +import ( + "crypto/internal/fips140/nistec" + "internal/goarch" + "testing" +) + +// package nistec +// func P256OrdInverse(k []byte) ([]byte, error) + +func p256OrdInverse(t *testing.T, k *[4]uint64) { + input := limbsToBytes(*k) + out, err := nistec.P256OrdInverse(input) + if err != nil { + switch goarch.GOARCH { + case "amd64", "arm64": + t.Fatal(err) + default: + t.Skip("this GOARCH didn't have P256OrdInverse in v1.0/v1.26") + } + } + *k = bytesToLimbs(out) +} diff --git a/src/crypto/internal/fips140test/nistec_ordinv_fips140v1.28_test.go b/src/crypto/internal/fips140test/nistec_ordinv_fips140v1.28_test.go new file mode 100644 index 00000000000000..51e99fa6525e8e --- /dev/null +++ b/src/crypto/internal/fips140test/nistec_ordinv_fips140v1.28_test.go @@ -0,0 +1,16 @@ +// 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. + +//go:build !(fips140v1.0 || fips140v1.26) + +package fipstest + +import ( + "crypto/internal/fips140/nistec" + "testing" +) + +func p256OrdInverse(t *testing.T, k *[4]uint64) { + nistec.P256OrdInverse(k) +} diff --git a/src/crypto/internal/fips140test/nistec_ordinv_test.go b/src/crypto/internal/fips140test/nistec_ordinv_test.go index 5eeb3d25268efb..b3eeeb324413e2 100644 --- a/src/crypto/internal/fips140test/nistec_ordinv_test.go +++ b/src/crypto/internal/fips140test/nistec_ordinv_test.go @@ -2,80 +2,83 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (amd64 || arm64) && !purego - package fipstest import ( "bytes" "crypto/elliptic" - "crypto/internal/fips140/nistec" + "internal/byteorder" "math/big" "testing" ) +func bytesToLimbs(b []byte) [4]uint64 { + var l [4]uint64 + l[0] = byteorder.BEUint64(b[24:]) + l[1] = byteorder.BEUint64(b[16:]) + l[2] = byteorder.BEUint64(b[8:]) + l[3] = byteorder.BEUint64(b[:]) + return l +} + +func limbsToBytes(l [4]uint64) []byte { + b := make([]byte, 32) + byteorder.BEPutUint64(b[24:], l[0]) + byteorder.BEPutUint64(b[16:], l[1]) + byteorder.BEPutUint64(b[8:], l[2]) + byteorder.BEPutUint64(b[:], l[3]) + return b +} + func TestP256OrdInverse(t *testing.T) { N := elliptic.P256().Params().N // inv(0) is expected to be 0. zero := make([]byte, 32) - out, err := nistec.P256OrdInverse(zero) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(out, zero) { + k := bytesToLimbs(zero) + p256OrdInverse(t, &k) + if !bytes.Equal(limbsToBytes(k), zero) { t.Error("unexpected output for inv(0)") } // inv(N) is also 0 mod N. input := make([]byte, 32) N.FillBytes(input) - out, err = nistec.P256OrdInverse(input) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(out, zero) { + k = bytesToLimbs(input) + p256OrdInverse(t, &k) + if !bytes.Equal(limbsToBytes(k), zero) { t.Error("unexpected output for inv(N)") } - if !bytes.Equal(input, N.Bytes()) { - t.Error("input was modified") - } // Check inv(1) and inv(N+1) against math/big exp := new(big.Int).ModInverse(big.NewInt(1), N).FillBytes(make([]byte, 32)) big.NewInt(1).FillBytes(input) - out, err = nistec.P256OrdInverse(input) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(out, exp) { + k = bytesToLimbs(input) + p256OrdInverse(t, &k) + if !bytes.Equal(limbsToBytes(k), exp) { t.Error("unexpected output for inv(1)") } + new(big.Int).Add(N, big.NewInt(1)).FillBytes(input) - out, err = nistec.P256OrdInverse(input) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(out, exp) { + k = bytesToLimbs(input) + p256OrdInverse(t, &k) + if !bytes.Equal(limbsToBytes(k), exp) { t.Error("unexpected output for inv(N+1)") } // Check inv(20) and inv(N+20) against math/big exp = new(big.Int).ModInverse(big.NewInt(20), N).FillBytes(make([]byte, 32)) big.NewInt(20).FillBytes(input) - out, err = nistec.P256OrdInverse(input) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(out, exp) { + k = bytesToLimbs(input) + p256OrdInverse(t, &k) + if !bytes.Equal(limbsToBytes(k), exp) { t.Error("unexpected output for inv(20)") } + new(big.Int).Add(N, big.NewInt(20)).FillBytes(input) - out, err = nistec.P256OrdInverse(input) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(out, exp) { + k = bytesToLimbs(input) + p256OrdInverse(t, &k) + if !bytes.Equal(limbsToBytes(k), exp) { t.Error("unexpected output for inv(N+20)") } @@ -84,11 +87,9 @@ func TestP256OrdInverse(t *testing.T) { bigInput.Sub(bigInput, big.NewInt(1)) exp = new(big.Int).ModInverse(bigInput, N).FillBytes(make([]byte, 32)) bigInput.FillBytes(input) - out, err = nistec.P256OrdInverse(input) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(out, exp) { + k = bytesToLimbs(input) + p256OrdInverse(t, &k) + if !bytes.Equal(limbsToBytes(k), exp) { t.Error("unexpected output for inv(2^256-1)") } } diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go index 25bf433edc2b64..e965d09b45654c 100644 --- a/src/database/sql/convert.go +++ b/src/database/sql/convert.go @@ -9,6 +9,7 @@ package sql import ( "bytes" "database/sql/driver" + "database/sql/internal" "errors" "fmt" "reflect" @@ -219,10 +220,25 @@ func driverArgsConnLocked(ci driver.Conn, ds *driverStmt, args []any) ([]driver. // See go.dev/issue/67401. // //go:linkname convertAssign -func convertAssign(dest, src any) error { +func convertAssign(dest any, src any) error { return convertAssignRows(dest, src, nil) } +// ConvertAssign copies the value in src to the value pointed at by dest. +// See the documentation on [Rows.Scan] for details on conversions. +// dest must be a pointer or must implement [Scanner]. +// +// Implementations of [driver.RowsColumnScanner] should pass through +// their [driver.ScanContext] parameter. +// In other cases, pass driver.ScanContext{} as the context. +// +// ConvertAssign is intended for use by driver implementations. +// Most users should not need to use it directly. +func ConvertAssign(scanCtx driver.ScanContext, dest any, src driver.Value) error { + rows, _ := internal.ScanContextValue(internal.ScanContext(scanCtx)).(*Rows) + return convertAssignRows(dest, src, rows) +} + // convertAssignRows copies to dest the value in src, converting it if possible. // An error is returned if the copy would result in loss of information. // dest should be a pointer type. If rows is passed in, the rows will @@ -353,8 +369,8 @@ func convertAssignRows(dest, src any, rows *Rows) error { } // The driver is returning a cursor the client may iterate over. case driver.Rows: - switch d := dest.(type) { - case *Rows: + d, ok := dest.(*Rows) + if ok { if d == nil { return errNilPtr } @@ -387,7 +403,7 @@ func convertAssignRows(dest, src any, rows *Rows) error { parentCancel := rows.cancel rows.cancel = func() { // When Rows.cancel is called, the closemu will be locked as well. - // So we can access rs.lasterr. + // So we can access rows.lasterr. d.close(rows.lasterr) if parentCancel != nil { parentCancel() diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go index 1294d51d8308db..10ae9acbd81f04 100644 --- a/src/database/sql/convert_test.go +++ b/src/database/sql/convert_test.go @@ -624,3 +624,14 @@ func TestDecimal(t *testing.T) { }) } } + +func TestConvertAssignNoContext(t *testing.T) { + const want = 42 + var got int64 + if err := ConvertAssign(driver.ScanContext{}, &got, want); err != nil { + t.Fatalf("ConvertAssign: %v", err) + } + if got != int64(want) { + t.Errorf("after ConvertAssign: got %v, want %v", got, want) + } +} diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go index d0892e80fc28d5..264fd67b8c236d 100644 --- a/src/database/sql/driver/driver.go +++ b/src/database/sql/driver/driver.go @@ -40,6 +40,7 @@ package driver import ( "context" + "database/sql/internal" "errors" "reflect" ) @@ -443,6 +444,28 @@ type Rows interface { Next(dest []Value) error } +// ScanContext carries state related to the current query +// through a [RowsColumnScanner.ScanColumn] function to [database/sql.ConvertAssign]. +type ScanContext internal.ScanContext + +// RowsColumnScanner extends the [Rows] interface by providing a way for the driver +// to scan directly into the user-provided destination. +// +// RowsColumnScanner supersedes the [Rows.Next] method. +type RowsColumnScanner interface { + Rows + + // NextRow advances to the next row of data. + // It should return io.EOF when there are no more rows. + NextRow() error + + // ScanColumn copies the column at the given index in the current row + // into the value pointed to by dest. + // + // The driver may assign a driver.Value to dest using [database/sql.ConvertAssign]. + ScanColumn(scanCtx ScanContext, index int, dest any) error +} + // RowsNextResultSet extends the [Rows] interface by providing a way to signal // the driver to advance to the next result set. type RowsNextResultSet interface { diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go index 23715e221f2149..d59275aa2c75f5 100644 --- a/src/database/sql/fakedb_test.go +++ b/src/database/sql/fakedb_test.go @@ -79,6 +79,16 @@ func (c *fakeConn) getFakeConn() *fakeConn { return c } +func getRowsCursor(rs *Rows) *rowsCursor { + return rs.rowsi.(interface { + getRowsCursor() *rowsCursor + }).getRowsCursor() +} + +func (rc *rowsCursor) getRowsCursor() *rowsCursor { + return rc +} + func (c *fakeConnector) Driver() driver.Driver { return fdriver } @@ -1242,6 +1252,8 @@ func converterForType(typ string) driver.ValueConverter { return driver.NotNull{Converter: driver.DefaultParameterConverter} case "nulluuid": return driver.Null{Converter: driver.DefaultParameterConverter} + case "nulltable": + return driver.Null{Converter: driver.DefaultParameterConverter} case "any": return anyTypeConverter{} } diff --git a/src/database/sql/internal/sql.go b/src/database/sql/internal/sql.go new file mode 100644 index 00000000000000..7b7769a0556a67 --- /dev/null +++ b/src/database/sql/internal/sql.go @@ -0,0 +1,22 @@ +// 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 internal contains internal symbols shared between +// database/sql and database/sql/driver. +package internal + +// ScanContext is database/sql/driver.ScanContext. +// We define it here so driver.ScanContext can be opaque to users but +// visible to database/sql. +type ScanContext struct { + v any +} + +func NewScanContext(v any) ScanContext { + return ScanContext{v} +} + +func ScanContextValue(c ScanContext) any { + return c.v +} diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index 6b7d073359ead3..a7dc70b281b3e1 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -18,6 +18,7 @@ package sql import ( "context" "database/sql/driver" + "database/sql/internal" "errors" "fmt" "io" @@ -2963,10 +2964,16 @@ type Rows struct { // expected not to be called concurrently. hitEOF bool + // nextCalled is set by the first call to Next. + nextCalled bool + // lastcols is only used in Scan, Next, and NextResultSet which are expected // not to be called concurrently. lastcols []driver.Value + // numCols is the number of columns, and is initialized by the first Next call. + numCols int + // raw is a buffer for RawBytes that persists between Scan calls. // This is used when the driver returns a mismatched type that requires // a cloning allocation. For example, if the driver returns a *string and @@ -3065,11 +3072,20 @@ func (rs *Rows) nextLocked() (doClose, ok bool) { rs.dc.Lock() defer rs.dc.Unlock() - if rs.lastcols == nil { - rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns())) + if !rs.nextCalled { + rs.numCols = len(rs.rowsi.Columns()) + rs.nextCalled = true + } + + if rscan, ok := rs.rowsi.(driver.RowsColumnScanner); ok { + rs.lasterr = rscan.NextRow() + } else { + if rs.lastcols == nil { + rs.lastcols = make([]driver.Value, rs.numCols) + } + rs.lasterr = rs.rowsi.Next(rs.lastcols) } - rs.lasterr = rs.rowsi.Next(rs.lastcols) if rs.lasterr != nil { // Close the connection if there is a driver error. if rs.lasterr != io.EOF { @@ -3117,6 +3133,7 @@ func (rs *Rows) NextResultSet() bool { return false } + rs.nextCalled = false rs.lastcols = nil nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet) if !ok { @@ -3386,6 +3403,11 @@ func (rs *Rows) Scan(dest ...any) error { return err } +// rowsScanContext is used to pass a *Rows through ScanColumn into ConvertAssign. +type rowsScanContext struct { + rs *Rows +} + func (rs *Rows) scanLocked(dest ...any) error { if rs.lasterr != nil && rs.lasterr != io.EOF { return rs.lasterr @@ -3394,11 +3416,26 @@ func (rs *Rows) scanLocked(dest ...any) error { return rs.lasterrOrErrLocked(errRowsClosed) } - if rs.lastcols == nil { + if !rs.nextCalled { return errors.New("sql: Scan called without calling Next") } - if len(dest) != len(rs.lastcols) { - return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) + if len(dest) != rs.numCols { + return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", rs.numCols, len(dest)) + } + + if rscan, ok := rs.rowsi.(driver.RowsColumnScanner); ok { + // Lock the driver connection before calling the driver interface + // rowsi to prevent a Tx from rolling back the connection at the same time. + rs.dc.Lock() + defer rs.dc.Unlock() + + for i, d := range dest { + scanCtx := driver.ScanContext(internal.NewScanContext(rs)) + if err := rscan.ScanColumn(scanCtx, i, d); err != nil { + return fmt.Errorf(`sql: Scan error on column index %d, name %q: %w`, i, rs.rowsi.Columns()[i], err) + } + } + return nil } for i, sv := range rs.lastcols { diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index e202213a4b4f02..1763b3bd3ab24e 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -70,6 +70,16 @@ Test: "Validator", }, }, + { + name: "scancols", + connector: &rowsColumnScannerConnector{name: fakeDBName}, + features: []string{ + "ConnBeginTx", + "NamedValue", + "Validator", + "ScanColumn", + }, + }, } { for _, req := range require { if !slices.Contains(test.features, req) { @@ -1671,6 +1681,68 @@ func testCursorFake(t *testing.T, db *DB) { } } +func TestCursorDoubleRowsPointer(t *testing.T) { + testDatabase(t, testCursorDoubleRowsPointer) +} +func testCursorDoubleRowsPointer(t *testing.T, db *DB) { + exec(t, db, "CREATE|table1|col=string") + exec(t, db, "INSERT|table1|col=value") + exec(t, db, "CREATE|cursor|list=table") + exec(t, db, "INSERT|cursor|list=table1!col") + + rows, err := db.QueryContext(t.Context(), `SELECT|cursor|list|`) + if err != nil { + t.Fatal(err) + } + defer rows.Close() + + if !rows.Next() { + t.Fatal("no rows") + } + var cursor *Rows + if err := rows.Scan(&cursor); err != nil { + t.Fatal(err) + } + defer cursor.Close() + + if !cursor.Next() { + t.Fatal("no child rows") + } + var col string + if err := cursor.Scan(&col); err != nil { + t.Fatal(err) + } + if got, want := col, "value"; got != want { + t.Errorf("read col=%q, want %q", got, want) + } +} + +func TestCursorNull(t *testing.T) { + testDatabase(t, testCursorNull) +} +func testCursorNull(t *testing.T, db *DB) { + exec(t, db, "CREATE|cursor|list=nulltable") + exec(t, db, "INSERT|cursor|list=?", nil) + + rows, err := db.QueryContext(t.Context(), `SELECT|cursor|list|`) + if err != nil { + t.Fatal(err) + } + defer rows.Close() + + if !rows.Next() { + t.Fatal("no rows") + } + + var cursor *Rows + if err := rows.Scan(&cursor); err != nil { + t.Fatal(err) + } + if cursor != nil { + t.Errorf("Scan returned cursor, expected nil") + } +} + // TestCursorCancel exercises calling Rows.Close at various places, // including canceling a cursor (child Rows). func TestCursorCancel(t *testing.T) { @@ -2949,7 +3021,7 @@ func testRowsImplicitClose(t *testing.T, db *DB) { } want, fail := 2, errors.New("fail") - r := rows.rowsi.(*rowsCursor) + r := getRowsCursor(rows) r.errPos, r.err = want, fail got := 0 @@ -2982,10 +3054,7 @@ func testRowsCloseError(t *testing.T, db *DB) { } got := []row{} - rc, ok := rows.rowsi.(*rowsCursor) - if !ok { - t.Fatal("not using *rowsCursor") - } + rc := getRowsCursor(rows) rc.closeErr = errors.New("rowsCursor: failed to close") for rows.Next() { @@ -5594,3 +5663,114 @@ func TestNullTypeScanNil(t *testing.T) { }) } } + +type testStringType struct { + s string +} + +func TestQueryRowsScanner(t *testing.T) { + testDatabase(t, testQueryRowsScanner, requireFeature("ScanColumn")) +} +func testQueryRowsScanner(t *testing.T, db *DB) { + populate(t, db, "people") + rows, err := db.Query("SELECT|people|age,name|") + if err != nil { + t.Fatalf("Query: %v", err) + } + defer rows.Close() + type row struct { + age int + name testStringType + } + got := []row{} + for rows.Next() { + var r row + err = rows.Scan(&r.age, &r.name) + if err != nil { + t.Fatalf("Scan: %v", err) + } + got = append(got, r) + } + err = rows.Err() + if err != nil { + t.Fatalf("Err: %v", err) + } + want := []row{ + {age: 1, name: testStringType{"Alice"}}, + {age: 2, name: testStringType{"Bob"}}, + {age: 3, name: testStringType{"Chris"}}, + } + if !slices.Equal(got, want) { + t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want) + } +} + +type rowsColumnScannerConnector struct { + fakeConnector +} + +func (c *rowsColumnScannerConnector) Connect(ctx context.Context) (driver.Conn, error) { + conn, err := c.fakeConnector.Connect(ctx) + fc := getFakeConn(conn) + return &rowsColumnScannerConn{fc}, err +} + +// rowsColumnScannerConn is a Conn with rows that implement RowsColumnScanner. +type rowsColumnScannerConn struct { + *fakeConn +} + +func (s *rowsColumnScannerConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { + stmt, err := s.fakeConn.PrepareContext(ctx, query) + if err != nil { + return nil, err + } + return &rowsColumnScannerStmt{stmt.(*fakeStmt)}, nil +} + +type rowsColumnScannerStmt struct { + *fakeStmt +} + +func (s *rowsColumnScannerStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { + rows, err := s.fakeStmt.QueryContext(ctx, args) + if err != nil { + return nil, err + } + return &rowsColumnScannerRows{rowsCursor: rows.(*rowsCursor)}, nil +} + +type rowsColumnScannerRows struct { + *rowsCursor + row []driver.Value +} + +func (c *rowsColumnScannerRows) NextRow() error { + if c.row == nil { + c.row = make([]driver.Value, len(c.rowsCursor.Columns())) + } + return c.rowsCursor.Next(c.row) +} + +func (c *rowsColumnScannerRows) NextResultSet() error { + c.row = nil + return c.rowsCursor.NextResultSet() +} + +func (c *rowsColumnScannerRows) ScanColumn(ctx driver.ScanContext, index int, dest any) error { + if index < 0 || index >= len(c.row) { + return fmt.Errorf("index %v out of range", index) + } + switch d := dest.(type) { + case *testStringType: + switch s := c.row[index].(type) { + case string: + d.s = s + return nil + case []byte: + d.s = string(s) + return nil + } + } + return ConvertAssign(ctx, dest, c.row[index]) +} diff --git a/src/encoding/json/jsontext/decode.go b/src/encoding/json/jsontext/decode.go index fead9ae657d7bf..282011f1079aa2 100644 --- a/src/encoding/json/jsontext/decode.go +++ b/src/encoding/json/jsontext/decode.go @@ -1134,6 +1134,17 @@ func (d *Decoder) InputOffset() int64 { // UnreadBuffer returns the data remaining in the unread buffer, // which may contain zero or more bytes. +// This is the data already consumed from the input [io.Reader], +// but not yet read by a [Decoder.ReadToken] or [Decoder.ReadValue] call. +// It may contain bytes that do not form valid JSON as it has not yet +// been validated according to the JSON grammar. +// The exact amount of buffered data is an implementation detail +// of the Decoder and may change over time. +// +// It is the caller's responsibility to concatenate this buffer with +// the remainder of the input Reader to obtain the full sequence +// of bytes after the last read JSON token or value. +// // The returned buffer must not be mutated while Decoder continues to be used. // The buffer contents are valid until the next Peek, Read, or Skip call. func (d *Decoder) UnreadBuffer() []byte { diff --git a/src/encoding/json/stream.go b/src/encoding/json/stream.go index fc480c994651d2..eb3907c39a0bc6 100644 --- a/src/encoding/json/stream.go +++ b/src/encoding/json/stream.go @@ -80,8 +80,20 @@ func (dec *Decoder) Decode(v any) error { return err } -// Buffered returns a reader of the data remaining in the Decoder's -// buffer. The reader is valid until the next call to [Decoder.Decode]. +// Buffered returns a reader of the data remaining in the unread buffer, +// which may contain zero or more bytes. +// This is the data already consumed from the input [io.Reader], +// but not yet read by a [Decoder.Decode] or [Decoder.Token] call. +// It may contain bytes that do not form valid JSON as it has not yet +// been validated according to the JSON grammar. +// The exact amount of buffered data is an implementation detail +// of the Decoder and may change over time. +// +// It is the caller's responsibility to concatenate this buffer with +// the remainder of the input Reader to obtain the full sequence +// of bytes after the last decoded JSON value. +// +// The reader is valid until the next call to [Decoder.Decode] or [Decoder.Token]. func (dec *Decoder) Buffered() io.Reader { return bytes.NewReader(dec.buf[dec.scanp:]) } diff --git a/src/encoding/json/v2_decode.go b/src/encoding/json/v2_decode.go index 959b31d78a970e..4a96d3d92562d2 100644 --- a/src/encoding/json/v2_decode.go +++ b/src/encoding/json/v2_decode.go @@ -35,12 +35,22 @@ import ( // the value pointed at by the pointer. If the pointer is nil, Unmarshal // allocates a new value for it to point to. // -// To unmarshal JSON into a value implementing [Unmarshaler], -// Unmarshal calls that value's [Unmarshaler.UnmarshalJSON] method, including -// when the input is a JSON null. -// Otherwise, if the value implements [encoding.TextUnmarshaler] -// and the input is a JSON quoted string, Unmarshal calls -// [encoding.TextUnmarshaler.UnmarshalText] with the unquoted form of the string. +// The JSON input is decoded according the following rules: +// +// - If the value type implements [jsonv2.UnmarshalerFrom], +// then the UnmarshalJSONFrom method is called to decode the JSON value. +// If the method returns [errors.ErrUnsupported], +// then the input is decoded according to subsequent rules. +// +// - If the value type implements [Unmarshaler], +// then the UnmarshalJSON method is called to decode the JSON value, +// including when the input is a JSON null. +// +// - If the value implements [encoding.TextUnmarshaler] and +// the input is a JSON string, then the UnmarshalText method +// is called with the unquoted form of the string. +// +// Otherwise, Unmarshal uses the following type-dependent default decodings: // // To unmarshal JSON into a struct, Unmarshal matches incoming object // keys to the keys used by [Marshal] (either the struct field name or its tag), diff --git a/src/encoding/json/v2_encode.go b/src/encoding/json/v2_encode.go index b5d27c3b34802d..a704e2ad22e66d 100644 --- a/src/encoding/json/v2_encode.go +++ b/src/encoding/json/v2_encode.go @@ -33,7 +33,7 @@ import ( // // The input value is encoded as JSON according the following rules: // -// - If the value type implements [encoding/json/v2.MarshalerTo], +// - If the value type implements [jsonv2.MarshalerTo], // then the MarshalJSONTo method is called to encode the value. // If the method returns [errors.ErrUnsupported], // then the input is encoded according to subsequent rules. diff --git a/src/encoding/json/v2_stream.go b/src/encoding/json/v2_stream.go index 68cc2591104e79..11656d94bd8dac 100644 --- a/src/encoding/json/v2_stream.go +++ b/src/encoding/json/v2_stream.go @@ -85,8 +85,20 @@ func (dec *Decoder) Decode(v any) error { return jsonv2.Unmarshal(b, v, dec.opts) } -// Buffered returns a reader of the data remaining in the Decoder's -// buffer. The reader is valid until the next call to [Decoder.Decode]. +// Buffered returns a reader of the data remaining in the unread buffer, +// which may contain zero or more bytes. +// This is the data already consumed from the input [io.Reader], +// but not yet read by a [Decoder.Decode] or [Decoder.Token] call. +// It may contain bytes that do not form valid JSON as it has not yet +// been validated according to the JSON grammar. +// The exact amount of buffered data is an implementation detail +// of the Decoder and may change over time. +// +// It is the caller's responsibility to concatenate this buffer with +// the remainder of the input Reader to obtain the full sequence +// of bytes after the last decoded JSON value. +// +// The reader is valid until the next call to [Decoder.Decode] or [Decoder.Token]. func (dec *Decoder) Buffered() io.Reader { return bytes.NewReader(dec.dec.UnreadBuffer()) } diff --git a/src/go/types/api.go b/src/go/types/api.go index cb6972992e6b56..3dbc35edb51f27 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -445,6 +445,10 @@ type Instance struct { Type Type } +func (inst Instance) String() string { + return fmt.Sprintf("%s%s", inst.TypeArgs, inst.Type) +} + // An Initializer describes a package-level variable, or a list of variables in case // of a multi-valued initialization expression, and the corresponding initialization // expression. diff --git a/src/go/types/typelists.go b/src/go/types/typelists.go index a857ea02a981bf..8d86f518b29d87 100644 --- a/src/go/types/typelists.go +++ b/src/go/types/typelists.go @@ -7,6 +7,8 @@ package types +import "bytes" + // TypeParamList holds a list of type parameters. type TypeParamList struct{ tparams []*TypeParam } @@ -27,6 +29,19 @@ func (l *TypeParamList) list() []*TypeParam { return l.tparams } +func (l *TypeParamList) String() string { + var buf bytes.Buffer + buf.WriteByte('[') + for i, tparam := range l.tparams { + if i > 0 { + buf.WriteString(", ") + } + WriteType(&buf, tparam, nil) + } + buf.WriteByte(']') + return buf.String() +} + // TypeList holds a list of types. type TypeList struct{ types []Type } @@ -55,6 +70,19 @@ func (l *TypeList) list() []Type { return l.types } +func (l *TypeList) String() string { + var buf bytes.Buffer + buf.WriteByte('[') + for i, t := range l.types { + if i > 0 { + buf.WriteString(", ") + } + WriteType(&buf, t, nil) + } + buf.WriteByte(']') + return buf.String() +} + // ---------------------------------------------------------------------------- // Implementation diff --git a/src/internal/cpu/cpu_windows.go b/src/internal/cpu/cpu_windows.go index e921a9566ea767..3587c0dbbbad07 100644 --- a/src/internal/cpu/cpu_windows.go +++ b/src/internal/cpu/cpu_windows.go @@ -16,5 +16,5 @@ const ( // isProcessorFeaturePresent calls windows IsProcessorFeaturePresent API. // -//go:linkname isProcessorFeaturePresent +//go:linknamestd isProcessorFeaturePresent func isProcessorFeaturePresent(processorFeature uint32) bool // Implemented in runtime package. diff --git a/src/internal/runtime/atomic/linkname.go b/src/internal/runtime/atomic/linkname.go index e5ddcf76e0dd96..72636465fb87d7 100644 --- a/src/internal/runtime/atomic/linkname.go +++ b/src/internal/runtime/atomic/linkname.go @@ -8,27 +8,27 @@ import _ "unsafe" // for linkname // Export some functions via linkname to assembly in sync/atomic. // -//go:linkname Load -//go:linkname Loadp -//go:linkname Load64 -//go:linkname Loaduintptr -//go:linkname Xadd -//go:linkname Xadd64 -//go:linkname Xadduintptr -//go:linkname Xchg -//go:linkname Xchg64 -//go:linkname Xchguintptr -//go:linkname Cas -//go:linkname Cas64 -//go:linkname Casint32 -//go:linkname Casint64 -//go:linkname Casuintptr -//go:linkname Store -//go:linkname Store64 -//go:linkname Storeuintptr -//go:linkname And32 -//go:linkname And64 -//go:linkname Anduintptr -//go:linkname Or32 -//go:linkname Or64 -//go:linkname Oruintptr +//go:linknamestd Load +//go:linknamestd Loadp +//go:linknamestd Load64 +//go:linknamestd Loaduintptr +//go:linknamestd Xadd +//go:linknamestd Xadd64 +//go:linknamestd Xadduintptr +//go:linknamestd Xchg +//go:linknamestd Xchg64 +//go:linknamestd Xchguintptr +//go:linknamestd Cas +//go:linknamestd Cas64 +//go:linknamestd Casint32 +//go:linknamestd Casint64 +//go:linknamestd Casuintptr +//go:linknamestd Store +//go:linknamestd Store64 +//go:linknamestd Storeuintptr +//go:linknamestd And32 +//go:linknamestd And64 +//go:linknamestd Anduintptr +//go:linknamestd Or32 +//go:linknamestd Or64 +//go:linknamestd Oruintptr diff --git a/src/internal/runtime/maps/runtime.go b/src/internal/runtime/maps/runtime.go index 39017c6fa88b65..4b6051d73ee84c 100644 --- a/src/internal/runtime/maps/runtime.go +++ b/src/internal/runtime/maps/runtime.go @@ -19,7 +19,7 @@ import ( //go:linkname fatal func fatal(s string) -//go:linkname bootstrapRand runtime.bootstrapRand +//go:linknamestd bootstrapRand runtime.bootstrapRand func bootstrapRand() uint64 //go:linkname rand diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go index 75042bf165bf4f..57b8c6f70a3698 100644 --- a/src/net/netip/netip.go +++ b/src/net/netip/netip.go @@ -97,7 +97,7 @@ func AddrFrom4(addr [4]byte) Addr { // AddrFrom16 returns the IPv6 address given by the bytes in addr. // An IPv4-mapped IPv6 address is left as an IPv6 address. -// (Use Unmap to convert them if needed.) +// (Use [Addr.Unmap] to convert them if needed.) func AddrFrom16(addr [16]byte) Addr { return Addr{ addr: uint128{ @@ -957,13 +957,13 @@ func (ip Addr) StringExpanded() string { return string(ret) } -// AppendText implements the [encoding.TextAppender] interface, -// It is the same as [Addr.AppendTo]. +// AppendText implements the [encoding.TextAppender] interface. +// The encoding is the same as returned by [Addr.AppendTo]. func (ip Addr) AppendText(b []byte) ([]byte, error) { return ip.AppendTo(b), nil } -// MarshalText implements the [encoding.TextMarshaler] interface, +// MarshalText implements the [encoding.TextMarshaler] interface. // The encoding is the same as returned by [Addr.String], with one exception: // If ip is the zero [Addr], the encoding is the empty string. func (ip Addr) MarshalText() ([]byte, error) { @@ -985,7 +985,7 @@ func (ip Addr) MarshalText() ([]byte, error) { return ip.AppendText(buf) } -// UnmarshalText implements the encoding.TextUnmarshaler interface. +// UnmarshalText implements the [encoding.TextUnmarshaler] interface. // The IP address is expected in a form accepted by [ParseAddr]. // // If text is empty, UnmarshalText sets *ip to the zero [Addr] and @@ -1034,7 +1034,7 @@ func (ip Addr) MarshalBinary() ([]byte, error) { } // UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface. -// It expects data in the form generated by MarshalBinary. +// It expects data in the form generated by [Addr.MarshalBinary]. func (ip *Addr) UnmarshalBinary(b []byte) error { n := len(b) switch { @@ -1201,15 +1201,15 @@ func (p AddrPort) AppendTo(b []byte) []byte { return b } -// AppendText implements the [encoding.TextAppender] interface. The -// encoding is the same as returned by [AddrPort.AppendTo]. +// AppendText implements the [encoding.TextAppender] interface. +// The encoding is the same as returned by [AddrPort.AppendTo]. func (p AddrPort) AppendText(b []byte) ([]byte, error) { return p.AppendTo(b), nil } -// MarshalText implements the [encoding.TextMarshaler] interface. The -// encoding is the same as returned by [AddrPort.String], with one exception: if -// p.Addr() is the zero [Addr], the encoding is the empty string. +// MarshalText implements the [encoding.TextMarshaler] interface. +// The encoding is the same as returned by [AddrPort.String], with one exception: +// If p.Addr() is the zero [Addr], the encoding is the empty string. func (p AddrPort) MarshalText() ([]byte, error) { buf := []byte{} switch p.ip.z { @@ -1224,9 +1224,9 @@ func (p AddrPort) MarshalText() ([]byte, error) { return p.AppendText(buf) } -// UnmarshalText implements the encoding.TextUnmarshaler -// interface. The [AddrPort] is expected in a form -// generated by [AddrPort.MarshalText] or accepted by [ParseAddrPort]. +// UnmarshalText implements the [encoding.TextUnmarshaler] interface. +// The [AddrPort] is expected in a form generated by [AddrPort.MarshalText] or +// accepted by [ParseAddrPort]. func (p *AddrPort) UnmarshalText(text []byte) error { if len(text) == 0 { *p = AddrPort{} @@ -1512,9 +1512,9 @@ func (p Prefix) AppendText(b []byte) ([]byte, error) { return p.AppendTo(b), nil } -// MarshalText implements the [encoding.TextMarshaler] interface, +// MarshalText implements the [encoding.TextMarshaler] interface. // The encoding is the same as returned by [Prefix.String], with one exception: -// If p is the zero value, the encoding is the empty string. +// If p is the zero [Prefix], the encoding is the empty string. func (p Prefix) MarshalText() ([]byte, error) { buf := []byte{} switch p.ip.z { @@ -1529,7 +1529,7 @@ func (p Prefix) MarshalText() ([]byte, error) { return p.AppendText(buf) } -// UnmarshalText implements the encoding.TextUnmarshaler interface. +// UnmarshalText implements the [encoding.TextUnmarshaler] interface. // The IP address is expected in a form accepted by [ParsePrefix] // or generated by [Prefix.MarshalText]. func (p *Prefix) UnmarshalText(text []byte) error { @@ -1542,7 +1542,7 @@ func (p *Prefix) UnmarshalText(text []byte) error { return err } -// AppendBinary implements the [encoding.AppendMarshaler] interface. +// AppendBinary implements the [encoding.BinaryAppender] interface. // It returns [Addr.AppendBinary] with an additional byte appended // containing the prefix bits. func (p Prefix) AppendBinary(b []byte) ([]byte, error) { diff --git a/src/reflect/type.go b/src/reflect/type.go index f03bc322fec8fd..cb040037b771b1 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -1719,6 +1719,8 @@ func haveIdenticalUnderlyingType(T, V *abi.Type, cmpTags bool) bool { // there can be more than one with a given string. // Only types we might want to look up are included: // pointers, channels, maps, slices, and arrays. +// +//go:linknamestd compiledTypelinks func compiledTypelinks() ([]*abi.Type, [][]*abi.Type) // rtypeOff should be an internal detail, @@ -2775,7 +2777,7 @@ func adjustAIXGCData(addr *byte) *byte { // adjustAIXGCDataForRuntime adjusts the GCData field pointer // as the runtime requires for AIX. See runtime.getGCMaskOnDemand. // -//go:linkname adjustAIXGCDataForRuntime +//go:linknamestd adjustAIXGCDataForRuntime //go:noescape func adjustAIXGCDataForRuntime(*byte) *byte diff --git a/src/runtime/_mkmalloc/constants.go b/src/runtime/_mkmalloc/constants.go index 7c7424eb6189f9..57fa8a7b46bb4e 100644 --- a/src/runtime/_mkmalloc/constants.go +++ b/src/runtime/_mkmalloc/constants.go @@ -27,5 +27,5 @@ const ( // size classes, and with the wrapper they are sometimes slower // than the non-specialized functions. // This must match the constant in the compiler. - specializedMallocMax = 128 + specializedMallocMax = 80 ) diff --git a/src/runtime/_mkmalloc/mkmalloc.go b/src/runtime/_mkmalloc/mkmalloc.go index f777349d9cbda6..892cfdcc8e97ae 100644 --- a/src/runtime/_mkmalloc/mkmalloc.go +++ b/src/runtime/_mkmalloc/mkmalloc.go @@ -888,16 +888,16 @@ package runtime import "unsafe" -var mallocScanTable = [129]func(size uintptr, typ *_type, needzero bool) unsafe.Pointer{`) +var mallocScanTable = [%d]func(size uintptr, typ *_type, needzero bool) unsafe.Pointer{`, specializedMallocMax+1) for i := range uintptr(specializedMallocMax + 1) { fmt.Fprintf(&b, "%s,\n", smallScanNoHeaderSCFuncName(sizeToSizeClass[i], scMax)) } - fmt.Fprintln(&b, ` + fmt.Fprintf(&b, ` } -var mallocNoScanTable = [129]func(size uintptr, typ *_type, needzero bool) unsafe.Pointer{`) +var mallocNoScanTable = [%d]func(size uintptr, typ *_type, needzero bool) unsafe.Pointer{`, specializedMallocMax+1) for i := range uintptr(specializedMallocMax + 1) { if i < 16 { fmt.Fprintf(&b, "%s,\n", "mallocPanic") diff --git a/src/runtime/malloc_generated.go b/src/runtime/malloc_generated.go index 0c2c115e7db2f1..2cced2341696a2 100644 --- a/src/runtime/malloc_generated.go +++ b/src/runtime/malloc_generated.go @@ -1053,663 +1053,17 @@ func mallocgcSmallScanNoHeaderSC7(size uintptr, typ *_type, needzero bool) unsaf return x } -func mallocgcSmallScanNoHeaderSC8(size uintptr, typ *_type, needzero bool) unsafe.Pointer { - - forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) - - if forceSlowPath { - - const spc = spanClass(8<<1) | spanClass(0) - const elemsize = uintptr(96) - return mallocgcSmallScanSlowPath(size, typ, needzero, spc, elemsize) - } - - if doubleCheckMalloc { - if gcphase == _GCmarktermination { - throw("mallocgc called with gcphase == _GCmarktermination") - } - } - - lockRankMayQueueFinalizer() - - const sizeclass = 8 - const elemsize = 96 - - mp := acquirem() - if doubleCheckMalloc { - - doubleCheckSmallScanNoHeader(size, typ, mp) - - } - mp.mallocing = 1 - - checkGCTrigger := false - c := getMCache(mp) - const spc = spanClass(sizeclass<<1) | spanClass(0) - span := c.alloc[spc] - - var v gclinkptr - var x unsafe.Pointer - - { - - var nextFreeFastResult gclinkptr - if span.allocCache != 0 { - theBit := sys.TrailingZeros64(span.allocCache) - result := span.freeindex + uint16(theBit) - if result < span.nelems { - freeidx := result + 1 - if !(freeidx%64 == 0 && freeidx != span.nelems) { - span.allocCache >>= uint(theBit + 1) - span.freeindex = freeidx - span.allocCount++ - nextFreeFastResult = gclinkptr(uintptr(result)*elemsize + span.base()) - } - } - } - v = nextFreeFastResult - if v == 0 { - v, span, checkGCTrigger = c.nextFree(spc) - } - x = unsafe.Pointer(v) - } - - if span.needzero != 0 { - memclrNoHeapPointers(x, elemsize) - } - if goarch.PtrSize == 8 && elemsize == 8 { - - c.scanAlloc += 8 - } else { - dataSize := size - x := uintptr(x) - - if doubleCheckHeapSetType && (!heapBitsInSpan(dataSize) || !heapBitsInSpan(96)) { - throw("tried to write heap bits, but no heap bits in span") - } - - src0 := readUintptr(getGCMask(typ)) - - const elemsize = 96 - - var scanSize uintptr - src := src0 - if typ.Size_ == goarch.PtrSize { - src = (1 << (dataSize / goarch.PtrSize)) - 1 - - scanSize = dataSize - } else { - - if doubleCheckHeapSetType && !asanenabled && dataSize%typ.Size_ != 0 { - throw("runtime: (*mspan).writeHeapBitsSmall: dataSize is not a multiple of typ.Size_") - } - scanSize = typ.PtrBytes - for i := typ.Size_; i < dataSize; i += typ.Size_ { - src |= src0 << (i / goarch.PtrSize) - scanSize += typ.Size_ - } - } - - dstBase, _ := spanHeapBitsRange(span.base(), pageSize, elemsize) - dst := unsafe.Pointer(dstBase) - o := (x - span.base()) / goarch.PtrSize - i := o / ptrBits - j := o % ptrBits - var bits uintptr = elemsize / goarch.PtrSize - - var bitsIsPowerOfTwo = bits&(bits-1) == 0 - if bits > ptrBits || (!bitsIsPowerOfTwo && j+bits > ptrBits) { - - bits0 := ptrBits - j - bits1 := bits - bits0 - dst0 := (*uintptr)(add(dst, (i+0)*goarch.PtrSize)) - dst1 := (*uintptr)(add(dst, (i+1)*goarch.PtrSize)) - *dst0 = (*dst0)&(^uintptr(0)>>bits0) | (src << j) - *dst1 = (*dst1)&^((1<> bits0) - } else { - - dst := (*uintptr)(add(dst, i*goarch.PtrSize)) - *dst = (*dst)&^(((1<<(min(bits, ptrBits)))-1)< 0) - - if forceSlowPath { - - const spc = spanClass(9<<1) | spanClass(0) - const elemsize = uintptr(112) - return mallocgcSmallScanSlowPath(size, typ, needzero, spc, elemsize) - } - - if doubleCheckMalloc { - if gcphase == _GCmarktermination { - throw("mallocgc called with gcphase == _GCmarktermination") - } - } - - lockRankMayQueueFinalizer() - - const sizeclass = 9 - const elemsize = 112 - - mp := acquirem() - if doubleCheckMalloc { - - doubleCheckSmallScanNoHeader(size, typ, mp) - - } - mp.mallocing = 1 - - checkGCTrigger := false - c := getMCache(mp) - const spc = spanClass(sizeclass<<1) | spanClass(0) - span := c.alloc[spc] - - var v gclinkptr - var x unsafe.Pointer - - { - - var nextFreeFastResult gclinkptr - if span.allocCache != 0 { - theBit := sys.TrailingZeros64(span.allocCache) - result := span.freeindex + uint16(theBit) - if result < span.nelems { - freeidx := result + 1 - if !(freeidx%64 == 0 && freeidx != span.nelems) { - span.allocCache >>= uint(theBit + 1) - span.freeindex = freeidx - span.allocCount++ - nextFreeFastResult = gclinkptr(uintptr(result)*elemsize + span.base()) - } - } - } - v = nextFreeFastResult - if v == 0 { - v, span, checkGCTrigger = c.nextFree(spc) - } - x = unsafe.Pointer(v) - } - - if span.needzero != 0 { - memclrNoHeapPointers(x, elemsize) - } - if goarch.PtrSize == 8 && elemsize == 8 { - - c.scanAlloc += 8 - } else { - dataSize := size - x := uintptr(x) - - if doubleCheckHeapSetType && (!heapBitsInSpan(dataSize) || !heapBitsInSpan(112)) { - throw("tried to write heap bits, but no heap bits in span") - } - - src0 := readUintptr(getGCMask(typ)) - - const elemsize = 112 - - var scanSize uintptr - src := src0 - if typ.Size_ == goarch.PtrSize { - src = (1 << (dataSize / goarch.PtrSize)) - 1 - - scanSize = dataSize - } else { - - if doubleCheckHeapSetType && !asanenabled && dataSize%typ.Size_ != 0 { - throw("runtime: (*mspan).writeHeapBitsSmall: dataSize is not a multiple of typ.Size_") - } - scanSize = typ.PtrBytes - for i := typ.Size_; i < dataSize; i += typ.Size_ { - src |= src0 << (i / goarch.PtrSize) - scanSize += typ.Size_ - } - } - - dstBase, _ := spanHeapBitsRange(span.base(), pageSize, elemsize) - dst := unsafe.Pointer(dstBase) - o := (x - span.base()) / goarch.PtrSize - i := o / ptrBits - j := o % ptrBits - var bits uintptr = elemsize / goarch.PtrSize - - var bitsIsPowerOfTwo = bits&(bits-1) == 0 - if bits > ptrBits || (!bitsIsPowerOfTwo && j+bits > ptrBits) { - - bits0 := ptrBits - j - bits1 := bits - bits0 - dst0 := (*uintptr)(add(dst, (i+0)*goarch.PtrSize)) - dst1 := (*uintptr)(add(dst, (i+1)*goarch.PtrSize)) - *dst0 = (*dst0)&(^uintptr(0)>>bits0) | (src << j) - *dst1 = (*dst1)&^((1<> bits0) - } else { - - dst := (*uintptr)(add(dst, i*goarch.PtrSize)) - *dst = (*dst)&^(((1<<(min(bits, ptrBits)))-1)< 0) - - if forceSlowPath { - - const spc = spanClass(10<<1) | spanClass(0) - const elemsize = uintptr(128) - return mallocgcSmallScanSlowPath(size, typ, needzero, spc, elemsize) - } - - if doubleCheckMalloc { - if gcphase == _GCmarktermination { - throw("mallocgc called with gcphase == _GCmarktermination") - } - } - - lockRankMayQueueFinalizer() - - const sizeclass = 10 - const elemsize = 128 - - mp := acquirem() - if doubleCheckMalloc { - - doubleCheckSmallScanNoHeader(size, typ, mp) - - } - mp.mallocing = 1 - - checkGCTrigger := false - c := getMCache(mp) - const spc = spanClass(sizeclass<<1) | spanClass(0) - span := c.alloc[spc] - - var v gclinkptr - var x unsafe.Pointer - - { - - var nextFreeFastResult gclinkptr - if span.allocCache != 0 { - theBit := sys.TrailingZeros64(span.allocCache) - result := span.freeindex + uint16(theBit) - if result < span.nelems { - freeidx := result + 1 - if !(freeidx%64 == 0 && freeidx != span.nelems) { - span.allocCache >>= uint(theBit + 1) - span.freeindex = freeidx - span.allocCount++ - nextFreeFastResult = gclinkptr(uintptr(result)*elemsize + span.base()) - } - } - } - v = nextFreeFastResult - if v == 0 { - v, span, checkGCTrigger = c.nextFree(spc) - } - x = unsafe.Pointer(v) - } - - if span.needzero != 0 { - memclrNoHeapPointers(x, elemsize) - } - if goarch.PtrSize == 8 && elemsize == 8 { - - c.scanAlloc += 8 - } else { - dataSize := size - x := uintptr(x) - - if doubleCheckHeapSetType && (!heapBitsInSpan(dataSize) || !heapBitsInSpan(128)) { - throw("tried to write heap bits, but no heap bits in span") - } - - src0 := readUintptr(getGCMask(typ)) - - const elemsize = 128 - - var scanSize uintptr - src := src0 - if typ.Size_ == goarch.PtrSize { - src = (1 << (dataSize / goarch.PtrSize)) - 1 - - scanSize = dataSize - } else { - - if doubleCheckHeapSetType && !asanenabled && dataSize%typ.Size_ != 0 { - throw("runtime: (*mspan).writeHeapBitsSmall: dataSize is not a multiple of typ.Size_") - } - scanSize = typ.PtrBytes - for i := typ.Size_; i < dataSize; i += typ.Size_ { - src |= src0 << (i / goarch.PtrSize) - scanSize += typ.Size_ - } - } - - dstBase, _ := spanHeapBitsRange(span.base(), pageSize, elemsize) - dst := unsafe.Pointer(dstBase) - o := (x - span.base()) / goarch.PtrSize - i := o / ptrBits - j := o % ptrBits - var bits uintptr = elemsize / goarch.PtrSize - - var bitsIsPowerOfTwo = bits&(bits-1) == 0 - if bits > ptrBits || (!bitsIsPowerOfTwo && j+bits > ptrBits) { - - bits0 := ptrBits - j - bits1 := bits - bits0 - dst0 := (*uintptr)(add(dst, (i+0)*goarch.PtrSize)) - dst1 := (*uintptr)(add(dst, (i+1)*goarch.PtrSize)) - *dst0 = (*dst0)&(^uintptr(0)>>bits0) | (src << j) - *dst1 = (*dst1)&^((1<> bits0) - } else { - - dst := (*uintptr)(add(dst, i*goarch.PtrSize)) - *dst = (*dst)&^(((1<<(min(bits, ptrBits)))-1)< 0) - - if forceSlowPath { - - return mallocgcTinySlowPath(size, typ, needzero) - - const spc = spanClass(2<<1) | spanClass(1) - const elemsize = uintptr(16) - return mallocgcSlowPathStub(size, typ, needzero, spc, elemsize) - } - - if doubleCheckMalloc { - if gcphase == _GCmarktermination { - throw("mallocgc called with gcphase == _GCmarktermination") - } - } - - lockRankMayQueueFinalizer() - - const elemsize = 16 - - mp := acquirem() - if doubleCheckMalloc { - doubleCheckTiny(size, typ, mp) - } - mp.mallocing = 1 - - c := getMCache(mp) - off := c.tinyoffset - - if size&7 == 0 { - off = alignUp(off, 8) - } else if goarch.PtrSize == 4 && size == 12 { - - off = alignUp(off, 8) - } else if size&3 == 0 { - off = alignUp(off, 4) - } else if size&1 == 0 { - off = alignUp(off, 2) - } - if off+size <= maxTinySize && c.tiny != 0 { - - x := unsafe.Pointer(c.tiny + off) - c.tinyoffset = off + size - c.tinyAllocs++ - mp.mallocing = 0 - releasem(mp) - const elemsize = 0 - - return x - } - - checkGCTrigger := false - span := c.alloc[tinySpanClass] - - const nbytes = 8192 - const nelems = uint16((nbytes - unsafe.Sizeof(spanInlineMarkBits{})) / 16) - var nextFreeFastResult gclinkptr - if span.allocCache != 0 { - theBit := sys.TrailingZeros64(span.allocCache) - result := span.freeindex + uint16(theBit) - if result < nelems { - freeidx := result + 1 - if !(freeidx%64 == 0 && freeidx != nelems) { - span.allocCache >>= uint(theBit + 1) - span.freeindex = freeidx - span.allocCount++ - nextFreeFastResult = gclinkptr(uintptr(result)*16 + span.base()) - } - } - } - v := nextFreeFastResult - if v == 0 { - v, span, checkGCTrigger = c.nextFree(tinySpanClass) - } - x := unsafe.Pointer(v) - (*[2]uint64)(x)[0] = 0 - (*[2]uint64)(x)[1] = 0 - - if !raceenabled && (size < c.tinyoffset || c.tiny == 0) { - - c.tiny = uintptr(x) - c.tinyoffset = size - } - - publicationBarrier() - - span.freeIndexForScan = span.freeindex - - c.nextSample -= int64(elemsize) - if c.nextSample < 0 || MemProfileRate != c.memProfRate { - profilealloc(mp, x, elemsize) - } - mp.mallocing = 0 - releasem(mp) - - if checkGCTrigger { - if t := (gcTrigger{kind: gcTriggerHeap}); t.test() { - gcStart(t) - } - } - - return x -} - -func mallocgcSmallNoScanSC2(size uintptr, typ *_type, needzero bool) unsafe.Pointer { - - forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) - - if forceSlowPath { - - const spc = spanClass(2<<1) | spanClass(1) - const elemsize = uintptr(16) - return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) - } - - if doubleCheckMalloc { - if gcphase == _GCmarktermination { - throw("mallocgc called with gcphase == _GCmarktermination") - } - } - - lockRankMayQueueFinalizer() - - const sizeclass = 2 - const elemsize = 16 - - mp := acquirem() - if doubleCheckMalloc { - - doubleCheckSmallNoScan(typ, mp) - - } - mp.mallocing = 1 - - checkGCTrigger := false - c := getMCache(mp) - const spc = spanClass(sizeclass<<1) | spanClass(1) - span := c.alloc[spc] - - var v gclinkptr - var x unsafe.Pointer - - if runtimeFreegcEnabled && c.hasReusableNoscan(spc) { - - x = mallocgcSmallNoscanReuse(c, span, spc, elemsize, needzero) - mp.mallocing = 0 - releasem(mp) - - return x - } - - { - - var nextFreeFastResult gclinkptr - if span.allocCache != 0 { - theBit := sys.TrailingZeros64(span.allocCache) - result := span.freeindex + uint16(theBit) - if result < span.nelems { - freeidx := result + 1 - if !(freeidx%64 == 0 && freeidx != span.nelems) { - span.allocCache >>= uint(theBit + 1) - span.freeindex = freeidx - span.allocCount++ - nextFreeFastResult = gclinkptr(uintptr(result)*elemsize + span.base()) - } - } - } - v = nextFreeFastResult - if v == 0 { - v, span, checkGCTrigger = c.nextFree(spc) - } - x = unsafe.Pointer(v) - } - - if needzero && span.needzero != 0 { - memclrNoHeapPointers(x, elemsize) - } - - publicationBarrier() - - span.freeIndexForScan = span.freeindex - - c.nextSample -= int64(elemsize) - if c.nextSample < 0 || MemProfileRate != c.memProfRate { - profilealloc(mp, x, elemsize) - } - mp.mallocing = 0 - releasem(mp) - - if checkGCTrigger { - if t := (gcTrigger{kind: gcTriggerHeap}); t.test() { - gcStart(t) - } - } - - return x -} - -func mallocgcSmallNoScanSC3(size uintptr, typ *_type, needzero bool) unsafe.Pointer { +func mallocgcTinySC2(size uintptr, typ *_type, needzero bool) unsafe.Pointer { forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) if forceSlowPath { - const spc = spanClass(3<<1) | spanClass(1) - const elemsize = uintptr(24) - return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) + return mallocgcTinySlowPath(size, typ, needzero) + + const spc = spanClass(2<<1) | spanClass(1) + const elemsize = uintptr(16) + return mallocgcSlowPathStub(size, typ, needzero, spc, elemsize) } if doubleCheckMalloc { @@ -1720,153 +1074,70 @@ func mallocgcSmallNoScanSC3(size uintptr, typ *_type, needzero bool) unsafe.Poin lockRankMayQueueFinalizer() - const sizeclass = 3 - const elemsize = 24 + const elemsize = 16 mp := acquirem() if doubleCheckMalloc { - - doubleCheckSmallNoScan(typ, mp) - + doubleCheckTiny(size, typ, mp) } mp.mallocing = 1 - checkGCTrigger := false c := getMCache(mp) - const spc = spanClass(sizeclass<<1) | spanClass(1) - span := c.alloc[spc] + off := c.tinyoffset - var v gclinkptr - var x unsafe.Pointer + if size&7 == 0 { + off = alignUp(off, 8) + } else if goarch.PtrSize == 4 && size == 12 { - if runtimeFreegcEnabled && c.hasReusableNoscan(spc) { + off = alignUp(off, 8) + } else if size&3 == 0 { + off = alignUp(off, 4) + } else if size&1 == 0 { + off = alignUp(off, 2) + } + if off+size <= maxTinySize && c.tiny != 0 { - x = mallocgcSmallNoscanReuse(c, span, spc, elemsize, needzero) + x := unsafe.Pointer(c.tiny + off) + c.tinyoffset = off + size + c.tinyAllocs++ mp.mallocing = 0 releasem(mp) + const elemsize = 0 return x } - { + checkGCTrigger := false + span := c.alloc[tinySpanClass] - var nextFreeFastResult gclinkptr - if span.allocCache != 0 { - theBit := sys.TrailingZeros64(span.allocCache) - result := span.freeindex + uint16(theBit) - if result < span.nelems { - freeidx := result + 1 - if !(freeidx%64 == 0 && freeidx != span.nelems) { - span.allocCache >>= uint(theBit + 1) - span.freeindex = freeidx - span.allocCount++ - nextFreeFastResult = gclinkptr(uintptr(result)*elemsize + span.base()) - } + const nbytes = 8192 + const nelems = uint16((nbytes - unsafe.Sizeof(spanInlineMarkBits{})) / 16) + var nextFreeFastResult gclinkptr + if span.allocCache != 0 { + theBit := sys.TrailingZeros64(span.allocCache) + result := span.freeindex + uint16(theBit) + if result < nelems { + freeidx := result + 1 + if !(freeidx%64 == 0 && freeidx != nelems) { + span.allocCache >>= uint(theBit + 1) + span.freeindex = freeidx + span.allocCount++ + nextFreeFastResult = gclinkptr(uintptr(result)*16 + span.base()) } } - v = nextFreeFastResult - if v == 0 { - v, span, checkGCTrigger = c.nextFree(spc) - } - x = unsafe.Pointer(v) - } - - if needzero && span.needzero != 0 { - memclrNoHeapPointers(x, elemsize) - } - - publicationBarrier() - - span.freeIndexForScan = span.freeindex - - c.nextSample -= int64(elemsize) - if c.nextSample < 0 || MemProfileRate != c.memProfRate { - profilealloc(mp, x, elemsize) - } - mp.mallocing = 0 - releasem(mp) - - if checkGCTrigger { - if t := (gcTrigger{kind: gcTriggerHeap}); t.test() { - gcStart(t) - } - } - - return x -} - -func mallocgcSmallNoScanSC4(size uintptr, typ *_type, needzero bool) unsafe.Pointer { - - forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) - - if forceSlowPath { - - const spc = spanClass(4<<1) | spanClass(1) - const elemsize = uintptr(32) - return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) - } - - if doubleCheckMalloc { - if gcphase == _GCmarktermination { - throw("mallocgc called with gcphase == _GCmarktermination") - } - } - - lockRankMayQueueFinalizer() - - const sizeclass = 4 - const elemsize = 32 - - mp := acquirem() - if doubleCheckMalloc { - - doubleCheckSmallNoScan(typ, mp) - } - mp.mallocing = 1 - - checkGCTrigger := false - c := getMCache(mp) - const spc = spanClass(sizeclass<<1) | spanClass(1) - span := c.alloc[spc] - - var v gclinkptr - var x unsafe.Pointer - - if runtimeFreegcEnabled && c.hasReusableNoscan(spc) { - - x = mallocgcSmallNoscanReuse(c, span, spc, elemsize, needzero) - mp.mallocing = 0 - releasem(mp) - - return x + v := nextFreeFastResult + if v == 0 { + v, span, checkGCTrigger = c.nextFree(tinySpanClass) } + x := unsafe.Pointer(v) + (*[2]uint64)(x)[0] = 0 + (*[2]uint64)(x)[1] = 0 - { - - var nextFreeFastResult gclinkptr - if span.allocCache != 0 { - theBit := sys.TrailingZeros64(span.allocCache) - result := span.freeindex + uint16(theBit) - if result < span.nelems { - freeidx := result + 1 - if !(freeidx%64 == 0 && freeidx != span.nelems) { - span.allocCache >>= uint(theBit + 1) - span.freeindex = freeidx - span.allocCount++ - nextFreeFastResult = gclinkptr(uintptr(result)*elemsize + span.base()) - } - } - } - v = nextFreeFastResult - if v == 0 { - v, span, checkGCTrigger = c.nextFree(spc) - } - x = unsafe.Pointer(v) - } + if !raceenabled && (size < c.tinyoffset || c.tiny == 0) { - if needzero && span.needzero != 0 { - memclrNoHeapPointers(x, elemsize) + c.tiny = uintptr(x) + c.tinyoffset = size } publicationBarrier() @@ -1889,14 +1160,14 @@ func mallocgcSmallNoScanSC4(size uintptr, typ *_type, needzero bool) unsafe.Poin return x } -func mallocgcSmallNoScanSC5(size uintptr, typ *_type, needzero bool) unsafe.Pointer { +func mallocgcSmallNoScanSC2(size uintptr, typ *_type, needzero bool) unsafe.Pointer { forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) if forceSlowPath { - const spc = spanClass(5<<1) | spanClass(1) - const elemsize = uintptr(48) + const spc = spanClass(2<<1) | spanClass(1) + const elemsize = uintptr(16) return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) } @@ -1908,8 +1179,8 @@ func mallocgcSmallNoScanSC5(size uintptr, typ *_type, needzero bool) unsafe.Poin lockRankMayQueueFinalizer() - const sizeclass = 5 - const elemsize = 48 + const sizeclass = 2 + const elemsize = 16 mp := acquirem() if doubleCheckMalloc { @@ -1983,14 +1254,14 @@ func mallocgcSmallNoScanSC5(size uintptr, typ *_type, needzero bool) unsafe.Poin return x } -func mallocgcSmallNoScanSC6(size uintptr, typ *_type, needzero bool) unsafe.Pointer { +func mallocgcSmallNoScanSC3(size uintptr, typ *_type, needzero bool) unsafe.Pointer { forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) if forceSlowPath { - const spc = spanClass(6<<1) | spanClass(1) - const elemsize = uintptr(64) + const spc = spanClass(3<<1) | spanClass(1) + const elemsize = uintptr(24) return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) } @@ -2002,8 +1273,8 @@ func mallocgcSmallNoScanSC6(size uintptr, typ *_type, needzero bool) unsafe.Poin lockRankMayQueueFinalizer() - const sizeclass = 6 - const elemsize = 64 + const sizeclass = 3 + const elemsize = 24 mp := acquirem() if doubleCheckMalloc { @@ -2077,14 +1348,14 @@ func mallocgcSmallNoScanSC6(size uintptr, typ *_type, needzero bool) unsafe.Poin return x } -func mallocgcSmallNoScanSC7(size uintptr, typ *_type, needzero bool) unsafe.Pointer { +func mallocgcSmallNoScanSC4(size uintptr, typ *_type, needzero bool) unsafe.Pointer { forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) if forceSlowPath { - const spc = spanClass(7<<1) | spanClass(1) - const elemsize = uintptr(80) + const spc = spanClass(4<<1) | spanClass(1) + const elemsize = uintptr(32) return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) } @@ -2096,8 +1367,8 @@ func mallocgcSmallNoScanSC7(size uintptr, typ *_type, needzero bool) unsafe.Poin lockRankMayQueueFinalizer() - const sizeclass = 7 - const elemsize = 80 + const sizeclass = 4 + const elemsize = 32 mp := acquirem() if doubleCheckMalloc { @@ -2171,14 +1442,14 @@ func mallocgcSmallNoScanSC7(size uintptr, typ *_type, needzero bool) unsafe.Poin return x } -func mallocgcSmallNoScanSC8(size uintptr, typ *_type, needzero bool) unsafe.Pointer { +func mallocgcSmallNoScanSC5(size uintptr, typ *_type, needzero bool) unsafe.Pointer { forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) if forceSlowPath { - const spc = spanClass(8<<1) | spanClass(1) - const elemsize = uintptr(96) + const spc = spanClass(5<<1) | spanClass(1) + const elemsize = uintptr(48) return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) } @@ -2190,8 +1461,8 @@ func mallocgcSmallNoScanSC8(size uintptr, typ *_type, needzero bool) unsafe.Poin lockRankMayQueueFinalizer() - const sizeclass = 8 - const elemsize = 96 + const sizeclass = 5 + const elemsize = 48 mp := acquirem() if doubleCheckMalloc { @@ -2265,14 +1536,14 @@ func mallocgcSmallNoScanSC8(size uintptr, typ *_type, needzero bool) unsafe.Poin return x } -func mallocgcSmallNoScanSC9(size uintptr, typ *_type, needzero bool) unsafe.Pointer { +func mallocgcSmallNoScanSC6(size uintptr, typ *_type, needzero bool) unsafe.Pointer { forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) if forceSlowPath { - const spc = spanClass(9<<1) | spanClass(1) - const elemsize = uintptr(112) + const spc = spanClass(6<<1) | spanClass(1) + const elemsize = uintptr(64) return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) } @@ -2284,8 +1555,8 @@ func mallocgcSmallNoScanSC9(size uintptr, typ *_type, needzero bool) unsafe.Poin lockRankMayQueueFinalizer() - const sizeclass = 9 - const elemsize = 112 + const sizeclass = 6 + const elemsize = 64 mp := acquirem() if doubleCheckMalloc { @@ -2359,14 +1630,14 @@ func mallocgcSmallNoScanSC9(size uintptr, typ *_type, needzero bool) unsafe.Poin return x } -func mallocgcSmallNoScanSC10(size uintptr, typ *_type, needzero bool) unsafe.Pointer { +func mallocgcSmallNoScanSC7(size uintptr, typ *_type, needzero bool) unsafe.Pointer { forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0) if forceSlowPath { - const spc = spanClass(10<<1) | spanClass(1) - const elemsize = uintptr(128) + const spc = spanClass(7<<1) | spanClass(1) + const elemsize = uintptr(80) return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) } @@ -2378,8 +1649,8 @@ func mallocgcSmallNoScanSC10(size uintptr, typ *_type, needzero bool) unsafe.Poi lockRankMayQueueFinalizer() - const sizeclass = 10 - const elemsize = 128 + const sizeclass = 7 + const elemsize = 80 mp := acquirem() if doubleCheckMalloc { diff --git a/src/runtime/malloc_tables_generated.go b/src/runtime/malloc_tables_generated.go index fbba92e1ca1982..32f4317d699eec 100644 --- a/src/runtime/malloc_tables_generated.go +++ b/src/runtime/malloc_tables_generated.go @@ -5,7 +5,7 @@ package runtime import "unsafe" -var mallocScanTable = [129]func(size uintptr, typ *_type, needzero bool) unsafe.Pointer{mallocPanic, +var mallocScanTable = [81]func(size uintptr, typ *_type, needzero bool) unsafe.Pointer{mallocPanic, mallocgcSmallScanNoHeaderSC1, mallocgcSmallScanNoHeaderSC1, mallocgcSmallScanNoHeaderSC1, @@ -86,58 +86,9 @@ var mallocScanTable = [129]func(size uintptr, typ *_type, needzero bool) unsafe. mallocgcSmallScanNoHeaderSC7, mallocgcSmallScanNoHeaderSC7, mallocgcSmallScanNoHeaderSC7, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC8, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC9, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, - mallocgcSmallScanNoHeaderSC10, } -var mallocNoScanTable = [129]func(size uintptr, typ *_type, needzero bool) unsafe.Pointer{ - mallocPanic, +var mallocNoScanTable = [81]func(size uintptr, typ *_type, needzero bool) unsafe.Pointer{mallocPanic, mallocPanic, mallocPanic, mallocPanic, @@ -218,52 +169,4 @@ var mallocNoScanTable = [129]func(size uintptr, typ *_type, needzero bool) unsaf mallocgcSmallNoScanSC7, mallocgcSmallNoScanSC7, mallocgcSmallNoScanSC7, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC8, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC9, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, - mallocgcSmallNoScanSC10, } diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 5af1b037886479..42af4200547b64 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -263,7 +263,7 @@ func windows_QueryPerformanceFrequency() int64 { return frequency } -//go:linkname cpu_isProcessorFeaturePresent internal/cpu.isProcessorFeaturePresent +//go:linknamestd cpu_isProcessorFeaturePresent internal/cpu.isProcessorFeaturePresent func cpu_isProcessorFeaturePresent(processorFeature uint32) bool { ret := stdcall(_IsProcessorFeaturePresent, uintptr(processorFeature)) return ret != 0 diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index 835aa8ecedb484..97f3d8e8878b64 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -655,7 +655,7 @@ func reflect_typelinks() ([]unsafe.Pointer, [][]int32) { // slice for the main module, and a slice of slices, normally nil, // for other modules. // -//go:linkname reflect_compiledTypelinks reflect.compiledTypelinks +//go:linknamestd reflect_compiledTypelinks reflect.compiledTypelinks func reflect_compiledTypelinks() ([]*abi.Type, [][]*abi.Type) { modules := activeModules() firstTypes := moduleTypelinks(modules[0]) @@ -753,7 +753,7 @@ func reflect_addReflectOff(ptr unsafe.Pointer) int32 { // the new address to use. This is only called on AIX. // See getGCMaskOnDemand. // -//go:linkname reflect_adjustAIXGCDataForRuntime reflect.adjustAIXGCDataForRuntime +//go:linknamestd reflect_adjustAIXGCDataForRuntime reflect.adjustAIXGCDataForRuntime func reflect_adjustAIXGCDataForRuntime(addr *byte) *byte { return (*byte)(add(unsafe.Pointer(addr), aixStaticDataBase-firstmoduledata.data)) }