From b6753baaedf6ae932c3ad4af1451163045e7ff84 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 15 Feb 2024 15:30:16 -0500 Subject: [PATCH] cmd/compile: compile len(ch) as call to runtime.chanlen An upcoming CL will give this call more to do. For now, separate out the compiler change that stops inlining the computation. Change-Id: I4c5cbd84a0694b306191bff38cc6ea2d69458d7d Reviewed-on: https://go-review.googlesource.com/c/go/+/564556 Auto-Submit: Russ Cox Reviewed-by: Matthew Dempsky Reviewed-by: Cuong Manh Le LUCI-TryBot-Result: Go LUCI --- src/cmd/compile/internal/ssagen/ssa.go | 3 + .../internal/typecheck/_builtin/coverage.go | 1 + .../internal/typecheck/_builtin/runtime.go | 4 +- src/cmd/compile/internal/typecheck/builtin.go | 267 +++++++++--------- src/cmd/compile/internal/walk/builtin.go | 17 +- src/runtime/chan.go | 13 +- 6 files changed, 163 insertions(+), 142 deletions(-) diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 68b1547048f..05919b99245 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -6349,6 +6349,9 @@ func (s *state) referenceTypeBuiltin(n *ir.UnaryExpr, x *ssa.Value) *ssa.Value { if !n.X.Type().IsMap() && !n.X.Type().IsChan() { s.Fatalf("node must be a map or a channel") } + if n.X.Type().IsChan() && n.Op() == ir.OLEN { + s.Fatalf("cannot inline len(chan)") // must use runtime.chanlen now + } // if n == nil { // return 0 // } else { diff --git a/src/cmd/compile/internal/typecheck/_builtin/coverage.go b/src/cmd/compile/internal/typecheck/_builtin/coverage.go index 02226356bc9..f5c9e24991e 100644 --- a/src/cmd/compile/internal/typecheck/_builtin/coverage.go +++ b/src/cmd/compile/internal/typecheck/_builtin/coverage.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // NOTE: If you change this file you must run "go generate" +// in cmd/compile/internal/typecheck // to update builtin.go. This is not done automatically // to avoid depending on having a working compiler binary. diff --git a/src/cmd/compile/internal/typecheck/_builtin/runtime.go b/src/cmd/compile/internal/typecheck/_builtin/runtime.go index 421152967cd..1ae9fe21d9f 100644 --- a/src/cmd/compile/internal/typecheck/_builtin/runtime.go +++ b/src/cmd/compile/internal/typecheck/_builtin/runtime.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // NOTE: If you change this file you must run "go generate" +// in cmd/compile/internal/typecheck // to update builtin.go. This is not done automatically // to avoid depending on having a working compiler binary. @@ -158,7 +159,8 @@ func makechan(chanType *byte, size int) (hchan chan any) func chanrecv1(hchan <-chan any, elem *any) func chanrecv2(hchan <-chan any, elem *any) bool func chansend1(hchan chan<- any, elem *any) -func closechan(hchan any) +func closechan(hchan chan<- any) +func chanlen(hchan any) int var writeBarrier struct { enabled bool diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go index 09f60c68c0f..975eff3f504 100644 --- a/src/cmd/compile/internal/typecheck/builtin.go +++ b/src/cmd/compile/internal/typecheck/builtin.go @@ -136,106 +136,107 @@ var runtimeDecls = [...]struct { {"chanrecv1", funcTag, 100}, {"chanrecv2", funcTag, 101}, {"chansend1", funcTag, 103}, - {"closechan", funcTag, 30}, - {"writeBarrier", varTag, 105}, - {"typedmemmove", funcTag, 106}, - {"typedmemclr", funcTag, 107}, - {"typedslicecopy", funcTag, 108}, - {"selectnbsend", funcTag, 109}, - {"selectnbrecv", funcTag, 110}, - {"selectsetpc", funcTag, 111}, - {"selectgo", funcTag, 112}, + {"closechan", funcTag, 104}, + {"chanlen", funcTag, 105}, + {"writeBarrier", varTag, 107}, + {"typedmemmove", funcTag, 108}, + {"typedmemclr", funcTag, 109}, + {"typedslicecopy", funcTag, 110}, + {"selectnbsend", funcTag, 111}, + {"selectnbrecv", funcTag, 112}, + {"selectsetpc", funcTag, 113}, + {"selectgo", funcTag, 114}, {"block", funcTag, 9}, - {"makeslice", funcTag, 113}, - {"makeslice64", funcTag, 114}, - {"makeslicecopy", funcTag, 115}, - {"growslice", funcTag, 117}, - {"unsafeslicecheckptr", funcTag, 118}, + {"makeslice", funcTag, 115}, + {"makeslice64", funcTag, 116}, + {"makeslicecopy", funcTag, 117}, + {"growslice", funcTag, 119}, + {"unsafeslicecheckptr", funcTag, 120}, {"panicunsafeslicelen", funcTag, 9}, {"panicunsafeslicenilptr", funcTag, 9}, - {"unsafestringcheckptr", funcTag, 119}, + {"unsafestringcheckptr", funcTag, 121}, {"panicunsafestringlen", funcTag, 9}, {"panicunsafestringnilptr", funcTag, 9}, - {"memmove", funcTag, 120}, - {"memclrNoHeapPointers", funcTag, 121}, - {"memclrHasPointers", funcTag, 121}, - {"memequal", funcTag, 122}, - {"memequal0", funcTag, 123}, - {"memequal8", funcTag, 123}, - {"memequal16", funcTag, 123}, - {"memequal32", funcTag, 123}, - {"memequal64", funcTag, 123}, - {"memequal128", funcTag, 123}, - {"f32equal", funcTag, 124}, - {"f64equal", funcTag, 124}, - {"c64equal", funcTag, 124}, - {"c128equal", funcTag, 124}, - {"strequal", funcTag, 124}, - {"interequal", funcTag, 124}, - {"nilinterequal", funcTag, 124}, - {"memhash", funcTag, 125}, - {"memhash0", funcTag, 126}, - {"memhash8", funcTag, 126}, - {"memhash16", funcTag, 126}, - {"memhash32", funcTag, 126}, - {"memhash64", funcTag, 126}, - {"memhash128", funcTag, 126}, - {"f32hash", funcTag, 127}, - {"f64hash", funcTag, 127}, - {"c64hash", funcTag, 127}, - {"c128hash", funcTag, 127}, - {"strhash", funcTag, 127}, - {"interhash", funcTag, 127}, - {"nilinterhash", funcTag, 127}, - {"int64div", funcTag, 128}, - {"uint64div", funcTag, 129}, - {"int64mod", funcTag, 128}, - {"uint64mod", funcTag, 129}, - {"float64toint64", funcTag, 130}, - {"float64touint64", funcTag, 131}, - {"float64touint32", funcTag, 132}, - {"int64tofloat64", funcTag, 133}, - {"int64tofloat32", funcTag, 135}, - {"uint64tofloat64", funcTag, 136}, - {"uint64tofloat32", funcTag, 137}, - {"uint32tofloat64", funcTag, 138}, - {"complex128div", funcTag, 139}, - {"getcallerpc", funcTag, 140}, - {"getcallersp", funcTag, 140}, + {"memmove", funcTag, 122}, + {"memclrNoHeapPointers", funcTag, 123}, + {"memclrHasPointers", funcTag, 123}, + {"memequal", funcTag, 124}, + {"memequal0", funcTag, 125}, + {"memequal8", funcTag, 125}, + {"memequal16", funcTag, 125}, + {"memequal32", funcTag, 125}, + {"memequal64", funcTag, 125}, + {"memequal128", funcTag, 125}, + {"f32equal", funcTag, 126}, + {"f64equal", funcTag, 126}, + {"c64equal", funcTag, 126}, + {"c128equal", funcTag, 126}, + {"strequal", funcTag, 126}, + {"interequal", funcTag, 126}, + {"nilinterequal", funcTag, 126}, + {"memhash", funcTag, 127}, + {"memhash0", funcTag, 128}, + {"memhash8", funcTag, 128}, + {"memhash16", funcTag, 128}, + {"memhash32", funcTag, 128}, + {"memhash64", funcTag, 128}, + {"memhash128", funcTag, 128}, + {"f32hash", funcTag, 129}, + {"f64hash", funcTag, 129}, + {"c64hash", funcTag, 129}, + {"c128hash", funcTag, 129}, + {"strhash", funcTag, 129}, + {"interhash", funcTag, 129}, + {"nilinterhash", funcTag, 129}, + {"int64div", funcTag, 130}, + {"uint64div", funcTag, 131}, + {"int64mod", funcTag, 130}, + {"uint64mod", funcTag, 131}, + {"float64toint64", funcTag, 132}, + {"float64touint64", funcTag, 133}, + {"float64touint32", funcTag, 134}, + {"int64tofloat64", funcTag, 135}, + {"int64tofloat32", funcTag, 137}, + {"uint64tofloat64", funcTag, 138}, + {"uint64tofloat32", funcTag, 139}, + {"uint32tofloat64", funcTag, 140}, + {"complex128div", funcTag, 141}, + {"getcallerpc", funcTag, 142}, + {"getcallersp", funcTag, 142}, {"racefuncenter", funcTag, 31}, {"racefuncexit", funcTag, 9}, {"raceread", funcTag, 31}, {"racewrite", funcTag, 31}, - {"racereadrange", funcTag, 141}, - {"racewriterange", funcTag, 141}, - {"msanread", funcTag, 141}, - {"msanwrite", funcTag, 141}, - {"msanmove", funcTag, 142}, - {"asanread", funcTag, 141}, - {"asanwrite", funcTag, 141}, - {"checkptrAlignment", funcTag, 143}, - {"checkptrArithmetic", funcTag, 145}, - {"libfuzzerTraceCmp1", funcTag, 146}, - {"libfuzzerTraceCmp2", funcTag, 147}, - {"libfuzzerTraceCmp4", funcTag, 148}, - {"libfuzzerTraceCmp8", funcTag, 149}, - {"libfuzzerTraceConstCmp1", funcTag, 146}, - {"libfuzzerTraceConstCmp2", funcTag, 147}, - {"libfuzzerTraceConstCmp4", funcTag, 148}, - {"libfuzzerTraceConstCmp8", funcTag, 149}, - {"libfuzzerHookStrCmp", funcTag, 150}, - {"libfuzzerHookEqualFold", funcTag, 150}, - {"addCovMeta", funcTag, 152}, + {"racereadrange", funcTag, 143}, + {"racewriterange", funcTag, 143}, + {"msanread", funcTag, 143}, + {"msanwrite", funcTag, 143}, + {"msanmove", funcTag, 144}, + {"asanread", funcTag, 143}, + {"asanwrite", funcTag, 143}, + {"checkptrAlignment", funcTag, 145}, + {"checkptrArithmetic", funcTag, 147}, + {"libfuzzerTraceCmp1", funcTag, 148}, + {"libfuzzerTraceCmp2", funcTag, 149}, + {"libfuzzerTraceCmp4", funcTag, 150}, + {"libfuzzerTraceCmp8", funcTag, 151}, + {"libfuzzerTraceConstCmp1", funcTag, 148}, + {"libfuzzerTraceConstCmp2", funcTag, 149}, + {"libfuzzerTraceConstCmp4", funcTag, 150}, + {"libfuzzerTraceConstCmp8", funcTag, 151}, + {"libfuzzerHookStrCmp", funcTag, 152}, + {"libfuzzerHookEqualFold", funcTag, 152}, + {"addCovMeta", funcTag, 154}, {"x86HasPOPCNT", varTag, 6}, {"x86HasSSE41", varTag, 6}, {"x86HasFMA", varTag, 6}, {"armHasVFPv4", varTag, 6}, {"arm64HasATOMICS", varTag, 6}, - {"asanregisterglobals", funcTag, 121}, + {"asanregisterglobals", funcTag, 123}, } func runtimeTypes() []*types.Type { - var typs [153]*types.Type + var typs [155]*types.Type typs[0] = types.ByteType typs[1] = types.NewPtr(typs[0]) typs[2] = types.Types[types.TANY] @@ -340,55 +341,57 @@ func runtimeTypes() []*types.Type { typs[101] = newSig(params(typs[99], typs[3]), params(typs[6])) typs[102] = types.NewChan(typs[2], types.Csend) typs[103] = newSig(params(typs[102], typs[3]), nil) - typs[104] = types.NewArray(typs[0], 3) - typs[105] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[104]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])}) - typs[106] = newSig(params(typs[1], typs[3], typs[3]), nil) - typs[107] = newSig(params(typs[1], typs[3]), nil) - typs[108] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15])) - typs[109] = newSig(params(typs[102], typs[3]), params(typs[6])) - typs[110] = newSig(params(typs[3], typs[99]), params(typs[6], typs[6])) - typs[111] = newSig(params(typs[71]), nil) - typs[112] = newSig(params(typs[1], typs[1], typs[71], typs[15], typs[15], typs[6]), params(typs[15], typs[6])) - typs[113] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7])) - typs[114] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7])) - typs[115] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7])) - typs[116] = types.NewSlice(typs[2]) - typs[117] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[116])) - typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil) - typs[119] = newSig(params(typs[7], typs[22]), nil) - typs[120] = newSig(params(typs[3], typs[3], typs[5]), nil) - typs[121] = newSig(params(typs[7], typs[5]), nil) - typs[122] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6])) - typs[123] = newSig(params(typs[3], typs[3]), params(typs[6])) - typs[124] = newSig(params(typs[7], typs[7]), params(typs[6])) - typs[125] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5])) - typs[126] = newSig(params(typs[7], typs[5]), params(typs[5])) - typs[127] = newSig(params(typs[3], typs[5]), params(typs[5])) - typs[128] = newSig(params(typs[22], typs[22]), params(typs[22])) - typs[129] = newSig(params(typs[24], typs[24]), params(typs[24])) - typs[130] = newSig(params(typs[20]), params(typs[22])) - typs[131] = newSig(params(typs[20]), params(typs[24])) - typs[132] = newSig(params(typs[20]), params(typs[60])) - typs[133] = newSig(params(typs[22]), params(typs[20])) - typs[134] = types.Types[types.TFLOAT32] - typs[135] = newSig(params(typs[22]), params(typs[134])) - typs[136] = newSig(params(typs[24]), params(typs[20])) - typs[137] = newSig(params(typs[24]), params(typs[134])) - typs[138] = newSig(params(typs[60]), params(typs[20])) - typs[139] = newSig(params(typs[26], typs[26]), params(typs[26])) - typs[140] = newSig(nil, params(typs[5])) - typs[141] = newSig(params(typs[5], typs[5]), nil) - typs[142] = newSig(params(typs[5], typs[5], typs[5]), nil) - typs[143] = newSig(params(typs[7], typs[1], typs[5]), nil) - typs[144] = types.NewSlice(typs[7]) - typs[145] = newSig(params(typs[7], typs[144]), nil) - typs[146] = newSig(params(typs[64], typs[64], typs[17]), nil) - typs[147] = newSig(params(typs[58], typs[58], typs[17]), nil) - typs[148] = newSig(params(typs[60], typs[60], typs[17]), nil) - typs[149] = newSig(params(typs[24], typs[24], typs[17]), nil) - typs[150] = newSig(params(typs[28], typs[28], typs[17]), nil) - typs[151] = types.NewArray(typs[0], 16) - typs[152] = newSig(params(typs[7], typs[60], typs[151], typs[28], typs[15], typs[64], typs[64]), params(typs[60])) + typs[104] = newSig(params(typs[102]), nil) + typs[105] = newSig(params(typs[2]), params(typs[15])) + typs[106] = types.NewArray(typs[0], 3) + typs[107] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[106]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])}) + typs[108] = newSig(params(typs[1], typs[3], typs[3]), nil) + typs[109] = newSig(params(typs[1], typs[3]), nil) + typs[110] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15])) + typs[111] = newSig(params(typs[102], typs[3]), params(typs[6])) + typs[112] = newSig(params(typs[3], typs[99]), params(typs[6], typs[6])) + typs[113] = newSig(params(typs[71]), nil) + typs[114] = newSig(params(typs[1], typs[1], typs[71], typs[15], typs[15], typs[6]), params(typs[15], typs[6])) + typs[115] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7])) + typs[116] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7])) + typs[117] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7])) + typs[118] = types.NewSlice(typs[2]) + typs[119] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[118])) + typs[120] = newSig(params(typs[1], typs[7], typs[22]), nil) + typs[121] = newSig(params(typs[7], typs[22]), nil) + typs[122] = newSig(params(typs[3], typs[3], typs[5]), nil) + typs[123] = newSig(params(typs[7], typs[5]), nil) + typs[124] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6])) + typs[125] = newSig(params(typs[3], typs[3]), params(typs[6])) + typs[126] = newSig(params(typs[7], typs[7]), params(typs[6])) + typs[127] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5])) + typs[128] = newSig(params(typs[7], typs[5]), params(typs[5])) + typs[129] = newSig(params(typs[3], typs[5]), params(typs[5])) + typs[130] = newSig(params(typs[22], typs[22]), params(typs[22])) + typs[131] = newSig(params(typs[24], typs[24]), params(typs[24])) + typs[132] = newSig(params(typs[20]), params(typs[22])) + typs[133] = newSig(params(typs[20]), params(typs[24])) + typs[134] = newSig(params(typs[20]), params(typs[60])) + typs[135] = newSig(params(typs[22]), params(typs[20])) + typs[136] = types.Types[types.TFLOAT32] + typs[137] = newSig(params(typs[22]), params(typs[136])) + typs[138] = newSig(params(typs[24]), params(typs[20])) + typs[139] = newSig(params(typs[24]), params(typs[136])) + typs[140] = newSig(params(typs[60]), params(typs[20])) + typs[141] = newSig(params(typs[26], typs[26]), params(typs[26])) + typs[142] = newSig(nil, params(typs[5])) + typs[143] = newSig(params(typs[5], typs[5]), nil) + typs[144] = newSig(params(typs[5], typs[5], typs[5]), nil) + typs[145] = newSig(params(typs[7], typs[1], typs[5]), nil) + typs[146] = types.NewSlice(typs[7]) + typs[147] = newSig(params(typs[7], typs[146]), nil) + typs[148] = newSig(params(typs[64], typs[64], typs[17]), nil) + typs[149] = newSig(params(typs[58], typs[58], typs[17]), nil) + typs[150] = newSig(params(typs[60], typs[60], typs[17]), nil) + typs[151] = newSig(params(typs[24], typs[24], typs[17]), nil) + typs[152] = newSig(params(typs[28], typs[28], typs[17]), nil) + typs[153] = types.NewArray(typs[0], 16) + typs[154] = newSig(params(typs[7], typs[60], typs[153], typs[28], typs[15], typs[64], typs[64]), params(typs[60])) return typs[:] } diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go index 41fc0bf5dcb..9a2c1353bb1 100644 --- a/src/cmd/compile/internal/walk/builtin.go +++ b/src/cmd/compile/internal/walk/builtin.go @@ -153,9 +153,7 @@ func walkClear(n *ir.UnaryExpr) ir.Node { // walkClose walks an OCLOSE node. func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { - // cannot use chanfn - closechan takes any, not chan any - fn := typecheck.LookupRuntime("closechan", n.X.Type()) - return mkcall1(fn, nil, init, n.X) + return mkcall1(chanfn("closechan", 1, n.X.Type()), nil, init, n.X) } // Lower copy(a, b) to a memmove call or a runtime call. @@ -263,6 +261,12 @@ func walkLenCap(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { _, len := backingArrayPtrLen(cheapExpr(conv.X, init)) return len } + if isChanLen(n) { + // cannot use chanfn - closechan takes any, not chan any, + // because it accepts both send-only and recv-only channels. + fn := typecheck.LookupRuntime("chanlen", n.X.Type()) + return mkcall1(fn, n.Type(), init, n.X) + } n.X = walkExpr(n.X, init) @@ -887,3 +891,10 @@ func isByteCount(n ir.Node) bool { return base.Flag.N == 0 && !base.Flag.Cfg.Instrumenting && n.Op() == ir.OLEN && (n.(*ir.UnaryExpr).X.Op() == ir.OBYTES2STR || n.(*ir.UnaryExpr).X.Op() == ir.OBYTES2STRTMP) } + +// isChanLen reports whether n is of the form len(c) for a channel c. +// Note that this does not check for -n or instrumenting because this +// is a correctness rewrite, not an optimization. +func isChanLen(n ir.Node) bool { + return n.Op() == ir.OLEN && n.(*ir.UnaryExpr).X.Type().IsChan() +} diff --git a/src/runtime/chan.go b/src/runtime/chan.go index ff9e2a9155a..c48b85f5763 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -724,20 +724,21 @@ func reflect_chanrecv(c *hchan, nb bool, elem unsafe.Pointer) (selected bool, re return chanrecv(c, elem, !nb) } -//go:linkname reflect_chanlen reflect.chanlen -func reflect_chanlen(c *hchan) int { +func chanlen(c *hchan) int { if c == nil { return 0 } return int(c.qcount) } +//go:linkname reflect_chanlen reflect.chanlen +func reflect_chanlen(c *hchan) int { + return chanlen(c) +} + //go:linkname reflectlite_chanlen internal/reflectlite.chanlen func reflectlite_chanlen(c *hchan) int { - if c == nil { - return 0 - } - return int(c.qcount) + return chanlen(c) } //go:linkname reflect_chancap reflect.chancap