From: David Chase Date: Sat, 11 Nov 2023 20:32:45 +0000 (-0500) Subject: cmd/compile: add compiler debug flag to disable range func iterator checking X-Git-Tag: go1.22rc1~304 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d411b3197cbd73b060f456b2518b94728bc4a91b;p=gostls13.git cmd/compile: add compiler debug flag to disable range func iterator checking E.g. `GOEXPERIMENT=rangefunc go test -v -gcflags=-d=rangefunccheck=0 rangefunc_test.go` will turn off the checking and fail. The benchmarks, which do not use pathological iterators, run slightly faster. Change-Id: Ia3e175e86d67ef74bbae9bcc5d2def6a2cdf519d Reviewed-on: https://go-review.googlesource.com/c/go/+/541995 Run-TryBot: David Chase Reviewed-by: Matthew Dempsky TryBot-Result: Gopher Robot --- diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go index 390ddf3176..b9b7d5d565 100644 --- a/src/cmd/compile/internal/base/debug.go +++ b/src/cmd/compile/internal/base/debug.go @@ -60,6 +60,7 @@ type DebugFlags struct { PGOInlineCDFThreshold string `help:"cumulative threshold percentage for determining call sites as hot candidates for inlining" concurrent:"ok"` PGOInlineBudget int `help:"inline budget for hot functions" concurrent:"ok"` PGODevirtualize int `help:"enable profile-guided devirtualization" concurrent:"ok"` + RangeFuncCheck int `help:"insert code to check behavior of range iterator functions" concurrent:"ok"` WrapGlobalMapDbg int `help:"debug trace output for global map init wrapping"` WrapGlobalMapCtl int `help:"global map init wrap control (0 => default, 1 => off, 2 => stress mode, no size cutoff)"` ZeroCopy int `help:"enable zero-copy string->[]byte conversions" concurrent:"ok"` diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go index dc0952a0f1..d436665129 100644 --- a/src/cmd/compile/internal/base/flag.go +++ b/src/cmd/compile/internal/base/flag.go @@ -182,6 +182,7 @@ func ParseFlags() { Debug.PGODevirtualize = 1 Debug.SyncFrames = -1 // disable sync markers by default Debug.ZeroCopy = 1 + Debug.RangeFuncCheck = 1 Debug.Checkptr = -1 // so we can tell whether it is set explicitly diff --git a/src/cmd/compile/internal/rangefunc/rewrite.go b/src/cmd/compile/internal/rangefunc/rewrite.go index 460efc69d1..7475c570aa 100644 --- a/src/cmd/compile/internal/rangefunc/rewrite.go +++ b/src/cmd/compile/internal/rangefunc/rewrite.go @@ -554,6 +554,11 @@ func rewriteFunc(pkg *types2.Package, info *types2.Info, typ *syntax.FuncType, b } } +// checkFuncMisuse reports whether to check for misuse of iterator callbacks functions. +func (r *rewriter) checkFuncMisuse() bool { + return base.Debug.RangeFuncCheck != 0 +} + // inspect is a callback for syntax.Inspect that drives the actual rewriting. // If it sees a func literal, it kicks off a separate rewrite for that literal. // Otherwise, it maintains a stack of range-over-func loops and @@ -621,7 +626,10 @@ func (r *rewriter) startLoop(loop *forLoop) { r.false = types2.Universe.Lookup("false") r.rewritten = make(map[*syntax.ForStmt]syntax.Stmt) } - loop.exitFlag, loop.exitFlagDecl = r.exitVar(loop.nfor.Pos()) + if r.checkFuncMisuse() { + // declare the exit flag for this loop's body + loop.exitFlag, loop.exitFlagDecl = r.exitVar(loop.nfor.Pos()) + } } // editStmt returns the replacement for the statement x, @@ -714,8 +722,11 @@ func (r *rewriter) editReturn(x *syntax.ReturnStmt) syntax.Stmt { bl.List = append(bl.List, &syntax.AssignStmt{Lhs: r.useList(r.retVars), Rhs: x.Results}) } bl.List = append(bl.List, &syntax.AssignStmt{Lhs: r.next(), Rhs: r.intConst(next)}) - for i := 0; i < len(r.forStack); i++ { - bl.List = append(bl.List, r.setExitedAt(i)) + if r.checkFuncMisuse() { + // mark all enclosing loop bodies as exited + for i := 0; i < len(r.forStack); i++ { + bl.List = append(bl.List, r.setExitedAt(i)) + } } bl.List = append(bl.List, &syntax.ReturnStmt{Results: r.useVar(r.false)}) setPos(bl, x.Pos()) @@ -811,8 +822,14 @@ func (r *rewriter) editBranch(x *syntax.BranchStmt) syntax.Stmt { // If this is a simple break, mark this loop as exited and return false. // No adjustments to #next. if depth == 0 { + var stmts []syntax.Stmt + if r.checkFuncMisuse() { + stmts = []syntax.Stmt{r.setExited(), ret} + } else { + stmts = []syntax.Stmt{ret} + } bl := &syntax.BlockStmt{ - List: []syntax.Stmt{r.setExited(), ret}, + List: stmts, } setPos(bl, x.Pos()) return bl @@ -846,9 +863,11 @@ func (r *rewriter) editBranch(x *syntax.BranchStmt) syntax.Stmt { List: []syntax.Stmt{as}, } - // Set #exitK for this loop and those exited by the control flow. - for i := exitFrom; i < len(r.forStack); i++ { - bl.List = append(bl.List, r.setExitedAt(i)) + if r.checkFuncMisuse() { + // Set #exitK for this loop and those exited by the control flow. + for i := exitFrom; i < len(r.forStack); i++ { + bl.List = append(bl.List, r.setExitedAt(i)) + } } bl.List = append(bl.List, ret) @@ -953,12 +972,18 @@ func (r *rewriter) endLoop(loop *forLoop) { } // declare the exitFlag here so it has proper scope and zeroing - exitFlagDecl := &syntax.DeclStmt{DeclList: []syntax.Decl{loop.exitFlagDecl}} - block.List = append(block.List, exitFlagDecl) + if r.checkFuncMisuse() { + exitFlagDecl := &syntax.DeclStmt{DeclList: []syntax.Decl{loop.exitFlagDecl}} + block.List = append(block.List, exitFlagDecl) + } + // iteratorFunc(bodyFunc) block.List = append(block.List, call) - block.List = append(block.List, r.setExited()) + if r.checkFuncMisuse() { + // iteratorFunc has exited, mark the exit flag for the body + block.List = append(block.List, r.setExited()) + } block.List = append(block.List, checks...) if len(r.forStack) == 1 { // ending an outermost loop @@ -1037,7 +1062,9 @@ func (r *rewriter) bodyFunc(body []syntax.Stmt, lhs []syntax.Expr, def bool, fty loop := r.forStack[len(r.forStack)-1] - bodyFunc.Body.List = append(bodyFunc.Body.List, r.assertNotExited(start, loop)) + if r.checkFuncMisuse() { + bodyFunc.Body.List = append(bodyFunc.Body.List, r.assertNotExited(start, loop)) + } // Original loop body (already rewritten by editStmt during inspect). bodyFunc.Body.List = append(bodyFunc.Body.List, body...)