Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
64315a2
bytes, strings: use builtin min function in genSplit
cuishuang May 17, 2026
e8c1e37
database/sql: add cursor cancelation test, document some cursor issues
neild Mar 24, 2026
e9edbce
encoding/json/v2: remove recursion and error on `string` on unsupport…
prattmic May 15, 2026
2378242
runtime/_mkmalloc: allow for folding const bool exprs
matloob May 17, 2026
6716b79
lib/time: update to 2026b/2026b
tklauser May 19, 2026
75560e6
runtime: introduce a mallocgc fast path
matloob Apr 30, 2026
fabaedc
cmd/compile: fold == != with a const and a bijective operation into t…
Jorropo Feb 28, 2026
880ef11
cmd/compile: make computeKnownBitsForShift iteration faster
Jorropo May 4, 2026
7d2eb15
net/http/fcgi: handle error returned by w.Close() in writePairs
cuiweixie May 5, 2026
e73e734
cmd/compile: improve known bits debug print
Jorropo May 14, 2026
e26a373
runtime/secret: implement goroutine inheriting secret state
DanielMorsing Dec 10, 2025
05f75fb
internal/runtime/maps,runtime/: pass keys by value to MemHash{32,64} …
May 9, 2026
5f47eb0
math/big: refactored TestIntDivide tests, added more test cases
griesemer May 18, 2026
93da303
math/big: move Int.Divide and corresponding test function up (cleanup)
griesemer May 19, 2026
4bf23b5
crypto/x509: honor SSL_CERT_{FILE,DIR} on windows/darwin
rolandshoemaker May 11, 2026
d80de8f
cmd/go: sort subcommands in help output
seankhliao Apr 21, 2026
4e51025
crypto/x509: add ML-DSA support
FiloSottile May 9, 2026
f142be8
go/printer: do not indent composite literals in return statements
yorukot Mar 6, 2026
99623c5
crypto/rsa: skip TestKeyGenerationVectors on older FIPS 140-3 modules
FiloSottile May 19, 2026
003833a
cmd/link: track content-hashed-ness for cloned symbols
cherrymui May 18, 2026
c74ba7d
crypto/tls: add ML-DSA support
FiloSottile May 9, 2026
95e935b
crypto/tls: update generated certificates
FiloSottile May 19, 2026
47cc607
runtime,runtime/cgo: port ios/arm64 working dir setup from C to Go
qmuntal Apr 21, 2026
e01f29f
crypto/internal/fips140/rsa: check hash length in PKCS#1 v1.5 signatures
FiloSottile Apr 11, 2026
c9a3e8b
encoding/json/jsontext: skip inline-dependent test on noopt builders
rhysh May 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/next/78888.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pkg crypto/x509, const MLDSA = 5 #78888
pkg crypto/x509, const MLDSA PublicKeyAlgorithm #78888
pkg crypto/x509, const MLDSA44 = 17 #78888
pkg crypto/x509, const MLDSA44 SignatureAlgorithm #78888
pkg crypto/x509, const MLDSA65 = 18 #78888
pkg crypto/x509, const MLDSA65 SignatureAlgorithm #78888
pkg crypto/x509, const MLDSA87 = 19 #78888
pkg crypto/x509, const MLDSA87 SignatureAlgorithm #78888
pkg crypto/tls, const MLDSA44 = 2308 #78888
pkg crypto/tls, const MLDSA44 SignatureScheme #78888
pkg crypto/tls, const MLDSA65 = 2309 #78888
pkg crypto/tls, const MLDSA65 SignatureScheme #78888
pkg crypto/tls, const MLDSA87 = 2310 #78888
pkg crypto/tls, const MLDSA87 SignatureScheme #78888
8 changes: 8 additions & 0 deletions doc/godebug.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ to `1`. This opt-out is expected to be kept indefinitely in case goroutine
labels acquire sensitive information that shouldn't be made available in
tracebacks.

Go 1.27 added a new `x509sslcertoverrideplatform` setting that controls whether
crypto/x509 will load roots from disk on Windows and Darwin when `SSL_CERT_FILE`
or `SSL_CERT_DIR` are set. The default value `x509sslcertoverrideplatform=1` will
cause roots to be loaded from disk when these environment variables are set.
Setting `x509sslcertoverrideplatform=0` disables this behavior in favor of using
the platform certificate store instead of honoring the environment variables. We
plan to remove this setting in Go 1.31.

### Go 1.26

Go 1.26 added a new `httpcookiemaxnum` setting that controls the maximum number
Expand Down
7 changes: 6 additions & 1 deletion doc/next/6-stdlib/70-mldsa.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
### crypto/mldsa

<!-- https://go.dev/issue/77626 --->
<!-- https://go.dev/issue/77626, https://go.dev/issue/78888 --->

The new [crypto/mldsa] package implements the post-quantum ML-DSA signature
scheme specified in FIPS 204.

[crypto/x509] now supports ML-DSA private keys, public keys, and signatures.

[crypto/tls] now supports ML-DSA signatures in TLS 1.3, with the new
[MLDSA44], [MLDSA65], and [MLDSA87] [SignatureScheme] values.
1 change: 1 addition & 0 deletions doc/next/6-stdlib/99-minor/crypto/tls/78888.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- crypto/tls ML-DSA support is documented in doc/next/6-stdlib/70-mldsa.md. -->
5 changes: 5 additions & 0 deletions doc/next/6-stdlib/99-minor/crypto/x509/77865.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[SystemCertPool] now respects SSL_CERT_FILE and SSL_CERT_DIR on Windows and
Darwin. When these environment variables are set, roots are loaded from disk and
instead of using the platform certificate verification APIs, the native Go
verifier is used. This behavior can be disabled with
`GODEBUG=x509sslcertoverrideplatform=0`.
1 change: 1 addition & 0 deletions doc/next/6-stdlib/99-minor/crypto/x509/78888.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- crypto/x509 ML-DSA support is documented in doc/next/6-stdlib/70-mldsa.md. -->
4 changes: 2 additions & 2 deletions lib/time/update.bash
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
# in the CL match the update.bash in the CL.

# Versions to use.
CODE=2025c
DATA=2025c
CODE=2026b
DATA=2026b

set -e

Expand Down
Binary file modified lib/time/zoneinfo.zip
Binary file not shown.
4 changes: 1 addition & 3 deletions src/bytes/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,9 +380,7 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte {
if n < 0 {
n = Count(s, sep) + 1
}
if n > len(s)+1 {
n = len(s) + 1
}
n = min(n, len(s)+1)

a := make([][]byte, n)
n--
Expand Down
34 changes: 24 additions & 10 deletions src/cmd/compile/internal/ssa/_gen/generic.rules
Original file line number Diff line number Diff line change
Expand Up @@ -289,20 +289,34 @@
(NeqB (ConstBool [true]) x) => (Not x)
(NeqB (Not x) y) => (EqB x y)

(Eq64 (Const64 <t> [c]) (Add64 (Const64 <t> [d]) x)) => (Eq64 (Const64 <t> [c-d]) x)
(Eq32 (Const32 <t> [c]) (Add32 (Const32 <t> [d]) x)) => (Eq32 (Const32 <t> [c-d]) x)
(Eq16 (Const16 <t> [c]) (Add16 (Const16 <t> [d]) x)) => (Eq16 (Const16 <t> [c-d]) x)
(Eq8 (Const8 <t> [c]) (Add8 (Const8 <t> [d]) x)) => (Eq8 (Const8 <t> [c-d]) x)

(Neq64 (Const64 <t> [c]) (Add64 (Const64 <t> [d]) x)) => (Neq64 (Const64 <t> [c-d]) x)
(Neq32 (Const32 <t> [c]) (Add32 (Const32 <t> [d]) x)) => (Neq32 (Const32 <t> [c-d]) x)
(Neq16 (Const16 <t> [c]) (Add16 (Const16 <t> [d]) x)) => (Neq16 (Const16 <t> [c-d]) x)
(Neq8 (Const8 <t> [c]) (Add8 (Const8 <t> [d]) x)) => (Neq8 (Const8 <t> [c-d]) x)

(CondSelect x _ (ConstBool [true ])) => x
(CondSelect _ y (ConstBool [false])) => y
(CondSelect x x _) => x

// fold eq / neq between a constant and a compile time bijective operation into the constant.
(Eq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Add(64|32|16|8) (Const(64|32|16|8) [d]) x)) && o.Uses == 1 => (Eq(64|32|16|8) (Const(64|32|16|8) <t> [c-d]) x)
(Neq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Add(64|32|16|8) (Const(64|32|16|8) [d]) x)) && o.Uses == 1 => (Neq(64|32|16|8) (Const(64|32|16|8) <t> [c-d]) x)

(Eq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Sub(64|32|16|8) x (Const(64|32|16|8) [d]))) && o.Uses == 1 => (Eq(64|32|16|8) (Const(64|32|16|8) <t> [c+d]) x)
(Neq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Sub(64|32|16|8) x (Const(64|32|16|8) [d]))) && o.Uses == 1 => (Neq(64|32|16|8) (Const(64|32|16|8) <t> [c+d]) x)

(Eq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Sub(64|32|16|8) (Const(64|32|16|8) [d]) x)) && o.Uses == 1 => (Eq(64|32|16|8) (Const(64|32|16|8) <t> [d-c]) x)
(Neq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Sub(64|32|16|8) (Const(64|32|16|8) [d]) x)) && o.Uses == 1 => (Neq(64|32|16|8) (Const(64|32|16|8) <t> [d-c]) x)

(Eq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Xor(64|32|16|8) (Const(64|32|16|8) [d]) x)) && o.Uses == 1 => (Eq(64|32|16|8) (Const(64|32|16|8) <t> [d^c]) x)
(Neq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Xor(64|32|16|8) (Const(64|32|16|8) [d]) x)) && o.Uses == 1 => (Neq(64|32|16|8) (Const(64|32|16|8) <t> [d^c]) x)

(Eq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Com(64|32|16|8) x)) && o.Uses == 1 => (Eq(64|32|16|8) (Const(64|32|16|8) <t> [^c]) x)
(Neq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Com(64|32|16|8) x)) && o.Uses == 1 => (Neq(64|32|16|8) (Const(64|32|16|8) <t> [^c]) x)

(Eq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Neg(64|32|16|8) x)) && o.Uses == 1 => (Eq(64|32|16|8) (Const(64|32|16|8) <t> [-c]) x)
(Neq(64|32|16|8) (Const(64|32|16|8) <t> [c]) o:(Neg(64|32|16|8) x)) && o.Uses == 1 => (Neq(64|32|16|8) (Const(64|32|16|8) <t> [-c]) x)

((Eq|Neq)64 (Const64 <t> [c]) o:(Mul64 (Const64 [d]) x)) && uint64(d)%2 == 1 && o.Uses == 1 => ((Eq|Neq)64 (Const64 <t> [int64(uint64(c) * modularMultiplicativeInverse(uint64(d))) ]) x)
((Eq|Neq)32 (Const32 <t> [c]) o:(Mul32 (Const32 [d]) x)) && uint32(d)%2 == 1 && o.Uses == 1 => ((Eq|Neq)32 (Const32 <t> [int32(uint32(c) * uint32(modularMultiplicativeInverse(uint64(d))))]) x)
((Eq|Neq)16 (Const16 <t> [c]) o:(Mul16 (Const16 [d]) x)) && uint16(d)%2 == 1 && o.Uses == 1 => ((Eq|Neq)16 (Const16 <t> [int16(uint16(c) * uint16(modularMultiplicativeInverse(uint64(d))))]) x)
((Eq|Neq)8 (Const8 <t> [c]) o:(Mul8 (Const8 [d]) x)) && uint8( d)%2 == 1 && o.Uses == 1 => ((Eq|Neq)8 (Const8 <t> [int8( uint8( c) * uint8( modularMultiplicativeInverse(uint64(d))))]) x)

// signed integer range: ( c <= x && x (<|<=) d ) -> ( unsigned(x-c) (<|<=) unsigned(d-c) )
(AndB (Leq64 (Const64 [c]) x) ((Less|Leq)64 x (Const64 [d]))) && d >= c => ((Less|Leq)64U (Sub64 <x.Type> x (Const64 <x.Type> [c])) (Const64 <x.Type> [d-c]))
(AndB (Leq32 (Const32 [c]) x) ((Less|Leq)32 x (Const32 [d]))) && d >= c => ((Less|Leq)32U (Sub32 <x.Type> x (Const32 <x.Type> [c])) (Const32 <x.Type> [d-c]))
Expand Down
67 changes: 57 additions & 10 deletions src/cmd/compile/internal/ssa/known_bits.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

package ssa

import "slices"
import (
"slices"
"strings"
)

func (kb *knownBitsState) fold(v *Value) (value, known int64) {
if kb.seenValues.Test(uint32(v.ID)) {
Expand Down Expand Up @@ -37,11 +40,11 @@ func (kb *knownBitsState) fold(v *Value) (value, known int64) {
// 1. unknown bits are always set to 0 inside value
value &= known

if v.Block.Func.pass.debug > 1 {
v.Block.Func.Warnl(v.Pos, "known bits state %v: k:%d v:%d", v, known, value)
}
kb.entries[v.ID].known = known
kb.entries[v.ID].value = value
if v.Block.Func.pass.debug > 1 {
v.Block.Func.Warnl(v.Pos, "known bits state %v: %v", v, kb.entries[v.ID])
}
}()
kb.seenValues.Set(uint32(v.ID)) // set seen early to give up on loops

Expand Down Expand Up @@ -228,6 +231,22 @@ type knownBitsEntry struct {
known, value int64
}

func (kbe knownBitsEntry) String() string {
lut := []rune{ // indexed by knownBit<<1 | valueBit
0b00: '?',
0b01: '¿', // violates invariant 1
0b10: '0',
0b11: '1',
}
var sb strings.Builder
sb.Grow(64)
for i := 63; i >= 0; i-- {
bits := (kbe.known>>i&1)<<1 | (kbe.value >> i & 1)
sb.WriteRune(lut[bits])
}
return sb.String()
}

func (kb *knownBitsState) isLiveInEdge(b *Block, index uint) bool {
inEdge := b.Preds[index]
return kb.isLiveOutEdge(inEdge.b, uint(inEdge.i))
Expand Down Expand Up @@ -289,13 +308,11 @@ func (kb *knownBitsState) computeKnownBitsForShift(v *Value, doShiftByAConst fun
value, known = doShiftByAConst(x, xk, xSize, 64)
set = true
}
yk &= xSize - 1

for i := range xSize {
if i&yk != y {
continue
}
a, k := doShiftByAConst(x, xk, xSize, int64(i))
yk |= ^(xSize - 1)

for i := range allPossibleValues(y, yk) {
a, k := doShiftByAConst(x, xk, xSize, i)
if !set {
value, known = a, k
set = true
Expand All @@ -310,3 +327,33 @@ func (kb *knownBitsState) computeKnownBitsForShift(v *Value, doShiftByAConst fun

return value & known, known
}

// allPossibleValues iterates over all values that could exist.
// It scales exponentially with the number of unknown bits,
// the exact number of iterations will be uint128(1)<<bits.OnesCount64(^known)
// thus be careful with what values you pass to it.
func allPossibleValues(value, known int64) func(yield func(v int64) bool) {
unknown := ^known
return func(yield func(v int64) bool) {
// This finds the next valid value for the variable bits.
// It is equivalent to (s|known + 1) & unknown.
// The s|known step creates blocks of 1s in all the known bits.
// +1 finds the next possible value, the blocks of 1s set in the previous step allows it to skip over blocks of known bits.
// & unknown clears garbage generated by the blocks of ones and overflow.
//
// You can transform (s|known + 1) & unknown into (s - unknown) & unknown through:
// (s + known + 1) & unknown: s | known → s + known (since s & known == 0)
// (s + ^unknown + 1) & unknown: known → ^unknown (definition of unknown)
// (s + -unknown) & unknown: ^unknown + 1 → -unknown (two's complement negation)
// (s - unknown) & unknown: s + -unknown → s - unknown (arithmetic)
for s := int64(0); ; s = (s - unknown) & unknown {
// fixed bits | current variable bits gives the current iteration
if !yield(value | s) {
return
}
if s == unknown {
break
}
}
}
}
66 changes: 66 additions & 0 deletions src/cmd/compile/internal/ssa/known_bits_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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 ssa

import (
"fmt"
"iter"
"math/bits"
"testing"
)

// allPossibleValuesRejection has identical behavior to allPossibleValues
// but it is implemented with an obviously correct rejection based algorithm.
// We use it to test that allPossibleValues.
func allPossibleValuesRejection(value, known, max int64) func(yield func(v int64) bool) {
return func(yield func(v int64) bool) {
for i := int64(0); i <= max; {
if i&known == value {
if !yield(i) {
return
}
}

next, overflow := bits.Add64(uint64(i), 1, 0)
if overflow != 0 {
// exit condition in case the 64th bit is unknown.
break
}
i = int64(next)
}
}
}

func TestAllPossibleValues(t *testing.T) {
// We can't test too much since it scales exponentially with the number of unknown bits.
const tryMask = int64(0b0111_1111)
for i := int64(0); uint64(i) <= uint64(tryMask); i++ {
unknown := ^i
known := i | ^tryMask

for value := range allPossibleValuesRejection(0, unknown, tryMask) { // don't use allPossibleValues since it's what we are about to test.
t.Run(fmt.Sprintf("%v", knownBitsEntry{known: known, value: value}), func(t *testing.T) {
truth, truthStop := iter.Pull(allPossibleValuesRejection(value, known, tryMask))
defer truthStop()
dut, dutStop := iter.Pull(allPossibleValues(value, known))
defer dutStop()
for i := int64(0); ; i++ {
want, wantOk := truth()
got, gotOk := dut()
if wantOk != gotOk {
t.Fatalf("unexpected ok at iteration %d: got %v, want %v", i, gotOk, wantOk)
}
if !gotOk {
break
}

if got != want {
t.Errorf("unexpected value at iteration %d: got %b, want %b", i, uint64(got), uint64(want))
}
}
})
}
}
}
16 changes: 16 additions & 0 deletions src/cmd/compile/internal/ssa/rewrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -2855,3 +2855,19 @@ func addToSub(op Op) Op {
panic(fmt.Sprintf("unexpected op %v", op))
}
}

func modularMultiplicativeInverse(x uint64) (y uint64) {
if x%2 != 1 {
panic("even numbers in a power-of-two modulus do not have a multiplicative inverse")
}
// we start with 3 bits of precision because each odd number is its own multiplicative inverse mod 8
y = x // 3 bits

// now use the Newton-Raphson method to double the number of correct bits in each iteration.
y *= 2 - x*y // 6 bits
y *= 2 - x*y // 12 bits
y *= 2 - x*y // 24 bits
y *= 2 - x*y // 48 bits
y *= 2 - x*y // 96 bits; good enough
return
}
17 changes: 17 additions & 0 deletions src/cmd/compile/internal/ssa/rewrite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,20 @@ func TestDisjointTypesRun(t *testing.T) {
t.Errorf("disjointTypes gives an incorrect answer that leads to an incorrect optimization.")
}
}

func TestModularMultiplicativeInverse(t *testing.T) {
t.Parallel()

// We've got 63 bits of phase space for the Multiplier
// Needless to say this is too much to bruteforce here.
// I've randomly picked a range of 1<<24 because it runs in 0.03s on my machine which isn't too slow.
// We test both sides of the wrapping point (0 and math.MaxUint64) since we need to test something and it's a usual place to have bugs.
const halfRange = 1 << 23
for i := -int64(halfRange) - 1; i < halfRange; i += 2 { // odd only, a bit after to a bit before the wrapping point
mmi := modularMultiplicativeInverse(uint64(i))

if uint64(i)*mmi != 1 {
t.Errorf("%d * modularMultiplicativeInverse(%d) != 1; modularMultiplicativeInverse(%d) == %d", i, i, i, mmi)
}
}
}
Loading
Loading