Skip to content

Commit f10155d

Browse files
Merge branch 'golang:master' into master
2 parents 3086b84 + a3688ab commit f10155d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2053
-485
lines changed

api/next/77169.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pkg testing/synctest, func Sleep(time.Duration) #77169
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The new [Sleep] helper function combines [time.Sleep] and [testing/synctest.Wait].

src/bytes/bytes.go

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func IndexAny(s []byte, chars string) int {
246246
}
247247
return IndexRune(s, r)
248248
}
249-
if len(s) > 8 {
249+
if shouldUseASCIISet(len(s)) {
250250
if as, isASCII := makeASCIISet(chars); isASCII {
251251
for i, c := range s {
252252
if as.contains(c) {
@@ -301,7 +301,7 @@ func LastIndexAny(s []byte, chars string) int {
301301
// Avoid scanning all of s.
302302
return -1
303303
}
304-
if len(s) > 8 {
304+
if shouldUseASCIISet(len(s)) {
305305
if as, isASCII := makeASCIISet(chars); isASCII {
306306
for i := len(s) - 1; i >= 0; i-- {
307307
if as.contains(s[i]) {
@@ -935,15 +935,22 @@ func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int {
935935
return -1
936936
}
937937

938-
// asciiSet is a 32-byte value, where each bit represents the presence of a
939-
// given ASCII character in the set. The 128-bits of the lower 16 bytes,
940-
// starting with the least-significant bit of the lowest word to the
941-
// most-significant bit of the highest word, map to the full range of all
942-
// 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed,
943-
// ensuring that any non-ASCII character will be reported as not in the set.
944-
// This allocates a total of 32 bytes even though the upper half
945-
// is unused to avoid bounds checks in asciiSet.contains.
946-
type asciiSet [8]uint32
938+
// asciiSet is a 256-byte lookup table for fast ASCII character membership testing.
939+
// Each element corresponds to an ASCII character value, with true indicating the
940+
// character is in the set. Using bool instead of byte allows the compiler to
941+
// eliminate the comparison instruction, as bool values are guaranteed to be 0 or 1.
942+
//
943+
// The full 256-element table is used rather than a 128-element table to avoid
944+
// additional operations in the lookup path. Alternative approaches were tested:
945+
// - [128]bool with explicit bounds check (if c >= 128): introduces branches
946+
// that cause pipeline stalls, resulting in ~70% slower performance
947+
// - [128]bool with masking (c&0x7f): eliminates bounds checks but the AND
948+
// operation still costs ~10% performance compared to direct indexing
949+
//
950+
// The 256-element array allows direct indexing with no bounds checks, no branches,
951+
// and no masking operations, providing optimal performance. The additional 128 bytes
952+
// of memory is a worthwhile tradeoff for the simpler, faster code.
953+
type asciiSet [256]bool
947954

948955
// makeASCIISet creates a set of ASCII characters and reports whether all
949956
// characters in chars are ASCII.
@@ -953,14 +960,24 @@ func makeASCIISet(chars string) (as asciiSet, ok bool) {
953960
if c >= utf8.RuneSelf {
954961
return as, false
955962
}
956-
as[c/32] |= 1 << (c % 32)
963+
as[c] = true
957964
}
958965
return as, true
959966
}
960967

961968
// contains reports whether c is inside the set.
962969
func (as *asciiSet) contains(c byte) bool {
963-
return (as[c/32] & (1 << (c % 32))) != 0
970+
return as[c]
971+
}
972+
973+
// shouldUseASCIISet returns whether to use the lookup table optimization.
974+
// The threshold of 8 bytes balances initialization cost against per-byte
975+
// search cost, performing well across all charset sizes.
976+
//
977+
// More complex heuristics (e.g., different thresholds per charset size)
978+
// add branching overhead that eats away any theoretical improvements.
979+
func shouldUseASCIISet(bufLen int) bool {
980+
return bufLen > 8
964981
}
965982

966983
// containsRune is a simplified version of strings.ContainsRune

src/cmd/compile/internal/base/debug.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var Debug DebugFlags
1818
type DebugFlags struct {
1919
AlignHot int `help:"enable hot block alignment (currently requires -pgo)" concurrent:"ok"`
2020
Append int `help:"print information about append compilation"`
21+
AstDump string `help:"for specified function/method, dump AST/IR at interesting points in compilation, to file pkg.func.ast. Use leading ~ for regular expression match."`
2122
Checkptr int `help:"instrument unsafe pointer conversions\n0: instrumentation disabled\n1: conversions involving unsafe.Pointer are instrumented\n2: conversions to unsafe.Pointer force heap allocation" concurrent:"ok"`
2223
Closure int `help:"print information about closure compilation"`
2324
CompressInstructions int `help:"use compressed instructions when possible (if supported by architecture)"`

src/cmd/compile/internal/bloop/bloop.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,5 +320,9 @@ func Walk(pkg *ir.Package) {
320320
for _, fn := range pkg.Funcs {
321321
e := editor{false, fn}
322322
ir.EditChildren(fn, e.edit)
323+
if ir.MatchAstDump(fn, "bloop") {
324+
ir.AstDump(fn, "bloop, "+ir.FuncName(fn))
325+
}
323326
}
327+
324328
}

src/cmd/compile/internal/deadlocals/deadlocals.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ func Funcs(fns []*ir.Func) {
2323
zero := ir.NewBasicLit(base.AutogeneratedPos, types.Types[types.TINT], constant.MakeInt64(0))
2424

2525
for _, fn := range fns {
26+
2627
if fn.IsClosure() {
28+
if ir.MatchAstDump(fn, "deadlocals closure") {
29+
ir.AstDump(fn, "deadlocals closure skipped, "+ir.FuncName(fn))
30+
}
2731
continue
2832
}
2933

@@ -50,6 +54,9 @@ func Funcs(fns []*ir.Func) {
5054
k.Defn = nil
5155
}
5256
}
57+
if ir.MatchAstDump(fn, "deadlocals") {
58+
ir.AstDump(fn, "deadLocals, "+ir.FuncName(fn))
59+
}
5360
}
5461
}
5562

src/cmd/compile/internal/escape/escape.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,11 @@ func (b *batch) finish(fns []*ir.Func) {
380380
}
381381
}
382382

383+
for _, fn := range fns {
384+
if ir.MatchAstDump(fn, "escape") {
385+
ir.AstDump(fn, "escape, "+ir.FuncName(fn))
386+
}
387+
}
383388
}
384389

385390
// inMutualBatch reports whether function fn is in the batch of

src/cmd/compile/internal/gc/compile.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ func prepareFunc(fn *ir.Func) {
121121

122122
ir.CurFunc = fn
123123
walk.Walk(fn)
124+
if ir.MatchAstDump(fn, "walk") {
125+
ir.AstDump(fn, "walk, "+ir.FuncName(fn))
126+
}
124127
ir.CurFunc = nil // enforce no further uses of CurFunc
125128

126129
base.Ctxt.DwTextCount++

src/cmd/compile/internal/gc/main.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
// already been some compiler errors). It may also be invoked from the explicit panic in
4848
// hcrash(), in which case, we pass the panic on through.
4949
func handlePanic() {
50+
ir.CloseHTMLWriters()
5051
if err := recover(); err != nil {
5152
if err == "-h" {
5253
// Force real panic now with -h option (hcrash) - the error
@@ -243,13 +244,25 @@ func Main(archInit func(*ssagen.ArchInfo)) {
243244
}
244245
}
245246

247+
for _, fn := range typecheck.Target.Funcs {
248+
if ir.MatchAstDump(fn, "start") {
249+
ir.AstDump(fn, "start, "+ir.FuncName(fn))
250+
}
251+
}
252+
246253
// Apply bloop markings.
247254
bloop.Walk(typecheck.Target)
248255

249256
// Interleaved devirtualization and inlining.
250257
base.Timer.Start("fe", "devirtualize-and-inline")
251258
interleaved.DevirtualizeAndInlinePackage(typecheck.Target, profile)
252259

260+
for _, fn := range typecheck.Target.Funcs {
261+
if ir.MatchAstDump(fn, "devirtualize-and-inline") {
262+
ir.AstDump(fn, "devirtualize-and-inline, "+ir.FuncName(fn))
263+
}
264+
}
265+
253266
noder.MakeWrappers(typecheck.Target) // must happen after inlining
254267

255268
// Get variable capture right in for loops.

src/cmd/compile/internal/inline/inl.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ func CanInline(fn *ir.Func, profile *pgoir.Profile) {
286286
// locals, and we use this map to produce a pruned Inline.Dcl
287287
// list. See issue 25459 for more context.
288288

289+
dbg := ir.MatchAstDump(fn, "inline")
290+
289291
visitor := hairyVisitor{
290292
curFunc: fn,
291293
debug: isDebugFn(fn),
@@ -294,10 +296,17 @@ func CanInline(fn *ir.Func, profile *pgoir.Profile) {
294296
maxBudget: budget,
295297
extraCallCost: cc,
296298
profile: profile,
299+
dbg: dbg, // Useful for downstream debugging
297300
}
301+
298302
if visitor.tooHairy(fn) {
299303
reason = visitor.reason
304+
if dbg {
305+
ir.AstDump(fn, "inline, too hairy because "+visitor.reason+", "+ir.FuncName(fn))
306+
}
300307
return
308+
} else if dbg {
309+
ir.AstDump(fn, "inline, OK, "+ir.FuncName(fn))
301310
}
302311

303312
n.Func.Inl = &ir.Inline{
@@ -441,6 +450,7 @@ type hairyVisitor struct {
441450
usedLocals ir.NameSet
442451
do func(ir.Node) bool
443452
profile *pgoir.Profile
453+
dbg bool
444454
}
445455

446456
func isDebugFn(fn *ir.Func) bool {

0 commit comments

Comments
 (0)