From bdb480fd623e58d0d1d0689a3755367379ea57bc Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 8 Sep 2020 15:28:43 +0700 Subject: cmd/compile: fix mishandling of unsafe-uintptr arguments in go/defer Currently, the statement: go g(uintptr(f())) gets rewritten into: tmp := f() newproc(8, g, uintptr(tmp)) runtime.KeepAlive(tmp) which doesn't guarantee that tmp is still alive by time the g call is scheduled to run. This CL fixes the issue, by wrapping g call in a closure: go func(p unsafe.Pointer) { g(uintptr(p)) }(f()) then this will be rewritten into: tmp := f() go func(p unsafe.Pointer) { g(uintptr(p)) runtime.KeepAlive(p) }(tmp) runtime.KeepAlive(tmp) // superfluous, but harmless So the unsafe.Pointer p will be kept alive at the time g call runs. Updates #24491 Change-Id: Ic10821251cbb1b0073daec92b82a866c6ebaf567 Reviewed-on: https://go-review.googlesource.com/c/go/+/253457 Run-TryBot: Cuong Manh Le Reviewed-by: Matthew Dempsky TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/order.go | 1 + src/cmd/compile/internal/gc/syntax.go | 41 +++++++++++++++++--------- src/cmd/compile/internal/gc/walk.go | 54 ++++++++++++++++++++++++++++------- 3 files changed, 72 insertions(+), 24 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index aa91160e5c..412f073a8d 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -502,6 +502,7 @@ func (o *Order) call(n *Node) { x := o.copyExpr(arg.Left, arg.Left.Type, false) x.Name.SetKeepalive(true) arg.Left = x + n.SetNeedsWrapper(true) } } diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 47e5e59156..5580f789c5 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -141,19 +141,20 @@ const ( nodeInitorder, _ // tracks state during init1; two bits _, _ // second nodeInitorder bit _, nodeHasBreak - _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only - _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND - _, nodeIsDDD // is the argument variadic - _, nodeDiag // already printed error about this - _, nodeColas // OAS resulting from := - _, nodeNonNil // guaranteed to be non-nil - _, nodeTransient // storage can be reused immediately after this statement - _, nodeBounded // bounds check unnecessary - _, nodeHasCall // expression contains a function call - _, nodeLikely // if statement condition likely - _, nodeHasVal // node.E contains a Val - _, nodeHasOpt // node.E contains an Opt - _, nodeEmbedded // ODCLFIELD embedded type + _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only + _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND + _, nodeIsDDD // is the argument variadic + _, nodeDiag // already printed error about this + _, nodeColas // OAS resulting from := + _, nodeNonNil // guaranteed to be non-nil + _, nodeTransient // storage can be reused immediately after this statement + _, nodeBounded // bounds check unnecessary + _, nodeHasCall // expression contains a function call + _, nodeLikely // if statement condition likely + _, nodeHasVal // node.E contains a Val + _, nodeHasOpt // node.E contains an Opt + _, nodeEmbedded // ODCLFIELD embedded type + _, nodeNeedsWrapper // OCALLxxx node that needs to be wrapped ) func (n *Node) Class() Class { return Class(n.flags.get3(nodeClass)) } @@ -286,6 +287,20 @@ func (n *Node) SetIota(x int64) { n.Xoffset = x } +func (n *Node) NeedsWrapper() bool { + return n.flags&nodeNeedsWrapper != 0 +} + +// SetNeedsWrapper indicates that OCALLxxx node needs to be wrapped by a closure. +func (n *Node) SetNeedsWrapper(b bool) { + switch n.Op { + case OCALLFUNC, OCALLMETH, OCALLINTER: + default: + Fatalf("Node.SetNeedsWrapper %v", n.Op) + } + n.flags.set(nodeNeedsWrapper, b) +} + // mayBeShared reports whether n may occur in multiple places in the AST. // Extra care must be taken when mutating such a node. func (n *Node) mayBeShared() bool { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 0158af8700..ab7f857031 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -232,7 +232,11 @@ func walkstmt(n *Node) *Node { n.Left = copyany(n.Left, &n.Ninit, true) default: - n.Left = walkexpr(n.Left, &n.Ninit) + if n.Left.NeedsWrapper() { + n.Left = wrapCall(n.Left, &n.Ninit) + } else { + n.Left = walkexpr(n.Left, &n.Ninit) + } } case OFOR, OFORUNTIL: @@ -3857,6 +3861,14 @@ func candiscard(n *Node) bool { // builtin(a1, a2, a3) // }(x, y, z) // for print, println, and delete. +// +// Rewrite +// go f(x, y, uintptr(unsafe.Pointer(z))) +// into +// go func(a1, a2, a3) { +// builtin(a1, a2, uintptr(a3)) +// }(x, y, unsafe.Pointer(z)) +// for function contains unsafe-uintptr arguments. var wrapCall_prgen int @@ -3868,9 +3880,17 @@ func wrapCall(n *Node, init *Nodes) *Node { init.AppendNodes(&n.Ninit) } + isBuiltinCall := n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER + // origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion. + origArgs := make([]*Node, n.List.Len()) t := nod(OTFUNC, nil, nil) for i, arg := range n.List.Slice() { s := lookupN("a", i) + if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR { + origArgs[i] = arg + arg = arg.Left + n.List.SetIndex(i, arg) + } t.List.Append(symfield(s, arg.Type)) } @@ -3878,10 +3898,22 @@ func wrapCall(n *Node, init *Nodes) *Node { sym := lookupN("wrap·", wrapCall_prgen) fn := dclfunc(sym, t) - a := nod(n.Op, nil, nil) - a.List.Set(paramNnames(t.Type)) - a = typecheck(a, ctxStmt) - fn.Nbody.Set1(a) + args := paramNnames(t.Type) + for i, origArg := range origArgs { + if origArg == nil { + continue + } + arg := nod(origArg.Op, args[i], nil) + arg.Type = origArg.Type + args[i] = arg + } + call := nod(n.Op, nil, nil) + if !isBuiltinCall { + call.Op = OCALL + call.Left = n.Left + } + call.List.Set(args) + fn.Nbody.Set1(call) funcbody() @@ -3889,12 +3921,12 @@ func wrapCall(n *Node, init *Nodes) *Node { typecheckslice(fn.Nbody.Slice(), ctxStmt) xtop = append(xtop, fn) - a = nod(OCALL, nil, nil) - a.Left = fn.Func.Nname - a.List.Set(n.List.Slice()) - a = typecheck(a, ctxStmt) - a = walkexpr(a, init) - return a + call = nod(OCALL, nil, nil) + call.Left = fn.Func.Nname + call.List.Set(n.List.Slice()) + call = typecheck(call, ctxStmt) + call = walkexpr(call, init) + return call } // substArgTypes substitutes the given list of types for -- cgit v1.3 From 9cf88333e8255155be4e136c572883bb5ad546bd Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 9 Sep 2020 12:06:18 +0700 Subject: cmd/compile: consistently use Type.IsUnsafePtr() Passes toolstash-check. Change-Id: Iaeae7cc20e26af733642c7c8c7ca0a059e5b07b2 Reviewed-on: https://go-review.googlesource.com/c/go/+/253657 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/escape.go | 6 +++--- src/cmd/compile/internal/gc/ssa.go | 2 +- src/cmd/compile/internal/gc/subr.go | 4 ++-- src/cmd/compile/internal/gc/walk.go | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index d5cca4a38b..dc469e276c 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -485,7 +485,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) { e.discard(max) case OCONV, OCONVNOP: - if checkPtr(e.curfn, 2) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() { + if checkPtr(e.curfn, 2) && n.Type.IsUnsafePtr() && n.Left.Type.IsPtr() { // When -d=checkptr=2 is enabled, treat // conversions to unsafe.Pointer as an // escaping operation. This allows better @@ -493,7 +493,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) { // easily detect object boundaries on the heap // than the stack. e.assignHeap(n.Left, "conversion to unsafe.Pointer", n) - } else if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { + } else if n.Type.IsUnsafePtr() && n.Left.Type.Etype == TUINTPTR { e.unsafeValue(k, n.Left) } else { e.expr(k, n.Left) @@ -625,7 +625,7 @@ func (e *Escape) unsafeValue(k EscHole, n *Node) { switch n.Op { case OCONV, OCONVNOP: - if n.Left.Type.Etype == TUNSAFEPTR { + if n.Left.Type.IsUnsafePtr() { e.expr(k, n.Left) } else { e.discard(n.Left) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 52083d999e..89644cd3f2 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -2113,7 +2113,7 @@ func (s *state) expr(n *Node) *ssa.Value { } // unsafe.Pointer <--> *T - if to.Etype == TUNSAFEPTR && from.IsPtrShaped() || from.Etype == TUNSAFEPTR && to.IsPtrShaped() { + if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() { return v } diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 8fa3fca50f..6d0a40c287 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -781,12 +781,12 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { } // 8. src is a pointer or uintptr and dst is unsafe.Pointer. - if (src.IsPtr() || src.Etype == TUINTPTR) && dst.Etype == TUNSAFEPTR { + if (src.IsPtr() || src.Etype == TUINTPTR) && dst.IsUnsafePtr() { return OCONVNOP } // 9. src is unsafe.Pointer and dst is a pointer or uintptr. - if src.Etype == TUNSAFEPTR && (dst.IsPtr() || dst.Etype == TUINTPTR) { + if src.IsUnsafePtr() && (dst.IsPtr() || dst.Etype == TUINTPTR) { return OCONVNOP } diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index ab7f857031..a9fefb3ddd 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -958,11 +958,11 @@ opswitch: case OCONV, OCONVNOP: n.Left = walkexpr(n.Left, init) if n.Op == OCONVNOP && checkPtr(Curfn, 1) { - if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T + if n.Type.IsPtr() && n.Left.Type.IsUnsafePtr() { // unsafe.Pointer to *T n = walkCheckPtrAlignment(n, init, nil) break } - if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer + if n.Type.IsUnsafePtr() && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer n = walkCheckPtrArithmetic(n, init) break } @@ -1127,7 +1127,7 @@ opswitch: n.List.SetSecond(walkexpr(n.List.Second(), init)) case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: - checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.Etype == TUNSAFEPTR + checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.IsUnsafePtr() if checkSlice { n.Left.Left = walkexpr(n.Left.Left, init) } else { @@ -3886,7 +3886,7 @@ func wrapCall(n *Node, init *Nodes) *Node { t := nod(OTFUNC, nil, nil) for i, arg := range n.List.Slice() { s := lookupN("a", i) - if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR { + if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.IsUnsafePtr() { origArgs[i] = arg arg = arg.Left n.List.SetIndex(i, arg) @@ -4041,7 +4041,7 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node { walk(n.Left) } case OCONVNOP: - if n.Left.Type.Etype == TUNSAFEPTR { + if n.Left.Type.IsUnsafePtr() { n.Left = cheapexpr(n.Left, init) originals = append(originals, convnop(n.Left, types.Types[TUNSAFEPTR])) } -- cgit v1.3 From 518369601ca2499cea68af86451f17d2856895f8 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 9 Sep 2020 12:09:26 +0700 Subject: cmd/compile: add Type.IsUintptr() to detect type is an uintptr Passes toolstash-check. Change-Id: I7051d45eafbfd4dea73a3d4b5ea6cff39d76cbc1 Reviewed-on: https://go-review.googlesource.com/c/go/+/253658 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/esc.go | 6 +++--- src/cmd/compile/internal/gc/escape.go | 2 +- src/cmd/compile/internal/gc/subr.go | 4 ++-- src/cmd/compile/internal/gc/walk.go | 4 ++-- src/cmd/compile/internal/types/type.go | 5 +++++ 5 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index 4b843aba35..375331d1f5 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -377,7 +377,7 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string { // This really doesn't have much to do with escape analysis per se, // but we are reusing the ability to annotate an individual function // argument and pass those annotations along to importing code. - if f.Type.Etype == TUINTPTR { + if f.Type.IsUintptr() { if Debug['m'] != 0 { Warnl(f.Pos, "assuming %v is unsafe uintptr", name()) } @@ -407,13 +407,13 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string { } if fn.Func.Pragma&UintptrEscapes != 0 { - if f.Type.Etype == TUINTPTR { + if f.Type.IsUintptr() { if Debug['m'] != 0 { Warnl(f.Pos, "marking %v as escaping uintptr", name()) } return uintptrEscapesTag } - if f.IsDDD() && f.Type.Elem().Etype == TUINTPTR { + if f.IsDDD() && f.Type.Elem().IsUintptr() { // final argument is ...uintptr. if Debug['m'] != 0 { Warnl(f.Pos, "marking %v as escaping ...uintptr", name()) diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index dc469e276c..75da439bb7 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -493,7 +493,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) { // easily detect object boundaries on the heap // than the stack. e.assignHeap(n.Left, "conversion to unsafe.Pointer", n) - } else if n.Type.IsUnsafePtr() && n.Left.Type.Etype == TUINTPTR { + } else if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() { e.unsafeValue(k, n.Left) } else { e.expr(k, n.Left) diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 6d0a40c287..d3ba53ff0c 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -781,12 +781,12 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { } // 8. src is a pointer or uintptr and dst is unsafe.Pointer. - if (src.IsPtr() || src.Etype == TUINTPTR) && dst.IsUnsafePtr() { + if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() { return OCONVNOP } // 9. src is unsafe.Pointer and dst is a pointer or uintptr. - if src.IsUnsafePtr() && (dst.IsPtr() || dst.Etype == TUINTPTR) { + if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) { return OCONVNOP } diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index a9fefb3ddd..361de7e0f3 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -962,7 +962,7 @@ opswitch: n = walkCheckPtrAlignment(n, init, nil) break } - if n.Type.IsUnsafePtr() && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer + if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() { // uintptr to unsafe.Pointer n = walkCheckPtrArithmetic(n, init) break } @@ -3886,7 +3886,7 @@ func wrapCall(n *Node, init *Nodes) *Node { t := nod(OTFUNC, nil, nil) for i, arg := range n.List.Slice() { s := lookupN("a", i) - if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.IsUnsafePtr() { + if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.IsUintptr() && arg.Left.Type.IsUnsafePtr() { origArgs[i] = arg arg = arg.Left n.List.SetIndex(i, arg) diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index e4b3d885d9..a777a5fd90 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -1230,6 +1230,11 @@ func (t *Type) IsUnsafePtr() bool { return t.Etype == TUNSAFEPTR } +// IsUintptr reports whether t is an uintptr. +func (t *Type) IsUintptr() bool { + return t.Etype == TUINTPTR +} + // IsPtrShaped reports whether t is represented by a single machine pointer. // In addition to regular Go pointer types, this includes map, channel, and // function types and unsafe.Pointer. It does not include array or struct types -- cgit v1.3 From 2c95e3a6a8377ca9c72608c25b4cf2506baf782f Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 9 Sep 2020 11:09:01 +0700 Subject: cmd/compile: use clearer error message for stuct literal This CL changes "T literal.M" error message to "T{...}.M". It's clearer expression and focusing user on actual issue. Updates #38745 Change-Id: I84b455a86742f37e0bde5bf390aa02984eecc3c9 Reviewed-on: https://go-review.googlesource.com/c/go/+/253677 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/fmt.go | 11 ++- test/alias2.go | 10 +-- test/ddd1.go | 2 +- test/escape2.go | 56 ++++++------- test/escape2n.go | 56 ++++++------- test/escape_calls.go | 2 +- test/escape_field.go | 6 +- test/escape_iface.go | 50 ++++++------ test/escape_indir.go | 34 ++++---- test/escape_map.go | 26 +++--- test/escape_param.go | 60 +++++++------- test/escape_slice.go | 14 ++-- test/escape_struct_param1.go | 158 ++++++++++++++++++------------------- test/escape_struct_param2.go | 158 ++++++++++++++++++------------------- test/fixedbugs/issue12006.go | 4 +- test/fixedbugs/issue13799.go | 4 +- test/fixedbugs/issue17645.go | 2 +- test/fixedbugs/issue21709.go | 4 +- test/fixedbugs/issue23732.go | 6 +- test/fixedbugs/issue26855.go | 4 +- test/fixedbugs/issue30898.go | 2 +- test/fixedbugs/issue31573.go | 24 +++--- test/fixedbugs/issue38745.go | 19 +++++ test/fixedbugs/issue39292.go | 6 +- test/fixedbugs/issue41247.go | 2 +- test/fixedbugs/issue7921.go | 10 +-- test/inline_variadic.go | 2 +- 27 files changed, 379 insertions(+), 353 deletions(-) create mode 100644 test/fixedbugs/issue38745.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 866cd0a714..43e501deaf 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -1407,7 +1407,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) { return } if n.Right != nil { - mode.Fprintf(s, "%v literal", n.Right) + mode.Fprintf(s, "%v{%s}", n.Right, ellipsisIf(n.List.Len() != 0)) return } @@ -1421,7 +1421,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) { case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT: if mode == FErr { - mode.Fprintf(s, "%v literal", n.Type) + mode.Fprintf(s, "%v{%s}", n.Type, ellipsisIf(n.List.Len() != 0)) return } mode.Fprintf(s, "(%v{ %.v })", n.Type, n.List) @@ -1934,3 +1934,10 @@ func indent(s fmt.State) { fmt.Fprint(s, ". ") } } + +func ellipsisIf(b bool) string { + if b { + return "..." + } + return "" +} diff --git a/test/alias2.go b/test/alias2.go index 7ea1b2908d..1c141ac490 100644 --- a/test/alias2.go +++ b/test/alias2.go @@ -46,8 +46,8 @@ var _ A0 = T0{} var _ T0 = A0{} // But aliases and original types cannot be used with new types based on them. -var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" -var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" +var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" +var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" var _ A5 = Value{} @@ -82,10 +82,10 @@ func _() { var _ A0 = T0{} var _ T0 = A0{} - var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" - var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" + var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" + var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" - var _ A5 = Value{} // ERROR "cannot use reflect\.Value literal \(type reflect.Value\) as type A5 in assignment|incompatible type" + var _ A5 = Value{} // ERROR "cannot use reflect\.Value{} \(type reflect.Value\) as type A5 in assignment|incompatible type" } // Invalid type alias declarations. diff --git a/test/ddd1.go b/test/ddd1.go index b582f221b7..2c7e83e374 100644 --- a/test/ddd1.go +++ b/test/ddd1.go @@ -19,7 +19,7 @@ var ( _ = sum(1.0, 2.0) _ = sum(1.5) // ERROR "integer" _ = sum("hello") // ERROR ".hello. .type untyped string. as type int|incompatible" - _ = sum([]int{1}) // ERROR "\[\]int literal.*as type int|incompatible" + _ = sum([]int{1}) // ERROR "\[\]int{...}.*as type int|incompatible" ) func sum3(int, int, int) int { return 0 } diff --git a/test/escape2.go b/test/escape2.go index cf24f4bebc..5c6eb559fa 100644 --- a/test/escape2.go +++ b/test/escape2.go @@ -118,15 +118,15 @@ type Bar struct { } func NewBar() *Bar { - return &Bar{42, nil} // ERROR "&Bar literal escapes to heap$" + return &Bar{42, nil} // ERROR "&Bar{...} escapes to heap$" } func NewBarp(x *int) *Bar { // ERROR "leaking param: x$" - return &Bar{42, x} // ERROR "&Bar literal escapes to heap$" + return &Bar{42, x} // ERROR "&Bar{...} escapes to heap$" } func NewBarp2(x *int) *Bar { // ERROR "x does not escape$" - return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap$" + return &Bar{*x, nil} // ERROR "&Bar{...} escapes to heap$" } func (b *Bar) NoLeak() int { // ERROR "b does not escape$" @@ -173,7 +173,7 @@ type Bar2 struct { } func NewBar2() *Bar2 { - return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap$" + return &Bar2{[12]int{42}, nil} // ERROR "&Bar2{...} escapes to heap$" } func (b *Bar2) NoLeak() int { // ERROR "b does not escape$" @@ -539,7 +539,7 @@ func foo72b() [10]*int { // issue 2145 func foo73() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // actually just escapes its scope @@ -550,7 +550,7 @@ func foo73() { } func foo731() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -562,7 +562,7 @@ func foo731() { } func foo74() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // actually just escapes its scope @@ -574,7 +574,7 @@ func foo74() { } func foo74a() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -589,7 +589,7 @@ func foo74a() { // issue 3975 func foo74b() { var array [3]func() - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for i, v := range s { vv := v // actually just escapes its scope @@ -601,7 +601,7 @@ func foo74b() { func foo74c() { var array [3]func() - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for i, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -759,15 +759,15 @@ type LimitedFooer struct { } func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: r$" - return &LimitedFooer{r, n} // ERROR "&LimitedFooer literal escapes to heap$" + return &LimitedFooer{r, n} // ERROR "&LimitedFooer{...} escapes to heap$" } func foo90(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo91(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$" @@ -870,15 +870,15 @@ func foo106(x *int) { // ERROR "leaking param: x$" } func foo107(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo108(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo109(x *int) *int { // ERROR "leaking param: x$" - m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal does not escape$" + m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} does not escape$" for k, _ := range m { return k } @@ -886,12 +886,12 @@ func foo109(x *int) *int { // ERROR "leaking param: x$" } func foo110(x *int) *int { // ERROR "leaking param: x$" - m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal does not escape$" + m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} does not escape$" return m[nil] } func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0" - m := []*int{x} // ERROR "\[\]\*int literal does not escape$" + m := []*int{x} // ERROR "\[\]\*int{...} does not escape$" return m[0] } @@ -906,7 +906,7 @@ func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" } func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" - m := &Bar{ii: x} // ERROR "&Bar literal does not escape$" + m := &Bar{ii: x} // ERROR "&Bar{...} does not escape$" return m.ii } @@ -1343,8 +1343,8 @@ func foo140() interface{} { X string T *T } - t := &T{} // ERROR "&T literal escapes to heap$" - return U{ // ERROR "U literal escapes to heap$" + t := &T{} // ERROR "&T{} escapes to heap$" + return U{ // ERROR "U{...} escapes to heap$" X: t.X, T: t, } @@ -1530,7 +1530,7 @@ type V struct { } func NewV(u U) *V { // ERROR "leaking param: u$" - return &V{u.String()} // ERROR "&V literal escapes to heap$" + return &V{u.String()} // ERROR "&V{...} escapes to heap$" } func foo152() { @@ -1571,21 +1571,21 @@ type Lit struct { func ptrlitNoescape() { // Both literal and element do not escape. i := 0 - x := &Lit{&i} // ERROR "&Lit literal does not escape$" + x := &Lit{&i} // ERROR "&Lit{...} does not escape$" _ = x } func ptrlitNoEscape2() { // Literal does not escape, but element does. i := 0 // ERROR "moved to heap: i$" - x := &Lit{&i} // ERROR "&Lit literal does not escape$" + x := &Lit{&i} // ERROR "&Lit{...} does not escape$" sink = *x } func ptrlitEscape() { // Both literal and element escape. i := 0 // ERROR "moved to heap: i$" - x := &Lit{&i} // ERROR "&Lit literal escapes to heap$" + x := &Lit{&i} // ERROR "&Lit{...} escapes to heap$" sink = x } @@ -1760,18 +1760,18 @@ func stringtoslicerune2() { } func slicerunetostring0() { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" s := string(r) // ERROR "string\(r\) does not escape$" _ = s } func slicerunetostring1() string { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" return string(r) // ERROR "string\(r\) escapes to heap$" } func slicerunetostring2() { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" sink = string(r) // ERROR "string\(r\) escapes to heap$" } diff --git a/test/escape2n.go b/test/escape2n.go index f771e0aef2..46e58f8566 100644 --- a/test/escape2n.go +++ b/test/escape2n.go @@ -118,15 +118,15 @@ type Bar struct { } func NewBar() *Bar { - return &Bar{42, nil} // ERROR "&Bar literal escapes to heap$" + return &Bar{42, nil} // ERROR "&Bar{...} escapes to heap$" } func NewBarp(x *int) *Bar { // ERROR "leaking param: x$" - return &Bar{42, x} // ERROR "&Bar literal escapes to heap$" + return &Bar{42, x} // ERROR "&Bar{...} escapes to heap$" } func NewBarp2(x *int) *Bar { // ERROR "x does not escape$" - return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap$" + return &Bar{*x, nil} // ERROR "&Bar{...} escapes to heap$" } func (b *Bar) NoLeak() int { // ERROR "b does not escape$" @@ -173,7 +173,7 @@ type Bar2 struct { } func NewBar2() *Bar2 { - return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap$" + return &Bar2{[12]int{42}, nil} // ERROR "&Bar2{...} escapes to heap$" } func (b *Bar2) NoLeak() int { // ERROR "b does not escape$" @@ -539,7 +539,7 @@ func foo72b() [10]*int { // issue 2145 func foo73() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // actually just escapes its scope @@ -550,7 +550,7 @@ func foo73() { } func foo731() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -562,7 +562,7 @@ func foo731() { } func foo74() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // actually just escapes its scope @@ -574,7 +574,7 @@ func foo74() { } func foo74a() { - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for _, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -589,7 +589,7 @@ func foo74a() { // issue 3975 func foo74b() { var array [3]func() - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for i, v := range s { vv := v // actually just escapes its scope @@ -601,7 +601,7 @@ func foo74b() { func foo74c() { var array [3]func() - s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$" + s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$" for i, v := range s { vv := v // ERROR "moved to heap: vv$" // actually just escapes its scope @@ -759,15 +759,15 @@ type LimitedFooer struct { } func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: r$" - return &LimitedFooer{r, n} // ERROR "&LimitedFooer literal escapes to heap$" + return &LimitedFooer{r, n} // ERROR "&LimitedFooer{...} escapes to heap$" } func foo90(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo91(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$" @@ -870,15 +870,15 @@ func foo106(x *int) { // ERROR "leaking param: x$" } func foo107(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo108(x *int) map[*int]*int { // ERROR "leaking param: x$" - return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } func foo109(x *int) *int { // ERROR "leaking param: x$" - m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal does not escape$" + m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} does not escape$" for k, _ := range m { return k } @@ -886,12 +886,12 @@ func foo109(x *int) *int { // ERROR "leaking param: x$" } func foo110(x *int) *int { // ERROR "leaking param: x$" - m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal does not escape$" + m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} does not escape$" return m[nil] } func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0" - m := []*int{x} // ERROR "\[\]\*int literal does not escape$" + m := []*int{x} // ERROR "\[\]\*int{...} does not escape$" return m[0] } @@ -906,7 +906,7 @@ func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" } func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" - m := &Bar{ii: x} // ERROR "&Bar literal does not escape$" + m := &Bar{ii: x} // ERROR "&Bar{...} does not escape$" return m.ii } @@ -1343,8 +1343,8 @@ func foo140() interface{} { X string T *T } - t := &T{} // ERROR "&T literal escapes to heap$" - return U{ // ERROR "U literal escapes to heap$" + t := &T{} // ERROR "&T{} escapes to heap$" + return U{ // ERROR "U{...} escapes to heap$" X: t.X, T: t, } @@ -1530,7 +1530,7 @@ type V struct { } func NewV(u U) *V { // ERROR "leaking param: u$" - return &V{u.String()} // ERROR "&V literal escapes to heap$" + return &V{u.String()} // ERROR "&V{...} escapes to heap$" } func foo152() { @@ -1571,21 +1571,21 @@ type Lit struct { func ptrlitNoescape() { // Both literal and element do not escape. i := 0 - x := &Lit{&i} // ERROR "&Lit literal does not escape$" + x := &Lit{&i} // ERROR "&Lit{...} does not escape$" _ = x } func ptrlitNoEscape2() { // Literal does not escape, but element does. i := 0 // ERROR "moved to heap: i$" - x := &Lit{&i} // ERROR "&Lit literal does not escape$" + x := &Lit{&i} // ERROR "&Lit{...} does not escape$" sink = *x } func ptrlitEscape() { // Both literal and element escape. i := 0 // ERROR "moved to heap: i$" - x := &Lit{&i} // ERROR "&Lit literal escapes to heap$" + x := &Lit{&i} // ERROR "&Lit{...} escapes to heap$" sink = x } @@ -1760,18 +1760,18 @@ func stringtoslicerune2() { } func slicerunetostring0() { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" s := string(r) // ERROR "string\(r\) does not escape$" _ = s } func slicerunetostring1() string { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" return string(r) // ERROR "string\(r\) escapes to heap$" } func slicerunetostring2() { - r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$" + r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$" sink = string(r) // ERROR "string\(r\) escapes to heap$" } diff --git a/test/escape_calls.go b/test/escape_calls.go index 2dbfee1558..9e1db5426e 100644 --- a/test/escape_calls.go +++ b/test/escape_calls.go @@ -50,5 +50,5 @@ func bar() { f := prototype f = func(ss []string) { got = append(got, ss) } // ERROR "leaking param: ss" "func literal does not escape" s := "string" - f([]string{s}) // ERROR "\[\]string literal escapes to heap" + f([]string{s}) // ERROR "\[\]string{...} escapes to heap" } diff --git a/test/escape_field.go b/test/escape_field.go index bf1dfb18ff..95d0784d91 100644 --- a/test/escape_field.go +++ b/test/escape_field.go @@ -127,20 +127,20 @@ func field12() { func field13() { i := 0 // ERROR "moved to heap: i$" - x := &X{p1: &i} // ERROR "&X literal does not escape$" + x := &X{p1: &i} // ERROR "&X{...} does not escape$" sink = x.p1 } func field14() { i := 0 // ERROR "moved to heap: i$" // BAD: &i should not escape - x := &X{p1: &i} // ERROR "&X literal does not escape$" + x := &X{p1: &i} // ERROR "&X{...} does not escape$" sink = x.p2 } func field15() { i := 0 // ERROR "moved to heap: i$" - x := &X{p1: &i} // ERROR "&X literal escapes to heap$" + x := &X{p1: &i} // ERROR "&X{...} escapes to heap$" sink = x } diff --git a/test/escape_iface.go b/test/escape_iface.go index 118ed3c56f..7b0914cadb 100644 --- a/test/escape_iface.go +++ b/test/escape_iface.go @@ -37,7 +37,7 @@ func efaceEscape0() { _ = x } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M0{&i} var x M = v sink = x @@ -50,7 +50,7 @@ func efaceEscape0() { _ = v1 } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M0{&i} // BAD: v does not escape to heap here var x M = v @@ -58,14 +58,14 @@ func efaceEscape0() { sink = v1 } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M0{&i} // BAD: v does not escape to heap here var x M = v x.M() } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M0{&i} var x M = v mescapes(x) @@ -91,46 +91,46 @@ func efaceEscape1() { { i := 0 v := M1{&i, 0} - var x M = v // ERROR "v does not escape" + var x M = v // ERROR "v does not escape" _ = x } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M1{&i, 0} - var x M = v // ERROR "v escapes to heap" + var x M = v // ERROR "v escapes to heap" sink = x } { i := 0 v := M1{&i, 0} - var x M = v // ERROR "v does not escape" + var x M = v // ERROR "v does not escape" v1 := x.(M1) _ = v1 } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M1{&i, 0} var x M = v // ERROR "v does not escape" v1 := x.(M1) sink = v1 // ERROR "v1 escapes to heap" } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M1{&i, 0} // BAD: v does not escape to heap here var x M = v // ERROR "v escapes to heap" x.M() } { - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" v := M1{&i, 0} - var x M = v // ERROR "v escapes to heap" + var x M = v // ERROR "v escapes to heap" mescapes(x) } { i := 0 v := M1{&i, 0} - var x M = v // ERROR "v does not escape" + var x M = v // ERROR "v does not escape" mdoesnotescape(x) } } @@ -146,26 +146,26 @@ func (*M2) M() { func efaceEscape2() { { i := 0 - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" var x M = v _ = x } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal escapes to heap" + v := &M2{&i} // ERROR "&M2{...} escapes to heap" var x M = v sink = x } { i := 0 - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" var x M = v v1 := x.(*M2) _ = v1 } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal escapes to heap" + v := &M2{&i} // ERROR "&M2{...} escapes to heap" // BAD: v does not escape to heap here var x M = v v1 := x.(*M2) @@ -173,7 +173,7 @@ func efaceEscape2() { } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" // BAD: v does not escape to heap here var x M = v v1 := x.(*M2) @@ -181,7 +181,7 @@ func efaceEscape2() { } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" // BAD: v does not escape to heap here var x M = v v1, ok := x.(*M2) @@ -190,20 +190,20 @@ func efaceEscape2() { } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal escapes to heap" + v := &M2{&i} // ERROR "&M2{...} escapes to heap" // BAD: v does not escape to heap here var x M = v x.M() } { i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2 literal escapes to heap" + v := &M2{&i} // ERROR "&M2{...} escapes to heap" var x M = v mescapes(x) } { i := 0 - v := &M2{&i} // ERROR "&M2 literal does not escape" + v := &M2{&i} // ERROR "&M2{...} does not escape" var x M = v mdoesnotescape(x) } @@ -219,8 +219,8 @@ type T2 struct { func dotTypeEscape() *T2 { // #11931 var x interface{} - x = &T1{p: new(int)} // ERROR "new\(int\) escapes to heap" "&T1 literal does not escape" - return &T2{ // ERROR "&T2 literal escapes to heap" + x = &T1{p: new(int)} // ERROR "new\(int\) escapes to heap" "&T1{...} does not escape" + return &T2{ // ERROR "&T2{...} escapes to heap" T1: *(x.(*T1)), } } @@ -244,7 +244,7 @@ func dotTypeEscape2() { // #13805, #15796 var x interface{} = i // ERROR "i does not escape" var y interface{} = j // ERROR "j does not escape" - sink = x.(int) // ERROR "x.\(int\) escapes to heap" + sink = x.(int) // ERROR "x.\(int\) escapes to heap" sink, *(&ok) = y.(int) } { diff --git a/test/escape_indir.go b/test/escape_indir.go index 19889f259f..12005e35f9 100644 --- a/test/escape_indir.go +++ b/test/escape_indir.go @@ -23,7 +23,7 @@ type ConstPtr2 struct { func constptr0() { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" // BAD: i should not escape here x.p = &i _ = x @@ -31,55 +31,55 @@ func constptr0() { func constptr01() *ConstPtr { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap" + x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap" x.p = &i return x } func constptr02() ConstPtr { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" x.p = &i return *x } func constptr03() **ConstPtr { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap" "moved to heap: x" + x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap" "moved to heap: x" x.p = &i return &x } func constptr1() { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap" + x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap" x.p = &i sink = x } func constptr2() { i := 0 // ERROR "moved to heap: i" - x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" x.p = &i - sink = *x // ERROR "\*x escapes to heap" + sink = *x // ERROR "\*x escapes to heap" } func constptr4() *ConstPtr { p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap" - *p = *&ConstPtr{} // ERROR "&ConstPtr literal does not escape" + *p = *&ConstPtr{} // ERROR "&ConstPtr{} does not escape" return p } func constptr5() *ConstPtr { p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap" - p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" *p = *p1 return p } // BAD: p should not escape here func constptr6(p *ConstPtr) { // ERROR "leaking param content: p" - p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" *p1 = *p _ = p1 } @@ -102,17 +102,17 @@ func constptr8() *ConstPtr { func constptr9() ConstPtr { p := new(ConstPtr) // ERROR "new\(ConstPtr\) does not escape" var p1 ConstPtr2 - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" p1.p = &i p.c = p1 return *p } func constptr10() ConstPtr { - x := &ConstPtr{} // ERROR "moved to heap: x" "&ConstPtr literal escapes to heap" + x := &ConstPtr{} // ERROR "moved to heap: x" "&ConstPtr{} escapes to heap" i := 0 // ERROR "moved to heap: i" var p *ConstPtr - p = &ConstPtr{p: &i, x: &x} // ERROR "&ConstPtr literal does not escape" + p = &ConstPtr{p: &i, x: &x} // ERROR "&ConstPtr{...} does not escape" var pp **ConstPtr pp = &p return **pp @@ -121,7 +121,7 @@ func constptr10() ConstPtr { func constptr11() *ConstPtr { i := 0 // ERROR "moved to heap: i" p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap" - p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape" + p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape" p1.p = &i *p = *p1 return p @@ -134,7 +134,7 @@ func foo(p **int) { // ERROR "p does not escape" } func foo1(p *int) { // ERROR "p does not escape" - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" y := &p *y = &i } @@ -148,13 +148,13 @@ func foo2() { var z Z z.f = &x p := z.f - i := 0 // ERROR "moved to heap: i" + i := 0 // ERROR "moved to heap: i" *p = &i } var global *byte func f() { - var x byte // ERROR "moved to heap: x" + var x byte // ERROR "moved to heap: x" global = &*&x } diff --git a/test/escape_map.go b/test/escape_map.go index 0e9896a9fc..23abaa1e0c 100644 --- a/test/escape_map.go +++ b/test/escape_map.go @@ -15,7 +15,7 @@ func map0() { // BAD: i should not escape i := 0 // ERROR "moved to heap: i" // BAD: j should not escape - j := 0 // ERROR "moved to heap: j" + j := 0 // ERROR "moved to heap: j" m[&i] = &j _ = m } @@ -23,8 +23,8 @@ func map0() { func map1() *int { m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape" // BAD: i should not escape - i := 0 // ERROR "moved to heap: i" - j := 0 // ERROR "moved to heap: j" + i := 0 // ERROR "moved to heap: i" + j := 0 // ERROR "moved to heap: j" m[&i] = &j return m[&i] } @@ -41,7 +41,7 @@ func map3() []*int { m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape" i := 0 // ERROR "moved to heap: i" // BAD: j should not escape - j := 0 // ERROR "moved to heap: j" + j := 0 // ERROR "moved to heap: j" m[&i] = &j var r []*int for k := range m { @@ -53,8 +53,8 @@ func map3() []*int { func map4() []*int { m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape" // BAD: i should not escape - i := 0 // ERROR "moved to heap: i" - j := 0 // ERROR "moved to heap: j" + i := 0 // ERROR "moved to heap: i" + j := 0 // ERROR "moved to heap: j" m[&i] = &j var r []*int for k, v := range m { @@ -68,8 +68,8 @@ func map4() []*int { } func map5(m map[*int]*int) { // ERROR "m does not escape" - i := 0 // ERROR "moved to heap: i" - j := 0 // ERROR "moved to heap: j" + i := 0 // ERROR "moved to heap: i" + j := 0 // ERROR "moved to heap: j" m[&i] = &j } @@ -77,8 +77,8 @@ func map6(m map[*int]*int) { // ERROR "m does not escape" if m != nil { m = make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape" } - i := 0 // ERROR "moved to heap: i" - j := 0 // ERROR "moved to heap: j" + i := 0 // ERROR "moved to heap: i" + j := 0 // ERROR "moved to heap: j" m[&i] = &j } @@ -87,14 +87,14 @@ func map7() { i := 0 // ERROR "moved to heap: i" // BAD: j should not escape j := 0 // ERROR "moved to heap: j" - m := map[*int]*int{&i: &j} // ERROR "literal does not escape" + m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} does not escape" _ = m } func map8() { i := 0 // ERROR "moved to heap: i" j := 0 // ERROR "moved to heap: j" - m := map[*int]*int{&i: &j} // ERROR "literal escapes to heap" + m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} escapes to heap" sink = m } @@ -102,6 +102,6 @@ func map9() *int { // BAD: i should not escape i := 0 // ERROR "moved to heap: i" j := 0 // ERROR "moved to heap: j" - m := map[*int]*int{&i: &j} // ERROR "literal does not escape" + m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} does not escape" return m[nil] } diff --git a/test/escape_param.go b/test/escape_param.go index d8fafc53f8..993e914e1d 100644 --- a/test/escape_param.go +++ b/test/escape_param.go @@ -26,7 +26,7 @@ func caller0a() { } func caller0b() { - i := 0 // ERROR "moved to heap: i$" + i := 0 // ERROR "moved to heap: i$" sink = param0(&i) } @@ -150,11 +150,11 @@ func caller3a() { } func caller3b() { - i := 0 // ERROR "moved to heap: i$" - j := 0 // ERROR "moved to heap: j$" + i := 0 // ERROR "moved to heap: i$" + j := 0 // ERROR "moved to heap: j$" p := Pair{&i, &j} param3(&p) - sink = p // ERROR "p escapes to heap$" + sink = p // ERROR "p escapes to heap$" } // in -> rcvr @@ -173,7 +173,7 @@ func caller4b() { i := 0 // ERROR "moved to heap: i$" p := Pair{} p.param4(&i) - sink = p // ERROR "p escapes to heap$" + sink = p // ERROR "p escapes to heap$" } // in -> heap @@ -182,7 +182,7 @@ func param5(i *int) { // ERROR "leaking param: i$" } func caller5() { - i := 0 // ERROR "moved to heap: i$" + i := 0 // ERROR "moved to heap: i$" param5(&i) } @@ -192,8 +192,8 @@ func param6(i ***int) { // ERROR "leaking param content: i$" } func caller6a() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p$" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p$" p2 := &p param6(&p2) } @@ -204,7 +204,7 @@ func param7(i ***int) { // ERROR "leaking param content: i$" } func caller7() { - i := 0 // ERROR "moved to heap: i$" + i := 0 // ERROR "moved to heap: i$" p := &i p2 := &p param7(&p2) @@ -234,8 +234,8 @@ func caller9a() { } func caller9b() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p$" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p$" p2 := &p sink = param9(&p2) } @@ -253,7 +253,7 @@ func caller10a() { } func caller10b() { - i := 0 // ERROR "moved to heap: i$" + i := 0 // ERROR "moved to heap: i$" p := &i p2 := &p sink = param10(&p2) @@ -265,26 +265,26 @@ func param11(i **int) ***int { // ERROR "moved to heap: i$" } func caller11a() { - i := 0 // ERROR "moved to heap: i" - p := &i // ERROR "moved to heap: p" + i := 0 // ERROR "moved to heap: i" + p := &i // ERROR "moved to heap: p" _ = param11(&p) } func caller11b() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p$" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p$" sink = param11(&p) } func caller11c() { // GOOD - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p" sink = *param11(&p) } func caller11d() { - i := 0 // ERROR "moved to heap: i$" - p := &i // ERROR "moved to heap: p" + i := 0 // ERROR "moved to heap: i$" + p := &i // ERROR "moved to heap: p" p2 := &p sink = param11(p2) } @@ -309,7 +309,7 @@ func caller12a() { func caller12b() { i := 0 // ERROR "moved to heap: i$" p := &i // ERROR "moved to heap: p$" - r := &Indir{} // ERROR "&Indir literal does not escape$" + r := &Indir{} // ERROR "&Indir{} does not escape$" r.param12(&p) _ = r } @@ -359,7 +359,7 @@ func caller13b() { func caller13c() { i := 0 // ERROR "moved to heap: i$" var p *int - v := &Val{&p} // ERROR "&Val literal does not escape$" + v := &Val{&p} // ERROR "&Val{...} does not escape$" v.param13(&i) _ = v } @@ -374,8 +374,8 @@ func caller13d() { } func caller13e() { - i := 0 // ERROR "moved to heap: i$" - var p *int // ERROR "moved to heap: p$" + i := 0 // ERROR "moved to heap: i$" + var p *int // ERROR "moved to heap: p$" v := Val{&p} v.param13(&i) sink = v @@ -384,7 +384,7 @@ func caller13e() { func caller13f() { i := 0 // ERROR "moved to heap: i$" var p *int // ERROR "moved to heap: p$" - v := &Val{&p} // ERROR "&Val literal escapes to heap$" + v := &Val{&p} // ERROR "&Val{...} escapes to heap$" v.param13(&i) sink = v } @@ -400,9 +400,9 @@ func caller13g() { func caller13h() { i := 0 // ERROR "moved to heap: i$" var p *int - v := &Val{&p} // ERROR "&Val literal does not escape$" + v := &Val{&p} // ERROR "&Val{...} does not escape$" v.param13(&i) - sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap" + sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap" } type Node struct { @@ -412,15 +412,15 @@ type Node struct { var Sink *Node func f(x *Node) { // ERROR "leaking param content: x" - Sink = &Node{x.p} // ERROR "&Node literal escapes to heap" + Sink = &Node{x.p} // ERROR "&Node{...} escapes to heap" } func g(x *Node) *Node { // ERROR "leaking param content: x" - return &Node{x.p} // ERROR "&Node literal escapes to heap" + return &Node{x.p} // ERROR "&Node{...} escapes to heap" } func h(x *Node) { // ERROR "leaking param: x" - y := &Node{x} // ERROR "&Node literal does not escape" + y := &Node{x} // ERROR "&Node{...} does not escape" Sink = g(y) f(y) } diff --git a/test/escape_slice.go b/test/escape_slice.go index d2cdaa6a01..6ce852e9c5 100644 --- a/test/escape_slice.go +++ b/test/escape_slice.go @@ -77,19 +77,19 @@ func slice7() *int { func slice8() { i := 0 - s := []*int{&i} // ERROR "literal does not escape" + s := []*int{&i} // ERROR "\[\]\*int{...} does not escape" _ = s } func slice9() *int { i := 0 // ERROR "moved to heap: i" - s := []*int{&i} // ERROR "literal does not escape" + s := []*int{&i} // ERROR "\[\]\*int{...} does not escape" return s[0] } func slice10() []*int { i := 0 // ERROR "moved to heap: i" - s := []*int{&i} // ERROR "literal escapes to heap" + s := []*int{&i} // ERROR "\[\]\*int{...} escapes to heap" return s } @@ -103,7 +103,7 @@ func slice11() { func envForDir(dir string) []string { // ERROR "dir does not escape" env := os.Environ() - return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string literal does not escape" + return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string{...} does not escape" } func mergeEnvLists(in, out []string) []string { // ERROR "leaking param content: in" "leaking param content: out" "leaking param: out to result ~r2 level=0" @@ -160,14 +160,14 @@ var resolveIPAddrTests = []resolveIPAddrTest{ func setupTestData() { resolveIPAddrTests = append(resolveIPAddrTests, - []resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest literal does not escape" + []resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest{...} does not escape" {"ip", "localhost", - &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap" + &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr{...} escapes to heap" nil}, {"ip4", "localhost", - &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap" + &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr{...} escapes to heap" nil}, }...) } diff --git a/test/escape_struct_param1.go b/test/escape_struct_param1.go index 70b36191ab..496172c166 100644 --- a/test/escape_struct_param1.go +++ b/test/escape_struct_param1.go @@ -35,27 +35,27 @@ func (u *U) SPPi() *string { // ERROR "leaking param: u to result ~r0 level=2$" } func tSPPi() { - s := "cat" // ERROR "moved to heap: s$" + s := "cat" // ERROR "moved to heap: s$" ps := &s pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = pu.SPPi() } func tiSPP() { - s := "cat" // ERROR "moved to heap: s$" + s := "cat" // ERROR "moved to heap: s$" ps := &s pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = *pu.SPP() } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of ps func tSP() { - s := "cat" // ERROR "moved to heap: s$" - ps := &s // ERROR "moved to heap: ps$" + s := "cat" // ERROR "moved to heap: s$" + ps := &s // ERROR "moved to heap: ps$" pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = pu.SP() } @@ -114,72 +114,72 @@ func (v *V) UPiSPd() *string { // ERROR "leaking param: v to result ~r0 level=2$ // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPa() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPa() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPb() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPb() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPc() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPc() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPd() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPd() // Ssink = &s3 (only &s3 really escapes) } @@ -204,16 +204,16 @@ func tUPiSPPia() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPia() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -222,16 +222,16 @@ func tUPiSPPib() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPib() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -240,16 +240,16 @@ func tUPiSPPic() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPic() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -258,16 +258,16 @@ func tUPiSPPid() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPid() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -286,13 +286,13 @@ func tUPPiSPPia() { s3 := "cat" s4 := "dog" s5 := "emu" - s6 := "fox" // ERROR "moved to heap: s6$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 ps6 := &s6 u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPPiSPPia() // Ssink = *&ps6 = &s6 (only &s6 really escapes) } diff --git a/test/escape_struct_param2.go b/test/escape_struct_param2.go index e42be79793..946397ea9f 100644 --- a/test/escape_struct_param2.go +++ b/test/escape_struct_param2.go @@ -35,27 +35,27 @@ func (u U) SPPi() *string { // ERROR "leaking param: u to result ~r0 level=1$" } func tSPPi() { - s := "cat" // ERROR "moved to heap: s$" + s := "cat" // ERROR "moved to heap: s$" ps := &s pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = pu.SPPi() } func tiSPP() { - s := "cat" // ERROR "moved to heap: s$" + s := "cat" // ERROR "moved to heap: s$" ps := &s pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = *pu.SPP() } // BAD: need fine-grained analysis to avoid spurious escape of ps func tSP() { - s := "cat" // ERROR "moved to heap: s$" - ps := &s // ERROR "moved to heap: ps$" + s := "cat" // ERROR "moved to heap: s$" + ps := &s // ERROR "moved to heap: ps$" pps := &ps - pu := &U{ps, pps} // ERROR "&U literal does not escape$" + pu := &U{ps, pps} // ERROR "&U{...} does not escape$" Ssink = pu.SP() } @@ -114,72 +114,72 @@ func (v V) UPiSPd() *string { // ERROR "leaking param: v to result ~r0 level=1$" // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPa() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPa() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPb() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPb() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPc() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPc() // Ssink = &s3 (only &s3 really escapes) } // BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3 func tUPiSPd() { s1 := "ant" - s2 := "bat" // ERROR "moved to heap: s2$" - s3 := "cat" // ERROR "moved to heap: s3$" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s2 := "bat" // ERROR "moved to heap: s2$" + s3 := "cat" // ERROR "moved to heap: s3$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 - ps4 := &s4 // ERROR "moved to heap: ps4$" - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps4 := &s4 // ERROR "moved to heap: ps4$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPd() // Ssink = &s3 (only &s3 really escapes) } @@ -204,16 +204,16 @@ func tUPiSPPia() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPia() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -222,16 +222,16 @@ func tUPiSPPib() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPib() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -240,16 +240,16 @@ func tUPiSPPic() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPic() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -258,16 +258,16 @@ func tUPiSPPid() { s1 := "ant" s2 := "bat" s3 := "cat" - s4 := "dog" // ERROR "moved to heap: s4$" - s5 := "emu" // ERROR "moved to heap: s5$" - s6 := "fox" // ERROR "moved to heap: s6$" + s4 := "dog" // ERROR "moved to heap: s4$" + s5 := "emu" // ERROR "moved to heap: s5$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 - ps6 := &s6 // ERROR "moved to heap: ps6$" + ps6 := &s6 // ERROR "moved to heap: ps6$" u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPiSPPid() // Ssink = *&ps4 = &s4 (only &s4 really escapes) } @@ -286,13 +286,13 @@ func tUPPiSPPia() { // This test is sensitive to the level cap in function summa s3 := "cat" s4 := "dog" s5 := "emu" - s6 := "fox" // ERROR "moved to heap: s6$" + s6 := "fox" // ERROR "moved to heap: s6$" ps2 := &s2 ps4 := &s4 ps6 := &s6 u1 := U{&s1, &ps2} - u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$" - u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$" - v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$" + u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$" + u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$" + v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$" Ssink = v.UPPiSPPia() // Ssink = *&ps6 = &s6 (only &s6 really escapes) } diff --git a/test/fixedbugs/issue12006.go b/test/fixedbugs/issue12006.go index c44f2e5547..0a2ef8dad0 100644 --- a/test/fixedbugs/issue12006.go +++ b/test/fixedbugs/issue12006.go @@ -144,7 +144,7 @@ func TFooK2() { a := int32(1) // ERROR "moved to heap: a" b := "cat" c := &a - fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "&\[4\]interface {} literal does not escape" + fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "&\[4\]interface {}{...} does not escape" isink = FooK(fs) } @@ -169,6 +169,6 @@ func TFooL2() { a := int32(1) // ERROR "moved to heap: a" b := "cat" c := &a - s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "\[\]interface {} literal does not escape" + s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "\[\]interface {}{...} does not escape" isink = FooL(s) } diff --git a/test/fixedbugs/issue13799.go b/test/fixedbugs/issue13799.go index 5c57494777..fbdd4c32bc 100644 --- a/test/fixedbugs/issue13799.go +++ b/test/fixedbugs/issue13799.go @@ -162,7 +162,7 @@ func test5(iter int) { var fn *str for i := 0; i < maxI; i++ { // var fn *str // this makes it work, because fn stays off heap - fn = &str{m} // ERROR "&str literal escapes to heap" + fn = &str{m} // ERROR "&str{...} escapes to heap" recur1(0, fn) } @@ -180,7 +180,7 @@ func test6(iter int) { // var fn *str for i := 0; i < maxI; i++ { var fn *str // this makes it work, because fn stays off heap - fn = &str{m} // ERROR "&str literal does not escape" + fn = &str{m} // ERROR "&str{...} does not escape" recur1(0, fn) } diff --git a/test/fixedbugs/issue17645.go b/test/fixedbugs/issue17645.go index af785eae2a..95fcecd1e0 100644 --- a/test/fixedbugs/issue17645.go +++ b/test/fixedbugs/issue17645.go @@ -12,5 +12,5 @@ type Foo struct { func main() { var s []int - var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type untyped string\) as type int in field value" "cannot use Foo literal \(type Foo\) as type int in append" "cannot use append\(s\, Foo literal\) \(type \[\]int\) as type string in assignment" + var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type untyped string\) as type int in field value" "cannot use Foo{...} \(type Foo\) as type int in append" "cannot use append\(s\, Foo{...}\) \(type \[\]int\) as type string in assignment" } diff --git a/test/fixedbugs/issue21709.go b/test/fixedbugs/issue21709.go index cc5896ab53..20be10e792 100644 --- a/test/fixedbugs/issue21709.go +++ b/test/fixedbugs/issue21709.go @@ -16,7 +16,7 @@ var N int func F1() { var s S for i := 0; i < N; i++ { - fs := []func(){ // ERROR "\[\]func\(\) literal does not escape" + fs := []func(){ // ERROR "\[\]func\(\){...} does not escape" s.Inc, // ERROR "s.Inc does not escape" } for _, f := range fs { @@ -28,7 +28,7 @@ func F1() { func F2() { var s S for i := 0; i < N; i++ { - for _, f := range []func(){ // ERROR "\[\]func\(\) literal does not escape" + for _, f := range []func(){ // ERROR "\[\]func\(\){...} does not escape" s.Inc, // ERROR "s.Inc does not escape" } { f() diff --git a/test/fixedbugs/issue23732.go b/test/fixedbugs/issue23732.go index be17bf4f61..5e63eb2074 100644 --- a/test/fixedbugs/issue23732.go +++ b/test/fixedbugs/issue23732.go @@ -24,19 +24,19 @@ func main() { _ = Foo{ 1, 2, - 3, // ERROR "too few values in Foo literal" + 3, // ERROR "too few values in Foo{...}" } _ = Foo{ 1, 2, 3, - Bar{"A", "B"}, // ERROR "too many values in Bar literal" + Bar{"A", "B"}, // ERROR "too many values in Bar{...}" } _ = Foo{ 1, 2, - Bar{"A", "B"}, // ERROR "too many values in Bar literal" "too few values in Foo literal" + Bar{"A", "B"}, // ERROR "too many values in Bar{...}" "too few values in Foo{...}" } } diff --git a/test/fixedbugs/issue26855.go b/test/fixedbugs/issue26855.go index d5b95ddbf1..144e4415f7 100644 --- a/test/fixedbugs/issue26855.go +++ b/test/fixedbugs/issue26855.go @@ -20,9 +20,9 @@ type P struct { type T struct{} var _ = S{ - f: &T{}, // ERROR "cannot use &T literal" + f: &T{}, // ERROR "cannot use &T{}" } var _ = P{ - f: T{}, // ERROR "cannot use T literal" + f: T{}, // ERROR "cannot use T{}" } diff --git a/test/fixedbugs/issue30898.go b/test/fixedbugs/issue30898.go index 012d5a2634..b6376d3f9e 100644 --- a/test/fixedbugs/issue30898.go +++ b/test/fixedbugs/issue30898.go @@ -15,5 +15,5 @@ func debugf(format string, args ...interface{}) { // ERROR "can inline debugf" " func bar() { // ERROR "can inline bar" value := 10 - debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\[\]interface {} literal does not escape" + debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\[\]interface {}{...} does not escape" } diff --git a/test/fixedbugs/issue31573.go b/test/fixedbugs/issue31573.go index c9ea84bbae..005910e00d 100644 --- a/test/fixedbugs/issue31573.go +++ b/test/fixedbugs/issue31573.go @@ -14,18 +14,18 @@ func g() { defer f(new(int), new(int)) // ERROR "... argument does not escape$" "new\(int\) does not escape$" defer f(nil...) - defer f([]*int{}...) // ERROR "\[\]\*int literal does not escape$" - defer f([]*int{new(int)}...) // ERROR "\[\]\*int literal does not escape$" "new\(int\) does not escape$" - defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal does not escape$" "new\(int\) does not escape$" + defer f([]*int{}...) // ERROR "\[\]\*int{} does not escape$" + defer f([]*int{new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) does not escape$" + defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) does not escape$" go f() go f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" go f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" go f(nil...) - go f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$" - go f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" - go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" + go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" for { defer f() @@ -33,17 +33,17 @@ func g() { defer f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" defer f(nil...) - defer f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$" - defer f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" - defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" + defer f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" + defer f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" go f() go f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" go f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" go f(nil...) - go f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$" - go f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" - go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" + go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" } } diff --git a/test/fixedbugs/issue38745.go b/test/fixedbugs/issue38745.go new file mode 100644 index 0000000000..21bd1ff3a7 --- /dev/null +++ b/test/fixedbugs/issue38745.go @@ -0,0 +1,19 @@ +// errorcheck + +// Copyright 2020 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 p + +type t struct{ x int } + +func f1() { + t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" + t{x: 1}.M() // ERROR "t{...}.M undefined \(type t has no field or method M\)" +} + +func f2() (*t, error) { + // BAD: should report undefined error only. + return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" "not enough arguments to return" +} diff --git a/test/fixedbugs/issue39292.go b/test/fixedbugs/issue39292.go index 1be88653e9..7dac2e5fc6 100644 --- a/test/fixedbugs/issue39292.go +++ b/test/fixedbugs/issue39292.go @@ -12,18 +12,18 @@ func (t) f() { } func x() { - x := t{}.f // ERROR "t literal.f escapes to heap" + x := t{}.f // ERROR "t{}.f escapes to heap" x() } func y() { var i int // ERROR "moved to heap: i" - y := (&t{&i}).f // ERROR "\(&t literal\).f escapes to heap" "&t literal escapes to heap" + y := (&t{&i}).f // ERROR "\(&t{...}\).f escapes to heap" "&t{...} escapes to heap" y() } func z() { var i int // ERROR "moved to heap: i" - z := t{&i}.f // ERROR "t literal.f escapes to heap" + z := t{&i}.f // ERROR "t{...}.f escapes to heap" z() } diff --git a/test/fixedbugs/issue41247.go b/test/fixedbugs/issue41247.go index 2df919c9e6..b8bd81274f 100644 --- a/test/fixedbugs/issue41247.go +++ b/test/fixedbugs/issue41247.go @@ -7,5 +7,5 @@ package p func f() [2]int { - return [...]int{2: 0} // ERROR "cannot use \[\.\.\.\]int literal \(type \[3\]int\)" + return [...]int{2: 0} // ERROR "cannot use \[\.\.\.\]int{...} \(type \[3\]int\)" } diff --git a/test/fixedbugs/issue7921.go b/test/fixedbugs/issue7921.go index a8efc8dd9e..5dce557ca3 100644 --- a/test/fixedbugs/issue7921.go +++ b/test/fixedbugs/issue7921.go @@ -18,12 +18,12 @@ func bufferNotEscape() string { // can be stack-allocated. var b bytes.Buffer b.WriteString("123") - b.Write([]byte{'4'}) // ERROR "\[\]byte literal does not escape$" + b.Write([]byte{'4'}) // ERROR "\[\]byte{...} does not escape$" return b.String() // ERROR "inlining call to bytes.\(\*Buffer\).String$" "string\(bytes.b.buf\[bytes.b.off:\]\) escapes to heap$" } func bufferNoEscape2(xs []string) int { // ERROR "xs does not escape$" - b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$" + b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$" for _, x := range xs { b.WriteString(x) } @@ -31,7 +31,7 @@ func bufferNoEscape2(xs []string) int { // ERROR "xs does not escape$" } func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$" - b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$" + b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$" for _, x := range xs { b.WriteString(x) b.WriteByte(',') @@ -41,13 +41,13 @@ func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$" func bufferNoEscape4() []byte { var b bytes.Buffer - b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m·3\]$" "inlining call to bytes.\(\*Buffer\).Grow$" + b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m·3\]$" "inlining call to bytes.\(\*Buffer\).Grow$" useBuffer(&b) return b.Bytes() // ERROR "inlining call to bytes.\(\*Buffer\).Bytes$" } func bufferNoEscape5() { // ERROR "can inline bufferNoEscape5$" - b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 128\) does not escape$" "inlining call to bytes.NewBuffer$" + b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 128\) does not escape$" "inlining call to bytes.NewBuffer$" useBuffer(b) } diff --git a/test/inline_variadic.go b/test/inline_variadic.go index fcc1cff1e8..687048a192 100644 --- a/test/inline_variadic.go +++ b/test/inline_variadic.go @@ -14,6 +14,6 @@ func head(xs ...string) string { // ERROR "can inline head" "leaking param: xs t } func f() string { // ERROR "can inline f" - x := head("hello", "world") // ERROR "inlining call to head" "\[\]string literal does not escape" + x := head("hello", "world") // ERROR "inlining call to head" "\[\]string{...} does not escape" return x } -- cgit v1.3 From 806f478499b57c5167fb5301101961b7563903d2 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 9 Sep 2020 16:25:48 +0700 Subject: cmd/compile: don't report not enough args error if call is undefined Fixes #38745 Change-Id: I2fbd8b512a8cf911b81a087162c74416116efea5 Reviewed-on: https://go-review.googlesource.com/c/go/+/253678 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/typecheck.go | 2 +- test/ddd1.go | 2 +- test/fixedbugs/issue38745.go | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index dec4b96fc4..fb169cfec8 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2667,7 +2667,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes, return notenough: - if n == nil || !n.Diag() { + if n == nil || (!n.Diag() && n.Type != nil) { details := errorDetails(nl, tstruct, isddd) if call != nil { // call is the expression being called, not the overall call. diff --git a/test/ddd1.go b/test/ddd1.go index 2c7e83e374..9857814648 100644 --- a/test/ddd1.go +++ b/test/ddd1.go @@ -29,7 +29,7 @@ var ( _ = sum(tuple()) _ = sum(tuple()...) // ERROR "multiple-value" _ = sum3(tuple()) - _ = sum3(tuple()...) // ERROR "multiple-value" "not enough" + _ = sum3(tuple()...) // ERROR "multiple-value" ) type T []T diff --git a/test/fixedbugs/issue38745.go b/test/fixedbugs/issue38745.go index 21bd1ff3a7..83a3bc6fad 100644 --- a/test/fixedbugs/issue38745.go +++ b/test/fixedbugs/issue38745.go @@ -14,6 +14,5 @@ func f1() { } func f2() (*t, error) { - // BAD: should report undefined error only. - return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" "not enough arguments to return" + return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" } -- cgit v1.3 From 1f4521669416a2e14fb0b84481447f4a93f19878 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 12 Sep 2020 01:57:27 +0700 Subject: cmd/compile: attach OVARLIVE nodes to OCALLxxx So we can insert theses OVARLIVE nodes right after OpStaticCall in SSA. This helps fixing issue that unsafe-uintptr arguments are not kept alive during return statement, or can be kept alive longer than expected. Fixes #24491 Change-Id: Ic04a5d1bbb5c90dcfae65bd95cdd1da393a66800 Reviewed-on: https://go-review.googlesource.com/c/go/+/254397 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/order.go | 14 +++------ src/cmd/compile/internal/gc/ssa.go | 2 ++ src/cmd/compile/internal/gc/syntax.go | 4 +-- test/fixedbugs/issue24491.go | 45 ----------------------------- test/fixedbugs/issue24491a.go | 54 +++++++++++++++++++++++++++++++++++ test/fixedbugs/issue24491b.go | 46 +++++++++++++++++++++++++++++ 6 files changed, 107 insertions(+), 58 deletions(-) delete mode 100644 test/fixedbugs/issue24491.go create mode 100644 test/fixedbugs/issue24491a.go create mode 100644 test/fixedbugs/issue24491b.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 412f073a8d..341f4ee66f 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -288,20 +288,13 @@ func (o *Order) popTemp(mark ordermarker) { o.temp = o.temp[:mark] } -// cleanTempNoPop emits VARKILL and if needed VARLIVE instructions -// to *out for each temporary above the mark on the temporary stack. +// cleanTempNoPop emits VARKILL instructions to *out +// for each temporary above the mark on the temporary stack. // It does not pop the temporaries from the stack. func (o *Order) cleanTempNoPop(mark ordermarker) []*Node { var out []*Node for i := len(o.temp) - 1; i >= int(mark); i-- { n := o.temp[i] - if n.Name.Keepalive() { - n.Name.SetKeepalive(false) - n.Name.SetAddrtaken(true) // ensure SSA keeps the n variable - live := nod(OVARLIVE, n, nil) - live = typecheck(live, ctxStmt) - out = append(out, live) - } kill := nod(OVARKILL, n, nil) kill = typecheck(kill, ctxStmt) out = append(out, kill) @@ -500,8 +493,9 @@ func (o *Order) call(n *Node) { // still alive when we pop the temp stack. if arg.Op == OCONVNOP && arg.Left.Type.IsUnsafePtr() { x := o.copyExpr(arg.Left, arg.Left.Type, false) - x.Name.SetKeepalive(true) arg.Left = x + x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable + n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt)) n.SetNeedsWrapper(true) } } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 89644cd3f2..3bdb5b0b9f 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4498,6 +4498,8 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { call.AuxInt = stksize // Call operations carry the argsize of the callee along with them } s.vars[&memVar] = call + // Insert OVARLIVE nodes + s.stmtList(n.Nbody) // Finish block for defers if k == callDefer || k == callDeferStack { diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 5580f789c5..9592b7484c 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -374,7 +374,6 @@ const ( nameReadonly nameByval // is the variable captured by value or by reference nameNeedzero // if it contains pointers, needs to be zeroed on function entry - nameKeepalive // mark value live across unknown assembly call nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap) nameUsed // for variable declared and not used error nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn @@ -391,7 +390,6 @@ func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 } func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 } func (n *Name) Byval() bool { return n.flags&nameByval != 0 } func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 } -func (n *Name) Keepalive() bool { return n.flags&nameKeepalive != 0 } func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 } func (n *Name) Used() bool { return n.flags&nameUsed != 0 } func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 } @@ -407,7 +405,6 @@ func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) } func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) } func (n *Name) SetByval(b bool) { n.flags.set(nameByval, b) } func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) } -func (n *Name) SetKeepalive(b bool) { n.flags.set(nameKeepalive, b) } func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) } func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) } func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) } @@ -707,6 +704,7 @@ const ( // Prior to walk, they are: Left(List), where List is all regular arguments. // After walk, List is a series of assignments to temporaries, // and Rlist is an updated set of arguments. + // Nbody is all OVARLIVE nodes that are attached to OCALLxxx. // TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797. OCALLFUNC // Left(List/Rlist) (function call f(args)) OCALLMETH // Left(List/Rlist) (direct method call x.Method(args)) diff --git a/test/fixedbugs/issue24491.go b/test/fixedbugs/issue24491.go deleted file mode 100644 index 4703368793..0000000000 --- a/test/fixedbugs/issue24491.go +++ /dev/null @@ -1,45 +0,0 @@ -// run - -// Copyright 2020 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. - -// This test makes sure unsafe-uintptr arguments are handled correctly. - -package main - -import ( - "runtime" - "unsafe" -) - -var done = make(chan bool, 1) - -func setup() unsafe.Pointer { - s := "ok" - runtime.SetFinalizer(&s, func(p *string) { *p = "FAIL" }) - return unsafe.Pointer(&s) -} - -//go:noinline -//go:uintptrescapes -func test(s string, p uintptr) { - runtime.GC() - if *(*string)(unsafe.Pointer(p)) != "ok" { - panic(s + " return unexpected result") - } - done <- true -} - -func main() { - test("normal", uintptr(setup())) - <-done - - go test("go", uintptr(setup())) - <-done - - func() { - defer test("defer", uintptr(setup())) - }() - <-done -} diff --git a/test/fixedbugs/issue24491a.go b/test/fixedbugs/issue24491a.go new file mode 100644 index 0000000000..148134d187 --- /dev/null +++ b/test/fixedbugs/issue24491a.go @@ -0,0 +1,54 @@ +// run + +// Copyright 2020 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. + +// This test makes sure unsafe-uintptr arguments are handled correctly. + +package main + +import ( + "runtime" + "unsafe" +) + +var done = make(chan bool, 1) + +func setup() unsafe.Pointer { + s := "ok" + runtime.SetFinalizer(&s, func(p *string) { *p = "FAIL" }) + return unsafe.Pointer(&s) +} + +//go:noinline +//go:uintptrescapes +func test(s string, p uintptr) int { + runtime.GC() + if *(*string)(unsafe.Pointer(p)) != "ok" { + panic(s + " return unexpected result") + } + done <- true + return 0 +} + +//go:noinline +func f() int { + return test("return", uintptr(setup())) +} + +func main() { + test("normal", uintptr(setup())) + <-done + + go test("go", uintptr(setup())) + <-done + + func() { + defer test("defer", uintptr(setup())) + }() + <-done + + f() + <-done +} diff --git a/test/fixedbugs/issue24491b.go b/test/fixedbugs/issue24491b.go new file mode 100644 index 0000000000..5f4a2f233e --- /dev/null +++ b/test/fixedbugs/issue24491b.go @@ -0,0 +1,46 @@ +// run + +// Copyright 2020 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. + +// This test makes sure unsafe-uintptr arguments are not +// kept alive longer than expected. + +package main + +import ( + "runtime" + "sync/atomic" + "unsafe" +) + +var done uint32 + +func setup() unsafe.Pointer { + s := "ok" + runtime.SetFinalizer(&s, func(p *string) { atomic.StoreUint32(&done, 1) }) + return unsafe.Pointer(&s) +} + +//go:noinline +//go:uintptrescapes +func before(p uintptr) int { + runtime.GC() + if atomic.LoadUint32(&done) != 0 { + panic("GC early") + } + return 0 +} + +func after() int { + runtime.GC() + if atomic.LoadUint32(&done) == 0 { + panic("GC late") + } + return 0 +} + +func main() { + _ = before(uintptr(setup())) + after() +} -- cgit v1.3 From 5f1b12bfbeb04ca6dbecbf064f5e5a42d8ba4b5a Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 12 Sep 2020 07:19:22 +0700 Subject: cmd/compile: remove nodeNeedsWrapper flag CL 254397 attached OVARLIVE nodes to OCALLxxx nodes Nbody. The NeedsWrapper flag is now redundant with n.Nbody.Len() > 0 condition, so use that condition instead and remove the flag. Passes toolstash-check. Change-Id: Iebc3e674d3c0040a876ca4be05025943d2b4fb31 Reviewed-on: https://go-review.googlesource.com/c/go/+/254398 Run-TryBot: Cuong Manh Le TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/order.go | 1 - src/cmd/compile/internal/gc/syntax.go | 41 +++++++++++------------------------ src/cmd/compile/internal/gc/walk.go | 7 ++++-- 3 files changed, 18 insertions(+), 31 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 341f4ee66f..75da154fe2 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -496,7 +496,6 @@ func (o *Order) call(n *Node) { arg.Left = x x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt)) - n.SetNeedsWrapper(true) } } diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 9592b7484c..14d2710da4 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -141,20 +141,19 @@ const ( nodeInitorder, _ // tracks state during init1; two bits _, _ // second nodeInitorder bit _, nodeHasBreak - _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only - _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND - _, nodeIsDDD // is the argument variadic - _, nodeDiag // already printed error about this - _, nodeColas // OAS resulting from := - _, nodeNonNil // guaranteed to be non-nil - _, nodeTransient // storage can be reused immediately after this statement - _, nodeBounded // bounds check unnecessary - _, nodeHasCall // expression contains a function call - _, nodeLikely // if statement condition likely - _, nodeHasVal // node.E contains a Val - _, nodeHasOpt // node.E contains an Opt - _, nodeEmbedded // ODCLFIELD embedded type - _, nodeNeedsWrapper // OCALLxxx node that needs to be wrapped + _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only + _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND + _, nodeIsDDD // is the argument variadic + _, nodeDiag // already printed error about this + _, nodeColas // OAS resulting from := + _, nodeNonNil // guaranteed to be non-nil + _, nodeTransient // storage can be reused immediately after this statement + _, nodeBounded // bounds check unnecessary + _, nodeHasCall // expression contains a function call + _, nodeLikely // if statement condition likely + _, nodeHasVal // node.E contains a Val + _, nodeHasOpt // node.E contains an Opt + _, nodeEmbedded // ODCLFIELD embedded type ) func (n *Node) Class() Class { return Class(n.flags.get3(nodeClass)) } @@ -287,20 +286,6 @@ func (n *Node) SetIota(x int64) { n.Xoffset = x } -func (n *Node) NeedsWrapper() bool { - return n.flags&nodeNeedsWrapper != 0 -} - -// SetNeedsWrapper indicates that OCALLxxx node needs to be wrapped by a closure. -func (n *Node) SetNeedsWrapper(b bool) { - switch n.Op { - case OCALLFUNC, OCALLMETH, OCALLINTER: - default: - Fatalf("Node.SetNeedsWrapper %v", n.Op) - } - n.flags.set(nodeNeedsWrapper, b) -} - // mayBeShared reports whether n may occur in multiple places in the AST. // Extra care must be taken when mutating such a node. func (n *Node) mayBeShared() bool { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 361de7e0f3..2d29366880 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -231,12 +231,15 @@ func walkstmt(n *Node) *Node { case OCOPY: n.Left = copyany(n.Left, &n.Ninit, true) - default: - if n.Left.NeedsWrapper() { + case OCALLFUNC, OCALLMETH, OCALLINTER: + if n.Left.Nbody.Len() > 0 { n.Left = wrapCall(n.Left, &n.Ninit) } else { n.Left = walkexpr(n.Left, &n.Ninit) } + + default: + n.Left = walkexpr(n.Left, &n.Ninit) } case OFOR, OFORUNTIL: -- cgit v1.3 From 237410547bb81ae3c58e9c5bf0cf59edc989e243 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 14 Sep 2020 12:53:36 -0700 Subject: cmd/compile: better dclcontext handling in func{hdr,body} funchdr and funcbody currently assume that either (1) Curfn == nil && dclcontext == PEXTERN, or (2) Curfn != nil && dclcontext == PAUTO. This is a reasonable assumption during parsing. However, these functions end up getting used in other contexts, and not all callers are so disciplined about Curfn/dclcontext handling. This CL changes them to save/restore arbitrary Curfn/dclcontext pairs instead. This is necessary for the followup CL, which pushes fninit earlier. Otherwise, Curfn/dclcontext fall out of sync, and funchdr panics. Passes toolstash-check. Updates #33485. Change-Id: I19b1be23db1bad6475345ae5c81bbdc66291a3a7 Reviewed-on: https://go-review.googlesource.com/c/go/+/254838 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Keith Randall Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/dcl.go | 28 +++++++++++++--------------- src/cmd/compile/internal/gc/main.go | 3 +++ 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 69eb13f607..a362d1a643 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -382,14 +382,11 @@ func ifacedcl(n *Node) { // returns in auto-declaration context. func funchdr(n *Node) { // change the declaration context from extern to auto - if Curfn == nil && dclcontext != PEXTERN { - Fatalf("funchdr: dclcontext = %d", dclcontext) - } - + funcStack = append(funcStack, funcStackEnt{Curfn, dclcontext}) + Curfn = n dclcontext = PAUTO + types.Markdcl() - funcstack = append(funcstack, Curfn) - Curfn = n if n.Func.Nname != nil { funcargs(n.Func.Nname.Name.Param.Ntype) @@ -497,21 +494,22 @@ func funcarg2(f *types.Field, ctxt Class) { declare(n, ctxt) } -var funcstack []*Node // stack of previous values of Curfn +var funcStack []funcStackEnt // stack of previous values of Curfn/dclcontext + +type funcStackEnt struct { + curfn *Node + dclcontext Class +} // finish the body. // called in auto-declaration context. // returns in extern-declaration context. func funcbody() { - // change the declaration context from auto to extern - if dclcontext != PAUTO { - Fatalf("funcbody: unexpected dclcontext %d", dclcontext) - } + // change the declaration context from auto to previous context types.Popdcl() - funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1] - if Curfn == nil { - dclcontext = PEXTERN - } + var e funcStackEnt + funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1] + Curfn, dclcontext = e.curfn, e.dclcontext } // structs, functions, and methods. diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index eedfc4bb25..9bce6cf8cb 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -809,6 +809,9 @@ func Main(archInit func(*Arch)) { } } + if len(funcStack) != 0 { + Fatalf("funcStack is non-empty: %v", len(funcStack)) + } if len(compilequeue) != 0 { Fatalf("%d uncompiled functions", len(compilequeue)) } -- cgit v1.3 From f4936d09fd5a1fff890d63ee2ab9543243dc4da6 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 14 Sep 2020 12:56:37 -0700 Subject: cmd/compile: call fninit earlier This allows the global initializers function to go through normal mid-end optimizations (e.g., inlining, escape analysis) like any other function. Updates #33485. Change-Id: I9bcfe98b8628d1aca09b4c238d8d3b74c69010a5 Reviewed-on: https://go-review.googlesource.com/c/go/+/254839 Reviewed-by: Keith Randall Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/init.go | 8 +++----- src/cmd/compile/internal/gc/main.go | 6 ++---- test/inline.go | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go index 94cbcf9846..ec9cc4bddc 100644 --- a/src/cmd/compile/internal/gc/init.go +++ b/src/cmd/compile/internal/gc/init.go @@ -59,7 +59,7 @@ func fninit(n []*Node) { Curfn = fn typecheckslice(nf, ctxStmt) Curfn = nil - funccompile(fn) + xtop = append(xtop, fn) fns = append(fns, initializers.Linksym()) } if dummyInitFn.Func.Dcl != nil { @@ -68,16 +68,14 @@ func fninit(n []*Node) { // something's weird if we get here. Fatalf("dummyInitFn still has declarations") } + dummyInitFn = nil // Record user init functions. for i := 0; i < renameinitgen; i++ { s := lookupN("init.", i) fn := asNode(s.Def).Name.Defn // Skip init functions with empty bodies. - // noder.go doesn't allow external init functions, and - // order.go has already removed any OEMPTY nodes, so - // checking Len() == 0 is sufficient here. - if fn.Nbody.Len() == 0 { + if fn.Nbody.Len() == 1 && fn.Nbody.First().Op == OEMPTY { continue } fns = append(fns, s.Linksym()) diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 9bce6cf8cb..8783cb4e46 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -642,6 +642,8 @@ func Main(archInit func(*Arch)) { errorexit() } + fninit(xtop) + // Phase 4: Decide how to capture closed variables. // This needs to run before escape analysis, // because variables captured by value do not escape. @@ -751,10 +753,6 @@ func Main(archInit func(*Arch)) { } timings.AddEvent(fcount, "funcs") - if nsavederrors+nerrors == 0 { - fninit(xtop) - } - compileFunctions() if nowritebarrierrecCheck != nil { diff --git a/test/inline.go b/test/inline.go index 0b3ad55d46..1c5c1bc8d3 100644 --- a/test/inline.go +++ b/test/inline.go @@ -50,7 +50,7 @@ func j(x int) int { // ERROR "can inline j" } } -var somethingWrong error = errors.New("something went wrong") +var somethingWrong error = errors.New("something went wrong") // ERROR "can inline init" "inlining call to errors.New" "errors.errorString.* escapes to heap" // local closures can be inlined func l(x, y int) (int, int, error) { -- cgit v1.3 From d20298e1c7d1df794a11ce7768e027c6759df2a4 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 14 Sep 2020 10:38:45 +0700 Subject: cmd/compile: make funccompile non-reentrant Currently, there's awkward reentrancy issue with funccompile: funccompile -> compile -> dtypesym -> geneq/genhash/genwrapper -> funccompile Though it's not a problem at this moment, some attempts by @mdempsky to move order/walk/instrument into buildssa was failed, due to SSA cache corruption. This commit fixes that reentrancy issue, by making generated functions to be pumped through the same compile workqueue that normal functions are compiled. We do this by adding them to xtop, instead of calling funccompile directly in geneq/genhash/genwrapper. In dumpdata, we look for uncompiled functions in xtop instead of compilequeue, then finish compiling them. Updates #38463 Fixes #33485 Change-Id: Ic9f0ce45b56ae2ff3862f17fd979253ddc144bb5 Reviewed-on: https://go-review.googlesource.com/c/go/+/254617 Run-TryBot: Cuong Manh Le Reviewed-by: Matthew Dempsky TryBot-Result: Go Bot Trust: Keith Randall Trust: Cuong Manh Le --- src/cmd/compile/internal/gc/alg.go | 4 ++-- src/cmd/compile/internal/gc/obj.go | 26 +++++++++++++++++++++++++- src/cmd/compile/internal/gc/subr.go | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index c9d71ea00b..6302b88f59 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -392,7 +392,7 @@ func genhash(t *types.Type) *obj.LSym { } fn.Func.SetNilCheckDisabled(true) - funccompile(fn) + xtop = append(xtop, fn) // Build closure. It doesn't close over any variables, so // it contains just the function pointer. @@ -754,7 +754,7 @@ func geneq(t *types.Type) *obj.LSym { // neither of which can be nil, and our comparisons // are shallow. fn.Func.SetNilCheckDisabled(true) - funccompile(fn) + xtop = append(xtop, fn) // Generate a closure which points at the function we just generated. dsymptr(closure, 0, sym.Linksym(), 0) diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index af5037c5a8..b55331a948 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -113,12 +113,16 @@ func dumpCompilerObj(bout *bio.Writer) { func dumpdata() { externs := len(externdcl) + xtops := len(xtop) dumpglobls() addptabs() + exportlistLen := len(exportlist) addsignats(externdcl) dumpsignats() dumptabs() + ptabsLen := len(ptabs) + itabsLen := len(itabs) dumpimportstrings() dumpbasictypes() @@ -129,9 +133,19 @@ func dumpdata() { // number of types in a finite amount of code. // In the typical case, we loop 0 or 1 times. // It was not until issue 24761 that we found any code that required a loop at all. - for len(compilequeue) > 0 { + for { + for i := xtops; i < len(xtop); i++ { + n := xtop[i] + if n.Op == ODCLFUNC { + funccompile(n) + } + } + xtops = len(xtop) compileFunctions() dumpsignats() + if xtops == len(xtop) { + break + } } // Dump extra globals. @@ -149,6 +163,16 @@ func dumpdata() { } addGCLocals() + + if exportlistLen != len(exportlist) { + Fatalf("exportlist changed after compile functions loop") + } + if ptabsLen != len(ptabs) { + Fatalf("ptabs changed after compile functions loop") + } + if itabsLen != len(itabs) { + Fatalf("itabs changed after compile functions loop") + } } func dumpLinkerObj(bout *bio.Writer) { diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index d3ba53ff0c..5a5833d19f 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1615,7 +1615,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) { escapeFuncs([]*Node{fn}, false) Curfn = nil - funccompile(fn) + xtop = append(xtop, fn) } func paramNnames(ft *types.Type) []*Node { -- cgit v1.3 From 790fa1c546a05936406f6bbf24f6a6ddeb6ec6ad Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Mon, 14 Sep 2020 16:30:43 +0200 Subject: cmd/compile: unify reflect, string and slice copy runtime functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a common runtime slicecopy function to copy strings or slices into slices. This deduplicates similar code previously used in reflect.slicecopy and runtime.stringslicecopy. Change-Id: I09572ff0647a9e12bb5c6989689ce1c43f16b7f1 Reviewed-on: https://go-review.googlesource.com/c/go/+/254658 Run-TryBot: Martin Möhrmann TryBot-Result: Go Bot Trust: Martin Möhrmann Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/builtin.go | 408 ++++++++++++------------- src/cmd/compile/internal/gc/builtin/runtime.go | 3 +- src/cmd/compile/internal/gc/subr.go | 12 +- src/cmd/compile/internal/gc/walk.go | 60 ++-- src/runtime/mbarrier.go | 23 +- src/runtime/slice.go | 44 +-- 6 files changed, 247 insertions(+), 303 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go index 861ffaaa5b..da7b107bfe 100644 --- a/src/cmd/compile/internal/gc/builtin.go +++ b/src/cmd/compile/internal/gc/builtin.go @@ -64,136 +64,135 @@ var runtimeDecls = [...]struct { {"stringtoslicebyte", funcTag, 49}, {"stringtoslicerune", funcTag, 52}, {"slicecopy", funcTag, 53}, - {"slicestringcopy", funcTag, 54}, - {"decoderune", funcTag, 55}, - {"countrunes", funcTag, 56}, - {"convI2I", funcTag, 57}, - {"convT16", funcTag, 58}, - {"convT32", funcTag, 58}, - {"convT64", funcTag, 58}, - {"convTstring", funcTag, 58}, - {"convTslice", funcTag, 58}, - {"convT2E", funcTag, 59}, - {"convT2Enoptr", funcTag, 59}, - {"convT2I", funcTag, 59}, - {"convT2Inoptr", funcTag, 59}, - {"assertE2I", funcTag, 57}, - {"assertE2I2", funcTag, 60}, - {"assertI2I", funcTag, 57}, - {"assertI2I2", funcTag, 60}, - {"panicdottypeE", funcTag, 61}, - {"panicdottypeI", funcTag, 61}, - {"panicnildottype", funcTag, 62}, - {"ifaceeq", funcTag, 64}, - {"efaceeq", funcTag, 64}, - {"fastrand", funcTag, 66}, - {"makemap64", funcTag, 68}, - {"makemap", funcTag, 69}, - {"makemap_small", funcTag, 70}, - {"mapaccess1", funcTag, 71}, - {"mapaccess1_fast32", funcTag, 72}, - {"mapaccess1_fast64", funcTag, 72}, - {"mapaccess1_faststr", funcTag, 72}, - {"mapaccess1_fat", funcTag, 73}, - {"mapaccess2", funcTag, 74}, - {"mapaccess2_fast32", funcTag, 75}, - {"mapaccess2_fast64", funcTag, 75}, - {"mapaccess2_faststr", funcTag, 75}, - {"mapaccess2_fat", funcTag, 76}, - {"mapassign", funcTag, 71}, - {"mapassign_fast32", funcTag, 72}, - {"mapassign_fast32ptr", funcTag, 72}, - {"mapassign_fast64", funcTag, 72}, - {"mapassign_fast64ptr", funcTag, 72}, - {"mapassign_faststr", funcTag, 72}, - {"mapiterinit", funcTag, 77}, - {"mapdelete", funcTag, 77}, - {"mapdelete_fast32", funcTag, 78}, - {"mapdelete_fast64", funcTag, 78}, - {"mapdelete_faststr", funcTag, 78}, - {"mapiternext", funcTag, 79}, - {"mapclear", funcTag, 80}, - {"makechan64", funcTag, 82}, - {"makechan", funcTag, 83}, - {"chanrecv1", funcTag, 85}, - {"chanrecv2", funcTag, 86}, - {"chansend1", funcTag, 88}, + {"decoderune", funcTag, 54}, + {"countrunes", funcTag, 55}, + {"convI2I", funcTag, 56}, + {"convT16", funcTag, 57}, + {"convT32", funcTag, 57}, + {"convT64", funcTag, 57}, + {"convTstring", funcTag, 57}, + {"convTslice", funcTag, 57}, + {"convT2E", funcTag, 58}, + {"convT2Enoptr", funcTag, 58}, + {"convT2I", funcTag, 58}, + {"convT2Inoptr", funcTag, 58}, + {"assertE2I", funcTag, 56}, + {"assertE2I2", funcTag, 59}, + {"assertI2I", funcTag, 56}, + {"assertI2I2", funcTag, 59}, + {"panicdottypeE", funcTag, 60}, + {"panicdottypeI", funcTag, 60}, + {"panicnildottype", funcTag, 61}, + {"ifaceeq", funcTag, 63}, + {"efaceeq", funcTag, 63}, + {"fastrand", funcTag, 65}, + {"makemap64", funcTag, 67}, + {"makemap", funcTag, 68}, + {"makemap_small", funcTag, 69}, + {"mapaccess1", funcTag, 70}, + {"mapaccess1_fast32", funcTag, 71}, + {"mapaccess1_fast64", funcTag, 71}, + {"mapaccess1_faststr", funcTag, 71}, + {"mapaccess1_fat", funcTag, 72}, + {"mapaccess2", funcTag, 73}, + {"mapaccess2_fast32", funcTag, 74}, + {"mapaccess2_fast64", funcTag, 74}, + {"mapaccess2_faststr", funcTag, 74}, + {"mapaccess2_fat", funcTag, 75}, + {"mapassign", funcTag, 70}, + {"mapassign_fast32", funcTag, 71}, + {"mapassign_fast32ptr", funcTag, 71}, + {"mapassign_fast64", funcTag, 71}, + {"mapassign_fast64ptr", funcTag, 71}, + {"mapassign_faststr", funcTag, 71}, + {"mapiterinit", funcTag, 76}, + {"mapdelete", funcTag, 76}, + {"mapdelete_fast32", funcTag, 77}, + {"mapdelete_fast64", funcTag, 77}, + {"mapdelete_faststr", funcTag, 77}, + {"mapiternext", funcTag, 78}, + {"mapclear", funcTag, 79}, + {"makechan64", funcTag, 81}, + {"makechan", funcTag, 82}, + {"chanrecv1", funcTag, 84}, + {"chanrecv2", funcTag, 85}, + {"chansend1", funcTag, 87}, {"closechan", funcTag, 30}, - {"writeBarrier", varTag, 90}, - {"typedmemmove", funcTag, 91}, - {"typedmemclr", funcTag, 92}, - {"typedslicecopy", funcTag, 93}, - {"selectnbsend", funcTag, 94}, - {"selectnbrecv", funcTag, 95}, - {"selectnbrecv2", funcTag, 97}, - {"selectsetpc", funcTag, 98}, - {"selectgo", funcTag, 99}, + {"writeBarrier", varTag, 89}, + {"typedmemmove", funcTag, 90}, + {"typedmemclr", funcTag, 91}, + {"typedslicecopy", funcTag, 92}, + {"selectnbsend", funcTag, 93}, + {"selectnbrecv", funcTag, 94}, + {"selectnbrecv2", funcTag, 96}, + {"selectsetpc", funcTag, 97}, + {"selectgo", funcTag, 98}, {"block", funcTag, 9}, - {"makeslice", funcTag, 100}, - {"makeslice64", funcTag, 101}, - {"makeslicecopy", funcTag, 102}, - {"growslice", funcTag, 104}, - {"memmove", funcTag, 105}, - {"memclrNoHeapPointers", funcTag, 106}, - {"memclrHasPointers", funcTag, 106}, - {"memequal", funcTag, 107}, - {"memequal0", funcTag, 108}, - {"memequal8", funcTag, 108}, - {"memequal16", funcTag, 108}, - {"memequal32", funcTag, 108}, - {"memequal64", funcTag, 108}, - {"memequal128", funcTag, 108}, - {"f32equal", funcTag, 109}, - {"f64equal", funcTag, 109}, - {"c64equal", funcTag, 109}, - {"c128equal", funcTag, 109}, - {"strequal", funcTag, 109}, - {"interequal", funcTag, 109}, - {"nilinterequal", funcTag, 109}, - {"memhash", funcTag, 110}, - {"memhash0", funcTag, 111}, - {"memhash8", funcTag, 111}, - {"memhash16", funcTag, 111}, - {"memhash32", funcTag, 111}, - {"memhash64", funcTag, 111}, - {"memhash128", funcTag, 111}, - {"f32hash", funcTag, 111}, - {"f64hash", funcTag, 111}, - {"c64hash", funcTag, 111}, - {"c128hash", funcTag, 111}, - {"strhash", funcTag, 111}, - {"interhash", funcTag, 111}, - {"nilinterhash", funcTag, 111}, - {"int64div", funcTag, 112}, - {"uint64div", funcTag, 113}, - {"int64mod", funcTag, 112}, - {"uint64mod", funcTag, 113}, - {"float64toint64", funcTag, 114}, - {"float64touint64", funcTag, 115}, - {"float64touint32", funcTag, 116}, - {"int64tofloat64", funcTag, 117}, - {"uint64tofloat64", funcTag, 118}, - {"uint32tofloat64", funcTag, 119}, - {"complex128div", funcTag, 120}, - {"racefuncenter", funcTag, 121}, + {"makeslice", funcTag, 99}, + {"makeslice64", funcTag, 100}, + {"makeslicecopy", funcTag, 101}, + {"growslice", funcTag, 103}, + {"memmove", funcTag, 104}, + {"memclrNoHeapPointers", funcTag, 105}, + {"memclrHasPointers", funcTag, 105}, + {"memequal", funcTag, 106}, + {"memequal0", funcTag, 107}, + {"memequal8", funcTag, 107}, + {"memequal16", funcTag, 107}, + {"memequal32", funcTag, 107}, + {"memequal64", funcTag, 107}, + {"memequal128", funcTag, 107}, + {"f32equal", funcTag, 108}, + {"f64equal", funcTag, 108}, + {"c64equal", funcTag, 108}, + {"c128equal", funcTag, 108}, + {"strequal", funcTag, 108}, + {"interequal", funcTag, 108}, + {"nilinterequal", funcTag, 108}, + {"memhash", funcTag, 109}, + {"memhash0", funcTag, 110}, + {"memhash8", funcTag, 110}, + {"memhash16", funcTag, 110}, + {"memhash32", funcTag, 110}, + {"memhash64", funcTag, 110}, + {"memhash128", funcTag, 110}, + {"f32hash", funcTag, 110}, + {"f64hash", funcTag, 110}, + {"c64hash", funcTag, 110}, + {"c128hash", funcTag, 110}, + {"strhash", funcTag, 110}, + {"interhash", funcTag, 110}, + {"nilinterhash", funcTag, 110}, + {"int64div", funcTag, 111}, + {"uint64div", funcTag, 112}, + {"int64mod", funcTag, 111}, + {"uint64mod", funcTag, 112}, + {"float64toint64", funcTag, 113}, + {"float64touint64", funcTag, 114}, + {"float64touint32", funcTag, 115}, + {"int64tofloat64", funcTag, 116}, + {"uint64tofloat64", funcTag, 117}, + {"uint32tofloat64", funcTag, 118}, + {"complex128div", funcTag, 119}, + {"racefuncenter", funcTag, 120}, {"racefuncenterfp", funcTag, 9}, {"racefuncexit", funcTag, 9}, - {"raceread", funcTag, 121}, - {"racewrite", funcTag, 121}, - {"racereadrange", funcTag, 122}, - {"racewriterange", funcTag, 122}, - {"msanread", funcTag, 122}, - {"msanwrite", funcTag, 122}, - {"checkptrAlignment", funcTag, 123}, - {"checkptrArithmetic", funcTag, 125}, - {"libfuzzerTraceCmp1", funcTag, 127}, - {"libfuzzerTraceCmp2", funcTag, 129}, - {"libfuzzerTraceCmp4", funcTag, 130}, - {"libfuzzerTraceCmp8", funcTag, 131}, - {"libfuzzerTraceConstCmp1", funcTag, 127}, - {"libfuzzerTraceConstCmp2", funcTag, 129}, - {"libfuzzerTraceConstCmp4", funcTag, 130}, - {"libfuzzerTraceConstCmp8", funcTag, 131}, + {"raceread", funcTag, 120}, + {"racewrite", funcTag, 120}, + {"racereadrange", funcTag, 121}, + {"racewriterange", funcTag, 121}, + {"msanread", funcTag, 121}, + {"msanwrite", funcTag, 121}, + {"checkptrAlignment", funcTag, 122}, + {"checkptrArithmetic", funcTag, 124}, + {"libfuzzerTraceCmp1", funcTag, 126}, + {"libfuzzerTraceCmp2", funcTag, 128}, + {"libfuzzerTraceCmp4", funcTag, 129}, + {"libfuzzerTraceCmp8", funcTag, 130}, + {"libfuzzerTraceConstCmp1", funcTag, 126}, + {"libfuzzerTraceConstCmp2", funcTag, 128}, + {"libfuzzerTraceConstCmp4", funcTag, 129}, + {"libfuzzerTraceConstCmp8", funcTag, 130}, {"x86HasPOPCNT", varTag, 6}, {"x86HasSSE41", varTag, 6}, {"x86HasFMA", varTag, 6}, @@ -202,7 +201,7 @@ var runtimeDecls = [...]struct { } func runtimeTypes() []*types.Type { - var typs [132]*types.Type + var typs [131]*types.Type typs[0] = types.Bytetype typs[1] = types.NewPtr(typs[0]) typs[2] = types.Types[TANY] @@ -257,83 +256,82 @@ func runtimeTypes() []*types.Type { typs[51] = types.NewPtr(typs[50]) typs[52] = functype(nil, []*Node{anonfield(typs[51]), anonfield(typs[28])}, []*Node{anonfield(typs[46])}) typs[53] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])}) - typs[54] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[28])}, []*Node{anonfield(typs[15])}) - typs[55] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[45]), anonfield(typs[15])}) - typs[56] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])}) - typs[57] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])}) - typs[58] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])}) - typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) - typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])}) - typs[61] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) - typs[62] = functype(nil, []*Node{anonfield(typs[1])}, nil) - typs[63] = types.NewPtr(typs[5]) - typs[64] = functype(nil, []*Node{anonfield(typs[63]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) - typs[65] = types.Types[TUINT32] - typs[66] = functype(nil, nil, []*Node{anonfield(typs[65])}) - typs[67] = types.NewMap(typs[2], typs[2]) - typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[67])}) - typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[67])}) - typs[70] = functype(nil, nil, []*Node{anonfield(typs[67])}) - typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) - typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) - typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) - typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, nil) - typs[78] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, nil) - typs[79] = functype(nil, []*Node{anonfield(typs[3])}, nil) - typs[80] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67])}, nil) - typs[81] = types.NewChan(typs[2], types.Cboth) - typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[81])}) - typs[83] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[81])}) - typs[84] = types.NewChan(typs[2], types.Crecv) - typs[85] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, nil) - typs[86] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[87] = types.NewChan(typs[2], types.Csend) - typs[88] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, nil) - typs[89] = types.NewArray(typs[0], 3) - typs[90] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[89]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])}) - typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) - typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) - typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) - typs[94] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[95] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[84])}, []*Node{anonfield(typs[6])}) - typs[96] = types.NewPtr(typs[6]) - typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[96]), anonfield(typs[84])}, []*Node{anonfield(typs[6])}) - typs[98] = functype(nil, []*Node{anonfield(typs[63])}, nil) - typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])}) - typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])}) - typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])}) - typs[102] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])}) - typs[103] = types.NewSlice(typs[2]) - typs[104] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[103]), anonfield(typs[15])}, []*Node{anonfield(typs[103])}) - typs[105] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, nil) - typs[106] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, nil) - typs[107] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, []*Node{anonfield(typs[6])}) - typs[108] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[109] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) - typs[110] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) - typs[111] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) - typs[112] = functype(nil, []*Node{anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[22])}) - typs[113] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])}) - typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])}) - typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])}) - typs[116] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[65])}) - typs[117] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])}) - typs[118] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])}) - typs[119] = functype(nil, []*Node{anonfield(typs[65])}, []*Node{anonfield(typs[20])}) - typs[120] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])}) - typs[121] = functype(nil, []*Node{anonfield(typs[5])}, nil) - typs[122] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil) - typs[123] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil) - typs[124] = types.NewSlice(typs[7]) - typs[125] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[124])}, nil) - typs[126] = types.Types[TUINT8] - typs[127] = functype(nil, []*Node{anonfield(typs[126]), anonfield(typs[126])}, nil) - typs[128] = types.Types[TUINT16] - typs[129] = functype(nil, []*Node{anonfield(typs[128]), anonfield(typs[128])}, nil) - typs[130] = functype(nil, []*Node{anonfield(typs[65]), anonfield(typs[65])}, nil) - typs[131] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil) + typs[54] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[45]), anonfield(typs[15])}) + typs[55] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])}) + typs[56] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])}) + typs[57] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])}) + typs[58] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) + typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])}) + typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) + typs[61] = functype(nil, []*Node{anonfield(typs[1])}, nil) + typs[62] = types.NewPtr(typs[5]) + typs[63] = functype(nil, []*Node{anonfield(typs[62]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) + typs[64] = types.Types[TUINT32] + typs[65] = functype(nil, nil, []*Node{anonfield(typs[64])}) + typs[66] = types.NewMap(typs[2], typs[2]) + typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[66])}) + typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[66])}) + typs[69] = functype(nil, nil, []*Node{anonfield(typs[66])}) + typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) + typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) + typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) + typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, nil) + typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, nil) + typs[78] = functype(nil, []*Node{anonfield(typs[3])}, nil) + typs[79] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66])}, nil) + typs[80] = types.NewChan(typs[2], types.Cboth) + typs[81] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[80])}) + typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[80])}) + typs[83] = types.NewChan(typs[2], types.Crecv) + typs[84] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, nil) + typs[85] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[86] = types.NewChan(typs[2], types.Csend) + typs[87] = functype(nil, []*Node{anonfield(typs[86]), anonfield(typs[3])}, nil) + typs[88] = types.NewArray(typs[0], 3) + typs[89] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[88]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])}) + typs[90] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) + typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) + typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) + typs[93] = functype(nil, []*Node{anonfield(typs[86]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[94] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[83])}, []*Node{anonfield(typs[6])}) + typs[95] = types.NewPtr(typs[6]) + typs[96] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[95]), anonfield(typs[83])}, []*Node{anonfield(typs[6])}) + typs[97] = functype(nil, []*Node{anonfield(typs[62])}, nil) + typs[98] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])}) + typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])}) + typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])}) + typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])}) + typs[102] = types.NewSlice(typs[2]) + typs[103] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[102]), anonfield(typs[15])}, []*Node{anonfield(typs[102])}) + typs[104] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, nil) + typs[105] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, nil) + typs[106] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, []*Node{anonfield(typs[6])}) + typs[107] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[108] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) + typs[109] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) + typs[110] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) + typs[111] = functype(nil, []*Node{anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[22])}) + typs[112] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])}) + typs[113] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])}) + typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])}) + typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[64])}) + typs[116] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])}) + typs[117] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])}) + typs[118] = functype(nil, []*Node{anonfield(typs[64])}, []*Node{anonfield(typs[20])}) + typs[119] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])}) + typs[120] = functype(nil, []*Node{anonfield(typs[5])}, nil) + typs[121] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil) + typs[122] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil) + typs[123] = types.NewSlice(typs[7]) + typs[124] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[123])}, nil) + typs[125] = types.Types[TUINT8] + typs[126] = functype(nil, []*Node{anonfield(typs[125]), anonfield(typs[125])}, nil) + typs[127] = types.Types[TUINT16] + typs[128] = functype(nil, []*Node{anonfield(typs[127]), anonfield(typs[127])}, nil) + typs[129] = functype(nil, []*Node{anonfield(typs[64]), anonfield(typs[64])}, nil) + typs[130] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil) return typs[:] } diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go index 635da80f7c..02d6c7b7f5 100644 --- a/src/cmd/compile/internal/gc/builtin/runtime.go +++ b/src/cmd/compile/internal/gc/builtin/runtime.go @@ -75,8 +75,7 @@ func slicebytetostringtmp(ptr *byte, n int) string func slicerunetostring(*[32]byte, []rune) string func stringtoslicebyte(*[32]byte, string) []byte func stringtoslicerune(*[32]rune, string) []rune -func slicecopy(toPtr *any, toLen int, frPtr *any, frLen int, wid uintptr) int -func slicestringcopy(toPtr *byte, toLen int, fr string) int +func slicecopy(toPtr *any, toLen int, fromPtr *any, fromLen int, wid uintptr) int func decoderune(string, int) (retv rune, retk int) func countrunes(string) int diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 5a5833d19f..8883e75c49 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -928,16 +928,20 @@ func (o Op) IsSlice3() bool { return false } -// slicePtrLen extracts the pointer and length from a slice. +// backingArrayPtrLen extracts the pointer and length from a slice or string. // This constructs two nodes referring to n, so n must be a cheapexpr. -func (n *Node) slicePtrLen() (ptr, len *Node) { +func (n *Node) backingArrayPtrLen() (ptr, len *Node) { var init Nodes c := cheapexpr(n, &init) if c != n || init.Len() != 0 { - Fatalf("slicePtrLen not cheap: %v", n) + Fatalf("backingArrayPtrLen not cheap: %v", n) } ptr = nod(OSPTR, n, nil) - ptr.Type = n.Type.Elem().PtrTo() + if n.Type.IsString() { + ptr.Type = types.Types[TUINT8].PtrTo() + } else { + ptr.Type = n.Type.Elem().PtrTo() + } len = nod(OLEN, n, nil) len.Type = types.Types[TINT] return ptr, len diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 2d29366880..c3a740d4cc 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -1484,7 +1484,7 @@ opswitch: } else { // slicebytetostring(*[32]byte, ptr *byte, n int) string n.Left = cheapexpr(n.Left, init) - ptr, len := n.Left.slicePtrLen() + ptr, len := n.Left.backingArrayPtrLen() n = mkcall("slicebytetostring", n.Type, init, a, ptr, len) } @@ -1497,7 +1497,7 @@ opswitch: } // slicebytetostringtmp(ptr *byte, n int) string n.Left = cheapexpr(n.Left, init) - ptr, len := n.Left.slicePtrLen() + ptr, len := n.Left.backingArrayPtrLen() n = mkcall("slicebytetostringtmp", n.Type, init, ptr, len) case OSTR2BYTES: @@ -2764,36 +2764,25 @@ func appendslice(n *Node, init *Nodes) *Node { // instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int fn := syslook("typedslicecopy") fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem()) - ptr1, len1 := nptr1.slicePtrLen() - ptr2, len2 := nptr2.slicePtrLen() + ptr1, len1 := nptr1.backingArrayPtrLen() + ptr2, len2 := nptr2.backingArrayPtrLen() ncopy = mkcall1(fn, types.Types[TINT], &nodes, typename(elemtype), ptr1, len1, ptr2, len2) - } else if instrumenting && !compiling_runtime { - // rely on runtime to instrument copy. - // copy(s[len(l1):], l2) + // rely on runtime to instrument: + // copy(s[len(l1):], l2) + // l2 can be a slice or string. nptr1 := nod(OSLICE, s, nil) nptr1.Type = s.Type nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil) nptr1 = cheapexpr(nptr1, &nodes) - nptr2 := l2 - if l2.Type.IsString() { - // instantiate func slicestringcopy(toPtr *byte, toLen int, fr string) int - fn := syslook("slicestringcopy") - ptr, len := nptr1.slicePtrLen() - str := nod(OCONVNOP, nptr2, nil) - str.Type = types.Types[TSTRING] - ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr, len, str) - } else { - // instantiate func slicecopy(to any, fr any, wid uintptr) int - fn := syslook("slicecopy") - fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem()) - ptr1, len1 := nptr1.slicePtrLen() - ptr2, len2 := nptr2.slicePtrLen() - ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width)) - } + ptr1, len1 := nptr1.backingArrayPtrLen() + ptr2, len2 := nptr2.backingArrayPtrLen() + fn := syslook("slicecopy") + fn = substArgTypes(fn, ptr1.Type.Elem(), ptr2.Type.Elem()) + ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width)) } else { // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) nptr1 := nod(OINDEX, s, nod(OLEN, l1, nil)) @@ -3092,28 +3081,25 @@ func copyany(n *Node, init *Nodes, runtimecall bool) *Node { Curfn.Func.setWBPos(n.Pos) fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem()) n.Left = cheapexpr(n.Left, init) - ptrL, lenL := n.Left.slicePtrLen() + ptrL, lenL := n.Left.backingArrayPtrLen() n.Right = cheapexpr(n.Right, init) - ptrR, lenR := n.Right.slicePtrLen() + ptrR, lenR := n.Right.backingArrayPtrLen() return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR) } if runtimecall { - if n.Right.Type.IsString() { - fn := syslook("slicestringcopy") - n.Left = cheapexpr(n.Left, init) - ptr, len := n.Left.slicePtrLen() - str := nod(OCONVNOP, n.Right, nil) - str.Type = types.Types[TSTRING] - return mkcall1(fn, n.Type, init, ptr, len, str) - } + // rely on runtime to instrument: + // copy(n.Left, n.Right) + // n.Right can be a slice or string. - fn := syslook("slicecopy") - fn = substArgTypes(fn, n.Left.Type.Elem(), n.Right.Type.Elem()) n.Left = cheapexpr(n.Left, init) - ptrL, lenL := n.Left.slicePtrLen() + ptrL, lenL := n.Left.backingArrayPtrLen() n.Right = cheapexpr(n.Right, init) - ptrR, lenR := n.Right.slicePtrLen() + ptrR, lenR := n.Right.backingArrayPtrLen() + + fn := syslook("slicecopy") + fn = substArgTypes(fn, ptrL.Type.Elem(), ptrR.Type.Elem()) + return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width)) } diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index f7875d327a..2b5affce52 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -281,28 +281,7 @@ func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe //go:linkname reflect_typedslicecopy reflect.typedslicecopy func reflect_typedslicecopy(elemType *_type, dst, src slice) int { if elemType.ptrdata == 0 { - n := dst.len - if n > src.len { - n = src.len - } - if n == 0 { - return 0 - } - - size := uintptr(n) * elemType.size - if raceenabled { - callerpc := getcallerpc() - pc := funcPC(reflect_typedslicecopy) - racewriterangepc(dst.array, size, callerpc, pc) - racereadrangepc(src.array, size, callerpc, pc) - } - if msanenabled { - msanwrite(dst.array, size) - msanread(src.array, size) - } - - memmove(dst.array, src.array, size) - return n + return slicecopy(dst.array, dst.len, src.array, src.len, elemType.size) } return typedslicecopy(elemType, dst.array, dst.len, src.array, src.len) } diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 0418ace25a..82a45c78a9 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -243,12 +243,13 @@ func isPowerOfTwo(x uintptr) bool { return x&(x-1) == 0 } -func slicecopy(toPtr unsafe.Pointer, toLen int, fmPtr unsafe.Pointer, fmLen int, width uintptr) int { - if fmLen == 0 || toLen == 0 { +// slicecopy is used to copy from a string or slice of pointerless elements into a slice. +func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int { + if fromLen == 0 || toLen == 0 { return 0 } - n := fmLen + n := fromLen if toLen < n { n = toLen } @@ -257,46 +258,23 @@ func slicecopy(toPtr unsafe.Pointer, toLen int, fmPtr unsafe.Pointer, fmLen int, return n } + size := uintptr(n) * width if raceenabled { callerpc := getcallerpc() pc := funcPC(slicecopy) - racereadrangepc(fmPtr, uintptr(n*int(width)), callerpc, pc) - racewriterangepc(toPtr, uintptr(n*int(width)), callerpc, pc) + racereadrangepc(fromPtr, size, callerpc, pc) + racewriterangepc(toPtr, size, callerpc, pc) } if msanenabled { - msanread(fmPtr, uintptr(n*int(width))) - msanwrite(toPtr, uintptr(n*int(width))) + msanread(fromPtr, size) + msanwrite(toPtr, size) } - size := uintptr(n) * width if size == 1 { // common case worth about 2x to do here // TODO: is this still worth it with new memmove impl? - *(*byte)(toPtr) = *(*byte)(fmPtr) // known to be a byte pointer + *(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer } else { - memmove(toPtr, fmPtr, size) - } - return n -} - -func slicestringcopy(toPtr *byte, toLen int, fm string) int { - if len(fm) == 0 || toLen == 0 { - return 0 - } - - n := len(fm) - if toLen < n { - n = toLen + memmove(toPtr, fromPtr, size) } - - if raceenabled { - callerpc := getcallerpc() - pc := funcPC(slicestringcopy) - racewriterangepc(unsafe.Pointer(toPtr), uintptr(n), callerpc, pc) - } - if msanenabled { - msanwrite(unsafe.Pointer(toPtr), uintptr(n)) - } - - memmove(unsafe.Pointer(toPtr), stringStructOf(&fm).str, uintptr(n)) return n } -- cgit v1.3 From 4f915911e84819b69329a224d5b646983ac9fed7 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 26 Aug 2020 14:07:35 -0700 Subject: cmd/compile: allow aliases to go:notinheap types The alias doesn't need to be marked go:notinheap. It gets its notinheap-ness from the target type. Without this change, the type alias test in the notinheap.go file generates these two errors: notinheap.go:62: misplaced compiler directive notinheap.go:63: type nih must be go:notinheap The first is a result of go:notinheap pragmas not applying to type alias declarations. The second is the result of then trying to match the notinheap-ness of the alias and the target type. Add a few more go:notinheap tests while we are here. Update #40954 Change-Id: I067ec47698df6e9e593e080d67796fd05a1d480f Reviewed-on: https://go-review.googlesource.com/c/go/+/250939 Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Emmanuel Odeke Trust: Keith Randall --- src/cmd/compile/internal/gc/typecheck.go | 2 +- test/notinheap.go | 8 ++++++++ test/notinheap2.go | 10 +++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index fb169cfec8..9bb3c69cd0 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2068,7 +2068,7 @@ func typecheck1(n *Node, top int) (res *Node) { ok |= ctxStmt n.Left = typecheck(n.Left, ctxType) checkwidth(n.Left.Type) - if n.Left.Type != nil && n.Left.Type.NotInHeap() && n.Left.Name.Param.Pragma&NotInHeap == 0 { + if n.Left.Type != nil && n.Left.Type.NotInHeap() && !n.Left.Name.Param.Alias && n.Left.Name.Param.Pragma&NotInHeap == 0 { // The type contains go:notinheap types, so it // must be marked as such (alternatively, we // could silently propagate go:notinheap). diff --git a/test/notinheap.go b/test/notinheap.go index 16c3f8faf0..a2284a5068 100644 --- a/test/notinheap.go +++ b/test/notinheap.go @@ -52,6 +52,14 @@ type t3 byte //go:notinheap type t4 rune +// Type aliases inherit the go:notinheap-ness of the type they alias. +type nihAlias = nih + +type embedAlias1 struct { // ERROR "must be go:notinheap" + x nihAlias +} +type embedAlias2 [1]nihAlias // ERROR "must be go:notinheap" + var sink interface{} func i() { diff --git a/test/notinheap2.go b/test/notinheap2.go index de1e6db1d3..09d0fc0b7b 100644 --- a/test/notinheap2.go +++ b/test/notinheap2.go @@ -27,14 +27,18 @@ func f() { // Heap allocation is not okay. var y *nih +var y2 *struct{ x nih } +var y3 *[1]nih var z []nih var w []nih var n int func g() { - y = new(nih) // ERROR "heap allocation disallowed" - z = make([]nih, 1) // ERROR "heap allocation disallowed" - z = append(z, x) // ERROR "heap allocation disallowed" + y = new(nih) // ERROR "heap allocation disallowed" + y2 = new(struct{ x nih }) // ERROR "heap allocation disallowed" + y3 = new([1]nih) // ERROR "heap allocation disallowed" + z = make([]nih, 1) // ERROR "heap allocation disallowed" + z = append(z, x) // ERROR "heap allocation disallowed" // Test for special case of OMAKESLICECOPY x := make([]nih, n) // ERROR "heap allocation disallowed" copy(x, z) -- cgit v1.3 From 37f261010f837f945eaa2d33d90cd822b4e93459 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 27 Aug 2020 14:05:52 -0700 Subject: cmd/compile: make go:notinheap error message friendlier for cgo Update #40954 Change-Id: Ifaab7349631ccb12fc892882bbdf7f0ebf3d845f Reviewed-on: https://go-review.googlesource.com/c/go/+/251158 Run-TryBot: Keith Randall Reviewed-by: Ian Lance Taylor TryBot-Result: Go Bot Trust: Keith Randall --- src/cmd/compile/internal/gc/escape.go | 2 +- src/cmd/compile/internal/gc/subr.go | 4 ++-- src/cmd/compile/internal/gc/typecheck.go | 6 +++--- src/cmd/compile/internal/gc/walk.go | 8 ++++---- test/notinheap.go | 14 +++++++------- test/notinheap2.go | 14 +++++++------- 6 files changed, 24 insertions(+), 24 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index 75da439bb7..f435d8ff6a 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -1030,7 +1030,7 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation { Fatalf("e.curfn isn't set") } if n != nil && n.Type != nil && n.Type.NotInHeap() { - yyerrorl(n.Pos, "%v is go:notinheap; stack allocation disallowed", n.Type) + yyerrorl(n.Pos, "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type) } n = canonicalNode(n) diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 8883e75c49..b5527e2f83 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -689,14 +689,14 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { // (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't. if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() { if why != nil { - *why = fmt.Sprintf(":\n\t%v is go:notinheap, but %v is not", dst.Elem(), src.Elem()) + *why = fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem()) } return OXXX } // (b) Disallow string to []T where T is go:notinheap. if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Etype == types.Bytetype.Etype || dst.Elem().Etype == types.Runetype.Etype) { if why != nil { - *why = fmt.Sprintf(":\n\t%v is go:notinheap", dst.Elem()) + *why = fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem()) } return OXXX } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 9bb3c69cd0..8d777c399e 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -471,10 +471,10 @@ func typecheck1(n *Node, top int) (res *Node) { return n } if l.Type.NotInHeap() { - yyerror("go:notinheap map key not allowed") + yyerror("incomplete (or unallocatable) map key not allowed") } if r.Type.NotInHeap() { - yyerror("go:notinheap map value not allowed") + yyerror("incomplete (or unallocatable) map value not allowed") } setTypeNode(n, types.NewMap(l.Type, r.Type)) @@ -491,7 +491,7 @@ func typecheck1(n *Node, top int) (res *Node) { return n } if l.Type.NotInHeap() { - yyerror("chan of go:notinheap type not allowed") + yyerror("chan of incomplete (or unallocatable) type not allowed") } setTypeNode(n, types.NewChan(l.Type, n.TChanDir())) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index c3a740d4cc..2db352c8d5 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -648,7 +648,7 @@ opswitch: // x = append(...) r := n.Right if r.Type.Elem().NotInHeap() { - yyerror("%v is go:notinheap; heap allocation disallowed", r.Type.Elem()) + yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", r.Type.Elem()) } switch { case isAppendOfMake(r): @@ -1164,7 +1164,7 @@ opswitch: case ONEW: if n.Type.Elem().NotInHeap() { - yyerror("%v is go:notinheap; heap allocation disallowed", n.Type.Elem()) + yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type.Elem()) } if n.Esc == EscNone { if n.Type.Elem().Width >= maxImplicitStackVarSize { @@ -1335,7 +1335,7 @@ opswitch: } t := n.Type if t.Elem().NotInHeap() { - yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem()) + yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem()) } if n.Esc == EscNone { if !isSmallMakeSlice(n) { @@ -1412,7 +1412,7 @@ opswitch: t := n.Type if t.Elem().NotInHeap() { - yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem()) + yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem()) } length := conv(n.Left, types.Types[TINT]) diff --git a/test/notinheap.go b/test/notinheap.go index a2284a5068..5dd4997a65 100644 --- a/test/notinheap.go +++ b/test/notinheap.go @@ -23,11 +23,11 @@ type embed3 struct { // ERROR "must be go:notinheap" x [1]nih } -type embed4 map[nih]int // ERROR "go:notinheap map key not allowed" +type embed4 map[nih]int // ERROR "incomplete \(or unallocatable\) map key not allowed" -type embed5 map[int]nih // ERROR "go:notinheap map value not allowed" +type embed5 map[int]nih // ERROR "incomplete \(or unallocatable\) map value not allowed" -type emebd6 chan nih // ERROR "chan of go:notinheap type not allowed" +type emebd6 chan nih // ERROR "chan of incomplete \(or unallocatable\) type not allowed" type okay1 *nih @@ -64,8 +64,8 @@ var sink interface{} func i() { sink = new(t1) // no error - sink = (*t2)(new(t1)) // ERROR "cannot convert(.|\n)*t2 is go:notinheap" - sink = (*t2)(new(struct{ x int })) // ERROR "cannot convert(.|\n)*t2 is go:notinheap" - sink = []t3("foo") // ERROR "cannot convert(.|\n)*t3 is go:notinheap" - sink = []t4("bar") // ERROR "cannot convert(.|\n)*t4 is go:notinheap" + sink = (*t2)(new(t1)) // ERROR "cannot convert(.|\n)*t2 is incomplete \(or unallocatable\)" + sink = (*t2)(new(struct{ x int })) // ERROR "cannot convert(.|\n)*t2 is incomplete \(or unallocatable\)" + sink = []t3("foo") // ERROR "cannot convert(.|\n)*t3 is incomplete \(or unallocatable\)" + sink = []t4("bar") // ERROR "cannot convert(.|\n)*t4 is incomplete \(or unallocatable\)" } diff --git a/test/notinheap2.go b/test/notinheap2.go index 09d0fc0b7b..23d4b0ae77 100644 --- a/test/notinheap2.go +++ b/test/notinheap2.go @@ -20,7 +20,7 @@ var x nih // Stack variables are not okay. func f() { - var y nih // ERROR "nih is go:notinheap; stack allocation disallowed" + var y nih // ERROR "nih is incomplete \(or unallocatable\); stack allocation disallowed" x = y } @@ -34,13 +34,13 @@ var w []nih var n int func g() { - y = new(nih) // ERROR "heap allocation disallowed" - y2 = new(struct{ x nih }) // ERROR "heap allocation disallowed" - y3 = new([1]nih) // ERROR "heap allocation disallowed" - z = make([]nih, 1) // ERROR "heap allocation disallowed" - z = append(z, x) // ERROR "heap allocation disallowed" + y = new(nih) // ERROR "can't be allocated in Go" + y2 = new(struct{ x nih }) // ERROR "can't be allocated in Go" + y3 = new([1]nih) // ERROR "can't be allocated in Go" + z = make([]nih, 1) // ERROR "can't be allocated in Go" + z = append(z, x) // ERROR "can't be allocated in Go" // Test for special case of OMAKESLICECOPY - x := make([]nih, n) // ERROR "heap allocation disallowed" + x := make([]nih, n) // ERROR "can't be allocated in Go" copy(x, z) z = x } -- cgit v1.3 From 7ee35cb301eddf4d53e7bb2d5bf0873922d63a6e Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Wed, 16 Sep 2020 13:13:50 +0200 Subject: cmd/compile: be more specific in cannot assign errors "cannot assign to" compiler errors are very laconic: they never explain why the lhs cannot be assigned to (with one exception, when assigning to a struct field in a map). This change makes them a little more specific, in two more cases: when assigning to a string, or to a const; by giving a very brief reason why the lhs cannot be assigned to. Change-Id: I244cca7fc3c3814e00e0ccadeec62f747c293979 Reviewed-on: https://go-review.googlesource.com/c/go/+/255199 Trust: Alberto Donizetti Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/typecheck.go | 9 +++++++-- test/cannotassign.go | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 test/cannotassign.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 8d777c399e..55773641ed 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -3135,9 +3135,14 @@ func checkassign(stmt *Node, n *Node) { return } - if n.Op == ODOT && n.Left.Op == OINDEXMAP { + switch { + case n.Op == ODOT && n.Left.Op == OINDEXMAP: yyerror("cannot assign to struct field %v in map", n) - } else { + case (n.Op == OINDEX && n.Left.Type.IsString()) || n.Op == OSLICESTR: + yyerror("cannot assign to %v (strings are immutable)", n) + case n.Op == OLITERAL && n.Sym != nil && n.isGoConst(): + yyerror("cannot assign to %v (declared const)", n) + default: yyerror("cannot assign to %v", n) } n.Type = nil diff --git a/test/cannotassign.go b/test/cannotassign.go new file mode 100644 index 0000000000..0de04ecad0 --- /dev/null +++ b/test/cannotassign.go @@ -0,0 +1,33 @@ +// errorcheck + +// Copyright 2020 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. + +// Test "cannot assign" errors + +package main + +func main() { + var s string = "hello" + s[1:2] = "a" // ERROR "cannot assign to .* \(strings are immutable\)" + s[3] = "b" // ERROR "cannot assign to .* \(strings are immutable\)" + + const n int = 1 + const cs string = "hello" + n = 2 // ERROR "cannot assign to .* \(declared const\)" + cs = "hi" // ERROR "cannot assign to .* \(declared const\)" + true = false // ERROR "cannot assign to .* \(declared const\)" + + var m map[int]struct{ n int } + m[0].n = 7 // ERROR "cannot assign to struct field .* in map$" + + 1 = 7 // ERROR "cannot assign to 1$" + "hi" = 7 // ERROR `cannot assign to "hi"$` + nil = 7 // ERROR "cannot assign to nil$" + len("") = 7 // ERROR `cannot assign to len\(""\)$` + []int{} = nil // ERROR "cannot assign to \[\]int\{\}$" + + var x int = 7 + x + 1 = 7 // ERROR "cannot assign to x \+ 1$" +} -- cgit v1.3 From b4ef49e527787ec932d0b371bb24c3fc370b1e8d Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 12 Jun 2020 13:48:26 -0400 Subject: cmd/compile: introduce special ssa Aux type for calls This is prerequisite to moving call expansion later into SSA, and probably a good idea anyway. Passes tests. This is the first minimal CL that does a 1-for-1 substitution of *ssa.AuxCall for *obj.LSym. Next step (next CL) is to make this change for all calls so that additional information can be stored in AuxCall. Change-Id: Ia3a7715648fd9fb1a176850767a726e6f5b959eb Reviewed-on: https://go-review.googlesource.com/c/go/+/237680 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/plive.go | 6 +-- src/cmd/compile/internal/gc/ssa.go | 40 ++++++++++------- src/cmd/compile/internal/ssa/check.go | 12 +++++ src/cmd/compile/internal/ssa/func_test.go | 7 +++ src/cmd/compile/internal/ssa/fuse_test.go | 4 +- src/cmd/compile/internal/ssa/gen/386Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/ARM64Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/ARMOps.go | 2 +- src/cmd/compile/internal/ssa/gen/MIPS64Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/MIPSOps.go | 2 +- src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 2 +- src/cmd/compile/internal/ssa/gen/RISCV64Ops.go | 6 +-- src/cmd/compile/internal/ssa/gen/S390XOps.go | 2 +- src/cmd/compile/internal/ssa/gen/WasmOps.go | 2 +- src/cmd/compile/internal/ssa/gen/generic.rules | 12 ++--- src/cmd/compile/internal/ssa/gen/genericOps.go | 6 +-- src/cmd/compile/internal/ssa/gen/rulegen.go | 10 ++++- src/cmd/compile/internal/ssa/loopreschedchecks.go | 2 +- src/cmd/compile/internal/ssa/op.go | 13 ++++++ src/cmd/compile/internal/ssa/opGen.go | 53 +++++++++-------------- src/cmd/compile/internal/ssa/regalloc_test.go | 12 ++--- src/cmd/compile/internal/ssa/rewrite.go | 33 +++++++------- src/cmd/compile/internal/ssa/rewritegeneric.go | 40 ++++++++--------- src/cmd/compile/internal/ssa/value.go | 4 +- src/cmd/compile/internal/ssa/writebarrier.go | 4 +- src/cmd/compile/internal/wasm/ssa.go | 5 ++- 27 files changed, 164 insertions(+), 123 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go index 8976ed657a..a9ea37701e 100644 --- a/src/cmd/compile/internal/gc/plive.go +++ b/src/cmd/compile/internal/gc/plive.go @@ -861,7 +861,7 @@ func (lv *Liveness) hasStackMap(v *ssa.Value) bool { // typedmemclr and typedmemmove are write barriers and // deeply non-preemptible. They are unsafe points and // hence should not have liveness maps. - if sym, _ := v.Aux.(*obj.LSym); sym == typedmemclr || sym == typedmemmove { + if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) { return false } return true @@ -1231,8 +1231,8 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) { s := "live at " if v == nil { s += fmt.Sprintf("entry to %s:", lv.fn.funcname()) - } else if sym, ok := v.Aux.(*obj.LSym); ok { - fn := sym.Name + } else if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil { + fn := sym.Fn.Name if pos := strings.Index(fn, "."); pos >= 0 { fn = fn[pos+1:] } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 3bdb5b0b9f..0ee31bd9a3 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -805,6 +805,11 @@ func (s *state) newValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa. return s.curBlock.NewValue2(s.peekPos(), op, t, arg0, arg1) } +// newValue2A adds a new value with two arguments and an aux value to the current block. +func (s *state) newValue2A(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value { + return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1) +} + // newValue2Apos adds a new value with two arguments and an aux value to the current block. // isStmt determines whether the created values may be a statement or not // (i.e., false means never, yes means maybe). @@ -4297,10 +4302,10 @@ func (s *state) openDeferExit() { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[TUINTPTR], v) - call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, v, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, nil, codeptr, v, s.mem()) } else { // Do a static call if the original call was a static function or method - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn.Sym.Linksym(), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: fn.Sym.Linksym()}, s.mem()) } call.AuxInt = stksize s.vars[&memVar] = call @@ -4432,7 +4437,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Call runtime.deferprocStack with pointer to _defer record. arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) s.store(types.Types[TUINTPTR], arg0, addr) - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferprocStack, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: deferprocStack}, s.mem()) if stksize < int64(Widthptr) { // We need room for both the call to deferprocStack and the call to // the deferred function. @@ -4477,9 +4482,9 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // call target switch { case k == callDefer: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: deferproc}, s.mem()) case k == callGo: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: newproc}, s.mem()) case closure != nil: // rawLoad because loading the code pointer from a // closure is always safe, but IsSanitizerSafeAddr @@ -4487,11 +4492,11 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // critical that we not clobber any arguments already // stored onto the stack. codeptr = s.rawLoad(types.Types[TUINTPTR], closure) - call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, closure, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, nil, codeptr, closure, s.mem()) case codeptr != nil: - call = s.newValue2(ssa.OpInterCall, types.TypeMem, codeptr, s.mem()) + call = s.newValue2A(ssa.OpInterCall, types.TypeMem, nil, codeptr, s.mem()) case sym != nil: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, sym.Linksym(), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: sym.Linksym()}, s.mem()) default: s.Fatalf("bad call type %v %v", n.Op, n) } @@ -4924,7 +4929,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . off = Rnd(off, int64(Widthreg)) // Issue call - call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn, s.mem()) + call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: fn}, s.mem()) s.vars[&memVar] = call if !returns { @@ -6355,6 +6360,9 @@ func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) { } // Add symbol's offset from its base register. switch n := v.Aux.(type) { + case *ssa.AuxCall: + a.Name = obj.NAME_EXTERN + a.Sym = n.Fn case *obj.LSym: a.Name = obj.NAME_EXTERN a.Sym = n @@ -6541,10 +6549,10 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog { } else { p.Pos = v.Pos.WithNotStmt() } - if sym, ok := v.Aux.(*obj.LSym); ok { + if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil { p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN - p.To.Sym = sym + p.To.Sym = sym.Fn } else { // TODO(mdempsky): Can these differences be eliminated? switch thearch.LinkArch.Family { @@ -6567,12 +6575,14 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) { idx := s.livenessMap.Get(v) if !idx.StackMapValid() { // See Liveness.hasStackMap. - if sym, _ := v.Aux.(*obj.LSym); !(sym == typedmemclr || sym == typedmemmove) { + if sym, ok := v.Aux.(*ssa.AuxCall); !ok || !(sym.Fn == typedmemclr || sym.Fn == typedmemmove) { Fatalf("missing stack map index for %v", v.LongString()) } } - if sym, _ := v.Aux.(*obj.LSym); sym == Deferreturn { + call, ok := v.Aux.(*ssa.AuxCall) + + if ok && call.Fn == Deferreturn { // Deferred calls will appear to be returning to // the CALL deferreturn(SB) that we are about to emit. // However, the stack trace code will show the line @@ -6584,11 +6594,11 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) { thearch.Ginsnopdefer(s.pp) } - if sym, ok := v.Aux.(*obj.LSym); ok { + if ok { // Record call graph information for nowritebarrierrec // analysis. if nowritebarrierrecCheck != nil { - nowritebarrierrecCheck.recordCall(s.pp.curfn, sym, v.Pos) + nowritebarrierrecCheck.recordCall(s.pp.curfn, call.Fn, v.Pos) } } diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 828f645b39..9ce87e0aea 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -165,6 +165,18 @@ func checkFunc(f *Func) { f.Fatalf("value %v has Aux type %T, want string", v, v.Aux) } canHaveAux = true + case auxCallOff: + canHaveAuxInt = true + fallthrough + case auxCall: + if ac, ok := v.Aux.(*AuxCall); ok { + if v.Op == OpStaticCall && ac.Fn == nil { + f.Fatalf("value %v has *AuxCall with nil Fn", v) + } + } else { + f.Fatalf("value %v has Aux type %T, want *AuxCall", v, v.Aux) + } + canHaveAux = true case auxSym, auxTyp: canHaveAux = true case auxSymOff, auxSymValAndOff, auxTypSize: diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index 5f6f80f72a..568c6436f5 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -38,6 +38,7 @@ package ssa import ( "cmd/compile/internal/types" + "cmd/internal/obj" "cmd/internal/src" "fmt" "reflect" @@ -140,6 +141,12 @@ var emptyPass pass = pass{ name: "empty pass", } +// AuxCallLSym returns an AuxCall initialized with an LSym that should pass "check" +// as the Aux of a static call. +func AuxCallLSym(name string) *AuxCall { + return &AuxCall{Fn: &obj.LSym{}} +} + // Fun takes the name of an entry bloc and a series of Bloc calls, and // returns a fun containing the composed Func. entry must be a name // supplied to one of the Bloc functions. Each of the bloc names and diff --git a/src/cmd/compile/internal/ssa/fuse_test.go b/src/cmd/compile/internal/ssa/fuse_test.go index 5fe3da93ca..15190997f2 100644 --- a/src/cmd/compile/internal/ssa/fuse_test.go +++ b/src/cmd/compile/internal/ssa/fuse_test.go @@ -142,10 +142,10 @@ func TestFuseSideEffects(t *testing.T) { Valu("b", OpArg, c.config.Types.Bool, 0, nil), If("b", "then", "else")), Bloc("then", - Valu("call1", OpStaticCall, types.TypeMem, 0, nil, "mem"), + Valu("call1", OpStaticCall, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Goto("empty")), Bloc("else", - Valu("call2", OpStaticCall, types.TypeMem, 0, nil, "mem"), + Valu("call2", OpStaticCall, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Goto("empty")), Bloc("empty", Goto("loop")), diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index 1061e5579d..64a17cb7a3 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -463,7 +463,7 @@ func init() { faultOnNilArg0: true, }, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index e6d66957dd..d267fe8753 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -767,7 +767,7 @@ func init() { faultOnNilArg0: true, }, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index e9af261a6a..f52d68dc33 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -471,7 +471,7 @@ func init() { {name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // auxint(flags) ? arg0 : 0 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go index 068fecf74c..1e6b4546da 100644 --- a/src/cmd/compile/internal/ssa/gen/ARMOps.go +++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go @@ -428,7 +428,7 @@ func init() { {name: "SRAcond", argLength: 3, reg: gp2flags1, asm: "SRA"}, // arg0 >> 31 if flags indicates HS, arg0 >> arg1 otherwise, signed shift, arg2=flags // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go index 5f00c080af..dc2e9d3ec9 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go @@ -273,7 +273,7 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go index a5f6c8df54..c66adcf93a 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go +++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go @@ -255,7 +255,7 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index 44f6a74c63..0c04e561ad 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -414,7 +414,7 @@ func init() { {name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, {name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go index 8ab4abe04a..17970918e2 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go @@ -224,9 +224,9 @@ func init() { {name: "MOVconvert", argLength: 2, reg: gp11, asm: "MOV"}, // arg0, but converted to int/ptr as appropriate; arg1=mem // Calls - {name: "CALLstatic", argLength: 1, reg: call, aux: "SymOff", call: true, symEffect: "None"}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: callInter, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: callInter, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // Generic moves and zeros diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go index 710beaddbb..eede8a654b 100644 --- a/src/cmd/compile/internal/ssa/gen/S390XOps.go +++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go @@ -475,7 +475,7 @@ func init() { {name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go index e43eae17e9..3286a68fb0 100644 --- a/src/cmd/compile/internal/ssa/gen/WasmOps.go +++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go @@ -122,7 +122,7 @@ func init() { ) var WasmOps = []opData{ - {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index f7e6bbebac..df70838aa9 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -1933,30 +1933,30 @@ // recognize runtime.newobject and don't Zero/Nilcheck it (Zero (Load (OffPtr [c] (SP)) mem) mem) && mem.Op == OpStaticCall - && isSameSym(mem.Aux, "runtime.newobject") + && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value => mem (Store (Load (OffPtr [c] (SP)) mem) x mem) && isConstZero(x) && mem.Op == OpStaticCall - && isSameSym(mem.Aux, "runtime.newobject") + && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value => mem (Store (OffPtr (Load (OffPtr [c] (SP)) mem)) x mem) && isConstZero(x) && mem.Op == OpStaticCall - && isSameSym(mem.Aux, "runtime.newobject") + && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value => mem // nil checks just need to rewrite to something useless. // they will be deadcode eliminated soon afterwards. (NilCheck (Load (OffPtr [c] (SP)) (StaticCall {sym} _)) _) - && symNamed(sym, "runtime.newobject") + && isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value && warnRule(fe.Debug_checknil(), v, "removed nil check") => (Invalid) (NilCheck (OffPtr (Load (OffPtr [c] (SP)) (StaticCall {sym} _))) _) - && symNamed(sym, "runtime.newobject") + && isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value && warnRule(fe.Debug_checknil(), v, "removed nil check") => (Invalid) @@ -2010,7 +2010,7 @@ // See the comment in op Move in genericOps.go for discussion of the type. (StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))) && sz >= 0 - && symNamed(sym, "runtime.memmove") + && isSameCall(sym, "runtime.memmove") && t.IsPtr() // avoids TUINTPTR, see issue 30061 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 5df0a164bf..acfe222089 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -387,9 +387,9 @@ var genericOps = []opData{ // as a phantom first argument. // TODO(josharian): ClosureCall and InterCall should have Int32 aux // to match StaticCall's 32 bit arg size limit. - {name: "ClosureCall", argLength: 3, aux: "Int64", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. - {name: "StaticCall", argLength: 1, aux: "SymOff", call: true, symEffect: "None"}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. - {name: "InterCall", argLength: 2, aux: "Int64", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. + {name: "ClosureCall", argLength: 3, aux: "Int64", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. + {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. + {name: "InterCall", argLength: 2, aux: "Int64", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", argLength: 1, typ: "Int16"}, diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go index 9e2e112cd7..be51a7c5f8 100644 --- a/src/cmd/compile/internal/ssa/gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -1424,7 +1424,7 @@ func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxi func opHasAuxInt(op opData) bool { switch op.aux { case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", - "SymOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop": + "SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop": return true } return false @@ -1432,7 +1432,7 @@ func opHasAuxInt(op opData) bool { func opHasAux(op opData) bool { switch op.aux { - case "String", "Sym", "SymOff", "SymValAndOff", "Typ", "TypSize", + case "String", "Sym", "SymOff", "Call", "CallOff", "SymValAndOff", "Typ", "TypSize", "S390XCCMask", "S390XRotateParams": return true } @@ -1775,6 +1775,10 @@ func (op opData) auxType() string { return "Sym" case "SymOff": return "Sym" + case "Call": + return "Call" + case "CallOff": + return "Call" case "SymValAndOff": return "Sym" case "Typ": @@ -1809,6 +1813,8 @@ func (op opData) auxIntType() string { return "float32" case "Float64": return "float64" + case "CallOff": + return "int32" case "SymOff": return "int32" case "SymValAndOff": diff --git a/src/cmd/compile/internal/ssa/loopreschedchecks.go b/src/cmd/compile/internal/ssa/loopreschedchecks.go index 1932f9d23a..4a720fdede 100644 --- a/src/cmd/compile/internal/ssa/loopreschedchecks.go +++ b/src/cmd/compile/internal/ssa/loopreschedchecks.go @@ -246,7 +246,7 @@ func insertLoopReschedChecks(f *Func) { // mem1 := call resched (mem0) // goto header resched := f.fe.Syslook("goschedguarded") - mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, resched, mem0) + mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, &AuxCall{resched}, mem0) sched.AddEdgeTo(h) headerMemPhi.AddArg(mem1) diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 063998c6a1..3aa506e3ab 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -67,6 +67,17 @@ type regInfo struct { type auxType int8 +type AuxCall struct { + Fn *obj.LSym +} + +func (a *AuxCall) String() string { + if a.Fn == nil { + return "AuxCall(nil)" + } + return fmt.Sprintf("AuxCall(%v)", a.Fn) +} + const ( auxNone auxType = iota auxBool // auxInt is 0/1 for false/true @@ -85,6 +96,8 @@ const ( auxTyp // aux is a type auxTypSize // aux is a type, auxInt is a size, must have Aux.(Type).Size() == AuxInt auxCCop // aux is a ssa.Op that represents a flags-to-bool conversion (e.g. LessThan) + auxCall // aux is a *ssa.AuxCall + auxCallOff // aux is a *ssa.AuxCall, AuxInt is int64 param (in+out) size // architecture specific aux types auxARM64BitField // aux is an arm64 bitfield lsb and width packed into auxInt diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 45401898c8..797d82f2d1 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -5816,11 +5816,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7 }, @@ -13152,11 +13151,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 4294967279, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 }, @@ -16922,11 +16920,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, @@ -20556,11 +20553,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, @@ -22257,11 +22253,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO }, @@ -23804,11 +23799,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO }, @@ -26504,11 +26498,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 }, @@ -27787,11 +27780,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "CALLstatic", - auxType: auxSymOff, - argLen: 1, - call: true, - symEffect: SymNone, + name: "CALLstatic", + auxType: auxCallOff, + argLen: 1, + call: true, reg: regInfo{ clobbers: 9223372035781033980, // X3 g X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, @@ -31386,11 +31378,10 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLstatic", - auxType: auxSymOff, + auxType: auxCallOff, argLen: 1, clobberFlags: true, call: true, - symEffect: SymNone, reg: regInfo{ clobbers: 4294933503, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 g R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, @@ -32031,11 +32022,10 @@ var opcodeTable = [...]opInfo{ }, { - name: "LoweredStaticCall", - auxType: auxSymOff, - argLen: 1, - call: true, - symEffect: SymNone, + name: "LoweredStaticCall", + auxType: auxCallOff, + argLen: 1, + call: true, reg: regInfo{ clobbers: 844424930131967, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 g }, @@ -34779,12 +34769,11 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "StaticCall", - auxType: auxSymOff, - argLen: 1, - call: true, - symEffect: SymNone, - generic: true, + name: "StaticCall", + auxType: auxCallOff, + argLen: 1, + call: true, + generic: true, }, { name: "InterCall", diff --git a/src/cmd/compile/internal/ssa/regalloc_test.go b/src/cmd/compile/internal/ssa/regalloc_test.go index bb8be5e7ac..d990cac47b 100644 --- a/src/cmd/compile/internal/ssa/regalloc_test.go +++ b/src/cmd/compile/internal/ssa/regalloc_test.go @@ -68,7 +68,7 @@ func TestNoGetgLoadReg(t *testing.T) { Exit("v16"), ), Bloc("b2", - Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, nil, "v1"), + Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "v1"), Goto("b3"), ), ) @@ -99,7 +99,7 @@ func TestSpillWithLoop(t *testing.T) { ), Bloc("loop", Valu("memphi", OpPhi, types.TypeMem, 0, nil, "mem", "call"), - Valu("call", OpAMD64CALLstatic, types.TypeMem, 0, nil, "memphi"), + Valu("call", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "memphi"), Valu("test", OpAMD64CMPBconst, types.TypeFlags, 0, nil, "cond"), Eq("test", "next", "exit"), ), @@ -140,12 +140,12 @@ func TestSpillMove1(t *testing.T) { Bloc("exit1", // store before call, y is available in a register Valu("mem2", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem"), - Valu("mem3", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem2"), + Valu("mem3", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem2"), Exit("mem3"), ), Bloc("exit2", // store after call, y must be loaded from a spill location - Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"), + Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"), Exit("mem5"), ), @@ -188,13 +188,13 @@ func TestSpillMove2(t *testing.T) { ), Bloc("exit1", // store after call, y must be loaded from a spill location - Valu("mem2", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"), + Valu("mem2", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Valu("mem3", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem2"), Exit("mem3"), ), Bloc("exit2", // store after call, y must be loaded from a spill location - Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, nil, "mem"), + Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"), Exit("mem5"), ), diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 09f94ef53e..8195d407e0 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -393,15 +393,9 @@ func canMergeLoad(target, load *Value) bool { return true } -// symNamed reports whether sym's name is name. -func symNamed(sym Sym, name string) bool { - return sym.String() == name -} - -// isSameSym reports whether sym is the same as the given named symbol -func isSameSym(sym interface{}, name string) bool { - s, ok := sym.(fmt.Stringer) - return ok && s.String() == name +// isSameCall reports whether sym is the same as the given named symbol +func isSameCall(sym interface{}, name string) bool { + return sym.(*AuxCall).Fn.String() == name } // nlz returns the number of leading zeros. @@ -713,6 +707,9 @@ func auxToSym(i interface{}) Sym { func auxToType(i interface{}) *types.Type { return i.(*types.Type) } +func auxToCall(i interface{}) *AuxCall { + return i.(*AuxCall) +} func auxToS390xCCMask(i interface{}) s390x.CCMask { return i.(s390x.CCMask) } @@ -726,6 +723,9 @@ func stringToAux(s string) interface{} { func symToAux(s Sym) interface{} { return s } +func callToAux(s *AuxCall) interface{} { + return s +} func typeToAux(t *types.Type) interface{} { return t } @@ -743,7 +743,7 @@ func uaddOvf(a, b int64) bool { // de-virtualize an InterCall // 'sym' is the symbol for the itab -func devirt(v *Value, sym Sym, offset int64) *obj.LSym { +func devirt(v *Value, sym Sym, offset int64) *AuxCall { f := v.Block.Func n, ok := sym.(*obj.LSym) if !ok { @@ -757,7 +757,10 @@ func devirt(v *Value, sym Sym, offset int64) *obj.LSym { f.Warnl(v.Pos, "couldn't de-virtualize call") } } - return lsym + if lsym == nil { + return nil + } + return &AuxCall{Fn: lsym} } // isSamePtr reports whether p1 and p2 point to the same address. @@ -1377,12 +1380,12 @@ func registerizable(b *Block, typ *types.Type) bool { } // needRaceCleanup reports whether this call to racefuncenter/exit isn't needed. -func needRaceCleanup(sym Sym, v *Value) bool { +func needRaceCleanup(sym *AuxCall, v *Value) bool { f := v.Block.Func if !f.Config.Race { return false } - if !symNamed(sym, "runtime.racefuncenter") && !symNamed(sym, "runtime.racefuncexit") { + if !isSameCall(sym, "runtime.racefuncenter") && !isSameCall(sym, "runtime.racefuncexit") { return false } for _, b := range f.Blocks { @@ -1391,7 +1394,7 @@ func needRaceCleanup(sym Sym, v *Value) bool { case OpStaticCall: // Check for racefuncenter will encounter racefuncexit and vice versa. // Allow calls to panic* - s := v.Aux.(fmt.Stringer).String() + s := v.Aux.(*AuxCall).Fn.String() switch s { case "runtime.racefuncenter", "runtime.racefuncexit", "runtime.panicdivide", "runtime.panicwrap", @@ -1409,7 +1412,7 @@ func needRaceCleanup(sym Sym, v *Value) bool { } } } - if symNamed(sym, "runtime.racefuncenter") { + if isSameCall(sym, "runtime.racefuncenter") { // If we're removing racefuncenter, remove its argument as well. if v.Args[0].Op != OpStore { return false diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 180e48b34c..4b388a68cd 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -8515,7 +8515,7 @@ func rewriteValuegeneric_OpInterCall(v *Value) bool { } v.reset(OpStaticCall) v.AuxInt = int32ToAuxInt(int32(argsize)) - v.Aux = symToAux(devirt(v, itab, off)) + v.Aux = callToAux(devirt(v, itab, off)) v.AddArg(mem) return true } @@ -16022,7 +16022,7 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool { return true } // match: (NilCheck (Load (OffPtr [c] (SP)) (StaticCall {sym} _)) _) - // cond: symNamed(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check") + // cond: isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check") // result: (Invalid) for { if v_0.Op != OpLoad { @@ -16042,15 +16042,15 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool { if v_0_1.Op != OpStaticCall { break } - sym := auxToSym(v_0_1.Aux) - if !(symNamed(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) { + sym := auxToCall(v_0_1.Aux) + if !(isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) { break } v.reset(OpInvalid) return true } // match: (NilCheck (OffPtr (Load (OffPtr [c] (SP)) (StaticCall {sym} _))) _) - // cond: symNamed(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check") + // cond: isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check") // result: (Invalid) for { if v_0.Op != OpOffPtr { @@ -16074,8 +16074,8 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool { if v_0_0_1.Op != OpStaticCall { break } - sym := auxToSym(v_0_0_1.Aux) - if !(symNamed(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) { + sym := auxToCall(v_0_0_1.Aux) + if !(isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) { break } v.reset(OpInvalid) @@ -21067,10 +21067,10 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { b := v.Block config := b.Func.Config // match: (StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))) - // cond: sz >= 0 && symNamed(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3) // result: (Move {t.Elem()} [int64(sz)] dst src mem) for { - sym := auxToSym(v.Aux) + sym := auxToCall(v.Aux) s1 := v_0 if s1.Op != OpStore { break @@ -21094,7 +21094,7 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { t := auxToType(s3.Aux) mem := s3.Args[2] dst := s3.Args[1] - if !(sz >= 0 && symNamed(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) { + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) { break } v.reset(OpMove) @@ -21104,10 +21104,10 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { return true } // match: (StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))) - // cond: sz >= 0 && symNamed(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3) // result: (Move {t.Elem()} [int64(sz)] dst src mem) for { - sym := auxToSym(v.Aux) + sym := auxToCall(v.Aux) s1 := v_0 if s1.Op != OpStore { break @@ -21131,7 +21131,7 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { t := auxToType(s3.Aux) mem := s3.Args[2] dst := s3.Args[1] - if !(sz >= 0 && symNamed(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) { + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) { break } v.reset(OpMove) @@ -21144,7 +21144,7 @@ func rewriteValuegeneric_OpStaticCall(v *Value) bool { // cond: needRaceCleanup(sym, v) // result: x for { - sym := auxToSym(v.Aux) + sym := auxToCall(v.Aux) x := v_0 if !(needRaceCleanup(sym, v)) { break @@ -21608,7 +21608,7 @@ func rewriteValuegeneric_OpStore(v *Value) bool { return true } // match: (Store (Load (OffPtr [c] (SP)) mem) x mem) - // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize + // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // result: mem for { if v_0.Op != OpLoad { @@ -21625,14 +21625,14 @@ func rewriteValuegeneric_OpStore(v *Value) bool { break } x := v_1 - if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { + if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { break } v.copyOf(mem) return true } // match: (Store (OffPtr (Load (OffPtr [c] (SP)) mem)) x mem) - // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize + // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // result: mem for { if v_0.Op != OpOffPtr { @@ -21653,7 +21653,7 @@ func rewriteValuegeneric_OpStore(v *Value) bool { break } x := v_1 - if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { + if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { break } v.copyOf(mem) @@ -24337,7 +24337,7 @@ func rewriteValuegeneric_OpZero(v *Value) bool { b := v.Block config := b.Func.Config // match: (Zero (Load (OffPtr [c] (SP)) mem) mem) - // cond: mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize + // cond: mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize // result: mem for { if v_0.Op != OpLoad { @@ -24350,7 +24350,7 @@ func rewriteValuegeneric_OpZero(v *Value) bool { } c := auxIntToInt64(v_0_0.AuxInt) v_0_0_0 := v_0_0.Args[0] - if v_0_0_0.Op != OpSP || mem != v_1 || !(mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { + if v_0_0_0.Op != OpSP || mem != v_1 || !(mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) { break } v.copyOf(mem) diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 6692df7921..94b8763d5d 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -193,11 +193,11 @@ func (v *Value) auxString() string { return fmt.Sprintf(" [%g]", v.AuxFloat()) case auxString: return fmt.Sprintf(" {%q}", v.Aux) - case auxSym, auxTyp: + case auxSym, auxCall, auxTyp: if v.Aux != nil { return fmt.Sprintf(" {%v}", v.Aux) } - case auxSymOff, auxTypSize: + case auxSymOff, auxCallOff, auxTypSize: s := "" if v.Aux != nil { s = fmt.Sprintf(" {%v}", v.Aux) diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 214798a1ab..c358406862 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -523,7 +523,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va off = round(off, config.PtrSize) // issue call - mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, fn, mem) + mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, &AuxCall{fn}, mem) mem.AuxInt = off - config.ctxt.FixedFrameSize() return mem } @@ -582,7 +582,7 @@ func IsNewObject(v *Value, mem *Value) bool { if mem.Op != OpStaticCall { return false } - if !isSameSym(mem.Aux, "runtime.newobject") { + if !isSameCall(mem.Aux, "runtime.newobject") { return false } if v.Args[0].Op != OpOffPtr { diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go index 7861667b88..a36fbca4e0 100644 --- a/src/cmd/compile/internal/wasm/ssa.go +++ b/src/cmd/compile/internal/wasm/ssa.go @@ -122,7 +122,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { switch v.Op { case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall: s.PrepareCall(v) - if v.Aux == gc.Deferreturn { + if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == gc.Deferreturn { // add a resume point before call to deferreturn so it can be called again via jmpdefer s.Prog(wasm.ARESUMEPOINT) } @@ -130,7 +130,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { getValue64(s, v.Args[1]) setReg(s, wasm.REG_CTXT) } - if sym, ok := v.Aux.(*obj.LSym); ok { + if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn != nil { + sym := call.Fn p := s.Prog(obj.ACALL) p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym} p.Pos = v.Pos -- cgit v1.3 From 3c85e995efd0c2adf8578ed27565ad3b427f1a43 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 15 Jun 2020 18:27:02 -0400 Subject: cmd/compile: extend ssa.AuxCall to closure and interface calls Also introduce helper methods. Change-Id: I11a744ed002bae0ca9ebabba3206e1c14147e03d Reviewed-on: https://go-review.googlesource.com/c/go/+/239080 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 18 +++++----- src/cmd/compile/internal/ssa/gen/386Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/ARM64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/ARMOps.go | 6 ++-- src/cmd/compile/internal/ssa/gen/MIPS64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/MIPSOps.go | 6 ++-- src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/RISCV64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/gen/S390XOps.go | 6 ++-- src/cmd/compile/internal/ssa/gen/WasmOps.go | 8 ++--- src/cmd/compile/internal/ssa/gen/genericOps.go | 6 ++-- src/cmd/compile/internal/ssa/loopreschedchecks.go | 2 +- src/cmd/compile/internal/ssa/op.go | 15 ++++++++ src/cmd/compile/internal/ssa/opGen.go | 44 +++++++++++------------ src/cmd/compile/internal/ssa/rewrite.go | 2 +- src/cmd/compile/internal/ssa/rewritegeneric.go | 2 +- src/cmd/compile/internal/ssa/writebarrier.go | 2 +- 18 files changed, 84 insertions(+), 69 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 0ee31bd9a3..542b1b51c2 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4302,10 +4302,10 @@ func (s *state) openDeferExit() { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[TUINTPTR], v) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, nil, codeptr, v, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, v, s.mem()) } else { // Do a static call if the original call was a static function or method - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: fn.Sym.Linksym()}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym()), s.mem()) } call.AuxInt = stksize s.vars[&memVar] = call @@ -4437,7 +4437,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Call runtime.deferprocStack with pointer to _defer record. arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) s.store(types.Types[TUINTPTR], arg0, addr) - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: deferprocStack}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack), s.mem()) if stksize < int64(Widthptr) { // We need room for both the call to deferprocStack and the call to // the deferred function. @@ -4482,9 +4482,9 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // call target switch { case k == callDefer: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: deferproc}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferproc), s.mem()) case k == callGo: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: newproc}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(newproc), s.mem()) case closure != nil: // rawLoad because loading the code pointer from a // closure is always safe, but IsSanitizerSafeAddr @@ -4492,11 +4492,11 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // critical that we not clobber any arguments already // stored onto the stack. codeptr = s.rawLoad(types.Types[TUINTPTR], closure) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, nil, codeptr, closure, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, closure, s.mem()) case codeptr != nil: - call = s.newValue2A(ssa.OpInterCall, types.TypeMem, nil, codeptr, s.mem()) + call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(), codeptr, s.mem()) case sym != nil: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: sym.Linksym()}, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym()), s.mem()) default: s.Fatalf("bad call type %v %v", n.Op, n) } @@ -4929,7 +4929,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . off = Rnd(off, int64(Widthreg)) // Issue call - call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, &ssa.AuxCall{Fn: fn}, s.mem()) + call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn), s.mem()) s.vars[&memVar] = call if !returns { diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index 64a17cb7a3..ddabde7d3d 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -463,9 +463,9 @@ func init() { faultOnNilArg0: true, }, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // arg0 = destination pointer // arg1 = source pointer diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index d267fe8753..2df5016d59 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -767,9 +767,9 @@ func init() { faultOnNilArg0: true, }, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // arg0 = destination pointer // arg1 = source pointer diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index f52d68dc33..9ff53f7e4e 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -471,9 +471,9 @@ func init() { {name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // auxint(flags) ? arg0 : 0 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // pseudo-ops {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go index 1e6b4546da..70c789937a 100644 --- a/src/cmd/compile/internal/ssa/gen/ARMOps.go +++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go @@ -428,9 +428,9 @@ func init() { {name: "SRAcond", argLength: 3, reg: gp2flags1, asm: "SRA"}, // arg0 >> 31 if flags indicates HS, arg0 >> arg1 otherwise, signed shift, arg2=flags // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // pseudo-ops {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go index dc2e9d3ec9..e1e3933502 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go @@ -273,9 +273,9 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // duffzero // arg0 = address of memory to zero diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go index c66adcf93a..cd7357f62b 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go +++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go @@ -255,9 +255,9 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // atomic ops diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index 0c04e561ad..37706b2dd9 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -414,9 +414,9 @@ func init() { {name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, {name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // large or unaligned zeroing // arg0 = address of memory to zero (in R3, changed as side effect) diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go index 17970918e2..b06b86075e 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go @@ -224,9 +224,9 @@ func init() { {name: "MOVconvert", argLength: 2, reg: gp11, asm: "MOV"}, // arg0, but converted to int/ptr as appropriate; arg1=mem // Calls - {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: callInter, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: callInter, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // Generic moves and zeros diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go index eede8a654b..417b33cf91 100644 --- a/src/cmd/compile/internal/ssa/gen/S390XOps.go +++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go @@ -475,9 +475,9 @@ func init() { {name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // (InvertFlags (CMP a b)) == (CMP b a) // InvertFlags is a pseudo-op which can't appear in assembly output. diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go index 3286a68fb0..36c53bc78c 100644 --- a/src/cmd/compile/internal/ssa/gen/WasmOps.go +++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go @@ -122,9 +122,9 @@ func init() { ) var WasmOps = []opData{ - {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "LoweredAddr", argLength: 1, reg: gp11, aux: "SymOff", rematerializeable: true, symEffect: "Addr"}, // returns base+aux+auxint, arg0=base {name: "LoweredMove", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp}}, aux: "Int64"}, // large move. arg0=dst, arg1=src, arg2=mem, auxint=len/8, returns mem @@ -137,7 +137,7 @@ func init() { {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp}}, aux: "Sym", symEffect: "None"}, // invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier // LoweredConvert converts between pointers and integers. - // We have a special op for this so as to not confuse GC + // We have a special op for this so as to not confuse GCCallOff // (particularly stack maps). It takes a memory arg so it // gets correctly ordered with respect to GC safepoints. // arg0=ptr/int arg1=mem, output=int/ptr diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index acfe222089..145ba2d50c 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -387,9 +387,9 @@ var genericOps = []opData{ // as a phantom first argument. // TODO(josharian): ClosureCall and InterCall should have Int32 aux // to match StaticCall's 32 bit arg size limit. - {name: "ClosureCall", argLength: 3, aux: "Int64", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. - {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. - {name: "InterCall", argLength: 2, aux: "Int64", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. + {name: "ClosureCall", argLength: 3, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. + {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. + {name: "InterCall", argLength: 2, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", argLength: 1, typ: "Int16"}, diff --git a/src/cmd/compile/internal/ssa/loopreschedchecks.go b/src/cmd/compile/internal/ssa/loopreschedchecks.go index 4a720fdede..ebd23b34c7 100644 --- a/src/cmd/compile/internal/ssa/loopreschedchecks.go +++ b/src/cmd/compile/internal/ssa/loopreschedchecks.go @@ -246,7 +246,7 @@ func insertLoopReschedChecks(f *Func) { // mem1 := call resched (mem0) // goto header resched := f.fe.Syslook("goschedguarded") - mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, &AuxCall{resched}, mem0) + mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, StaticAuxCall(resched), mem0) sched.AddEdgeTo(h) headerMemPhi.AddArg(mem1) diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 3aa506e3ab..c498a288a1 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -78,6 +78,21 @@ func (a *AuxCall) String() string { return fmt.Sprintf("AuxCall(%v)", a.Fn) } +// StaticAuxCall returns an AuxCall for a static call. +func StaticAuxCall(sym *obj.LSym) *AuxCall { + return &AuxCall{Fn: sym} +} + +// InterfaceAuxCall returns an AuxCall for an interface call. +func InterfaceAuxCall() *AuxCall { + return &AuxCall{} +} + +// ClosureAuxCall returns an AuxCall for a closure call. +func ClosureAuxCall() *AuxCall { + return &AuxCall{} +} + const ( auxNone auxType = iota auxBool // auxInt is 0/1 for false/true diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 797d82f2d1..d95943231a 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -5826,7 +5826,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -5840,7 +5840,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -13161,7 +13161,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -13175,7 +13175,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -16930,7 +16930,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -16944,7 +16944,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -20563,7 +20563,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -20577,7 +20577,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -22263,7 +22263,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -22277,7 +22277,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -23809,7 +23809,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -23823,7 +23823,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -26508,7 +26508,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -26522,7 +26522,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -27790,7 +27790,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, call: true, reg: regInfo{ @@ -27803,7 +27803,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, call: true, reg: regInfo{ @@ -31388,7 +31388,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLclosure", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, clobberFlags: true, call: true, @@ -31402,7 +31402,7 @@ var opcodeTable = [...]opInfo{ }, { name: "CALLinter", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, clobberFlags: true, call: true, @@ -32032,7 +32032,7 @@ var opcodeTable = [...]opInfo{ }, { name: "LoweredClosureCall", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, call: true, reg: regInfo{ @@ -32045,7 +32045,7 @@ var opcodeTable = [...]opInfo{ }, { name: "LoweredInterCall", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, call: true, reg: regInfo{ @@ -34763,7 +34763,7 @@ var opcodeTable = [...]opInfo{ }, { name: "ClosureCall", - auxType: auxInt64, + auxType: auxCallOff, argLen: 3, call: true, generic: true, @@ -34777,7 +34777,7 @@ var opcodeTable = [...]opInfo{ }, { name: "InterCall", - auxType: auxInt64, + auxType: auxCallOff, argLen: 2, call: true, generic: true, diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 8195d407e0..eb371ce38b 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -760,7 +760,7 @@ func devirt(v *Value, sym Sym, offset int64) *AuxCall { if lsym == nil { return nil } - return &AuxCall{Fn: lsym} + return StaticAuxCall(lsym) } // isSamePtr reports whether p1 and p2 point to the same address. diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 4b388a68cd..1ecfabf7cb 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -8483,7 +8483,7 @@ func rewriteValuegeneric_OpInterCall(v *Value) bool { // cond: devirt(v, itab, off) != nil // result: (StaticCall [int32(argsize)] {devirt(v, itab, off)} mem) for { - argsize := auxIntToInt64(v.AuxInt) + argsize := auxIntToInt32(v.AuxInt) if v_0.Op != OpLoad { break } diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index c358406862..4322a85c90 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -523,7 +523,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va off = round(off, config.PtrSize) // issue call - mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, &AuxCall{fn}, mem) + mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, StaticAuxCall(fn), mem) mem.AuxInt = off - config.ctxt.FixedFrameSize() return mem } -- cgit v1.3 From acde81e0a9c17ea23a6fc545b40bfabc80133f78 Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 19 Jun 2020 15:29:51 -0400 Subject: cmd/compile: initialize ACArgs and ACResults AuxCall fields for static and interface calls. Extend use of AuxCall Change-Id: I68b6d9bad09506532e1415fd70d44cf6c15b4b93 Reviewed-on: https://go-review.googlesource.com/c/go/+/239081 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 53 ++++++++++++++++++----- src/cmd/compile/internal/ssa/gen/generic.rules | 4 +- src/cmd/compile/internal/ssa/loopreschedchecks.go | 2 +- src/cmd/compile/internal/ssa/op.go | 50 ++++++++++++++++++--- src/cmd/compile/internal/ssa/rewrite.go | 5 ++- src/cmd/compile/internal/ssa/rewritegeneric.go | 11 ++--- src/cmd/compile/internal/ssa/writebarrier.go | 6 ++- 7 files changed, 102 insertions(+), 29 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 542b1b51c2..6c0b027c17 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4280,16 +4280,20 @@ func (s *state) openDeferExit() { argStart := Ctxt.FixedFrameSize() fn := r.n.Left stksize := fn.Type.ArgWidth() + var ACArgs []ssa.Param + var ACResults []ssa.Param if r.rcvr != nil { // rcvr in case of OCALLINTER v := s.load(r.rcvr.Type.Elem(), r.rcvr) addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)}) s.store(types.Types[TUINTPTR], addr, v) } for j, argAddrVal := range r.argVals { f := getParam(r.n, j) pt := types.NewPtr(f.Type) addr := s.constOffPtrSP(pt, argStart+f.Offset) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart + f.Offset)}) if !canSSAType(f.Type) { s.move(f.Type, addr, argAddrVal) } else { @@ -4305,7 +4309,7 @@ func (s *state) openDeferExit() { call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, v, s.mem()) } else { // Do a static call if the original call was a static function or method - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym()), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults), s.mem()) } call.AuxInt = stksize s.vars[&memVar] = call @@ -4340,6 +4344,17 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { var codeptr *ssa.Value // ptr to target code (if dynamic) var rcvr *ssa.Value // receiver to set fn := n.Left + var ACArgs []ssa.Param + var ACResults []ssa.Param + res := n.Left.Type.Results() + if k == callNormal { + nf := res.NumFields() + for i := 0; i < nf; i++ { + fp := res.Field(i) + ACResults = append(ACResults, ssa.Param{Type: fp.Type, Offset: int32(fp.Offset + Ctxt.FixedFrameSize())}) + } + } + switch n.Op { case OCALLFUNC: if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC { @@ -4437,10 +4452,12 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Call runtime.deferprocStack with pointer to _defer record. arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) s.store(types.Types[TUINTPTR], arg0, addr) - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack), s.mem()) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())}) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults), s.mem()) if stksize < int64(Widthptr) { // We need room for both the call to deferprocStack and the call to // the deferred function. + // TODO Revisit this if/when we pass args in registers. stksize = int64(Widthptr) } call.AuxInt = stksize @@ -4453,8 +4470,10 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Write argsize and closure (args to newproc/deferproc). argsize := s.constInt32(types.Types[TUINT32], int32(stksize)) addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINT32], Offset: int32(argStart)}) s.store(types.Types[TUINT32], addr, argsize) addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr)) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart) + int32(Widthptr)}) s.store(types.Types[TUINTPTR], addr, closure) stksize += 2 * int64(Widthptr) argStart += 2 * int64(Widthptr) @@ -4463,6 +4482,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Set receiver (for interface calls). if rcvr != nil { addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)}) s.store(types.Types[TUINTPTR], addr, rcvr) } @@ -4471,20 +4491,20 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { args := n.Rlist.Slice() if n.Op == OCALLMETH { f := t.Recv() - s.storeArg(args[0], f.Type, argStart+f.Offset) + ACArgs = append(ACArgs, s.storeArg(args[0], f.Type, argStart+f.Offset)) args = args[1:] } for i, n := range args { f := t.Params().Field(i) - s.storeArg(n, f.Type, argStart+f.Offset) + ACArgs = append(ACArgs, s.storeArg(n, f.Type, argStart+f.Offset)) } // call target switch { case k == callDefer: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferproc), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferproc, ACArgs, ACResults), s.mem()) case k == callGo: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(newproc), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(newproc, ACArgs, ACResults), s.mem()) case closure != nil: // rawLoad because loading the code pointer from a // closure is always safe, but IsSanitizerSafeAddr @@ -4494,9 +4514,9 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { codeptr = s.rawLoad(types.Types[TUINTPTR], closure) call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, closure, s.mem()) case codeptr != nil: - call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(), codeptr, s.mem()) + call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem()) case sym != nil: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym()), s.mem()) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem()) default: s.Fatalf("bad call type %v %v", n.Op, n) } @@ -4522,7 +4542,6 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { s.startBlock(bNext) } - res := n.Left.Type.Results() if res.NumFields() == 0 || k != callNormal { // call has no return value. Continue with the next statement. return nil @@ -4918,18 +4937,29 @@ func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value { func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value { // Write args to the stack off := Ctxt.FixedFrameSize() + var ACArgs []ssa.Param + var ACResults []ssa.Param for _, arg := range args { t := arg.Type off = Rnd(off, t.Alignment()) ptr := s.constOffPtrSP(t.PtrTo(), off) size := t.Size() + ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)}) s.store(t, ptr, arg) off += size } off = Rnd(off, int64(Widthreg)) + // Accumulate results types and offsets + offR := off + for _, t := range results { + offR = Rnd(offR, t.Alignment()) + ACResults = append(ACResults, ssa.Param{Type: t, Offset: int32(offR)}) + offR += t.Size() + } + // Issue call - call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn), s.mem()) + call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn, ACArgs, ACResults), s.mem()) s.vars[&memVar] = call if !returns { @@ -5064,8 +5094,9 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) { } } -func (s *state) storeArg(n *Node, t *types.Type, off int64) { +func (s *state) storeArg(n *Node, t *types.Type, off int64) ssa.Param { s.storeArgWithBase(n, t, s.sp, off) + return ssa.Param{Type: t, Offset: int32(off)} } func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) { diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index df70838aa9..39f8cc8889 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -2021,8 +2021,8 @@ // Note that (ITab (IMake)) doesn't get // rewritten until after the first opt pass, // so this rule should trigger reliably. -(InterCall [argsize] (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) && devirt(v, itab, off) != nil => - (StaticCall [int32(argsize)] {devirt(v, itab, off)} mem) +(InterCall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) && devirt(v, auxCall, itab, off) != nil => + (StaticCall [int32(argsize)] {devirt(v, auxCall, itab, off)} mem) // Move and Zero optimizations. // Move source and destination may overlap. diff --git a/src/cmd/compile/internal/ssa/loopreschedchecks.go b/src/cmd/compile/internal/ssa/loopreschedchecks.go index ebd23b34c7..9c73bcff26 100644 --- a/src/cmd/compile/internal/ssa/loopreschedchecks.go +++ b/src/cmd/compile/internal/ssa/loopreschedchecks.go @@ -246,7 +246,7 @@ func insertLoopReschedChecks(f *Func) { // mem1 := call resched (mem0) // goto header resched := f.fe.Syslook("goschedguarded") - mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, StaticAuxCall(resched), mem0) + mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, StaticAuxCall(resched, nil, nil), mem0) sched.AddEdgeTo(h) headerMemPhi.AddArg(mem1) diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index c498a288a1..f94399028a 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -5,6 +5,7 @@ package ssa import ( + "cmd/compile/internal/types" "cmd/internal/obj" "fmt" ) @@ -67,25 +68,60 @@ type regInfo struct { type auxType int8 +type Param struct { + Type *types.Type + Offset int32 // TODO someday this will be a register +} + type AuxCall struct { - Fn *obj.LSym + Fn *obj.LSym + args []Param // Includes receiver for method calls. Does NOT include hidden closure pointer. + results []Param } func (a *AuxCall) String() string { + var fn string if a.Fn == nil { - return "AuxCall(nil)" + fn = "AuxCall{nil" // could be interface/closure etc. + } else { + fn = fmt.Sprintf("AuxCall{%v", a.Fn) + } + + if len(a.args) == 0 { + fn += "()" + } else { + s := "(" + for _, arg := range a.args { + fn += fmt.Sprintf("%s[%v,%v]", s, arg.Type, arg.Offset) + s = "," + } + fn += ")" } - return fmt.Sprintf("AuxCall(%v)", a.Fn) + + if len(a.results) > 0 { // usual is zero or one; only some RT calls have more than one. + if len(a.results) == 1 { + fn += fmt.Sprintf("[%v,%v]", a.results[0].Type, a.results[0].Offset) + } else { + s := "(" + for _, result := range a.results { + fn += fmt.Sprintf("%s[%v,%v]", s, result.Type, result.Offset) + s = "," + } + fn += ")" + } + } + + return fn + "}" } // StaticAuxCall returns an AuxCall for a static call. -func StaticAuxCall(sym *obj.LSym) *AuxCall { - return &AuxCall{Fn: sym} +func StaticAuxCall(sym *obj.LSym, args []Param, results []Param) *AuxCall { + return &AuxCall{Fn: sym, args: args, results: results} } // InterfaceAuxCall returns an AuxCall for an interface call. -func InterfaceAuxCall() *AuxCall { - return &AuxCall{} +func InterfaceAuxCall(args []Param, results []Param) *AuxCall { + return &AuxCall{Fn: nil, args: args, results: results} } // ClosureAuxCall returns an AuxCall for a closure call. diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index eb371ce38b..2ab310ad85 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -743,7 +743,7 @@ func uaddOvf(a, b int64) bool { // de-virtualize an InterCall // 'sym' is the symbol for the itab -func devirt(v *Value, sym Sym, offset int64) *AuxCall { +func devirt(v *Value, aux interface{}, sym Sym, offset int64) *AuxCall { f := v.Block.Func n, ok := sym.(*obj.LSym) if !ok { @@ -760,7 +760,8 @@ func devirt(v *Value, sym Sym, offset int64) *AuxCall { if lsym == nil { return nil } - return StaticAuxCall(lsym) + va := aux.(*AuxCall) + return StaticAuxCall(lsym, va.args, va.results) } // isSamePtr reports whether p1 and p2 point to the same address. diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 1ecfabf7cb..925ff53fd1 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -8479,11 +8479,12 @@ func rewriteValuegeneric_OpIMake(v *Value) bool { func rewriteValuegeneric_OpInterCall(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (InterCall [argsize] (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) - // cond: devirt(v, itab, off) != nil - // result: (StaticCall [int32(argsize)] {devirt(v, itab, off)} mem) + // match: (InterCall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) + // cond: devirt(v, auxCall, itab, off) != nil + // result: (StaticCall [int32(argsize)] {devirt(v, auxCall, itab, off)} mem) for { argsize := auxIntToInt32(v.AuxInt) + auxCall := auxToCall(v.Aux) if v_0.Op != OpLoad { break } @@ -8510,12 +8511,12 @@ func rewriteValuegeneric_OpInterCall(v *Value) bool { break } mem := v_1 - if !(devirt(v, itab, off) != nil) { + if !(devirt(v, auxCall, itab, off) != nil) { break } v.reset(OpStaticCall) v.AuxInt = int32ToAuxInt(int32(argsize)) - v.Aux = callToAux(devirt(v, itab, off)) + v.Aux = callToAux(devirt(v, auxCall, itab, off)) v.AddArg(mem) return true } diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 4322a85c90..7cc8bf7af9 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -501,29 +501,33 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va // put arguments on stack off := config.ctxt.FixedFrameSize() + var ACArgs []Param if typ != nil { // for typedmemmove taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb) off = round(off, taddr.Type.Alignment()) arg := b.NewValue1I(pos, OpOffPtr, taddr.Type.PtrTo(), off, sp) mem = b.NewValue3A(pos, OpStore, types.TypeMem, ptr.Type, arg, taddr, mem) + ACArgs = append(ACArgs, Param{Type: b.Func.Config.Types.Uintptr, Offset: int32(off)}) off += taddr.Type.Size() } off = round(off, ptr.Type.Alignment()) arg := b.NewValue1I(pos, OpOffPtr, ptr.Type.PtrTo(), off, sp) mem = b.NewValue3A(pos, OpStore, types.TypeMem, ptr.Type, arg, ptr, mem) + ACArgs = append(ACArgs, Param{Type: ptr.Type, Offset: int32(off)}) off += ptr.Type.Size() if val != nil { off = round(off, val.Type.Alignment()) arg = b.NewValue1I(pos, OpOffPtr, val.Type.PtrTo(), off, sp) mem = b.NewValue3A(pos, OpStore, types.TypeMem, val.Type, arg, val, mem) + ACArgs = append(ACArgs, Param{Type: val.Type, Offset: int32(off)}) off += val.Type.Size() } off = round(off, config.PtrSize) // issue call - mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, StaticAuxCall(fn), mem) + mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, StaticAuxCall(fn, ACArgs, nil), mem) mem.AuxInt = off - config.ctxt.FixedFrameSize() return mem } -- cgit v1.3 From 39da81da5e35e50da74ebb8c4fe12fd363bf41b9 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 24 Jun 2020 17:00:48 -0400 Subject: cmd/compile: populate AuxCall fields for OpClosureCall Change-Id: Ib5f62826d5249c1727b57d9f8ff2f3a1d6dc5032 Reviewed-on: https://go-review.googlesource.com/c/go/+/240185 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 4 ++-- src/cmd/compile/internal/ssa/op.go | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 6c0b027c17..75fdbbae04 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4306,7 +4306,7 @@ func (s *state) openDeferExit() { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[TUINTPTR], v) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, v, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, v, s.mem()) } else { // Do a static call if the original call was a static function or method call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults), s.mem()) @@ -4512,7 +4512,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // critical that we not clobber any arguments already // stored onto the stack. codeptr = s.rawLoad(types.Types[TUINTPTR], closure) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(), codeptr, closure, s.mem()) + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, closure, s.mem()) case codeptr != nil: call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem()) case sym != nil: diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index f94399028a..02ecdef5e6 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -79,6 +79,10 @@ type AuxCall struct { results []Param } +// String returns +// "AuxCall{()}" if len(results) == 0; +// "AuxCall{()}" if len(results) == 1; +// "AuxCall{()()}" otherwise. func (a *AuxCall) String() string { var fn string if a.Fn == nil { @@ -125,8 +129,8 @@ func InterfaceAuxCall(args []Param, results []Param) *AuxCall { } // ClosureAuxCall returns an AuxCall for a closure call. -func ClosureAuxCall() *AuxCall { - return &AuxCall{} +func ClosureAuxCall(args []Param, results []Param) *AuxCall { + return &AuxCall{Fn: nil, args: args, results: results} } const ( -- cgit v1.3 From 396688af7ee121d478e9b8d2cc9d06999ba7fc6e Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 26 Jun 2020 18:19:01 -0400 Subject: cmd/compile: make translation to calls for SSA look more "value-oriented" The existing translation assumes an in-memory return values, thus it returns the address of the result(s). Most consumers immediately load from the address to get the value, and in late call expansion that is the favored idiom, and it is also the favored idiom when arguments and results use registers instead of memory. Change-Id: Ie0ccc70f399682a42509d847b330ef3956462d56 Reviewed-on: https://go-review.googlesource.com/c/go/+/240186 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 44 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 20 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 75fdbbae04..c59945f206 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -1076,7 +1076,7 @@ func (s *state) stmt(n *Node) { fallthrough case OCALLMETH, OCALLINTER: - s.call(n, callNormal) + s.callAddr(n, callNormal) if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class() == PFUNC { if fn := n.Left.Sym.Name; compiling_runtime && fn == "throw" || n.Left.Sym.Pkg == Runtimepkg && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap") { @@ -1108,10 +1108,10 @@ func (s *state) stmt(n *Node) { if n.Esc == EscNever { d = callDeferStack } - s.call(n.Left, d) + s.callAddr(n.Left, d) } case OGO: - s.call(n.Left, callGo) + s.callAddr(n.Left, callGo) case OAS2DOTTYPE: res, resok := s.dottype(n.Right, true) @@ -2715,8 +2715,7 @@ func (s *state) expr(n *Node) *ssa.Value { fallthrough case OCALLINTER, OCALLMETH: - a := s.call(n, callNormal) - return s.load(n.Type, a) + return s.callResult(n, callNormal) case OGETG: return s.newValue1(ssa.OpGetG, n.Type, s.mem()) @@ -3589,8 +3588,7 @@ func init() { addF("math", "FMA", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { if !s.config.UseFMA { - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] return s.variable(n, types.Types[TFLOAT64]) } v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasFMA) @@ -3611,8 +3609,7 @@ func init() { // Call the pure Go version. s.startBlock(bFalse) - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] s.endBlock().AddEdgeTo(bEnd) // Merge results. @@ -3623,8 +3620,7 @@ func init() { addF("math", "FMA", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { if !s.config.UseFMA { - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] return s.variable(n, types.Types[TFLOAT64]) } addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb) @@ -3646,8 +3642,7 @@ func init() { // Call the pure Go version. s.startBlock(bFalse) - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] s.endBlock().AddEdgeTo(bEnd) // Merge results. @@ -3676,8 +3671,7 @@ func init() { // Call the pure Go version. s.startBlock(bFalse) - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TFLOAT64], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] s.endBlock().AddEdgeTo(bEnd) // Merge results. @@ -3887,8 +3881,7 @@ func init() { // Call the pure Go version. s.startBlock(bFalse) - a := s.call(n, callNormal) - s.vars[n] = s.load(types.Types[TINT], a) + s.vars[n] = s.callResult(n, callNormal) // types.Types[TINT] s.endBlock().AddEdgeTo(bEnd) // Merge results. @@ -4336,9 +4329,17 @@ func (s *state) openDeferExit() { } } +func (s *state) callResult(n *Node, k callKind) *ssa.Value { + return s.call(n, k, false) +} + +func (s *state) callAddr(n *Node, k callKind) *ssa.Value { + return s.call(n, k, true) +} + // Calls the function n using the specified call type. // Returns the address of the return value (or nil if none). -func (s *state) call(n *Node, k callKind) *ssa.Value { +func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { var sym *types.Sym // target symbol (if static) var closure *ssa.Value // ptr to closure to run (if dynamic) var codeptr *ssa.Value // ptr to target code (if dynamic) @@ -4547,7 +4548,10 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { return nil } fp := res.Field(0) - return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()) + if returnResultAddr { + return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()) + } + return s.load(n.Type, s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize())) } // maybeNilCheckClosure checks if a nil check of a closure is needed in some @@ -4676,7 +4680,7 @@ func (s *state) addr(n *Node) *ssa.Value { addr := s.addr(n.Left) return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type case OCALLFUNC, OCALLINTER, OCALLMETH: - return s.call(n, callNormal) + return s.callAddr(n, callNormal) case ODOTTYPE: v, _ := s.dottype(n, false) if v.Op != ssa.OpLoad { -- cgit v1.3 From f5d59d0e382dc59195537a128fe9423a49a4cea8 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 16 Sep 2020 15:41:47 -0700 Subject: cmd/compile: skip looking for OCLOSURE nodes in xtop xtop holds package's top-level declaration statements, but OCLOSURE only appears in expression contexts. xtop will instead hold the synthetic ODCLFUNC representing OCLOSURE's function body. This CL makes the loop consistent with the later phases that only look for ODCLFUNC nodes in xtop. Passes toolstash-check. Change-Id: I852a10ef1bf75bb3351e3da0357ca8b2e26aec6e Reviewed-on: https://go-review.googlesource.com/c/go/+/255340 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 8783cb4e46..7ad3bfe0c8 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -617,7 +617,7 @@ func Main(archInit func(*Arch)) { var fcount int64 for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op == ODCLFUNC || op == OCLOSURE { + if n.Op == ODCLFUNC { Curfn = n decldepth = 1 saveerrors() -- cgit v1.3 From f554eb7bc332cc265e6cc0490a7595b2f5873cba Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 13 Jul 2020 14:44:14 -0400 Subject: cmd/compile: add variable length TRESULTS type for SSA use. This type is very much like TTUPLE, but not just for pairs. Used to describe results of a pre-expansion function call. (will later probably also be used to describe the incoming args). Change-Id: I811850cfcc2b3de85085eb4c2eca217c04c330b3 Reviewed-on: https://go-review.googlesource.com/c/go/+/242360 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Than McIntosh Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/fmt.go | 11 +++++ src/cmd/compile/internal/types/etype_string.go | 7 +-- src/cmd/compile/internal/types/type.go | 66 +++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 10 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 43e501deaf..d4af451506 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -711,6 +711,17 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited return } + if t.Etype == types.TRESULTS { + tys := t.Extra.(*types.Results).Types + for i, et := range tys { + if i > 0 { + b.WriteByte(',') + } + b.WriteString(et.String()) + } + return + } + flag, mode = flag.update(mode) if mode == FTypeIdName { flag |= FmtUnsigned diff --git a/src/cmd/compile/internal/types/etype_string.go b/src/cmd/compile/internal/types/etype_string.go index 0ff05a8c2a..14fd5b71df 100644 --- a/src/cmd/compile/internal/types/etype_string.go +++ b/src/cmd/compile/internal/types/etype_string.go @@ -44,12 +44,13 @@ func _() { _ = x[TCHANARGS-33] _ = x[TSSA-34] _ = x[TTUPLE-35] - _ = x[NTYPE-36] + _ = x[TRESULTS-36] + _ = x[NTYPE-37] } -const _EType_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRIDEALNILBLANKFUNCARGSCHANARGSSSATUPLENTYPE" +const _EType_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE" -var _EType_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 158, 161, 166, 174, 182, 185, 190, 195} +var _EType_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 158, 161, 166, 174, 182, 185, 190, 197, 202} func (i EType) String() string { if i >= EType(len(_EType_index)-1) { diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index a777a5fd90..9b05aef429 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -66,8 +66,9 @@ const ( TCHANARGS // SSA backend types - TSSA // internal types used by SSA backend (flags, memory, etc.) - TTUPLE // a pair of types, used by SSA backend + TSSA // internal types used by SSA backend (flags, memory, etc.) + TTUPLE // a pair of types, used by SSA backend + TRESULTS // multiuple types; the resulting of calling a function or method, plus a memory at the end. NTYPE ) @@ -330,6 +331,11 @@ type Tuple struct { // Any tuple with a memory type must put that memory type second. } +type Results struct { + Types []*Type + // Any Results with a memory type must put that memory type last. +} + // Array contains Type fields specific to array types. type Array struct { Elem *Type // element type @@ -466,6 +472,8 @@ func New(et EType) *Type { t.Extra = new(Chan) case TTUPLE: t.Extra = new(Tuple) + case TRESULTS: + t.Extra = new(Results) } return t } @@ -512,6 +520,12 @@ func NewTuple(t1, t2 *Type) *Type { return t } +func NewResults(types []*Type) *Type { + t := New(TRESULTS) + t.Extra.(*Results).Types = types + return t +} + func newSSA(name string) *Type { t := New(TSSA) t.Extra = name @@ -688,7 +702,7 @@ func (t *Type) copy() *Type { case TARRAY: x := *t.Extra.(*Array) nt.Extra = &x - case TTUPLE, TSSA: + case TTUPLE, TSSA, TRESULTS: Fatalf("ssa types cannot be copied") } // TODO(mdempsky): Find out why this is necessary and explain. @@ -1051,6 +1065,23 @@ func (t *Type) cmp(x *Type) Cmp { } return ttup.second.Compare(xtup.second) + case TRESULTS: + xResults := x.Extra.(*Results) + tResults := t.Extra.(*Results) + xl, tl := len(xResults.Types), len(tResults.Types) + if tl != xl { + if tl < xl { + return CMPlt + } + return CMPgt + } + for i := 0; i < tl; i++ { + if c := tResults.Types[i].Compare(xResults.Types[i]); c != CMPeq { + return c + } + } + return CMPeq + case TMAP: if c := t.Key().cmp(x.Key()); c != CMPeq { return c @@ -1305,6 +1336,9 @@ func (t *Type) FieldType(i int) *Type { panic("bad tuple index") } } + if t.Etype == TRESULTS { + return t.Extra.(*Results).Types[i] + } return t.Field(i).Type } func (t *Type) FieldOff(i int) int64 { @@ -1382,11 +1416,20 @@ func (t *Type) ChanDir() ChanDir { } func (t *Type) IsMemory() bool { - return t == TypeMem || t.Etype == TTUPLE && t.Extra.(*Tuple).second == TypeMem + if t == TypeMem || t.Etype == TTUPLE && t.Extra.(*Tuple).second == TypeMem { + return true + } + if t.Etype == TRESULTS { + if types := t.Extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem { + return true + } + } + return false } -func (t *Type) IsFlags() bool { return t == TypeFlags } -func (t *Type) IsVoid() bool { return t == TypeVoid } -func (t *Type) IsTuple() bool { return t.Etype == TTUPLE } +func (t *Type) IsFlags() bool { return t == TypeFlags } +func (t *Type) IsVoid() bool { return t == TypeVoid } +func (t *Type) IsTuple() bool { return t.Etype == TTUPLE } +func (t *Type) IsResults() bool { return t.Etype == TRESULTS } // IsUntyped reports whether t is an untyped type. func (t *Type) IsUntyped() bool { @@ -1431,6 +1474,15 @@ func (t *Type) HasPointers() bool { case TTUPLE: ttup := t.Extra.(*Tuple) return ttup.first.HasPointers() || ttup.second.HasPointers() + + case TRESULTS: + types := t.Extra.(*Results).Types + for _, et := range types { + if et.HasPointers() { + return true + } + } + return false } return true -- cgit v1.3 From 22053790fa2c0944df53ea95df476ad2f855424f Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 17 Sep 2020 09:55:23 -0700 Subject: cmd/compile: propagate go:notinheap implicitly //go:notinheap type T int type U T We already correctly propagate the notinheap-ness of T to U. But we have an assertion in the typechecker that if there's no explicit //go:notinheap associated with U, then report an error. Get rid of that error so that implicit propagation is allowed. Adjust the tests so that we make sure that uses of types like U do correctly report an error when U is used in a context that might cause a Go heap allocation. Fixes #41451 Update #40954 Update #41432 Change-Id: I1692bc7cceff21ebb3f557f3748812a40887118d Reviewed-on: https://go-review.googlesource.com/c/go/+/255637 Run-TryBot: Keith Randall Reviewed-by: Cuong Manh Le Reviewed-by: Ian Lance Taylor Trust: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/typecheck.go | 6 ------ test/notinheap.go | 20 -------------------- test/notinheap2.go | 26 ++++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 26 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 55773641ed..834c1a8ee6 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2068,12 +2068,6 @@ func typecheck1(n *Node, top int) (res *Node) { ok |= ctxStmt n.Left = typecheck(n.Left, ctxType) checkwidth(n.Left.Type) - if n.Left.Type != nil && n.Left.Type.NotInHeap() && !n.Left.Name.Param.Alias && n.Left.Name.Param.Pragma&NotInHeap == 0 { - // The type contains go:notinheap types, so it - // must be marked as such (alternatively, we - // could silently propagate go:notinheap). - yyerror("type %v must be go:notinheap", n.Left.Type) - } } t := n.Type diff --git a/test/notinheap.go b/test/notinheap.go index 5dd4997a65..2188a38a14 100644 --- a/test/notinheap.go +++ b/test/notinheap.go @@ -11,18 +11,6 @@ package p //go:notinheap type nih struct{} -// Types embedding notinheap types must be notinheap. - -type embed1 struct { // ERROR "must be go:notinheap" - x nih -} - -type embed2 [1]nih // ERROR "must be go:notinheap" - -type embed3 struct { // ERROR "must be go:notinheap" - x [1]nih -} - type embed4 map[nih]int // ERROR "incomplete \(or unallocatable\) map key not allowed" type embed5 map[int]nih // ERROR "incomplete \(or unallocatable\) map value not allowed" @@ -52,14 +40,6 @@ type t3 byte //go:notinheap type t4 rune -// Type aliases inherit the go:notinheap-ness of the type they alias. -type nihAlias = nih - -type embedAlias1 struct { // ERROR "must be go:notinheap" - x nihAlias -} -type embedAlias2 [1]nihAlias // ERROR "must be go:notinheap" - var sink interface{} func i() { diff --git a/test/notinheap2.go b/test/notinheap2.go index 23d4b0ae77..100ed37b72 100644 --- a/test/notinheap2.go +++ b/test/notinheap2.go @@ -32,6 +32,25 @@ var y3 *[1]nih var z []nih var w []nih var n int +var sink interface{} + +type embed1 struct { // implicitly notinheap + x nih +} + +type embed2 [1]nih // implicitly notinheap + +type embed3 struct { // implicitly notinheap + x [1]nih +} + +// Type aliases inherit the go:notinheap-ness of the type they alias. +type nihAlias = nih + +type embedAlias1 struct { // implicitly notinheap + x nihAlias +} +type embedAlias2 [1]nihAlias // implicitly notinheap func g() { y = new(nih) // ERROR "can't be allocated in Go" @@ -39,6 +58,13 @@ func g() { y3 = new([1]nih) // ERROR "can't be allocated in Go" z = make([]nih, 1) // ERROR "can't be allocated in Go" z = append(z, x) // ERROR "can't be allocated in Go" + + sink = new(embed1) // ERROR "can't be allocated in Go" + sink = new(embed2) // ERROR "can't be allocated in Go" + sink = new(embed3) // ERROR "can't be allocated in Go" + sink = new(embedAlias1) // ERROR "can't be allocated in Go" + sink = new(embedAlias2) // ERROR "can't be allocated in Go" + // Test for special case of OMAKESLICECOPY x := make([]nih, n) // ERROR "can't be allocated in Go" copy(x, z) -- cgit v1.3 From 0dc369b127651830edef453938dfb5c149aa37cf Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 16 Sep 2020 15:42:00 +0700 Subject: cmd/compile: make typecheck set correct untyped type Passes toolstash-check. Change-Id: Ie631d8dacb1cc76613e1f50da8422850ac7119a1 Reviewed-on: https://go-review.googlesource.com/c/go/+/255217 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/const.go | 122 +++++++++++-------------------- src/cmd/compile/internal/gc/typecheck.go | 19 ++++- 2 files changed, 60 insertions(+), 81 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index fe73df9d57..59b2c56051 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -44,7 +44,7 @@ func (v Val) Ctype() Ctype { Fatalf("unexpected Ctype for %T", v.U) panic("unreachable") case nil: - return 0 + return CTxxx case *NilVal: return CTNIL case bool: @@ -261,7 +261,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Nod } if t == nil || !okforconst[t.Etype] { - t = defaultType(idealkind(n)) + t = defaultType(n.Type) } switch n.Op { @@ -994,10 +994,8 @@ func setconst(n *Node, v Val) { Xoffset: BADWIDTH, } n.SetVal(v) - if n.Type.IsUntyped() { - // TODO(mdempsky): Make typecheck responsible for setting - // the correct untyped type. - n.Type = idealType(v.Ctype()) + if vt := idealType(v.Ctype()); n.Type.IsUntyped() && n.Type != vt { + Fatalf("untyped type mismatch, have: %v, want: %v", n.Type, vt) } // Check range. @@ -1056,67 +1054,6 @@ func idealType(ct Ctype) *types.Type { return nil } -// idealkind returns a constant kind like consttype -// but for an arbitrary "ideal" (untyped constant) expression. -func idealkind(n *Node) Ctype { - if n == nil || !n.Type.IsUntyped() { - return CTxxx - } - - switch n.Op { - default: - return CTxxx - - case OLITERAL: - return n.Val().Ctype() - - // numeric kinds. - case OADD, - OAND, - OANDNOT, - OBITNOT, - ODIV, - ONEG, - OMOD, - OMUL, - OSUB, - OXOR, - OOR, - OPLUS: - k1 := idealkind(n.Left) - k2 := idealkind(n.Right) - if k1 > k2 { - return k1 - } else { - return k2 - } - - case OREAL, OIMAG: - return CTFLT - - case OCOMPLEX: - return CTCPLX - - case OADDSTR: - return CTSTR - - case OANDAND, - OEQ, - OGE, - OGT, - OLE, - OLT, - ONE, - ONOT, - OOROR: - return CTBOOL - - // shifts (beware!). - case OLSH, ORSH: - return idealkind(n.Left) - } -} - // defaultlit on both nodes simultaneously; // if they're both ideal going in they better // get the same type going out. @@ -1152,32 +1089,57 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) { return l, r } - k := idealkind(l) - if rk := idealkind(r); rk > k { - k = rk + nn := l + if ctype(r.Type) > ctype(l.Type) { + nn = r } - t := defaultType(k) + + t := defaultType(nn.Type) l = convlit(l, t) r = convlit(r, t) return l, r } -func defaultType(k Ctype) *types.Type { - switch k { - case CTBOOL: +func ctype(t *types.Type) Ctype { + switch t { + case types.Idealbool: + return CTBOOL + case types.Idealstring: + return CTSTR + case types.Idealint: + return CTINT + case types.Idealrune: + return CTRUNE + case types.Idealfloat: + return CTFLT + case types.Idealcomplex: + return CTCPLX + } + Fatalf("bad type %v", t) + panic("unreachable") +} + +func defaultType(t *types.Type) *types.Type { + if !t.IsUntyped() { + return t + } + + switch t { + case types.Idealbool: return types.Types[TBOOL] - case CTSTR: + case types.Idealstring: return types.Types[TSTRING] - case CTINT: + case types.Idealint: return types.Types[TINT] - case CTRUNE: + case types.Idealrune: return types.Runetype - case CTFLT: + case types.Idealfloat: return types.Types[TFLOAT64] - case CTCPLX: + case types.Idealcomplex: return types.Types[TCOMPLEX128] } - Fatalf("bad idealkind: %v", k) + + Fatalf("bad type %v", t) return nil } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 834c1a8ee6..274787a22b 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -623,6 +623,9 @@ func typecheck1(n *Node, top int) (res *Node) { // no defaultlit for left // the outer context gives the type n.Type = l.Type + if (l.Type == types.Idealfloat || l.Type == types.Idealcomplex) && r.Op == OLITERAL { + n.Type = types.Idealint + } break } @@ -798,6 +801,20 @@ func typecheck1(n *Node, top int) (res *Node) { } n.Type = t + if t.Etype == TIDEAL { + switch { + case l.Type == types.Idealcomplex || r.Type == types.Idealcomplex: + n.Type = types.Idealcomplex + case l.Type == types.Idealfloat || r.Type == types.Idealfloat: + n.Type = types.Idealfloat + case l.Type == types.Idealrune || r.Type == types.Idealrune: + n.Type = types.Idealrune + case l.Type == types.Idealint || r.Type == types.Idealint: + n.Type = types.Idealint + default: + Fatalf("bad untyped type: %v", t) + } + } case OBITNOT, ONEG, ONOT, OPLUS: ok |= ctxExpr @@ -1678,7 +1695,7 @@ func typecheck1(n *Node, top int) (res *Node) { } var why string n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why) - if n.Op == 0 { + if n.Op == OXXX { if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() { yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why) n.SetDiag(true) -- cgit v1.3 From dc59469f5178a0715a582cbfcc4cf9c06a2c9e82 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 17 Sep 2020 22:53:39 +0700 Subject: cmd/compile: move validation from unary/binaryOp to typecheck CL 254400 makes typecheck set untyped type correctly. We now have enough information to check valid operators for a type in typecheck. Passes toolstash-check. Change-Id: I01a7606ee6ce9964ec52430d53eaa886442bd17f Reviewed-on: https://go-review.googlesource.com/c/go/+/255617 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/const.go | 19 +------------- src/cmd/compile/internal/gc/typecheck.go | 44 +++++++++++++------------------- 2 files changed, 19 insertions(+), 44 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index 59b2c56051..399d0148bb 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -838,10 +838,6 @@ Outer: return Val{} } u.Quo(y) - case OMOD, OOR, OAND, OANDNOT, OXOR: - // TODO(mdempsky): Move to typecheck; see #31060. - yyerror("invalid operation: operator %v not defined on untyped float", op) - return Val{} default: break Outer } @@ -867,10 +863,6 @@ Outer: yyerror("complex division by zero") return Val{} } - case OMOD, OOR, OAND, OANDNOT, OXOR: - // TODO(mdempsky): Move to typecheck; see #31060. - yyerror("invalid operation: operator %v not defined on untyped complex", op) - return Val{} default: break Outer } @@ -932,15 +924,6 @@ func unaryOp(op Op, x Val, t *types.Type) Val { } u.Xor(x) return Val{U: u} - - case CTFLT: - // TODO(mdempsky): Move to typecheck; see #31060. - yyerror("invalid operation: operator %v not defined on untyped float", op) - return Val{} - case CTCPLX: - // TODO(mdempsky): Move to typecheck; see #31060. - yyerror("invalid operation: operator %v not defined on untyped complex", op) - return Val{} } case ONOT: @@ -1120,7 +1103,7 @@ func ctype(t *types.Type) Ctype { } func defaultType(t *types.Type) *types.Type { - if !t.IsUntyped() { + if !t.IsUntyped() || t.Etype == TNIL { return t } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 274787a22b..faa13d72f9 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -716,8 +716,22 @@ func typecheck1(n *Node, top int) (res *Node) { } } - if !okfor[op][et] { - yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t)) + if t.Etype == TIDEAL { + switch { + case l.Type == types.Idealcomplex || r.Type == types.Idealcomplex: + t = types.Idealcomplex + case l.Type == types.Idealfloat || r.Type == types.Idealfloat: + t = types.Idealfloat + case l.Type == types.Idealrune || r.Type == types.Idealrune: + t = types.Idealrune + case l.Type == types.Idealint || r.Type == types.Idealint: + t = types.Idealint + default: + Fatalf("bad untyped type: %v", t) + } + } + if dt := defaultType(t); !okfor[op][dt.Etype] { + yyerror("invalid operation: %v (operator %v not defined on %v)", n, op, t) n.Type = nil return n } @@ -756,15 +770,7 @@ func typecheck1(n *Node, top int) (res *Node) { } } - t = l.Type if iscmp[n.Op] { - // TIDEAL includes complex constant, but only OEQ and ONE are defined for complex, - // so check that the n.op is available for complex here before doing evconst. - if !okfor[n.Op][TCOMPLEX128] && (Isconst(l, CTCPLX) || Isconst(r, CTCPLX)) { - yyerror("invalid operation: %v (operator %v not defined on untyped complex)", n, n.Op) - n.Type = nil - return n - } evconst(n) t = types.Idealbool if n.Op != OLITERAL { @@ -801,20 +807,6 @@ func typecheck1(n *Node, top int) (res *Node) { } n.Type = t - if t.Etype == TIDEAL { - switch { - case l.Type == types.Idealcomplex || r.Type == types.Idealcomplex: - n.Type = types.Idealcomplex - case l.Type == types.Idealfloat || r.Type == types.Idealfloat: - n.Type = types.Idealfloat - case l.Type == types.Idealrune || r.Type == types.Idealrune: - n.Type = types.Idealrune - case l.Type == types.Idealint || r.Type == types.Idealint: - n.Type = types.Idealint - default: - Fatalf("bad untyped type: %v", t) - } - } case OBITNOT, ONEG, ONOT, OPLUS: ok |= ctxExpr @@ -825,8 +817,8 @@ func typecheck1(n *Node, top int) (res *Node) { n.Type = nil return n } - if !okfor[n.Op][t.Etype] { - yyerror("invalid operation: %v %v", n.Op, t) + if !okfor[n.Op][defaultType(t).Etype] { + yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(t)) n.Type = nil return n } -- cgit v1.3 From ddd35f8d71120b0d0508dd0d8f3a727ba681dfb3 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 17 Sep 2020 21:38:42 -0700 Subject: cmd/compile: more comprehensive tests for #24991 The revised test now checks that unsafe-uintptr correctly works for variadic uintptr parameters too, and the CL corrects the code so this code compiles again. The pointers are still not kept alive properly. That will be fixed by a followup CL. But this CL at least allows programs not affected by that to build again. Updates #24991. Updates #41460. Change-Id: If4c39167b6055e602213fb7522c4f527c43ebda9 Reviewed-on: https://go-review.googlesource.com/c/go/+/255877 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/syntax.go | 2 +- src/cmd/compile/internal/gc/walk.go | 1 + test/fixedbugs/issue24491a.go | 26 ++++++++++++++++++++------ 3 files changed, 22 insertions(+), 7 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 14d2710da4..4aa2e230ce 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -716,7 +716,7 @@ const ( ODCLCONST // const pi = 3.14 ODCLTYPE // type Int int or type Int = int - ODELETE // delete(Left, Right) + ODELETE // delete(List) ODOT // Left.Sym (Left is of struct type) ODOTPTR // Left.Sym (Left is of pointer to struct type) ODOTMETH // Left.Sym (Left is non-interface, Right is method name) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 2db352c8d5..933f16d9a0 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -3900,6 +3900,7 @@ func wrapCall(n *Node, init *Nodes) *Node { if !isBuiltinCall { call.Op = OCALL call.Left = n.Left + call.SetIsDDD(n.IsDDD()) } call.List.Set(args) fn.Nbody.Set1(call) diff --git a/test/fixedbugs/issue24491a.go b/test/fixedbugs/issue24491a.go index 148134d187..3c595798b5 100644 --- a/test/fixedbugs/issue24491a.go +++ b/test/fixedbugs/issue24491a.go @@ -23,29 +23,43 @@ func setup() unsafe.Pointer { //go:noinline //go:uintptrescapes -func test(s string, p uintptr) int { +func test(s string, p, q uintptr, rest ...uintptr) int { runtime.GC() + runtime.GC() + if *(*string)(unsafe.Pointer(p)) != "ok" { - panic(s + " return unexpected result") + panic(s + ": p failed") + } + if *(*string)(unsafe.Pointer(q)) != "ok" { + panic(s + ": q failed") } + for _, r := range rest { + // TODO(mdempsky): Remove. + break + + if *(*string)(unsafe.Pointer(r)) != "ok" { + panic(s + ": r[i] failed") + } + } + done <- true return 0 } //go:noinline func f() int { - return test("return", uintptr(setup())) + return test("return", uintptr(setup()), uintptr(setup()), uintptr(setup()), uintptr(setup())) } func main() { - test("normal", uintptr(setup())) + test("normal", uintptr(setup()), uintptr(setup()), uintptr(setup()), uintptr(setup())) <-done - go test("go", uintptr(setup())) + go test("go", uintptr(setup()), uintptr(setup()), uintptr(setup()), uintptr(setup())) <-done func() { - defer test("defer", uintptr(setup())) + defer test("defer", uintptr(setup()), uintptr(setup()), uintptr(setup()), uintptr(setup())) }() <-done -- cgit v1.3 From 4ffc2bc533ed39b3cbb343ad5873105bfd58ff10 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 17 Sep 2020 07:46:01 +0700 Subject: cmd/compile: rename retsigerr to sigerr retsigerr was used to create error message for both wrong function arguments and return arguments, so change its name to sigerr to reflect that. While at it, also add documentation for the wrong function arguments case. Passes toolstash-check. Change-Id: I740c717ad38d4afab9e8c20f2e94579c8bca67ff Reviewed-on: https://go-review.googlesource.com/c/go/+/255240 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/typecheck.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index faa13d72f9..cbfaa3073e 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2711,7 +2711,7 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string { return "" } } - return fmt.Sprintf("\n\thave %s\n\twant %v", nl.retsigerr(isddd), tstruct) + return fmt.Sprintf("\n\thave %s\n\twant %v", nl.sigerr(isddd), tstruct) } // sigrepr is a type's representation to the outside world, @@ -2735,9 +2735,8 @@ func sigrepr(t *types.Type) string { return t.String() } -// retsigerr returns the signature of the types -// at the respective return call site of a function. -func (nl Nodes) retsigerr(isddd bool) string { +// sigerr returns the signature of the types at the call or return. +func (nl Nodes) sigerr(isddd bool) string { if nl.Len() < 1 { return "()" } -- cgit v1.3 From df73945fd2fa0b7c168a042e87e648fdfdfc2c70 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 17 Sep 2020 07:54:01 +0700 Subject: cmd/compile: make error message involving variadic calls clearer Fixes #41440 Change-Id: I2fbac72ae3b76bca32cdeaee678a19af3595d116 Reviewed-on: https://go-review.googlesource.com/c/go/+/255241 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/typecheck.go | 20 ++++++++++++-------- test/fixedbugs/issue41440.go | 14 ++++++++++++++ test/fixedbugs/issue6750.go | 2 +- 3 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 test/fixedbugs/issue41440.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index cbfaa3073e..2654177c25 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2717,7 +2717,7 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string { // sigrepr is a type's representation to the outside world, // in string representations of return signatures // e.g in error messages about wrong arguments to return. -func sigrepr(t *types.Type) string { +func sigrepr(t *types.Type, isddd bool) string { switch t { case types.Idealstring: return "string" @@ -2732,6 +2732,13 @@ func sigrepr(t *types.Type) string { return "number" } + // Turn []T... argument to ...T for clearer error message. + if isddd { + if !t.IsSlice() { + Fatalf("bad type for ... argument: %v", t) + } + return "..." + t.Elem().String() + } return t.String() } @@ -2742,15 +2749,12 @@ func (nl Nodes) sigerr(isddd bool) string { } var typeStrings []string - for _, n := range nl.Slice() { - typeStrings = append(typeStrings, sigrepr(n.Type)) + for i, n := range nl.Slice() { + isdddArg := isddd && i == nl.Len()-1 + typeStrings = append(typeStrings, sigrepr(n.Type, isdddArg)) } - ddd := "" - if isddd { - ddd = "..." - } - return fmt.Sprintf("(%s%s)", strings.Join(typeStrings, ", "), ddd) + return fmt.Sprintf("(%s)", strings.Join(typeStrings, ", ")) } // type check composite diff --git a/test/fixedbugs/issue41440.go b/test/fixedbugs/issue41440.go new file mode 100644 index 0000000000..2b441db803 --- /dev/null +++ b/test/fixedbugs/issue41440.go @@ -0,0 +1,14 @@ +// errorcheck + +// Copyright 2020 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 p + +func f(...int) {} + +func g() { + var x []int + f(x, x...) // ERROR "have \(\[\]int, \.\.\.int\)" +} diff --git a/test/fixedbugs/issue6750.go b/test/fixedbugs/issue6750.go index dbbb454435..f62a85009c 100644 --- a/test/fixedbugs/issue6750.go +++ b/test/fixedbugs/issue6750.go @@ -18,5 +18,5 @@ func printmany(nums ...int) { func main() { printmany(1, 2, 3) printmany([]int{1, 2, 3}...) - printmany(1, "abc", []int{2, 3}...) // ERROR "too many arguments in call to printmany\n\thave \(number, string, \[\]int\.\.\.\)\n\twant \(...int\)" + printmany(1, "abc", []int{2, 3}...) // ERROR "too many arguments in call to printmany\n\thave \(number, string, \.\.\.int\)\n\twant \(...int\)" } -- cgit v1.3 From 06f7e655d1764f5ad57bc14f82326c181c37901c Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 18 Sep 2020 13:57:04 +0700 Subject: cmd/compile: refactoring mixing untyped type logic defaultlit2 and typecheck use the same logic for getting mixing untyped type, so move that logic to a function. This is a followup of CL 255217. Passes toolstash-check. Change-Id: Ic0eadb7ed27a2f0f72e2d28fd5438500bf4c79e0 Reviewed-on: https://go-review.googlesource.com/c/go/+/255897 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/const.go | 15 +++++++++------ src/cmd/compile/internal/gc/typecheck.go | 13 +------------ 2 files changed, 10 insertions(+), 18 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index 399d0148bb..c0ed8192d9 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -1072,12 +1072,7 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) { return l, r } - nn := l - if ctype(r.Type) > ctype(l.Type) { - nn = r - } - - t := defaultType(nn.Type) + t := defaultType(mixUntyped(l.Type, r.Type)) l = convlit(l, t) r = convlit(r, t) return l, r @@ -1102,6 +1097,14 @@ func ctype(t *types.Type) Ctype { panic("unreachable") } +func mixUntyped(t1, t2 *types.Type) *types.Type { + t := t1 + if ctype(t2) > ctype(t1) { + t = t2 + } + return t +} + func defaultType(t *types.Type) *types.Type { if !t.IsUntyped() || t.Etype == TNIL { return t diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 2654177c25..12c99bf48f 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -717,18 +717,7 @@ func typecheck1(n *Node, top int) (res *Node) { } if t.Etype == TIDEAL { - switch { - case l.Type == types.Idealcomplex || r.Type == types.Idealcomplex: - t = types.Idealcomplex - case l.Type == types.Idealfloat || r.Type == types.Idealfloat: - t = types.Idealfloat - case l.Type == types.Idealrune || r.Type == types.Idealrune: - t = types.Idealrune - case l.Type == types.Idealint || r.Type == types.Idealint: - t = types.Idealint - default: - Fatalf("bad untyped type: %v", t) - } + t = mixUntyped(l.Type, r.Type) } if dt := defaultType(t); !okfor[op][dt.Etype] { yyerror("invalid operation: %v (operator %v not defined on %v)", n, op, t) -- cgit v1.3 From 65dfe4a772a4bc612219d93886e5c07290785ee6 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 15 Jul 2020 09:07:00 -0400 Subject: cmd/compile: generate late-lowering static calls This is testing for the limited case of zero or one SSA-able results. One regression is that the later expansion of "Dereference" into Move into argument slots thwarts the MOVE A -> B, MOVE B -> C replaced-by MOVE A -> B, MOVE A -> C optimization; the second move is written instead as a Dereference at the phase where the optimization occurs, and because the target of the dereference is not visible in the dereference, it's not possible to verify that A and B or A and C don't overlap in some peculiar way (and for results fed to args, they can). Regression is repaired in a later CL by changing when calls are expanded. Change-Id: Ia0f48a9d483d5a54a99657a24b12b25b8edde55f Reviewed-on: https://go-review.googlesource.com/c/go/+/242782 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 94 ++++++++++++++++++++++++++++------ src/cmd/compile/internal/ssa/op.go | 5 ++ src/cmd/compile/internal/types/type.go | 6 +-- 3 files changed, 87 insertions(+), 18 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index c59945f206..0bd87beb84 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -10,6 +10,7 @@ import ( "html" "os" "sort" + "strings" "bufio" "bytes" @@ -651,6 +652,8 @@ type state struct { lastDeferExit *ssa.Block // Entry block of last defer exit code we generated lastDeferFinalBlock *ssa.Block // Final block of last defer exit code we generated lastDeferCount int // Number of defers encountered at that point + + prevCall *ssa.Value // the previous call; use this to tie results to the call op. } type funcLine struct { @@ -1076,7 +1079,7 @@ func (s *state) stmt(n *Node) { fallthrough case OCALLMETH, OCALLINTER: - s.callAddr(n, callNormal) + s.callResult(n, callNormal) if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class() == PFUNC { if fn := n.Left.Sym.Name; compiling_runtime && fn == "throw" || n.Left.Sym.Pkg == Runtimepkg && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap") { @@ -1108,10 +1111,10 @@ func (s *state) stmt(n *Node) { if n.Esc == EscNever { d = callDeferStack } - s.callAddr(n.Left, d) + s.callResult(n.Left, d) } case OGO: - s.callAddr(n.Left, callGo) + s.callResult(n.Left, callGo) case OAS2DOTTYPE: res, resok := s.dottype(n.Right, true) @@ -4340,6 +4343,7 @@ func (s *state) callAddr(n *Node, k callKind) *ssa.Value { // Calls the function n using the specified call type. // Returns the address of the return value (or nil if none). func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { + s.prevCall = nil var sym *types.Sym // target symbol (if static) var closure *ssa.Value // ptr to closure to run (if dynamic) var codeptr *ssa.Value // ptr to target code (if dynamic) @@ -4347,6 +4351,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { fn := n.Left var ACArgs []ssa.Param var ACResults []ssa.Param + var callArgs []*ssa.Value res := n.Left.Type.Results() if k == callNormal { nf := res.NumFields() @@ -4356,10 +4361,15 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { } } + testLateExpansion := false + switch n.Op { case OCALLFUNC: if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC { sym = fn.Sym + if !returnResultAddr && strings.Contains(sym.Name, "testLateExpansion") { + testLateExpansion = true + } break } closure = s.expr(fn) @@ -4374,6 +4384,9 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { } if k == callNormal { sym = fn.Sym + if !returnResultAddr && strings.Contains(sym.Name, "testLateExpansion") { + testLateExpansion = true + } break } closure = s.getMethodClosure(fn) @@ -4470,12 +4483,20 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { if k != callNormal { // Write argsize and closure (args to newproc/deferproc). argsize := s.constInt32(types.Types[TUINT32], int32(stksize)) - addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart) ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINT32], Offset: int32(argStart)}) - s.store(types.Types[TUINT32], addr, argsize) - addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr)) + if testLateExpansion { + callArgs = append(callArgs, argsize) + } else { + addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart) + s.store(types.Types[TUINT32], addr, argsize) + } ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart) + int32(Widthptr)}) - s.store(types.Types[TUINTPTR], addr, closure) + if testLateExpansion { + callArgs = append(callArgs, closure) + } else { + addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr)) + s.store(types.Types[TUINTPTR], addr, closure) + } stksize += 2 * int64(Widthptr) argStart += 2 * int64(Widthptr) } @@ -4484,7 +4505,11 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { if rcvr != nil { addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart) ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)}) - s.store(types.Types[TUINTPTR], addr, rcvr) + if testLateExpansion { + callArgs = append(callArgs, rcvr) + } else { + s.store(types.Types[TUINTPTR], addr, rcvr) + } } // Write args. @@ -4492,14 +4517,20 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { args := n.Rlist.Slice() if n.Op == OCALLMETH { f := t.Recv() - ACArgs = append(ACArgs, s.storeArg(args[0], f.Type, argStart+f.Offset)) + ACArg, arg := s.putArg(args[0], f.Type, argStart+f.Offset, testLateExpansion) + ACArgs = append(ACArgs, ACArg) + callArgs = append(callArgs, arg) args = args[1:] } for i, n := range args { f := t.Params().Field(i) - ACArgs = append(ACArgs, s.storeArg(n, f.Type, argStart+f.Offset)) + ACArg, arg := s.putArg(n, f.Type, argStart+f.Offset, testLateExpansion) + ACArgs = append(ACArgs, ACArg) + callArgs = append(callArgs, arg) } + callArgs = append(callArgs, s.mem()) + // call target switch { case k == callDefer: @@ -4517,13 +4548,29 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { case codeptr != nil: call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem()) case sym != nil: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem()) + if testLateExpansion { + var tys []*types.Type + aux := ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults) + for i := int64(0); i < aux.NResults(); i++ { + tys = append(tys, aux.TypeOfResult(i)) + } + tys = append(tys, types.TypeMem) + call = s.newValue0A(ssa.OpStaticLECall, types.NewResults(tys), aux) + call.AddArgs(callArgs...) + } else { + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem()) + } default: s.Fatalf("bad call type %v %v", n.Op, n) } call.AuxInt = stksize // Call operations carry the argsize of the callee along with them } - s.vars[&memVar] = call + if testLateExpansion { + s.prevCall = call + s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call) + } else { + s.vars[&memVar] = call + } // Insert OVARLIVE nodes s.stmtList(n.Nbody) @@ -4551,6 +4598,10 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { if returnResultAddr { return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()) } + + if testLateExpansion { + return s.newValue1I(ssa.OpSelectN, fp.Type, 0, call) + } return s.load(n.Type, s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize())) } @@ -4939,6 +4990,7 @@ func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value { // The call is added to the end of the current block. // If returns is false, the block is marked as an exit block. func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value { + s.prevCall = nil // Write args to the stack off := Ctxt.FixedFrameSize() var ACArgs []ssa.Param @@ -5098,9 +5150,21 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) { } } -func (s *state) storeArg(n *Node, t *types.Type, off int64) ssa.Param { - s.storeArgWithBase(n, t, s.sp, off) - return ssa.Param{Type: t, Offset: int32(off)} +// putArg evaluates n for the purpose of passing it as an argument to a function and returns the corresponding Param for the call. +// If forLateExpandedCall is true, it returns the argument value to pass to the call operation. +// If forLateExpandedCall is false, then the value is stored at the specified stack offset, and the returned value is nil. +func (s *state) putArg(n *Node, t *types.Type, off int64, forLateExpandedCall bool) (ssa.Param, *ssa.Value) { + var a *ssa.Value + if forLateExpandedCall { + if !canSSAType(t) { + a = s.newValue2(ssa.OpDereference, t, s.addr(n), s.mem()) + } else { + a = s.expr(n) + } + } else { + s.storeArgWithBase(n, t, s.sp, off) + } + return ssa.Param{Type: t, Offset: int32(off)}, a } func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) { diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index b8f80f7ea4..1ab53cf285 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -83,22 +83,27 @@ type AuxCall struct { func (a *AuxCall) OffsetOfResult(which int64) int64 { return int64(a.results[which].Offset) } + // OffsetOfArg returns the SP offset of argument which (indexed 0, 1, etc). func (a *AuxCall) OffsetOfArg(which int64) int64 { return int64(a.args[which].Offset) } + // TypeOfResult returns the type of result which (indexed 0, 1, etc). func (a *AuxCall) TypeOfResult(which int64) *types.Type { return a.results[which].Type } + // TypeOfArg returns the type of argument which (indexed 0, 1, etc). func (a *AuxCall) TypeOfArg(which int64) *types.Type { return a.args[which].Type } + // SizeOfResult returns the size of result which (indexed 0, 1, etc). func (a *AuxCall) SizeOfResult(which int64) int64 { return a.TypeOfResult(which).Width } + // SizeOfArg returns the size of argument which (indexed 0, 1, etc). func (a *AuxCall) SizeOfArg(which int64) int64 { return a.TypeOfArg(which).Width diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 9b05aef429..5d1d5d4008 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -68,7 +68,7 @@ const ( // SSA backend types TSSA // internal types used by SSA backend (flags, memory, etc.) TTUPLE // a pair of types, used by SSA backend - TRESULTS // multiuple types; the resulting of calling a function or method, plus a memory at the end. + TRESULTS // multiple types; the result of calling a function or method, with a memory at the end. NTYPE ) @@ -331,9 +331,9 @@ type Tuple struct { // Any tuple with a memory type must put that memory type second. } +// Results are the output from calls that will be late-expanded. type Results struct { - Types []*Type - // Any Results with a memory type must put that memory type last. + Types []*Type // Last element is memory output from call. } // Array contains Type fields specific to array types. -- cgit v1.3 From 23573d0ea225d4b93ccd2b946b1de121c3a6cee5 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 21 Sep 2020 12:00:24 +0700 Subject: cmd/compile: clearer error when non-bool used as "||" and "&&" operand Fixes #41500 Change-Id: I658d8921b7769b6e4288ca781cbdca5ff14a84ee Reviewed-on: https://go-review.googlesource.com/c/go/+/255899 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/typecheck.go | 16 ++++++++++++++++ test/fixedbugs/issue41500.go | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 test/fixedbugs/issue41500.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 12c99bf48f..2c445567de 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -630,6 +630,22 @@ func typecheck1(n *Node, top int) (res *Node) { break } + // For "x == x && len(s)", it's better to report that "len(s)" (type int) + // can't be used with "&&" than to report that "x == x" (type untyped bool) + // can't be converted to int (see issue #41500). + if n.Op == OANDAND || n.Op == OOROR { + if !n.Left.Type.IsBoolean() { + yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Left.Type)) + n.Type = nil + return n + } + if !n.Right.Type.IsBoolean() { + yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Right.Type)) + n.Type = nil + return n + } + } + // ideal mixed with non-ideal l, r = defaultlit2(l, r, false) diff --git a/test/fixedbugs/issue41500.go b/test/fixedbugs/issue41500.go new file mode 100644 index 0000000000..d1e4efc8fd --- /dev/null +++ b/test/fixedbugs/issue41500.go @@ -0,0 +1,20 @@ +// errorcheck + +// Copyright 2020 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 p + +type s struct { + slice []int +} + +func f() { + var x *s + + _ = x == nil || len(x.slice) // ERROR "invalid operation: .+ \(operator \|\| not defined on int\)" + _ = len(x.slice) || x == nil // ERROR "invalid operation: .+ \(operator \|\| not defined on int\)" + _ = x == nil && len(x.slice) // ERROR "invalid operation: .+ \(operator && not defined on int\)" + _ = len(x.slice) && x == nil // ERROR "invalid operation: .+ \(operator && not defined on int\)" +} -- cgit v1.3 From 754776850a62fa37ff6d68a8d5250497b2869b26 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 22 Sep 2020 12:43:16 +0700 Subject: cmd/compile: consistently use typekind when reporting invalid operation While at it, make "typekind" awares of "types.Ideal*" types. Passes toolstash-check. Change-Id: I092fa8c57ab6b8d9ba0f25d8e1ea44fba48675e5 Reviewed-on: https://go-review.googlesource.com/c/go/+/256438 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le Reviewed-by: Matthew Dempsky TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/typecheck.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 2c445567de..8e87fc9df0 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -151,8 +151,8 @@ var _typekind = []string{ } func typekind(t *types.Type) string { - if t.IsSlice() { - return "slice" + if t.IsUntyped() { + return fmt.Sprintf("%v", t) } et := t.Etype if int(et) < len(_typekind) { @@ -736,7 +736,7 @@ func typecheck1(n *Node, top int) (res *Node) { t = mixUntyped(l.Type, r.Type) } if dt := defaultType(t); !okfor[op][dt.Etype] { - yyerror("invalid operation: %v (operator %v not defined on %v)", n, op, t) + yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t)) n.Type = nil return n } -- cgit v1.3 From 4d7abd7ae6711d1bc453da368cbf153b0f42a211 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 27 Jul 2020 16:40:35 -0400 Subject: cmd/compile: enable late call expansion for multiple results This does not work yet for SSA-able aggregates. Change-Id: Ib16b9c6158b25bb957145c5f934040b2bab9babd Reviewed-on: https://go-review.googlesource.com/c/go/+/245132 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 31 +++++++++++++++++++++++++--- src/cmd/compile/internal/ssa/expand_calls.go | 1 - src/cmd/compile/internal/ssa/op.go | 13 ++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 0bd87beb84..d0b3e8df94 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -2557,8 +2557,23 @@ func (s *state) expr(n *Node) *ssa.Value { return s.addr(n.Left) case ORESULT: - addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset) - return s.load(n.Type, addr) + if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall { + // Do the old thing + addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset) + return s.load(n.Type, addr) + } + which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset) + if which == -1 { + // Do the old thing // TODO: Panic instead. + addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset) + return s.load(n.Type, addr) + } + if canSSAType(n.Type) { + return s.newValue1I(ssa.OpSelectN, n.Type, which, s.prevCall) + } else { + addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(n.Type), which, s.prevCall) + return s.load(n.Type, addr) + } case ODEREF: p := s.exprPtr(n.Left, n.Bounded(), n.Pos) @@ -4700,7 +4715,17 @@ func (s *state) addr(n *Node) *ssa.Value { } case ORESULT: // load return from callee - return s.constOffPtrSP(t, n.Xoffset) + if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall { + return s.constOffPtrSP(t, n.Xoffset) + } + which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset) + if which == -1 { + // Do the old thing // TODO: Panic instead. + return s.constOffPtrSP(t, n.Xoffset) + } + x := s.newValue1I(ssa.OpSelectNAddr, t, which, s.prevCall) + return x + case OINDEX: if n.Left.Type.IsSlice() { a := s.expr(n.Left) diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 13c7f532d6..34cff51c00 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -98,4 +98,3 @@ func expandCalls(f *Func) { } } } - diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 1ab53cf285..b22b095401 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -79,6 +79,19 @@ type AuxCall struct { results []Param } +// ResultForOffset returns the index of the result at a particular offset among the results +// This does not include the mem result for the call opcode. +func (a *AuxCall) ResultForOffset(offset int64) int64 { + which := int64(-1) + for i := int64(0); i < a.NResults(); i++ { // note aux NResults does not include mem result. + if a.OffsetOfResult(i) == offset { + which = i + break + } + } + return which +} + // OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc). func (a *AuxCall) OffsetOfResult(which int64) int64 { return int64(a.results[which].Offset) -- cgit v1.3 From d54a9a9c42e751a020308cae296add426b56d0f0 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Tue, 25 Aug 2020 16:33:50 +0800 Subject: math/big: replace division with multiplication by reciprocal word MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Division is much slower than multiplication. And the method of using multiplication by multiplying reciprocal and replacing division with it can increase the speed of divWVW algorithm by three times,and at the same time increase the speed of nats division. The benchmark test on arm64 is as follows: name old time/op new time/op delta DivWVW/1-4 13.1ns ± 4% 13.3ns ± 4% ~ (p=0.444 n=5+5) DivWVW/2-4 48.6ns ± 1% 51.2ns ± 2% +5.39% (p=0.008 n=5+5) DivWVW/3-4 82.0ns ± 1% 69.7ns ± 1% -15.03% (p=0.008 n=5+5) DivWVW/4-4 116ns ± 1% 71ns ± 2% -38.88% (p=0.008 n=5+5) DivWVW/5-4 152ns ± 1% 84ns ± 4% -44.70% (p=0.008 n=5+5) DivWVW/10-4 319ns ± 1% 155ns ± 4% -51.50% (p=0.008 n=5+5) DivWVW/100-4 3.44µs ± 3% 1.30µs ± 8% -62.30% (p=0.008 n=5+5) DivWVW/1000-4 33.8µs ± 0% 10.9µs ± 1% -67.74% (p=0.008 n=5+5) DivWVW/10000-4 343µs ± 4% 111µs ± 5% -67.63% (p=0.008 n=5+5) DivWVW/100000-4 3.35ms ± 1% 1.25ms ± 3% -62.79% (p=0.008 n=5+5) QuoRem-4 3.08µs ± 2% 2.21µs ± 4% -28.40% (p=0.008 n=5+5) ModSqrt225_Tonelli-4 444µs ± 2% 457µs ± 3% ~ (p=0.095 n=5+5) ModSqrt225_3Mod4-4 136µs ± 1% 138µs ± 3% ~ (p=0.151 n=5+5) ModSqrt231_Tonelli-4 473µs ± 3% 483µs ± 4% ~ (p=0.548 n=5+5) ModSqrt231_5Mod8-4 164µs ± 9% 169µs ±12% ~ (p=0.421 n=5+5) Sqrt-4 36.8µs ± 1% 28.6µs ± 0% -22.17% (p=0.016 n=5+4) Div/20/10-4 50.0ns ± 3% 51.3ns ± 6% ~ (p=0.238 n=5+5) Div/40/20-4 49.8ns ± 2% 51.3ns ± 6% ~ (p=0.222 n=5+5) Div/100/50-4 85.8ns ± 4% 86.5ns ± 5% ~ (p=0.246 n=5+5) Div/200/100-4 335ns ± 3% 296ns ± 2% -11.60% (p=0.008 n=5+5) Div/400/200-4 442ns ± 2% 359ns ± 5% -18.81% (p=0.008 n=5+5) Div/1000/500-4 858ns ± 3% 643ns ± 6% -25.06% (p=0.008 n=5+5) Div/2000/1000-4 1.70µs ± 3% 1.28µs ± 4% -24.80% (p=0.008 n=5+5) Div/20000/10000-4 45.0µs ± 5% 41.8µs ± 4% -7.17% (p=0.016 n=5+5) Div/200000/100000-4 1.51ms ± 7% 1.43ms ± 3% -5.42% (p=0.016 n=5+5) Div/2000000/1000000-4 57.6ms ± 4% 57.5ms ± 3% ~ (p=1.000 n=5+5) Div/20000000/10000000-4 2.08s ± 3% 2.04s ± 1% ~ (p=0.095 n=5+5) name old speed new speed delta DivWVW/1-4 4.87GB/s ± 4% 4.80GB/s ± 4% ~ (p=0.310 n=5+5) DivWVW/2-4 2.63GB/s ± 1% 2.50GB/s ± 2% -5.07% (p=0.008 n=5+5) DivWVW/3-4 2.34GB/s ± 1% 2.76GB/s ± 1% +17.70% (p=0.008 n=5+5) DivWVW/4-4 2.21GB/s ± 1% 3.61GB/s ± 2% +63.42% (p=0.008 n=5+5) DivWVW/5-4 2.10GB/s ± 2% 3.81GB/s ± 4% +80.89% (p=0.008 n=5+5) DivWVW/10-4 2.01GB/s ± 0% 4.13GB/s ± 4% +105.91% (p=0.008 n=5+5) DivWVW/100-4 1.86GB/s ± 2% 4.95GB/s ± 7% +165.63% (p=0.008 n=5+5) DivWVW/1000-4 1.89GB/s ± 0% 5.86GB/s ± 1% +209.96% (p=0.008 n=5+5) DivWVW/10000-4 1.87GB/s ± 4% 5.76GB/s ± 5% +208.96% (p=0.008 n=5+5) DivWVW/100000-4 1.91GB/s ± 1% 5.14GB/s ± 3% +168.85% (p=0.008 n=5+5) Change-Id: I049f1196562b20800e6ef8a6493fd147f93ad830 Reviewed-on: https://go-review.googlesource.com/c/go/+/250417 Trust: Giovanni Bajo Trust: Keith Randall Run-TryBot: Giovanni Bajo TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/ssa.go | 5 --- src/math/big/arith.go | 89 ++++++++++++++++++++++++++++++++++---- src/math/big/arith_386.s | 27 ------------ src/math/big/arith_amd64.s | 26 ----------- src/math/big/arith_arm.s | 11 ----- src/math/big/arith_arm64.s | 9 +--- src/math/big/arith_decl.go | 2 - src/math/big/arith_decl_pure.go | 8 ---- src/math/big/arith_mips64x.s | 5 --- src/math/big/arith_mipsx.s | 5 --- src/math/big/arith_ppc64x.s | 40 ----------------- src/math/big/arith_riscv64.s | 5 --- src/math/big/arith_s390x.s | 33 -------------- src/math/big/arith_test.go | 54 ++++++++++++++++++++++- src/math/big/arith_wasm.s | 5 --- src/math/big/nat.go | 3 +- 16 files changed, 136 insertions(+), 191 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index d0b3e8df94..815ff7f99f 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4022,11 +4022,6 @@ func init() { return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1]) }, sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64LE, sys.ArchPPC64, sys.ArchS390X) - add("math/big", "divWW", - func(s *state, n *Node, args []*ssa.Value) *ssa.Value { - return s.newValue3(ssa.OpDiv128u, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1], args[2]) - }, - sys.ArchAMD64) } // findIntrinsic returns a function which builds the SSA equivalent of the diff --git a/src/math/big/arith.go b/src/math/big/arith.go index b0885f261f..750ce8aa39 100644 --- a/src/math/big/arith.go +++ b/src/math/big/arith.go @@ -60,12 +60,6 @@ func nlz(x Word) uint { return uint(bits.LeadingZeros(uint(x))) } -// q = (u1<<_W + u0 - r)/v -func divWW_g(u1, u0, v Word) (q, r Word) { - qq, rr := bits.Div(uint(u1), uint(u0), uint(v)) - return Word(qq), Word(rr) -} - // The resulting carry c is either 0 or 1. func addVV_g(z, x, y []Word) (c Word) { // The comment near the top of this file discusses this for loop condition. @@ -207,10 +201,87 @@ func addMulVVW_g(z, x []Word, y Word) (c Word) { return } -func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) { +// q = ( x1 << _W + x0 - r)/y. m = floor(( _B^2 - 1 ) / d - _B). Requiring x1>(_W-s) + x0 <<= s + y <<= s + } + d := uint(y) + // We know that + // m = ⎣(B^2-1)/d⎦-B + // ⎣(B^2-1)/d⎦ = m+B + // (B^2-1)/d = m+B+delta1 0 <= delta1 <= (d-1)/d + // B^2/d = m+B+delta2 0 <= delta2 <= 1 + // The quotient we're trying to compute is + // quotient = ⎣(x1*B+x0)/d⎦ + // = ⎣(x1*B*(B^2/d)+x0*(B^2/d))/B^2⎦ + // = ⎣(x1*B*(m+B+delta2)+x0*(m+B+delta2))/B^2⎦ + // = ⎣(x1*m+x1*B+x0)/B + x0*m/B^2 + delta2*(x1*B+x0)/B^2⎦ + // The latter two terms of this three-term sum are between 0 and 1. + // So we can compute just the first term, and we will be low by at most 2. + t1, t0 := bits.Mul(uint(m), uint(x1)) + _, c := bits.Add(t0, uint(x0), 0) + t1, _ = bits.Add(t1, uint(x1), c) + // The quotient is either t1, t1+1, or t1+2. + // We'll try t1 and adjust if needed. + qq := t1 + // compute remainder r=x-d*q. + dq1, dq0 := bits.Mul(d, qq) + r0, b := bits.Sub(uint(x0), dq0, 0) + r1, _ := bits.Sub(uint(x1), dq1, b) + // The remainder we just computed is bounded above by B+d: + // r = x1*B + x0 - d*q. + // = x1*B + x0 - d*⎣(x1*m+x1*B+x0)/B⎦ + // = x1*B + x0 - d*((x1*m+x1*B+x0)/B-alpha) 0 <= alpha < 1 + // = x1*B + x0 - x1*d/B*m - x1*d - x0*d/B + d*alpha + // = x1*B + x0 - x1*d/B*⎣(B^2-1)/d-B⎦ - x1*d - x0*d/B + d*alpha + // = x1*B + x0 - x1*d/B*⎣(B^2-1)/d-B⎦ - x1*d - x0*d/B + d*alpha + // = x1*B + x0 - x1*d/B*((B^2-1)/d-B-beta) - x1*d - x0*d/B + d*alpha 0 <= beta < 1 + // = x1*B + x0 - x1*B + x1/B + x1*d + x1*d/B*beta - x1*d - x0*d/B + d*alpha + // = x0 + x1/B + x1*d/B*beta - x0*d/B + d*alpha + // = x0*(1-d/B) + x1*(1+d*beta)/B + d*alpha + // < B*(1-d/B) + d*B/B + d because x00), x1= d { + qq++ + r0 -= d + } + return Word(qq), Word(r0 >> s) +} + +func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) { r = xn + if len(x) == 1 { + qq, rr := bits.Div(uint(r), uint(x[0]), uint(y)) + z[0] = Word(qq) + return Word(rr) + } + rec := reciprocalWord(y) for i := len(z) - 1; i >= 0; i-- { - z[i], r = divWW_g(r, x[i], y) + z[i], r = divWW(r, x[i], y, rec) } - return + return r +} + +// reciprocalWord return the reciprocal of the divisor. rec = floor(( _B^2 - 1 ) / u - _B). u = d1 << nlz(d1). +func reciprocalWord(d1 Word) Word { + u := uint(d1 << nlz(d1)) + x1 := ^u + x0 := uint(_M) + rec, _ := bits.Div(x1, x0, u) // (_B^2-1)/U-_B = (_B*(_M-C)+_M)/U + return Word(rec) } diff --git a/src/math/big/arith_386.s b/src/math/big/arith_386.s index f61da2aba7..d0ea949fe6 100644 --- a/src/math/big/arith_386.s +++ b/src/math/big/arith_386.s @@ -18,16 +18,6 @@ TEXT ·mulWW(SB),NOSPLIT,$0 RET -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),NOSPLIT,$0 - MOVL x1+0(FP), DX - MOVL x0+4(FP), AX - DIVL y+8(FP) - MOVL AX, q+12(FP) - MOVL DX, r+16(FP) - RET - - // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 MOVL z+0(FP), DI @@ -251,21 +241,4 @@ E6: CMPL BX, $0 // i < 0 RET -// func divWVW(z* Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),NOSPLIT,$0 - MOVL z+0(FP), DI - MOVL xn+12(FP), DX // r = xn - MOVL x+16(FP), SI - MOVL y+28(FP), CX - MOVL z_len+4(FP), BX // i = z - JMP E7 -L7: MOVL (SI)(BX*4), AX - DIVL CX - MOVL AX, (DI)(BX*4) - -E7: SUBL $1, BX // i-- - JGE L7 // i >= 0 - - MOVL DX, r+32(FP) - RET diff --git a/src/math/big/arith_amd64.s b/src/math/big/arith_amd64.s index b75639f540..61043ca2d9 100644 --- a/src/math/big/arith_amd64.s +++ b/src/math/big/arith_amd64.s @@ -18,14 +18,6 @@ TEXT ·mulWW(SB),NOSPLIT,$0 RET -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),NOSPLIT,$0 - MOVQ x1+0(FP), DX - MOVQ x0+8(FP), AX - DIVQ y+16(FP) - MOVQ AX, q+24(FP) - MOVQ DX, r+32(FP) - RET // The carry bit is saved with SBBQ Rx, Rx: if the carry was set, Rx is -1, otherwise it is 0. // It is restored with ADDQ Rx, Rx: if Rx was -1 the carry is set, otherwise it is cleared. @@ -531,21 +523,3 @@ adx_short: -// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),NOSPLIT,$0 - MOVQ z+0(FP), R10 - MOVQ xn+24(FP), DX // r = xn - MOVQ x+32(FP), R8 - MOVQ y+56(FP), R9 - MOVQ z_len+8(FP), BX // i = z - JMP E7 - -L7: MOVQ (R8)(BX*8), AX - DIVQ R9 - MOVQ AX, (R10)(BX*8) - -E7: SUBQ $1, BX // i-- - JGE L7 // i >= 0 - - MOVQ DX, r+64(FP) - RET diff --git a/src/math/big/arith_arm.s b/src/math/big/arith_arm.s index 33aa36f709..cbf7445e7a 100644 --- a/src/math/big/arith_arm.s +++ b/src/math/big/arith_arm.s @@ -272,17 +272,6 @@ E9: RET -// func divWVW(z* Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),NOSPLIT,$0 - // ARM has no multiword division, so use portable code. - B ·divWVW_g(SB) - - -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),NOSPLIT,$0 - // ARM has no multiword division, so use portable code. - B ·divWW_g(SB) - // func mulWW(x, y Word) (z1, z0 Word) TEXT ·mulWW(SB),NOSPLIT,$0 diff --git a/src/math/big/arith_arm64.s b/src/math/big/arith_arm64.s index da6e408e19..22357d088e 100644 --- a/src/math/big/arith_arm64.s +++ b/src/math/big/arith_arm64.s @@ -23,11 +23,6 @@ TEXT ·mulWW(SB),NOSPLIT,$0 RET -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),NOSPLIT,$0 - B ·divWW_g(SB) // ARM64 has no multiword division - - // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 MOVD z_len+8(FP), R0 @@ -585,6 +580,4 @@ done: MOVD R4, c+56(FP) RET -// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),NOSPLIT,$0 - B ·divWVW_g(SB) + diff --git a/src/math/big/arith_decl.go b/src/math/big/arith_decl.go index 41e592334c..d519bdc87b 100644 --- a/src/math/big/arith_decl.go +++ b/src/math/big/arith_decl.go @@ -8,7 +8,6 @@ package big // implemented in arith_$GOARCH.s func mulWW(x, y Word) (z1, z0 Word) -func divWW(x1, x0, y Word) (q, r Word) func addVV(z, x, y []Word) (c Word) func subVV(z, x, y []Word) (c Word) func addVW(z, x []Word, y Word) (c Word) @@ -17,4 +16,3 @@ func shlVU(z, x []Word, s uint) (c Word) func shrVU(z, x []Word, s uint) (c Word) func mulAddVWW(z, x []Word, y, r Word) (c Word) func addMulVVW(z, x []Word, y Word) (c Word) -func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) diff --git a/src/math/big/arith_decl_pure.go b/src/math/big/arith_decl_pure.go index 305f7ee03b..5faa3bd281 100644 --- a/src/math/big/arith_decl_pure.go +++ b/src/math/big/arith_decl_pure.go @@ -10,10 +10,6 @@ func mulWW(x, y Word) (z1, z0 Word) { return mulWW_g(x, y) } -func divWW(x1, x0, y Word) (q, r Word) { - return divWW_g(x1, x0, y) -} - func addVV(z, x, y []Word) (c Word) { return addVV_g(z, x, y) } @@ -55,7 +51,3 @@ func mulAddVWW(z, x []Word, y, r Word) (c Word) { func addMulVVW(z, x []Word, y Word) (c Word) { return addMulVVW_g(z, x, y) } - -func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) { - return divWVW_g(z, xn, x, y) -} diff --git a/src/math/big/arith_mips64x.s b/src/math/big/arith_mips64x.s index 983510ee3d..804b9fe06e 100644 --- a/src/math/big/arith_mips64x.s +++ b/src/math/big/arith_mips64x.s @@ -12,9 +12,6 @@ TEXT ·mulWW(SB),NOSPLIT,$0 JMP ·mulWW_g(SB) -TEXT ·divWW(SB),NOSPLIT,$0 - JMP ·divWW_g(SB) - TEXT ·addVV(SB),NOSPLIT,$0 JMP ·addVV_g(SB) @@ -39,5 +36,3 @@ TEXT ·mulAddVWW(SB),NOSPLIT,$0 TEXT ·addMulVVW(SB),NOSPLIT,$0 JMP ·addMulVVW_g(SB) -TEXT ·divWVW(SB),NOSPLIT,$0 - JMP ·divWVW_g(SB) diff --git a/src/math/big/arith_mipsx.s b/src/math/big/arith_mipsx.s index 54cafbd9c0..efdecb80f3 100644 --- a/src/math/big/arith_mipsx.s +++ b/src/math/big/arith_mipsx.s @@ -12,9 +12,6 @@ TEXT ·mulWW(SB),NOSPLIT,$0 JMP ·mulWW_g(SB) -TEXT ·divWW(SB),NOSPLIT,$0 - JMP ·divWW_g(SB) - TEXT ·addVV(SB),NOSPLIT,$0 JMP ·addVV_g(SB) @@ -39,5 +36,3 @@ TEXT ·mulAddVWW(SB),NOSPLIT,$0 TEXT ·addMulVVW(SB),NOSPLIT,$0 JMP ·addMulVVW_g(SB) -TEXT ·divWVW(SB),NOSPLIT,$0 - JMP ·divWVW_g(SB) diff --git a/src/math/big/arith_ppc64x.s b/src/math/big/arith_ppc64x.s index 409e10ab48..b299ccc2fb 100644 --- a/src/math/big/arith_ppc64x.s +++ b/src/math/big/arith_ppc64x.s @@ -478,44 +478,4 @@ done: MOVD R4, c+56(FP) RET -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB), NOSPLIT, $0 - MOVD x1+0(FP), R4 - MOVD x0+8(FP), R5 - MOVD y+16(FP), R6 - - CMPU R4, R6 - BGE divbigger - - // from the programmer's note in ch. 3 of the ISA manual, p.74 - DIVDEU R6, R4, R3 - DIVDU R6, R5, R7 - MULLD R6, R3, R8 - MULLD R6, R7, R20 - SUB R20, R5, R10 - ADD R7, R3, R3 - SUB R8, R10, R4 - CMPU R4, R10 - BLT adjust - CMPU R4, R6 - BLT end - -adjust: - MOVD $1, R21 - ADD R21, R3, R3 - SUB R6, R4, R4 - -end: - MOVD R3, q+24(FP) - MOVD R4, r+32(FP) - RET - -divbigger: - MOVD $-1, R7 - MOVD R7, q+24(FP) - MOVD R7, r+32(FP) - RET - -TEXT ·divWVW(SB), NOSPLIT, $0 - BR ·divWVW_g(SB) diff --git a/src/math/big/arith_riscv64.s b/src/math/big/arith_riscv64.s index 59065c3f7b..a2f7666c7b 100644 --- a/src/math/big/arith_riscv64.s +++ b/src/math/big/arith_riscv64.s @@ -19,9 +19,6 @@ TEXT ·mulWW(SB),NOSPLIT,$0 MOV X8, z0+24(FP) RET -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),NOSPLIT,$0 - JMP ·divWW_g(SB) // riscv64 has no multiword division TEXT ·addVV(SB),NOSPLIT,$0 JMP ·addVV_g(SB) @@ -47,5 +44,3 @@ TEXT ·mulAddVWW(SB),NOSPLIT,$0 TEXT ·addMulVVW(SB),NOSPLIT,$0 JMP ·addMulVVW_g(SB) -TEXT ·divWVW(SB),NOSPLIT,$0 - JMP ·divWVW_g(SB) diff --git a/src/math/big/arith_s390x.s b/src/math/big/arith_s390x.s index 4891768111..242aca7434 100644 --- a/src/math/big/arith_s390x.s +++ b/src/math/big/arith_s390x.s @@ -17,15 +17,6 @@ TEXT ·mulWW(SB), NOSPLIT, $0 MOVD R11, z0+24(FP) RET -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB), NOSPLIT, $0 - MOVD x1+0(FP), R10 - MOVD x0+8(FP), R11 - MOVD y+16(FP), R5 - WORD $0xb98700a5 // dlgr r10,r5 - MOVD R11, q+24(FP) - MOVD R10, r+32(FP) - RET // DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11 // func addVV(z, x, y []Word) (c Word) @@ -990,27 +981,3 @@ E6: MOVD R4, c+56(FP) RET -// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) -// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1(*8) , (R0 set to 0) + use R11 + use R7 for i -TEXT ·divWVW(SB), NOSPLIT, $0 - MOVD z+0(FP), R2 - MOVD xn+24(FP), R10 // r = xn - MOVD x+32(FP), R8 - MOVD y+56(FP), R9 - MOVD z_len+8(FP), R7 // i = z - SLD $3, R7, R1 // i*8 - MOVD $0, R0 // make sure it's zero - BR E7 - -L7: - MOVD (R8)(R1*1), R11 - WORD $0xB98700A9 // DLGR R10,R9 - MOVD R11, (R2)(R1*1) - -E7: - SUB $1, R7 // i-- - SUB $8, R1 - BGE L7 // i >= 0 - - MOVD R10, r+64(FP) - RET diff --git a/src/math/big/arith_test.go b/src/math/big/arith_test.go index fc205934c5..808d178459 100644 --- a/src/math/big/arith_test.go +++ b/src/math/big/arith_test.go @@ -7,6 +7,7 @@ package big import ( "fmt" "internal/testenv" + "math/bits" "math/rand" "strings" "testing" @@ -493,7 +494,6 @@ func TestFunVWW(t *testing.T) { if a.y != 0 && a.r < a.y { arg := argWVW{a.x, a.c, a.z, a.y, a.r} - testFunWVW(t, "divWVW_g", divWVW_g, arg) testFunWVW(t, "divWVW", divWVW, arg) } } @@ -536,6 +536,42 @@ func TestMulAddWWW(t *testing.T) { } } +var divWWTests = []struct { + x1, x0, y Word + q, r Word +}{ + {_M >> 1, 0, _M, _M >> 1, _M >> 1}, + {_M - (1 << (_W - 2)), _M, 3 << (_W - 2), _M, _M - (1 << (_W - 2))}, +} + +const testsNumber = 1 << 16 + +func TestDivWW(t *testing.T) { + i := 0 + for i, test := range divWWTests { + rec := reciprocalWord(test.y) + q, r := divWW(test.x1, test.x0, test.y, rec) + if q != test.q || r != test.r { + t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) + } + } + //random tests + for ; i < testsNumber; i++ { + x1 := rndW() + x0 := rndW() + y := rndW() + if x1 >= y { + continue + } + rec := reciprocalWord(y) + qGot, rGot := divWW(x1, x0, y, rec) + qWant, rWant := bits.Div(uint(x1), uint(x0), uint(y)) + if uint(qGot) != qWant || uint(rGot) != rWant { + t.Errorf("#%d got (%x, %x) want (%x, %x)", i, qGot, rGot, qWant, rWant) + } + } +} + func BenchmarkMulAddVWW(b *testing.B) { for _, n := range benchSizes { if isRaceBuilder && n > 1e3 { @@ -570,3 +606,19 @@ func BenchmarkAddMulVVW(b *testing.B) { }) } } +func BenchmarkDivWVW(b *testing.B) { + for _, n := range benchSizes { + if isRaceBuilder && n > 1e3 { + continue + } + x := rndV(n) + y := rndW() + z := make([]Word, n) + b.Run(fmt.Sprint(n), func(b *testing.B) { + b.SetBytes(int64(n * _W)) + for i := 0; i < b.N; i++ { + divWVW(z, 0, x, y) + } + }) + } +} diff --git a/src/math/big/arith_wasm.s b/src/math/big/arith_wasm.s index 382597c694..add1064469 100644 --- a/src/math/big/arith_wasm.s +++ b/src/math/big/arith_wasm.s @@ -9,9 +9,6 @@ TEXT ·mulWW(SB),NOSPLIT,$0 JMP ·mulWW_g(SB) -TEXT ·divWW(SB),NOSPLIT,$0 - JMP ·divWW_g(SB) - TEXT ·addVV(SB),NOSPLIT,$0 JMP ·addVV_g(SB) @@ -36,5 +33,3 @@ TEXT ·mulAddVWW(SB),NOSPLIT,$0 TEXT ·addMulVVW(SB),NOSPLIT,$0 JMP ·addMulVVW_g(SB) -TEXT ·divWVW(SB),NOSPLIT,$0 - JMP ·divWVW_g(SB) diff --git a/src/math/big/nat.go b/src/math/big/nat.go index 6a3989bf9d..c2f3787848 100644 --- a/src/math/big/nat.go +++ b/src/math/big/nat.go @@ -751,6 +751,7 @@ func (q nat) divBasic(u, v nat) { // D2. vn1 := v[n-1] + rec := reciprocalWord(vn1) for j := m; j >= 0; j-- { // D3. qhat := Word(_M) @@ -760,7 +761,7 @@ func (q nat) divBasic(u, v nat) { } if ujn != vn1 { var rhat Word - qhat, rhat = divWW(ujn, u[j+n-1], vn1) + qhat, rhat = divWW(ujn, u[j+n-1], vn1, rec) // x1 | x2 = q̂v_{n-2} vn2 := v[n-2] -- cgit v1.3 From e572218d1273bf54bf8cafd39f93f22de196dd55 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Sat, 26 Sep 2020 09:42:59 +0200 Subject: cmd/compile: fix escape reason for MAKESLICE with no cap When explaining why the slice from a make() call escapes for the -m -m message, we print "non-const size" if any one of Isconst(n.Left) and Isconst(n.Right) return false; but for OMAKESLICE nodes with no cap, n.Right is nil, so Isconst(n.Right, CTINT) will be always false. Only call Isconst on n.Right if it's not nil. Fixes #41635 Change-Id: I8729801a9b234b68ae40adad64d66fa7653adf09 Reviewed-on: https://go-review.googlesource.com/c/go/+/257641 Reviewed-by: Cuong Manh Le Reviewed-by: Keith Randall Trust: Alberto Donizetti --- src/cmd/compile/internal/gc/escape.go | 2 +- test/fixedbugs/issue41635.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/fixedbugs/issue41635.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index f435d8ff6a..d79d32ec48 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -1053,7 +1053,7 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation { if mustHeapAlloc(n) { why := "too large for stack" - if n.Op == OMAKESLICE && (!Isconst(n.Left, CTINT) || !Isconst(n.Right, CTINT)) { + if n.Op == OMAKESLICE && (!Isconst(n.Left, CTINT) || (n.Right != nil && !Isconst(n.Right, CTINT))) { why = "non-constant size" } e.flow(e.heapHole().addr(n, why), loc) diff --git a/test/fixedbugs/issue41635.go b/test/fixedbugs/issue41635.go new file mode 100644 index 0000000000..b33c1a07e7 --- /dev/null +++ b/test/fixedbugs/issue41635.go @@ -0,0 +1,18 @@ +//errorcheck -0 -m -m + +// Copyright 2020 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 p + +func f() { // ERROR "" + b1 := make([]byte, 1<<17) // ERROR "too large for stack" "" + b2 := make([]byte, 100, 1<<17) // ERROR "too large for stack" "" + + n, m := 100, 200 + b1 = make([]byte, n) // ERROR "non-constant size" "" + b2 = make([]byte, 100, m) // ERROR "non-constant size" "" + + _, _ = b1, b2 +} -- cgit v1.3 From a424f6e45e29960c933a7ccc1cd8fc9bb2766f15 Mon Sep 17 00:00:00 2001 From: Lynn Boger Date: Wed, 23 Sep 2020 11:06:39 -0400 Subject: cmd/asm,cmd/compile,cmd/internal/obj/ppc64: add extswsli support on power9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the extswsli instruction which combines extsw followed by a shift. New benchmark demonstrates the improvement: name old time/op new time/op delta ExtShift 1.34µs ± 0% 1.30µs ± 0% -3.15% (p=0.057 n=4+3) Change-Id: I21b410676fdf15d20e0cbbaa75d7c6dcd3bbb7b0 Reviewed-on: https://go-review.googlesource.com/c/go/+/257017 Run-TryBot: Lynn Boger TryBot-Result: Go Bot Reviewed-by: Carlos Eduardo Seo Trust: Lynn Boger --- src/cmd/asm/internal/asm/testdata/ppc64enc.s | 1 + src/cmd/compile/internal/gc/bench_test.go | 12 ++++ src/cmd/compile/internal/ppc64/ssa.go | 2 +- src/cmd/compile/internal/ssa/gen/PPC64.rules | 2 + src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 1 + src/cmd/compile/internal/ssa/opGen.go | 15 ++++ src/cmd/compile/internal/ssa/rewritePPC64.go | 36 ++++++++++ src/cmd/internal/obj/ppc64/a.out.go | 2 + src/cmd/internal/obj/ppc64/anames.go | 2 + src/cmd/internal/obj/ppc64/asm9.go | 104 +++++++++++++++++---------- test/codegen/shift.go | 7 +- 11 files changed, 142 insertions(+), 42 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/asm/internal/asm/testdata/ppc64enc.s b/src/cmd/asm/internal/asm/testdata/ppc64enc.s index e26f6f8933..88a7609ba8 100644 --- a/src/cmd/asm/internal/asm/testdata/ppc64enc.s +++ b/src/cmd/asm/internal/asm/testdata/ppc64enc.s @@ -266,6 +266,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 SRDCC R3, R4 // 7c841c37 ROTLW $16, R3, R4 // 5464803e ROTLW R3, R4, R5 // 5c85183e + EXTSWSLI $3, R4, R5 // 7c851ef4 RLWMI $7, R3, $65535, R6 // 50663c3e RLWMICC $7, R3, $65535, R6 // 50663c3f RLWNM $3, R4, $7, R6 // 54861f7e diff --git a/src/cmd/compile/internal/gc/bench_test.go b/src/cmd/compile/internal/gc/bench_test.go index 09aaf428c3..a2887f2f7b 100644 --- a/src/cmd/compile/internal/gc/bench_test.go +++ b/src/cmd/compile/internal/gc/bench_test.go @@ -20,6 +20,18 @@ func BenchmarkLoadAdd(b *testing.B) { } } +// Added for ppc64 extswsli on power9 +func BenchmarkExtShift(b *testing.B) { + x := make([]int32, 1024) + for i := 0; i < b.N; i++ { + var s int64 + for i := range x { + s ^= int64(x[i]+32) * 8 + } + globl = s + } +} + func BenchmarkModify(b *testing.B) { a := make([]int64, 1024) v := globl diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go index 4a83a0bdd7..a5fbdaffba 100644 --- a/src/cmd/compile/internal/ppc64/ssa.go +++ b/src/cmd/compile/internal/ppc64/ssa.go @@ -677,7 +677,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.From.Reg = v.Args[0].Reg() case ssa.OpPPC64ADDconst, ssa.OpPPC64ANDconst, ssa.OpPPC64ORconst, ssa.OpPPC64XORconst, - ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst: + ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst, ssa.OpPPC64EXTSWSLconst: p := s.Prog(v.Op.Asm()) p.Reg = v.Args[0].Reg() p.From.Type = obj.TYPE_CONST diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules index 774d5096de..de30d003e6 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64.rules +++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules @@ -1025,6 +1025,8 @@ (SLWconst [c] z:(MOVWZreg x)) && z.Uses == 1 && c < 24 => (CLRLSLWI [newPPC64ShiftAuxInt(c,8,31,32)] x) (SLWconst [c] z:(ANDconst [d] x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) (SLWconst [c] z:(AND (MOVDconst [d]) x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) +// special case for power9 +(SL(W|D)const [c] z:(MOVWreg x)) && c < 32 && objabi.GOPPC64 >= 9 => (EXTSWSLconst [c] x) // Lose widening ops fed to stores (MOVBstore [off] {sym} ptr (MOV(B|BZ|H|HZ|W|WZ)reg x) mem) => (MOVBstore [off] {sym} ptr x mem) diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index ed99c40cd2..28317928a8 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -223,6 +223,7 @@ func init() { {name: "ROTLconst", argLength: 1, reg: gp11, asm: "ROTL", aux: "Int64"}, // arg0 rotate left by auxInt bits {name: "ROTLWconst", argLength: 1, reg: gp11, asm: "ROTLW", aux: "Int64"}, // uint32(arg0) rotate left by auxInt bits + {name: "EXTSWSLconst", argLength: 1, reg: gp11, asm: "EXTSWSLI", aux: "Int64"}, {name: "CNTLZD", argLength: 1, reg: gp11, asm: "CNTLZD", clobberFlags: true}, // count leading zeros {name: "CNTLZW", argLength: 1, reg: gp11, asm: "CNTLZW", clobberFlags: true}, // count leading zeros (32 bit) diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 1fc0f7ea79..1fe00c7026 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1865,6 +1865,7 @@ const ( OpPPC64SLWconst OpPPC64ROTLconst OpPPC64ROTLWconst + OpPPC64EXTSWSLconst OpPPC64CNTLZD OpPPC64CNTLZW OpPPC64CNTTZD @@ -24849,6 +24850,20 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "EXTSWSLconst", + auxType: auxInt64, + argLen: 1, + asm: ppc64.AEXTSWSLI, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "CNTLZD", argLen: 1, diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go index 12b08824b5..29ec3992f2 100644 --- a/src/cmd/compile/internal/ssa/rewritePPC64.go +++ b/src/cmd/compile/internal/ssa/rewritePPC64.go @@ -12877,6 +12877,24 @@ func rewriteValuePPC64_OpPPC64SLDconst(v *Value) bool { } break } + // match: (SLDconst [c] z:(MOVWreg x)) + // cond: c < 32 && objabi.GOPPC64 >= 9 + // result: (EXTSWSLconst [c] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64MOVWreg { + break + } + x := z.Args[0] + if !(c < 32 && objabi.GOPPC64 >= 9) { + break + } + v.reset(OpPPC64EXTSWSLconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } return false } func rewriteValuePPC64_OpPPC64SLW(v *Value) bool { @@ -13000,6 +13018,24 @@ func rewriteValuePPC64_OpPPC64SLWconst(v *Value) bool { } break } + // match: (SLWconst [c] z:(MOVWreg x)) + // cond: c < 32 && objabi.GOPPC64 >= 9 + // result: (EXTSWSLconst [c] x) + for { + c := auxIntToInt64(v.AuxInt) + z := v_0 + if z.Op != OpPPC64MOVWreg { + break + } + x := z.Args[0] + if !(c < 32 && objabi.GOPPC64 >= 9) { + break + } + v.reset(OpPPC64EXTSWSLconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } return false } func rewriteValuePPC64_OpPPC64SRAD(v *Value) bool { diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go index f438803fb5..4c97302f83 100644 --- a/src/cmd/internal/obj/ppc64/a.out.go +++ b/src/cmd/internal/obj/ppc64/a.out.go @@ -733,6 +733,8 @@ const ( ASRAD ASRADCC ASRDCC + AEXTSWSLI + AEXTSWSLICC ASTDCCC ATD diff --git a/src/cmd/internal/obj/ppc64/anames.go b/src/cmd/internal/obj/ppc64/anames.go index accd87fe00..fca4b3e355 100644 --- a/src/cmd/internal/obj/ppc64/anames.go +++ b/src/cmd/internal/obj/ppc64/anames.go @@ -329,6 +329,8 @@ var Anames = []string{ "SRAD", "SRADCC", "SRDCC", + "EXTSWSLI", + "EXTSWSLICC", "STDCCC", "TD", "DWORD", diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go index 60dda72507..9f06bdf8b3 100644 --- a/src/cmd/internal/obj/ppc64/asm9.go +++ b/src/cmd/internal/obj/ppc64/asm9.go @@ -160,6 +160,8 @@ var optab = []Optab{ {ASLD, C_REG, C_REG, C_NONE, C_REG, 6, 4, 0}, {ASLD, C_SCON, C_REG, C_NONE, C_REG, 25, 4, 0}, {ASLD, C_SCON, C_NONE, C_NONE, C_REG, 25, 4, 0}, + {AEXTSWSLI, C_SCON, C_NONE, C_NONE, C_REG, 25, 4, 0}, + {AEXTSWSLI, C_SCON, C_REG, C_NONE, C_REG, 25, 4, 0}, {ASLW, C_SCON, C_REG, C_NONE, C_REG, 57, 4, 0}, {ASLW, C_SCON, C_NONE, C_NONE, C_REG, 57, 4, 0}, {ASRAW, C_REG, C_NONE, C_NONE, C_REG, 6, 4, 0}, @@ -1877,6 +1879,9 @@ func buildop(ctxt *obj.Link) { case ASRAW: /* sraw Rb,Rs,Ra; srawi sh,Rs,Ra */ opset(ASRAWCC, r0) + case AEXTSWSLI: + opset(AEXTSWSLICC, r0) + case ASRAD: /* sraw Rb,Rs,Ra; srawi sh,Rs,Ra */ opset(ASRADCC, r0) @@ -2189,49 +2194,54 @@ func AOP_RLDIC(op uint32, a uint32, s uint32, sh uint32, m uint32) uint32 { return op | (s&31)<<21 | (a&31)<<16 | (sh&31)<<11 | ((sh&32)>>5)<<1 | (m&31)<<6 | ((m&32)>>5)<<5 } +func AOP_EXTSWSLI(op uint32, a uint32, s uint32, sh uint32) uint32 { + return op | (a&31)<<21 | (s&31)<<16 | (sh&31)<<11 | ((sh&32)>>5)<<1 +} + func AOP_ISEL(op uint32, t uint32, a uint32, b uint32, bc uint32) uint32 { return op | (t&31)<<21 | (a&31)<<16 | (b&31)<<11 | (bc&0x1F)<<6 } const ( /* each rhs is OPVCC(_, _, _, _) */ - OP_ADD = 31<<26 | 266<<1 | 0<<10 | 0 - OP_ADDI = 14<<26 | 0<<1 | 0<<10 | 0 - OP_ADDIS = 15<<26 | 0<<1 | 0<<10 | 0 - OP_ANDI = 28<<26 | 0<<1 | 0<<10 | 0 - OP_EXTSB = 31<<26 | 954<<1 | 0<<10 | 0 - OP_EXTSH = 31<<26 | 922<<1 | 0<<10 | 0 - OP_EXTSW = 31<<26 | 986<<1 | 0<<10 | 0 - OP_ISEL = 31<<26 | 15<<1 | 0<<10 | 0 - OP_MCRF = 19<<26 | 0<<1 | 0<<10 | 0 - OP_MCRFS = 63<<26 | 64<<1 | 0<<10 | 0 - OP_MCRXR = 31<<26 | 512<<1 | 0<<10 | 0 - OP_MFCR = 31<<26 | 19<<1 | 0<<10 | 0 - OP_MFFS = 63<<26 | 583<<1 | 0<<10 | 0 - OP_MFMSR = 31<<26 | 83<<1 | 0<<10 | 0 - OP_MFSPR = 31<<26 | 339<<1 | 0<<10 | 0 - OP_MFSR = 31<<26 | 595<<1 | 0<<10 | 0 - OP_MFSRIN = 31<<26 | 659<<1 | 0<<10 | 0 - OP_MTCRF = 31<<26 | 144<<1 | 0<<10 | 0 - OP_MTFSF = 63<<26 | 711<<1 | 0<<10 | 0 - OP_MTFSFI = 63<<26 | 134<<1 | 0<<10 | 0 - OP_MTMSR = 31<<26 | 146<<1 | 0<<10 | 0 - OP_MTMSRD = 31<<26 | 178<<1 | 0<<10 | 0 - OP_MTSPR = 31<<26 | 467<<1 | 0<<10 | 0 - OP_MTSR = 31<<26 | 210<<1 | 0<<10 | 0 - OP_MTSRIN = 31<<26 | 242<<1 | 0<<10 | 0 - OP_MULLW = 31<<26 | 235<<1 | 0<<10 | 0 - OP_MULLD = 31<<26 | 233<<1 | 0<<10 | 0 - OP_OR = 31<<26 | 444<<1 | 0<<10 | 0 - OP_ORI = 24<<26 | 0<<1 | 0<<10 | 0 - OP_ORIS = 25<<26 | 0<<1 | 0<<10 | 0 - OP_RLWINM = 21<<26 | 0<<1 | 0<<10 | 0 - OP_RLWNM = 23<<26 | 0<<1 | 0<<10 | 0 - OP_SUBF = 31<<26 | 40<<1 | 0<<10 | 0 - OP_RLDIC = 30<<26 | 4<<1 | 0<<10 | 0 - OP_RLDICR = 30<<26 | 2<<1 | 0<<10 | 0 - OP_RLDICL = 30<<26 | 0<<1 | 0<<10 | 0 - OP_RLDCL = 30<<26 | 8<<1 | 0<<10 | 0 + OP_ADD = 31<<26 | 266<<1 | 0<<10 | 0 + OP_ADDI = 14<<26 | 0<<1 | 0<<10 | 0 + OP_ADDIS = 15<<26 | 0<<1 | 0<<10 | 0 + OP_ANDI = 28<<26 | 0<<1 | 0<<10 | 0 + OP_EXTSB = 31<<26 | 954<<1 | 0<<10 | 0 + OP_EXTSH = 31<<26 | 922<<1 | 0<<10 | 0 + OP_EXTSW = 31<<26 | 986<<1 | 0<<10 | 0 + OP_ISEL = 31<<26 | 15<<1 | 0<<10 | 0 + OP_MCRF = 19<<26 | 0<<1 | 0<<10 | 0 + OP_MCRFS = 63<<26 | 64<<1 | 0<<10 | 0 + OP_MCRXR = 31<<26 | 512<<1 | 0<<10 | 0 + OP_MFCR = 31<<26 | 19<<1 | 0<<10 | 0 + OP_MFFS = 63<<26 | 583<<1 | 0<<10 | 0 + OP_MFMSR = 31<<26 | 83<<1 | 0<<10 | 0 + OP_MFSPR = 31<<26 | 339<<1 | 0<<10 | 0 + OP_MFSR = 31<<26 | 595<<1 | 0<<10 | 0 + OP_MFSRIN = 31<<26 | 659<<1 | 0<<10 | 0 + OP_MTCRF = 31<<26 | 144<<1 | 0<<10 | 0 + OP_MTFSF = 63<<26 | 711<<1 | 0<<10 | 0 + OP_MTFSFI = 63<<26 | 134<<1 | 0<<10 | 0 + OP_MTMSR = 31<<26 | 146<<1 | 0<<10 | 0 + OP_MTMSRD = 31<<26 | 178<<1 | 0<<10 | 0 + OP_MTSPR = 31<<26 | 467<<1 | 0<<10 | 0 + OP_MTSR = 31<<26 | 210<<1 | 0<<10 | 0 + OP_MTSRIN = 31<<26 | 242<<1 | 0<<10 | 0 + OP_MULLW = 31<<26 | 235<<1 | 0<<10 | 0 + OP_MULLD = 31<<26 | 233<<1 | 0<<10 | 0 + OP_OR = 31<<26 | 444<<1 | 0<<10 | 0 + OP_ORI = 24<<26 | 0<<1 | 0<<10 | 0 + OP_ORIS = 25<<26 | 0<<1 | 0<<10 | 0 + OP_RLWINM = 21<<26 | 0<<1 | 0<<10 | 0 + OP_RLWNM = 23<<26 | 0<<1 | 0<<10 | 0 + OP_SUBF = 31<<26 | 40<<1 | 0<<10 | 0 + OP_RLDIC = 30<<26 | 4<<1 | 0<<10 | 0 + OP_RLDICR = 30<<26 | 2<<1 | 0<<10 | 0 + OP_RLDICL = 30<<26 | 0<<1 | 0<<10 | 0 + OP_RLDCL = 30<<26 | 8<<1 | 0<<10 | 0 + OP_EXTSWSLI = 31<<26 | 445<<2 ) func oclass(a *obj.Addr) int { @@ -2965,14 +2975,21 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { case AROTL: a = int(0) op = OP_RLDICL + case AEXTSWSLI: + a = int(v) default: c.ctxt.Diag("unexpected op in sldi case\n%v", p) a = 0 o1 = 0 } - o1 = AOP_RLDIC(op, uint32(p.To.Reg), uint32(r), uint32(v), uint32(a)) - if p.As == ASLDCC || p.As == ASRDCC { + if p.As == AEXTSWSLI || p.As == AEXTSWSLICC { + o1 = AOP_EXTSWSLI(OP_EXTSWSLI, uint32(r), uint32(p.To.Reg), uint32(v)) + + } else { + o1 = AOP_RLDIC(op, uint32(p.To.Reg), uint32(r), uint32(v), uint32(a)) + } + if p.As == ASLDCC || p.As == ASRDCC || p.As == AEXTSWSLICC { o1 |= 1 // Set the condition code bit } @@ -4350,6 +4367,11 @@ func (c *ctxt9) oprrr(a obj.As) uint32 { case ASRADCC: return OPVCC(31, 794, 0, 1) + case AEXTSWSLI: + return OPVCC(31, 445, 0, 0) + case AEXTSWSLICC: + return OPVCC(31, 445, 0, 1) + case ASRW: return OPVCC(31, 536, 0, 0) case ASRWCC: @@ -5013,6 +5035,10 @@ func (c *ctxt9) opirr(a obj.As) uint32 { return OPVCC(31, (413 << 1), 0, 0) case ASRADCC: return OPVCC(31, (413 << 1), 0, 1) + case AEXTSWSLI: + return OPVCC(31, 445, 0, 0) + case AEXTSWSLICC: + return OPVCC(31, 445, 0, 1) case ASTSW: return OPVCC(31, 725, 0, 0) diff --git a/test/codegen/shift.go b/test/codegen/shift.go index 32214851b5..abc4b091c9 100644 --- a/test/codegen/shift.go +++ b/test/codegen/shift.go @@ -182,7 +182,7 @@ func checkUnneededTrunc(tab *[100000]uint32, d uint64, v uint32, h uint16, b byt return f, g } -func checkCombinedShifts(v8 uint8, v16 uint16, v32 uint32, v64 uint64) (uint8, uint16, uint32, uint64) { +func checkCombinedShifts(v8 uint8, v16 uint16, v32 uint32, x32 int32, v64 uint64) (uint8, uint16, uint32, uint64, int64) { // ppc64le:-"AND","CLRLSLWI" // ppc64:-"AND","CLRLSLWI" @@ -202,7 +202,10 @@ func checkCombinedShifts(v8 uint8, v16 uint16, v32 uint32, v64 uint64) (uint8, u // ppc64le:-"AND","CLRLSLDI" // ppc64:-"AND","CLRLSLDI" i := (v64 & 0xFFFFFFFF) << 5 - return f, g, h, i + // ppc64le/power9:-"SLD","EXTSWSLI" + // ppc64/power9:-"SLD","EXTSWSLI" + j := int64(x32+32)*8 + return f, g, h, i, j } func checkWidenAfterShift(v int64, u uint64) (int64, uint64) { -- cgit v1.3 From ad0ab812f8b80416c92ed227974e3194e98f4cdc Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 28 Sep 2020 12:19:56 -0700 Subject: cmd/compile: fix type checking of "make" arguments As part of type checking make's arguments, we were converting untyped float and complex constant arguments to integers. However, we were doing this without concern for whether the argument was a declared constant. Thus a call like "make([]T, n)" could change n from an untyped float or untyped complex to an untyped integer. The fix here is to simply change checkmake to not call SetVal, which will be handled by defaultlit anyway. However, we also need to properly return the defaultlit result value to the caller, so checkmake's *Node parameter is also changed to **Node. Fixes #41680. Change-Id: I858927a052f384ec38684570d37b10a6906961f7 Reviewed-on: https://go-review.googlesource.com/c/go/+/257966 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/typecheck.go | 16 +++++++++------- test/fixedbugs/issue41680.go | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 test/fixedbugs/issue41680.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 8e87fc9df0..0eb0dae373 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -1770,7 +1770,7 @@ func typecheck1(n *Node, top int) (res *Node) { n.Type = nil return n } - if !checkmake(t, "len", l) || r != nil && !checkmake(t, "cap", r) { + if !checkmake(t, "len", &l) || r != nil && !checkmake(t, "cap", &r) { n.Type = nil return n } @@ -1794,7 +1794,7 @@ func typecheck1(n *Node, top int) (res *Node) { n.Type = nil return n } - if !checkmake(t, "size", l) { + if !checkmake(t, "size", &l) { n.Type = nil return n } @@ -1815,7 +1815,7 @@ func typecheck1(n *Node, top int) (res *Node) { n.Type = nil return n } - if !checkmake(t, "buffer", l) { + if !checkmake(t, "buffer", &l) { n.Type = nil return n } @@ -3729,7 +3729,8 @@ ret: n.SetWalkdef(1) } -func checkmake(t *types.Type, arg string, n *Node) bool { +func checkmake(t *types.Type, arg string, np **Node) bool { + n := *np if !n.Type.IsInteger() && n.Type.Etype != TIDEAL { yyerror("non-integer %s argument in make(%v) - %v", arg, t, n.Type) return false @@ -3739,12 +3740,12 @@ func checkmake(t *types.Type, arg string, n *Node) bool { // to avoid redundant "constant NNN overflows int" errors. switch consttype(n) { case CTINT, CTRUNE, CTFLT, CTCPLX: - n.SetVal(toint(n.Val())) - if n.Val().U.(*Mpint).CmpInt64(0) < 0 { + v := toint(n.Val()).U.(*Mpint) + if v.CmpInt64(0) < 0 { yyerror("negative %s argument in make(%v)", arg, t) return false } - if n.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 { + if v.Cmp(maxintval[TINT]) > 0 { yyerror("%s argument too large in make(%v)", arg, t) return false } @@ -3756,6 +3757,7 @@ func checkmake(t *types.Type, arg string, n *Node) bool { // for instance, indexlit might be called here and incorporate some // of the bounds checks done for make. n = defaultlit(n, types.Types[TINT]) + *np = n return true } diff --git a/test/fixedbugs/issue41680.go b/test/fixedbugs/issue41680.go new file mode 100644 index 0000000000..9dfeb7d503 --- /dev/null +++ b/test/fixedbugs/issue41680.go @@ -0,0 +1,21 @@ +// compile + +// Copyright 2020 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 p + +func F(s string) bool { + const m = 16 + const n = 1e5 + _ = make([]int, n) + return len(s) < n*m +} + +func G() { + const n = 1e5 + _ = make([]int, n) + f := n + var _ float64 = f +} -- cgit v1.3 From af18bce87cc7ee1ffc68f91abefa241ab209539e Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sun, 20 Sep 2020 23:29:20 -0400 Subject: cmd/link: consider interface conversions only in reachable code The linker prunes methods that are not directly reachable if the receiver type is never converted to interface. Currently, this "never" is too strong: it is invalidated even if the interface conversion is in an unreachable function. This CL improves it by only considering interface conversions in reachable code. To do that, we introduce a marker relocation R_USEIFACE, which marks the target symbol as UsedInIface if the source symbol is reached. binary size before after cmd/compile 18897528 18887400 cmd/go 13607372 13470652 Change-Id: I66c6b69eeff9ae02d84d2e6f2bc7f1b29dd53910 Reviewed-on: https://go-review.googlesource.com/c/go/+/256797 Trust: Cherry Zhang Reviewed-by: Jeremy Faller Reviewed-by: Than McIntosh --- src/cmd/compile/internal/gc/pgen.go | 8 ++- src/cmd/compile/internal/gc/sinit.go | 2 +- src/cmd/compile/internal/gc/walk.go | 15 +++-- src/cmd/internal/obj/s390x/asmz.go | 6 +- src/cmd/internal/obj/wasm/wasmobj.go | 2 + src/cmd/internal/obj/x86/asm6.go | 7 ++- src/cmd/internal/objabi/reloctype.go | 5 ++ src/cmd/internal/objabi/reloctype_string.go | 66 +++++++++++++++++++++- src/cmd/link/internal/ld/data.go | 6 ++ src/cmd/link/internal/ld/deadcode.go | 14 +++++ .../internal/ld/testdata/deadcode/ifacemethod.go | 9 ++- src/cmd/link/internal/loader/loader.go | 1 + src/cmd/link/internal/wasm/asm.go | 3 + 13 files changed, 129 insertions(+), 15 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 74262595b0..52b1ed351d 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -231,6 +231,11 @@ func compile(fn *Node) { return } + // Set up the function's LSym early to avoid data races with the assemblers. + // Do this before walk, as walk needs the LSym to set attributes/relocations + // (e.g. in markTypeUsedInInterface). + fn.Func.initLSym(true) + walk(fn) if nerrors != 0 { return @@ -250,9 +255,6 @@ func compile(fn *Node) { return } - // Set up the function's LSym early to avoid data races with the assemblers. - fn.Func.initLSym(true) - // Make sure type syms are declared for all types that might // be types of stack objects. We need to do this here // because symbols must be allocated before the parallel diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index 71ed558461..af19a96bbc 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -278,7 +278,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool { return Isconst(val, CTNIL) } - markTypeUsedInInterface(val.Type) + markTypeUsedInInterface(val.Type, l.Sym.Linksym()) var itab *Node if l.Type.IsEmptyInterface() { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 933f16d9a0..d238cc2f45 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -805,8 +805,8 @@ opswitch: fromType := n.Left.Type toType := n.Type - if !fromType.IsInterface() { - markTypeUsedInInterface(fromType) + if !fromType.IsInterface() && !Curfn.Func.Nname.isBlank() { // skip unnamed functions (func _()) + markTypeUsedInInterface(fromType, Curfn.Func.lsym) } // typeword generates the type word of the interface value. @@ -1621,8 +1621,13 @@ opswitch: // markTypeUsedInInterface marks that type t is converted to an interface. // This information is used in the linker in dead method elimination. -func markTypeUsedInInterface(t *types.Type) { - typenamesym(t).Linksym().Set(obj.AttrUsedInIface, true) +func markTypeUsedInInterface(t *types.Type, from *obj.LSym) { + tsym := typenamesym(t).Linksym() + // Emit a marker relocation. The linker will know the type is converted + // to an interface if "from" is reachable. + r := obj.Addrel(from) + r.Sym = tsym + r.Type = objabi.R_USEIFACE } // rtconvfn returns the parameter and result types that will be used by a @@ -3687,6 +3692,8 @@ func usemethod(n *Node) { // Also need to check for reflect package itself (see Issue #38515). if s := res0.Type.Sym; s != nil && s.Name == "Method" && isReflectPkg(s.Pkg) { Curfn.Func.SetReflectMethod(true) + // The LSym is initialized at this point. We need to set the attribute on the LSym. + Curfn.Func.lsym.Set(obj.AttrReflectMethod, true) } } diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go index 68f01f1c5d..cb3a2c3196 100644 --- a/src/cmd/internal/obj/s390x/asmz.go +++ b/src/cmd/internal/obj/s390x/asmz.go @@ -461,6 +461,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { buffer := make([]byte, 0) changed := true loop := 0 + nrelocs0 := len(c.cursym.R) for changed { if loop > 100 { c.ctxt.Diag("stuck in spanz loop") @@ -468,7 +469,10 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } changed = false buffer = buffer[:0] - c.cursym.R = make([]obj.Reloc, 0) + for i := range c.cursym.R[nrelocs0:] { + c.cursym.R[nrelocs0+i] = obj.Reloc{} + } + c.cursym.R = c.cursym.R[:nrelocs0] // preserve marker relocations generated by the compiler for p := c.cursym.Func.Text; p != nil; p = p.Link { pc := int64(len(buffer)) if pc != p.Pc { diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index 70e8e51e65..a9e093a8ad 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -1007,6 +1007,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { panic("bad name for Call") } r := obj.Addrel(s) + r.Siz = 1 // actually variable sized r.Off = int32(w.Len()) r.Type = objabi.R_CALL if p.Mark&WasmImport != 0 { @@ -1033,6 +1034,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { case AI32Const, AI64Const: if p.From.Name == obj.NAME_EXTERN { r := obj.Addrel(s) + r.Siz = 1 // actually variable sized r.Off = int32(w.Len()) r.Type = objabi.R_ADDR r.Sym = p.From.Sym diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index fb99c620ad..4940c79eaa 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -2100,14 +2100,15 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { var c int32 errors := ctxt.Errors var nops []nopPad // Padding for a particular assembly (reuse slice storage if multiple assemblies) + nrelocs0 := len(s.R) for { // This loop continues while there are reasons to re-assemble // whole block, like the presence of long forward jumps. reAssemble := false - for i := range s.R { - s.R[i] = obj.Reloc{} + for i := range s.R[nrelocs0:] { + s.R[nrelocs0+i] = obj.Reloc{} } - s.R = s.R[:0] + s.R = s.R[:nrelocs0] // preserve marker relocations generated by the compiler s.P = s.P[:0] c = 0 var pPrev *obj.Prog diff --git a/src/cmd/internal/objabi/reloctype.go b/src/cmd/internal/objabi/reloctype.go index f029a3c396..1e328d659f 100644 --- a/src/cmd/internal/objabi/reloctype.go +++ b/src/cmd/internal/objabi/reloctype.go @@ -89,6 +89,11 @@ const ( // should be linked into the final binary, even if there are no other // direct references. (This is used for types reachable by reflection.) R_USETYPE + // R_USEIFACE marks a type is converted to an interface in the function this + // relocation is applied to. The target is a type descriptor. + // This is a marker relocation (0-sized), for the linker's reachabililty + // analysis. + R_USEIFACE // R_METHODOFF resolves to a 32-bit offset from the beginning of the section // holding the data being relocated to the referenced symbol. // It is a variant of R_ADDROFF used when linking from the uncommonType of a diff --git a/src/cmd/internal/objabi/reloctype_string.go b/src/cmd/internal/objabi/reloctype_string.go index 83dfe71e07..caf24eea58 100644 --- a/src/cmd/internal/objabi/reloctype_string.go +++ b/src/cmd/internal/objabi/reloctype_string.go @@ -4,9 +4,71 @@ package objabi import "strconv" -const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF" +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[R_ADDR-1] + _ = x[R_ADDRPOWER-2] + _ = x[R_ADDRARM64-3] + _ = x[R_ADDRMIPS-4] + _ = x[R_ADDROFF-5] + _ = x[R_WEAKADDROFF-6] + _ = x[R_SIZE-7] + _ = x[R_CALL-8] + _ = x[R_CALLARM-9] + _ = x[R_CALLARM64-10] + _ = x[R_CALLIND-11] + _ = x[R_CALLPOWER-12] + _ = x[R_CALLMIPS-13] + _ = x[R_CALLRISCV-14] + _ = x[R_CONST-15] + _ = x[R_PCREL-16] + _ = x[R_TLS_LE-17] + _ = x[R_TLS_IE-18] + _ = x[R_GOTOFF-19] + _ = x[R_PLT0-20] + _ = x[R_PLT1-21] + _ = x[R_PLT2-22] + _ = x[R_USEFIELD-23] + _ = x[R_USETYPE-24] + _ = x[R_USEIFACE-25] + _ = x[R_METHODOFF-26] + _ = x[R_POWER_TOC-27] + _ = x[R_GOTPCREL-28] + _ = x[R_JMPMIPS-29] + _ = x[R_DWARFSECREF-30] + _ = x[R_DWARFFILEREF-31] + _ = x[R_ARM64_TLS_LE-32] + _ = x[R_ARM64_TLS_IE-33] + _ = x[R_ARM64_GOTPCREL-34] + _ = x[R_ARM64_GOT-35] + _ = x[R_ARM64_PCREL-36] + _ = x[R_ARM64_LDST8-37] + _ = x[R_ARM64_LDST32-38] + _ = x[R_ARM64_LDST64-39] + _ = x[R_ARM64_LDST128-40] + _ = x[R_POWER_TLS_LE-41] + _ = x[R_POWER_TLS_IE-42] + _ = x[R_POWER_TLS-43] + _ = x[R_ADDRPOWER_DS-44] + _ = x[R_ADDRPOWER_GOT-45] + _ = x[R_ADDRPOWER_PCREL-46] + _ = x[R_ADDRPOWER_TOCREL-47] + _ = x[R_ADDRPOWER_TOCREL_DS-48] + _ = x[R_RISCV_PCREL_ITYPE-49] + _ = x[R_RISCV_PCREL_STYPE-50] + _ = x[R_PCRELDBL-51] + _ = x[R_ADDRMIPSU-52] + _ = x[R_ADDRMIPSTLS-53] + _ = x[R_ADDRCUOFF-54] + _ = x[R_WASMIMPORT-55] + _ = x[R_XCOFFREF-56] +} + +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF" -var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 219, 230, 240, 249, 262, 276, 290, 304, 320, 331, 344, 357, 371, 385, 400, 414, 428, 439, 453, 468, 485, 503, 524, 543, 562, 572, 583, 596, 607, 619, 629} +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 218, 229, 240, 250, 259, 272, 286, 300, 314, 330, 341, 354, 367, 381, 395, 410, 424, 438, 449, 463, 478, 495, 513, 534, 553, 572, 582, 593, 606, 617, 629, 639} func (i RelocType) String() string { i -= 1 diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index a730125cf2..0a3418bfc9 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -698,6 +698,9 @@ func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) { relocs := ctxt.loader.Relocs(s) for ri := 0; ri < relocs.Count(); ri++ { r := relocs.At(ri) + if r.IsMarker() { + continue // skip marker relocations + } targ := r.Sym() if targ == 0 { continue @@ -775,6 +778,9 @@ func dynrelocsym(ctxt *Link, s loader.Sym) { relocs := ldr.Relocs(s) for ri := 0; ri < relocs.Count(); ri++ { r := relocs.At(ri) + if r.IsMarker() { + continue // skip marker relocations + } if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal { // It's expected that some relocations will be done // later by relocsym (R_TLS_LE, R_ADDROFF), so diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 7f14aa3d27..816a23b9a7 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -153,6 +153,20 @@ func (d *deadcodePass) flood() { // do nothing for now as we still load all type symbols. continue } + if t == objabi.R_USEIFACE { + // R_USEIFACE is a marker relocation that tells the linker the type is + // converted to an interface, i.e. should have UsedInIface set. See the + // comment below for why we need to unset the Reachable bit and re-mark it. + rs := r.Sym() + if !d.ldr.AttrUsedInIface(rs) { + d.ldr.SetAttrUsedInIface(rs, true) + if d.ldr.AttrReachable(rs) { + d.ldr.SetAttrReachable(rs, false) + d.mark(rs, symIdx) + } + } + continue + } rs := r.Sym() if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) { // If a type is converted to an interface, it is possible to obtain an diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go index b62f18c342..32a24cf6f0 100644 --- a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go @@ -18,6 +18,13 @@ var p *T var e interface{} func main() { - p = new(T) // used T, but never converted to interface + p = new(T) // used T, but never converted to interface in any reachable code e.(I).M() // used I and I.M } + +func Unused() { // convert T to interface, but this function is not reachable + var i I = T(0) + i.M() +} + +var Unused2 interface{} = T(1) // convert T to interface, in an unreachable global initializer diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 43a0352e0b..ea99233f67 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -63,6 +63,7 @@ type Reloc struct { func (rel Reloc) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc.Type()) + rel.typ } func (rel Reloc) Sym() Sym { return rel.l.resolve(rel.r, rel.Reloc.Sym()) } func (rel Reloc) SetSym(s Sym) { rel.Reloc.SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) } +func (rel Reloc) IsMarker() bool { return rel.Siz() == 0 } func (rel Reloc) SetType(t objabi.RelocType) { if t != objabi.RelocType(uint8(t)) { diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go index 3bd56a6e3a..31851fbb56 100644 --- a/src/cmd/link/internal/wasm/asm.go +++ b/src/cmd/link/internal/wasm/asm.go @@ -167,6 +167,9 @@ func asmb2(ctxt *ld.Link, ldr *loader.Loader) { off := int32(0) for ri := 0; ri < relocs.Count(); ri++ { r := relocs.At(ri) + if r.Siz() == 0 { + continue // skip marker relocations + } wfn.Write(P[off:r.Off()]) off = r.Off() rs := ldr.ResolveABIAlias(r.Sym()) -- cgit v1.3 From 39dde09126be02f5f8c38ddf7590ae8f9825fcaa Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Mon, 21 Sep 2020 20:44:53 -0400 Subject: cmd/link: retain only used interface methods Currently, in the linker's deadcode pass, when an interface type is live, the linker thinks all its methods are live, and uses them to match methods on concrete types. The interface method may never be used, though. This CL changes it to only keep used interface methods, for matching concrete type methods. To do that, when an interface method is used, the compiler generates a mark relocation. The linker uses the marker relocations to mark used interface methods, and only the used ones. binary size before after cmd/compile 18887400 18812200 cmd/go 13470652 13470492 Change-Id: I3cfd9df4a53783330ba87735853f2a0ec3c42802 Reviewed-on: https://go-review.googlesource.com/c/go/+/256798 Trust: Cherry Zhang Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/compile/internal/gc/reflect.go | 19 +++++- src/cmd/compile/internal/gc/walk.go | 15 +++++ src/cmd/internal/objabi/reloctype.go | 6 ++ src/cmd/internal/objabi/reloctype_string.go | 67 +++++++++++----------- src/cmd/link/internal/ld/deadcode.go | 53 ++++++++--------- src/cmd/link/internal/ld/deadcode_test.go | 1 + .../internal/ld/testdata/deadcode/ifacemethod4.go | 23 ++++++++ 7 files changed, 120 insertions(+), 64 deletions(-) create mode 100644 src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 49b2a0ed49..ae3e2f8e65 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -61,8 +61,9 @@ const ( MAXELEMSIZE = 128 ) -func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{}) -func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{}) +func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{}) +func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{}) +func commonSize() int { return 4*Widthptr + 8 + 8 } // Sizeof(runtime._type{}) func uncommonSize(t *types.Type) int { // Sizeof(runtime.uncommontype{}) if t.Sym == nil && len(methods(t)) == 0 { @@ -1422,6 +1423,20 @@ func dtypesym(t *types.Type) *obj.LSym { return lsym } +// ifaceMethodOffset returns the offset of the i-th method in the interface +// type descriptor, ityp. +func ifaceMethodOffset(ityp *types.Type, i int64) int64 { + // interface type descriptor layout is struct { + // _type // commonSize + // pkgpath // 1 word + // []imethod // 3 words (pointing to [...]imethod below) + // uncommontype // uncommonSize + // [...]imethod + // } + // The size of imethod is 8. + return int64(commonSize()+4*Widthptr+uncommonSize(ityp)) + i*8 +} + // for each itabEntry, gather the methods on // the concrete type that implement the interface func peekitabs() { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index d238cc2f45..8e45059eab 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -565,6 +565,7 @@ opswitch: case OCALLINTER, OCALLFUNC, OCALLMETH: if n.Op == OCALLINTER { usemethod(n) + markUsedIfaceMethod(n) } if n.Op == OCALLFUNC && n.Left.Op == OCLOSURE { @@ -1630,6 +1631,20 @@ func markTypeUsedInInterface(t *types.Type, from *obj.LSym) { r.Type = objabi.R_USEIFACE } +// markUsedIfaceMethod marks that an interface method is used in the current +// function. n is OCALLINTER node. +func markUsedIfaceMethod(n *Node) { + ityp := n.Left.Left.Type + tsym := typenamesym(ityp).Linksym() + r := obj.Addrel(Curfn.Func.lsym) + r.Sym = tsym + // n.Left.Xoffset is the method index * Widthptr (the offset of code pointer + // in itab). + midx := n.Left.Xoffset / int64(Widthptr) + r.Add = ifaceMethodOffset(ityp, midx) + r.Type = objabi.R_USEIFACEMETHOD +} + // rtconvfn returns the parameter and result types that will be used by a // runtime function to convert from type src to type dst. The runtime function // name can be derived from the names of the returned types. diff --git a/src/cmd/internal/objabi/reloctype.go b/src/cmd/internal/objabi/reloctype.go index 1e328d659f..9e2e4a150a 100644 --- a/src/cmd/internal/objabi/reloctype.go +++ b/src/cmd/internal/objabi/reloctype.go @@ -94,6 +94,12 @@ const ( // This is a marker relocation (0-sized), for the linker's reachabililty // analysis. R_USEIFACE + // R_USEIFACEMETHOD marks an interface method that is used in the function + // this relocation is applied to. The target is an interface type descriptor. + // The addend is the offset of the method in the type descriptor. + // This is a marker relocation (0-sized), for the linker's reachabililty + // analysis. + R_USEIFACEMETHOD // R_METHODOFF resolves to a 32-bit offset from the beginning of the section // holding the data being relocated to the referenced symbol. // It is a variant of R_ADDROFF used when linking from the uncommonType of a diff --git a/src/cmd/internal/objabi/reloctype_string.go b/src/cmd/internal/objabi/reloctype_string.go index caf24eea58..01df4cce62 100644 --- a/src/cmd/internal/objabi/reloctype_string.go +++ b/src/cmd/internal/objabi/reloctype_string.go @@ -33,42 +33,43 @@ func _() { _ = x[R_USEFIELD-23] _ = x[R_USETYPE-24] _ = x[R_USEIFACE-25] - _ = x[R_METHODOFF-26] - _ = x[R_POWER_TOC-27] - _ = x[R_GOTPCREL-28] - _ = x[R_JMPMIPS-29] - _ = x[R_DWARFSECREF-30] - _ = x[R_DWARFFILEREF-31] - _ = x[R_ARM64_TLS_LE-32] - _ = x[R_ARM64_TLS_IE-33] - _ = x[R_ARM64_GOTPCREL-34] - _ = x[R_ARM64_GOT-35] - _ = x[R_ARM64_PCREL-36] - _ = x[R_ARM64_LDST8-37] - _ = x[R_ARM64_LDST32-38] - _ = x[R_ARM64_LDST64-39] - _ = x[R_ARM64_LDST128-40] - _ = x[R_POWER_TLS_LE-41] - _ = x[R_POWER_TLS_IE-42] - _ = x[R_POWER_TLS-43] - _ = x[R_ADDRPOWER_DS-44] - _ = x[R_ADDRPOWER_GOT-45] - _ = x[R_ADDRPOWER_PCREL-46] - _ = x[R_ADDRPOWER_TOCREL-47] - _ = x[R_ADDRPOWER_TOCREL_DS-48] - _ = x[R_RISCV_PCREL_ITYPE-49] - _ = x[R_RISCV_PCREL_STYPE-50] - _ = x[R_PCRELDBL-51] - _ = x[R_ADDRMIPSU-52] - _ = x[R_ADDRMIPSTLS-53] - _ = x[R_ADDRCUOFF-54] - _ = x[R_WASMIMPORT-55] - _ = x[R_XCOFFREF-56] + _ = x[R_USEIFACEMETHOD-26] + _ = x[R_METHODOFF-27] + _ = x[R_POWER_TOC-28] + _ = x[R_GOTPCREL-29] + _ = x[R_JMPMIPS-30] + _ = x[R_DWARFSECREF-31] + _ = x[R_DWARFFILEREF-32] + _ = x[R_ARM64_TLS_LE-33] + _ = x[R_ARM64_TLS_IE-34] + _ = x[R_ARM64_GOTPCREL-35] + _ = x[R_ARM64_GOT-36] + _ = x[R_ARM64_PCREL-37] + _ = x[R_ARM64_LDST8-38] + _ = x[R_ARM64_LDST32-39] + _ = x[R_ARM64_LDST64-40] + _ = x[R_ARM64_LDST128-41] + _ = x[R_POWER_TLS_LE-42] + _ = x[R_POWER_TLS_IE-43] + _ = x[R_POWER_TLS-44] + _ = x[R_ADDRPOWER_DS-45] + _ = x[R_ADDRPOWER_GOT-46] + _ = x[R_ADDRPOWER_PCREL-47] + _ = x[R_ADDRPOWER_TOCREL-48] + _ = x[R_ADDRPOWER_TOCREL_DS-49] + _ = x[R_RISCV_PCREL_ITYPE-50] + _ = x[R_RISCV_PCREL_STYPE-51] + _ = x[R_PCRELDBL-52] + _ = x[R_ADDRMIPSU-53] + _ = x[R_ADDRMIPSTLS-54] + _ = x[R_ADDRCUOFF-55] + _ = x[R_WASMIMPORT-56] + _ = x[R_XCOFFREF-57] } -const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF" +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF" -var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 218, 229, 240, 250, 259, 272, 286, 300, 314, 330, 341, 354, 367, 381, 395, 410, 424, 438, 449, 463, 478, 495, 513, 534, 553, 572, 582, 593, 606, 617, 629, 639} +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 218, 234, 245, 256, 266, 275, 288, 302, 316, 330, 346, 357, 370, 383, 397, 411, 426, 440, 454, 465, 479, 494, 511, 529, 550, 569, 588, 598, 609, 622, 633, 645, 655} func (i RelocType) String() string { i -= 1 diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 816a23b9a7..74d61fa495 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -106,25 +106,16 @@ func (d *deadcodePass) flood() { if isgotype { usedInIface = d.ldr.AttrUsedInIface(symIdx) - p := d.ldr.Data(symIdx) - if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface { - for _, sig := range d.decodeIfaceMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs) { - if d.ctxt.Debugvlog > 1 { - d.ctxt.Logf("reached iface method: %v\n", sig) - } - d.ifaceMethod[sig] = true - } - } } methods = methods[:0] for i := 0; i < relocs.Count(); i++ { r := relocs.At(i) t := r.Type() - if t == objabi.R_WEAKADDROFF { + switch t { + case objabi.R_WEAKADDROFF: continue - } - if t == objabi.R_METHODOFF { + case objabi.R_METHODOFF: if i+2 >= relocs.Count() { panic("expect three consecutive R_METHODOFF relocs") } @@ -146,14 +137,12 @@ func (d *deadcodePass) flood() { } i += 2 continue - } - if t == objabi.R_USETYPE { + case objabi.R_USETYPE: // type symbol used for DWARF. we need to load the symbol but it may not // be otherwise reachable in the program. // do nothing for now as we still load all type symbols. continue - } - if t == objabi.R_USEIFACE { + case objabi.R_USEIFACE: // R_USEIFACE is a marker relocation that tells the linker the type is // converted to an interface, i.e. should have UsedInIface set. See the // comment below for why we need to unset the Reachable bit and re-mark it. @@ -166,6 +155,18 @@ func (d *deadcodePass) flood() { } } continue + case objabi.R_USEIFACEMETHOD: + // R_USEIFACEMETHOD is a marker relocation that marks an interface + // method as used. + rs := r.Sym() + if d.ldr.SymType(rs) != sym.SDYNIMPORT { // don't decode DYNIMPORT symbol (we'll mark all exported methods anyway) + m := d.decodeIfaceMethod(d.ldr, d.ctxt.Arch, rs, r.Add()) + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("reached iface method: %v\n", m) + } + d.ifaceMethod[m] = true + } + continue } rs := r.Sym() if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) { @@ -378,23 +379,17 @@ func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symId return methods } -func (d *deadcodePass) decodeIfaceMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { +// Decode the method of interface type symbol symIdx at offset off. +func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig { p := ldr.Data(symIdx) if decodetypeKind(arch, p)&kindMask != kindInterface { panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx))) } - rel := decodeReloc(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize)) - s := rel.Sym() - if s == 0 { - return nil - } - if s != symIdx { - panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx))) - } - off := int(rel.Add()) // array of reflect.imethod values - numMethods := int(decodetypeIfaceMethodCount(arch, p)) - sizeofIMethod := 4 + 4 - return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods) + relocs := ldr.Relocs(symIdx) + var m methodsig + m.name = decodetypeName(ldr, symIdx, &relocs, int(off)) + m.typ = decodeRelocSym(ldr, symIdx, &relocs, int32(off+4)) + return m } func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { diff --git a/src/cmd/link/internal/ld/deadcode_test.go b/src/cmd/link/internal/ld/deadcode_test.go index ab836dc8f8..b756091613 100644 --- a/src/cmd/link/internal/ld/deadcode_test.go +++ b/src/cmd/link/internal/ld/deadcode_test.go @@ -33,6 +33,7 @@ func TestDeadcode(t *testing.T) { {"ifacemethod", "", "main.T.M"}, {"ifacemethod2", "main.T.M", ""}, {"ifacemethod3", "main.S.M", ""}, + {"ifacemethod4", "", "main.T.M"}, } for _, test := range tests { test := test diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go new file mode 100644 index 0000000000..52ee2e3d86 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go @@ -0,0 +1,23 @@ +// Copyright 2020 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. + +// Test that a live type's method is not live even if +// it matches an interface method, as long as the interface +// method is not used. + +package main + +type T int + +func (T) M() {} + +type I interface{ M() } + +var p *T +var pp *I + +func main() { + p = new(T) // use type T + pp = new(I) // use type I +} -- cgit v1.3 From 66770f4b1de37d9c5c962edb2980a70102e09ec3 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Mon, 28 Sep 2020 13:10:30 -0400 Subject: cmd/compile: mark type namedata symbols content-addressable Type namedata symbols are for type/field/method names and package paths. We can use content-addressable symbol mechanism for them. Change-Id: I923fda17b7094c7a0e46aad7c450622eb3826294 Reviewed-on: https://go-review.googlesource.com/c/go/+/257960 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/compile/internal/gc/reflect.go | 2 ++ src/cmd/internal/obj/objfile.go | 7 +++++++ src/cmd/internal/obj/sym.go | 5 ++++- src/cmd/link/internal/loader/loader.go | 4 ++-- 4 files changed, 15 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index ae3e2f8e65..21429af782 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -511,6 +511,7 @@ func dimportpath(p *types.Pkg) { s := Ctxt.Lookup("type..importpath." + p.Prefix + ".") ot := dnameData(s, 0, str, "", nil, false) ggloblsym(s, int32(ot), obj.DUPOK|obj.RODATA) + s.Set(obj.AttrContentAddressable, true) p.Pathsym = s } @@ -638,6 +639,7 @@ func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym { } ot := dnameData(s, 0, name, tag, pkg, exported) ggloblsym(s, int32(ot), obj.DUPOK|obj.RODATA) + s.Set(obj.AttrContentAddressable, true) return s } diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index e4b9620568..186016c970 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -372,6 +372,13 @@ func contentHash64(s *LSym) goobj.Hash64Type { // hashed symbols. func (w *writer) contentHash(s *LSym) goobj.HashType { h := sha1.New() + // Don't dedup type symbols with others, as they are in a different + // section. + if strings.HasPrefix(s.Name, "type.") { + h.Write([]byte{'T'}) + } else { + h.Write([]byte{0}) + } // The compiler trims trailing zeros _sometimes_. We just do // it always. h.Write(bytes.TrimRight(s.P, "\x00")) diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index d58877ee15..e5d7b2cbfd 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -38,6 +38,7 @@ import ( "log" "math" "sort" + "strings" ) func Linknew(arch *LinkArch) *Link { @@ -204,7 +205,9 @@ func (ctxt *Link) NumberSyms() { // if Pkgpath is unknown, cannot hash symbols with relocations, as it // may reference named symbols whose names are not fully expanded. if s.ContentAddressable() && (ctxt.Pkgpath != "" || len(s.R) == 0) { - if len(s.P) <= 8 && len(s.R) == 0 { // we can use short hash only for symbols without relocations + if len(s.P) <= 8 && len(s.R) == 0 && !strings.HasPrefix(s.Name, "type.") { + // We can use short hash only for symbols without relocations. + // Don't use short hash for type symbols, as they need special handling. s.PkgIdx = goobj.PkgIdxHashed64 s.SymIdx = hashed64idx if hashed64idx != int32(len(ctxt.hashed64defs)) { diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index ea99233f67..4025edc23f 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -2153,11 +2153,11 @@ func (l *Loader) LoadNonpkgSyms(arch *sys.Arch) { l.npkgsyms = l.NSym() // Preallocate some space (a few hundreds KB) for some symbols. // As of Go 1.15, linking cmd/compile has ~8000 hashed64 symbols and - // ~13000 hashed symbols. + // ~27000 hashed symbols. st := loadState{ l: l, hashed64Syms: make(map[uint64]symAndSize, 10000), - hashedSyms: make(map[goobj.HashType]symAndSize, 15000), + hashedSyms: make(map[goobj.HashType]symAndSize, 30000), } for _, o := range l.objs[goObjStart:] { st.preloadSyms(o.r, hashed64Def) -- cgit v1.3 From 0e85fd7561de869add933801c531bf25dee9561c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 29 Sep 2020 02:11:10 -0700 Subject: cmd/compile: report type loop for invalid recursive types Similar to how we report initialization loops in initorder.go and type alias loops in typecheck.go, this CL updates align.go to warn about invalid recursive types. The code is based on the loop code from initorder.go, with minimal changes to adapt from detecting variable/function initialization loops to detecting type declaration loops. Thanks to Cuong Manh Le for investigating this, helping come up with test cases, and exploring solutions. Fixes #41575 Updates #41669. Change-Id: Idb2cb8c5e1d645e62900e178fcb50af33e1700a1 Reviewed-on: https://go-review.googlesource.com/c/go/+/258177 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Robert Griesemer Reviewed-by: Cuong Manh Le Trust: Matthew Dempsky Trust: Cuong Manh Le --- src/cmd/compile/internal/gc/align.go | 98 ++++++++++++++++++++++++++++++++---- src/cmd/compile/internal/gc/subr.go | 10 ++++ test/fixedbugs/bug195.go | 16 +++--- test/fixedbugs/issue22904.go | 4 +- test/fixedbugs/issue23823.go | 3 +- test/fixedbugs/issue41575.go | 36 +++++++++++++ 6 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 test/fixedbugs/issue41575.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index ab578ee8c7..5af403afa3 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -5,7 +5,9 @@ package gc import ( + "bytes" "cmd/compile/internal/types" + "fmt" "sort" ) @@ -173,6 +175,91 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 { return o } +// findTypeLoop searches for an invalid type declaration loop involving +// type t and reports whether one is found. If so, path contains the +// loop. +// +// path points to a slice used for tracking the sequence of types +// visited. Using a pointer to a slice allows the slice capacity to +// grow and limit reallocations. +func findTypeLoop(t *types.Type, path *[]*types.Type) bool { + // We implement a simple DFS loop-finding algorithm. This + // could be faster, but type cycles are rare. + + if t.Sym != nil { + // Declared type. Check for loops and otherwise + // recurse on the type expression used in the type + // declaration. + + for i, x := range *path { + if x == t { + *path = (*path)[i:] + return true + } + } + + *path = append(*path, t) + if findTypeLoop(asNode(t.Nod).Name.Param.Ntype.Type, path) { + return true + } + *path = (*path)[:len(*path)-1] + } else { + // Anonymous type. Recurse on contained types. + + switch t.Etype { + case TARRAY: + if findTypeLoop(t.Elem(), path) { + return true + } + case TSTRUCT: + for _, f := range t.Fields().Slice() { + if findTypeLoop(f.Type, path) { + return true + } + } + case TINTER: + for _, m := range t.Methods().Slice() { + if m.Type.IsInterface() { // embedded interface + if findTypeLoop(m.Type, path) { + return true + } + } + } + } + } + + return false +} + +func reportTypeLoop(t *types.Type) { + if t.Broke() { + return + } + + var l []*types.Type + if !findTypeLoop(t, &l) { + Fatalf("failed to find type loop for: %v", t) + } + + // Rotate loop so that the earliest type declaration is first. + i := 0 + for j, t := range l[1:] { + if typePos(t).Before(typePos(l[i])) { + i = j + 1 + } + } + l = append(l[i:], l[:i]...) + + var msg bytes.Buffer + fmt.Fprintf(&msg, "invalid recursive type %v\n", l[0]) + for _, t := range l { + fmt.Fprintf(&msg, "\t%v: %v refers to\n", linestr(typePos(t)), t) + t.SetBroke(true) + } + fmt.Fprintf(&msg, "\t%v: %v", linestr(typePos(l[0])), l[0]) + yyerrorl(typePos(l[0]), msg.String()) +} + // dowidth calculates and stores the size and alignment for t. // If sizeCalculationDisabled is set, and the size/alignment // have not already been calculated, it calls Fatal. @@ -192,11 +279,7 @@ func dowidth(t *types.Type) { } if t.Width == -2 { - if !t.Broke() { - t.SetBroke(true) - yyerrorl(asNode(t.Nod).Pos, "invalid recursive type %v", t) - } - + reportTypeLoop(t) t.Width = 0 t.Align = 1 return @@ -308,10 +391,7 @@ func dowidth(t *types.Type) { checkwidth(t.Key()) case TFORW: // should have been filled in - if !t.Broke() { - t.SetBroke(true) - yyerror("invalid recursive type %v", t) - } + reportTypeLoop(t) w = 1 // anything will do case TANY: diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index b5527e2f83..07547df36e 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1921,3 +1921,13 @@ func ifaceData(pos src.XPos, n *Node, t *types.Type) *Node { ind.SetBounded(true) return ind } + +// typePos returns the position associated with t. +// This is where t was declared or where it appeared as a type expression. +func typePos(t *types.Type) src.XPos { + n := asNode(t.Nod) + if n == nil || !n.Pos.IsKnown() { + Fatalf("bad type: %v", t) + } + return n.Pos +} diff --git a/test/fixedbugs/bug195.go b/test/fixedbugs/bug195.go index 496c0be610..aef7bd2d89 100644 --- a/test/fixedbugs/bug195.go +++ b/test/fixedbugs/bug195.go @@ -6,22 +6,22 @@ package main -type I1 interface { I2 } // ERROR "interface" +type I1 interface{ I2 } // ERROR "interface" type I2 int -type I3 interface { int } // ERROR "interface" +type I3 interface{ int } // ERROR "interface" type S struct { - x interface{ S } // ERROR "interface" + x interface{ S } // ERROR "interface" } -type I4 interface { // GC_ERROR "invalid recursive type" - I4 // GCCGO_ERROR "interface" +type I4 interface { // GC_ERROR "invalid recursive type I4\n\tLINE: I4 refers to\n\tLINE: I4$" + I4 // GCCGO_ERROR "interface" } -type I5 interface { // GC_ERROR "invalid recursive type" - I6 // GCCGO_ERROR "interface" +type I5 interface { // GC_ERROR "invalid recursive type I5\n\tLINE: I5 refers to\n\tLINE+4: I6 refers to\n\tLINE: I5$" + I6 // GCCGO_ERROR "interface" } type I6 interface { - I5 // GCCGO_ERROR "interface" + I5 // GCCGO_ERROR "interface" } diff --git a/test/fixedbugs/issue22904.go b/test/fixedbugs/issue22904.go index 46cb7c048a..09f4a2118e 100644 --- a/test/fixedbugs/issue22904.go +++ b/test/fixedbugs/issue22904.go @@ -9,8 +9,8 @@ package p -type a struct{ b } -type b struct{ a } // ERROR "invalid recursive type" +type a struct{ b } // ERROR "invalid recursive type" +type b struct{ a } var x interface{} diff --git a/test/fixedbugs/issue23823.go b/test/fixedbugs/issue23823.go index 2f802d0988..fe6cef1fb4 100644 --- a/test/fixedbugs/issue23823.go +++ b/test/fixedbugs/issue23823.go @@ -10,6 +10,7 @@ type I1 = interface { I2 } -type I2 interface { // ERROR "invalid recursive type" +// BAD: type loop should mention I1; see also #41669 +type I2 interface { // ERROR "invalid recursive type I2\n\tLINE: I2 refers to\n\tLINE: I2$" I1 } diff --git a/test/fixedbugs/issue41575.go b/test/fixedbugs/issue41575.go new file mode 100644 index 0000000000..d03d1c8b3e --- /dev/null +++ b/test/fixedbugs/issue41575.go @@ -0,0 +1,36 @@ +// errorcheck + +// Copyright 2020 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 p + +type T1 struct { // ERROR "invalid recursive type T1\n\tLINE: T1 refers to\n\tLINE+4: T2 refers to\n\tLINE: T1$" + f2 T2 +} + +type T2 struct { + f1 T1 +} + +type a b +type b c // ERROR "invalid recursive type b\n\tLINE: b refers to\n\tLINE+1: c refers to\n\tLINE: b$" +type c b + +type d e +type e f +type f f // ERROR "invalid recursive type f\n\tLINE: f refers to\n\tLINE: f$" + +type g struct { // ERROR "invalid recursive type g\n\tLINE: g refers to\n\tLINE: g$" + h struct { + g + } +} + +type w x +type x y // ERROR "invalid recursive type x\n\tLINE: x refers to\n\tLINE+1: y refers to\n\tLINE+2: z refers to\n\tLINE: x$" +type y struct{ z } +type z [10]x + +type w2 w // refer to the type loop again -- cgit v1.3 From ad8447bed94ccb89338b05e7e38f7d53874f0340 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 27 Jul 2020 16:46:35 -0400 Subject: cmd/compile: fix late call expansion for SSA-able aggregate results and arguments This change incorporates the decision that it should be possible to run call expansion relatively late in the optimization chain, so that (1) calls themselves can be exposed to useful optimizations (2) the effect of selectors on aggregates is seen at the rewrite, so that assignment of parts into registers is less complicated (at least I hope it works that way). That means that selectors feeding into SelectN need to be processed, and Make* feeding into call parameters need to be processed. This does however require that call expansion run before decompose builtins. This doesn't yet handle rewrites of strings, slices, interfaces, and complex numbers. Passes run.bash and race.bash Change-Id: I71ff23d3c491043beb30e926949970c4f63ef1a4 Reviewed-on: https://go-review.googlesource.com/c/go/+/245133 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 11 +- src/cmd/compile/internal/ssa/compile.go | 2 +- src/cmd/compile/internal/ssa/config.go | 8 + src/cmd/compile/internal/ssa/expand_calls.go | 392 +++++++++++++++++++++++---- src/cmd/compile/internal/ssa/writebarrier.go | 6 +- 5 files changed, 360 insertions(+), 59 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 815ff7f99f..aebb40568c 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -10,7 +10,6 @@ import ( "html" "os" "sort" - "strings" "bufio" "bytes" @@ -2560,19 +2559,19 @@ func (s *state) expr(n *Node) *ssa.Value { if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall { // Do the old thing addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset) - return s.load(n.Type, addr) + return s.rawLoad(n.Type, addr) } which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset) if which == -1 { // Do the old thing // TODO: Panic instead. addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset) - return s.load(n.Type, addr) + return s.rawLoad(n.Type, addr) } if canSSAType(n.Type) { return s.newValue1I(ssa.OpSelectN, n.Type, which, s.prevCall) } else { addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(n.Type), which, s.prevCall) - return s.load(n.Type, addr) + return s.rawLoad(n.Type, addr) } case ODEREF: @@ -4377,7 +4376,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { case OCALLFUNC: if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC { sym = fn.Sym - if !returnResultAddr && strings.Contains(sym.Name, "testLateExpansion") { + if !returnResultAddr && ssa.LateCallExpansionEnabledWithin(s.f) { testLateExpansion = true } break @@ -4394,7 +4393,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { } if k == callNormal { sym = fn.Sym - if !returnResultAddr && strings.Contains(sym.Name, "testLateExpansion") { + if !returnResultAddr && ssa.LateCallExpansionEnabledWithin(s.f) { testLateExpansion = true } break diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index 4eed612977..3dec1cd85b 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -431,9 +431,9 @@ var passes = [...]pass{ {name: "nilcheckelim", fn: nilcheckelim}, {name: "prove", fn: prove}, {name: "early fuse", fn: fuseEarly}, + {name: "expand calls", fn: expandCalls, required: true}, {name: "decompose builtin", fn: decomposeBuiltIn, required: true}, {name: "softfloat", fn: softfloat, required: true}, - {name: "expand calls", fn:expandCalls, required: true}, {name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules {name: "dead auto elim", fn: elimDeadAutosGeneric}, {name: "generic deadcode", fn: deadcode, required: true}, // remove dead stores, which otherwise mess up store chain diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 7f01f8047f..a73bcf8fca 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -196,6 +196,14 @@ const ( ClassParamOut // return value ) +const go116lateCallExpansion = true + +// LateCallExpansionEnabledWithin returns true if late call expansion should be tested +// within compilation of a function/method triggered by GOSSAHASH (defaults to "yes"). +func LateCallExpansionEnabledWithin(f *Func) bool { + return go116lateCallExpansion && f.DebugTest // Currently set up for GOSSAHASH bug searches +} + // NewConfig returns a new configuration object for the given architecture. func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config { c := &Config{arch: arch, Types: types} diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 34cff51c00..8456dbab8d 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -4,14 +4,59 @@ package ssa -import "cmd/compile/internal/types" +import ( + "cmd/compile/internal/types" + "cmd/internal/src" + "fmt" + "sort" +) // expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form -// that is more oriented to a platform's ABI. The SelectN operations that extract results are also rewritten into -// more appropriate forms. +// that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into +// more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are +// reached (for now, Strings, Slices, Complex, and Interface are not decomposed because they are rewritten in +// a subsequent phase, but that may need to change for a register ABI in case one of those composite values is +// split between registers and memory). +// +// TODO: when it comes time to use registers, might want to include builtin selectors as well, but currently that happens in lower. func expandCalls(f *Func) { + if !LateCallExpansionEnabledWithin(f) { + return + } canSSAType := f.fe.CanSSA + regSize := f.Config.RegSize sp, _ := f.spSb() + + debug := f.pass.debug > 0 + + // For 32-bit, need to deal with decomposition of 64-bit integers + tUint32 := types.Types[types.TUINT32] + tInt32 := types.Types[types.TINT32] + var hiOffset, lowOffset int64 + if f.Config.BigEndian { + lowOffset = 4 + } else { + hiOffset = 4 + } + pairTypes := func(et types.EType) (tHi, tLo *types.Type) { + tHi = tUint32 + if et == types.TINT64 { + tHi = tInt32 + } + tLo = tUint32 + return + } + + // isAlreadyExpandedAggregateType returns whether a type is an SSA-able "aggregate" (multiple register) type + // that was expanded in an earlier phase (small user-defined arrays and structs, lowered in decomposeUser). + // Other aggregate types are expanded in decomposeBuiltin, which comes later. + isAlreadyExpandedAggregateType := func(t *types.Type) bool { + if !canSSAType(t) { + return false + } + return t.IsStruct() || t.IsArray() || regSize == 4 && t.Size() > 4 && t.IsInteger() + } + // Calls that need lowering have some number of inputs, including a memory input, // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able. @@ -21,80 +66,329 @@ func expandCalls(f *Func) { // With the current ABI, the outputs need to be converted to loads, which will all use the call's // memory output as their input. - // Step 1: find all references to calls as values and rewrite those. - for _, b := range f.Blocks { - for _, v := range b.Values { - switch v.Op { - case OpSelectN: - call := v.Args[0] - aux := call.Aux.(*AuxCall) - which := v.AuxInt - t := v.Type - if which == aux.NResults() { // mem is after the results. - // rewrite v as a Copy of call -- the replacement call will produce a mem. - v.copyOf(call) - } else { - pt := types.NewPtr(t) - if canSSAType(t) { - off := f.ConstOffPtrSP(pt, aux.OffsetOfResult(which), sp) - v.reset(OpLoad) - v.SetArgs2(off, call) + // rewriteSelect recursively walks leaf selector to a root (OpSelectN) through + // a chain of Struct/Array Select operations. If the chain of selectors does not + // end in OpSelectN, it does nothing (this can happen depending on compiler phase ordering). + // It emits the code necessary to implement the leaf select operation that leads to the call. + // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. + var rewriteSelect func(leaf *Value, selector *Value, offset int64) + rewriteSelect = func(leaf *Value, selector *Value, offset int64) { + switch selector.Op { + case OpSelectN: + // TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there. + call := selector.Args[0] + aux := call.Aux.(*AuxCall) + which := selector.AuxInt + if which == aux.NResults() { // mem is after the results. + // rewrite v as a Copy of call -- the replacement call will produce a mem. + leaf.copyOf(call) + } else { + leafType := leaf.Type + pt := types.NewPtr(leafType) + if canSSAType(leafType) { + off := f.ConstOffPtrSP(pt, offset+aux.OffsetOfResult(which), sp) + // Any selection right out of the arg area/registers has to be same Block as call, use call as mem input. + if leaf.Block == call.Block { + leaf.reset(OpLoad) + leaf.SetArgs2(off, call) } else { - panic("Should not have non-SSA-able OpSelectN") + w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call) + leaf.copyOf(w) } + } else { + panic("Should not have non-SSA-able OpSelectN") } - v.Type = t // not right for the mem operand yet, but will be when call is rewritten. + } + case OpStructSelect: + w := selector.Args[0] + if w.Type.Etype != types.TSTRUCT { + fmt.Printf("Bad type for w:\nv=%v\nsel=%v\nw=%v\n,f=%s\n", leaf.LongString(), selector.LongString(), w.LongString(), f.Name) + } + rewriteSelect(leaf, w, offset+w.Type.FieldOff(int(selector.AuxInt))) - case OpSelectNAddr: - call := v.Args[0] - which := v.AuxInt - aux := call.Aux.(*AuxCall) - pt := v.Type - off := f.ConstOffPtrSP(pt, aux.OffsetOfResult(which), sp) - v.copyOf(off) + case OpInt64Hi: + w := selector.Args[0] + rewriteSelect(leaf, w, offset+hiOffset) + + case OpInt64Lo: + w := selector.Args[0] + rewriteSelect(leaf, w, offset+lowOffset) + + case OpArraySelect: + w := selector.Args[0] + rewriteSelect(leaf, w, offset+selector.Type.Size()*selector.AuxInt) + default: + // Ignore dead ends; on 32-bit, these can occur running before decompose builtins. + } + } + + // storeArg converts stores of SSA-able aggregates into a series of stores of smaller types into + // individual parameter slots. + // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. + var storeArg func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value + storeArg = func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value { + switch a.Op { + case OpArrayMake0, OpStructMake0: + return mem + case OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4: + for i := 0; i < t.NumFields(); i++ { + fld := t.Field(i) + mem = storeArg(pos, b, a.Args[i], fld.Type, offset+fld.Offset, mem) + } + return mem + case OpArrayMake1: + return storeArg(pos, b, a.Args[0], t.Elem(), offset, mem) + + case OpInt64Make: + tHi, tLo := pairTypes(t.Etype) + mem = storeArg(pos, b, a.Args[0], tHi, offset+hiOffset, mem) + return storeArg(pos, b, a.Args[1], tLo, offset+lowOffset, mem) + } + dst := f.ConstOffPtrSP(types.NewPtr(t), offset, sp) + x := b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, a, mem) + if debug { + fmt.Printf("storeArg(%v) returns %s\n", a, x.LongString()) + } + return x + } + + // offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP + // TODO should also optimize offsets from SB? + offsetFrom := func(dst *Value, offset int64, t *types.Type) *Value { + pt := types.NewPtr(t) + if offset == 0 && dst.Type == pt { // this is not actually likely + return dst + } + if dst.Op != OpOffPtr { + return dst.Block.NewValue1I(dst.Pos.WithNotStmt(), OpOffPtr, pt, offset, dst) + } + // Simplify OpOffPtr + from := dst.Args[0] + offset += dst.AuxInt + if from == sp { + return f.ConstOffPtrSP(pt, offset, sp) + } + return dst.Block.NewValue1I(dst.Pos.WithNotStmt(), OpOffPtr, pt, offset, from) + } + + // splitStore converts a store of an SSA-able aggregate into a series of smaller stores, emitting + // appropriate Struct/Array Select operations (which will soon go dead) to obtain the parts. + var splitStore func(dst, src, mem, v *Value, t *types.Type, offset int64, firstStorePos src.XPos) *Value + splitStore = func(dst, src, mem, v *Value, t *types.Type, offset int64, firstStorePos src.XPos) *Value { + // TODO might be worth commoning up duplicate selectors, but since they go dead, maybe no point. + pos := v.Pos.WithNotStmt() + switch t.Etype { + case types.TINT64, types.TUINT64: + if t.Width == regSize { + break } + tHi, tLo := pairTypes(t.Etype) + sel := src.Block.NewValue1(pos, OpInt64Hi, tHi, src) + mem = splitStore(dst, sel, mem, v, tHi, offset+hiOffset, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + sel = src.Block.NewValue1(pos, OpInt64Lo, tLo, src) + return splitStore(dst, sel, mem, v, tLo, offset+lowOffset, firstStorePos) + + case types.TARRAY: + elt := t.Elem() + for i := int64(0); i < t.NumElem(); i++ { + sel := src.Block.NewValue1I(pos, OpArraySelect, elt, i, src) + mem = splitStore(dst, sel, mem, v, elt, offset+i*elt.Width, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + } + return mem + case types.TSTRUCT: + if src.Op == OpIData && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize { + // This peculiar test deals with accesses to immediate interface data. + // It works okay because everything is the same size. + // Example code that triggers this can be found in go/constant/value.go, function ToComplex + // v119 (+881) = IData v6 + // v121 (+882) = StaticLECall {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1 + // This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)" + // Guard against "struct{struct{*foo}}" + for t.Etype == types.TSTRUCT && t.NumFields() == 1 { + t = t.Field(0).Type + } + if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY { + f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct in it") + } + break // handle the leaf type. + } + for i := 0; i < t.NumFields(); i++ { + fld := t.Field(i) + sel := src.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), src) + mem = splitStore(dst, sel, mem, v, fld.Type, offset+fld.Offset, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + } + return mem } + // Default, including for aggregates whose single element exactly fills their container + // TODO this will be a problem for cast interfaces containing floats when we move to registers. + x := v.Block.NewValue3A(firstStorePos, OpStore, types.TypeMem, t, offsetFrom(dst, offset, t), src, mem) + if debug { + fmt.Printf("splitStore(%v, %v, %v, %v) returns %s\n", dst, src, mem, v, x.LongString()) + } + return x } - // Step 2: rewrite the calls + // Step 0: rewrite the calls to convert incoming args to stores. for _, b := range f.Blocks { for _, v := range b.Values { switch v.Op { case OpStaticLECall: // Thread the stores on the memory arg - m0 := v.Args[len(v.Args)-1] + m0 := v.MemoryArg() mem := m0 pos := v.Pos.WithNotStmt() aux := v.Aux.(*AuxCall) - auxInt := v.AuxInt for i, a := range v.Args { - if a == m0 { + if a == m0 { // mem is last. break } if a.Op == OpDereference { // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move + // TODO this will be more complicated with registers in the picture. + if a.MemoryArg() != m0 { + f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString()) + } src := a.Args[0] dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(int64(i)), sp) - a.reset(OpMove) - a.Pos = pos - a.Type = types.TypeMem - a.Aux = aux.TypeOfArg(int64(i)) - a.AuxInt = aux.SizeOfArg(int64(i)) - a.SetArgs3(dst, src, mem) - mem = a + if a.Uses == 1 { + a.reset(OpMove) + a.Pos = pos + a.Type = types.TypeMem + a.Aux = aux.TypeOfArg(int64(i)) + a.AuxInt = aux.SizeOfArg(int64(i)) + a.SetArgs3(dst, src, mem) + mem = a + } else { + mem = a.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(int64(i)), dst, src, mem) + mem.AuxInt = aux.SizeOfArg(int64(i)) + } } else { - // Add a new store. - t := aux.TypeOfArg(int64(i)) - dst := f.ConstOffPtrSP(types.NewPtr(t), aux.OffsetOfArg(int64(i)), sp) - mem = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, a, mem) + mem = storeArg(pos, b, a, aux.TypeOfArg(int64(i)), aux.OffsetOfArg(int64(i)), mem) } } - v.reset(OpStaticCall) - v.Type = types.TypeMem - v.Aux = aux - v.AuxInt = auxInt + v.resetArgs() v.SetArgs1(mem) } } } + + // Step 1: any stores of aggregates remaining are believed to be sourced from call results. + // Decompose those stores into a series of smaller stores, adding selection ops as necessary. + for _, b := range f.Blocks { + for _, v := range b.Values { + if v.Op == OpStore { + t := v.Aux.(*types.Type) + if isAlreadyExpandedAggregateType(t) { + dst, src, mem := v.Args[0], v.Args[1], v.Args[2] + mem = splitStore(dst, src, mem, v, t, 0, v.Pos) + v.copyOf(mem) + } + } + } + } + + val2Preds := make(map[*Value]int32) // Used to accumulate dependency graph of selection operations for topological ordering. + + // Step 2: accumulate selection operations for rewrite in topological order. + // Any select-for-addressing applied to call results can be transformed directly. + // TODO this is overkill; with the transformation of aggregate references into series of leaf references, it is only necessary to remember and recurse on the leaves. + for _, b := range f.Blocks { + for _, v := range b.Values { + // Accumulate chains of selectors for processing in topological order + switch v.Op { + case OpStructSelect, OpArraySelect, OpInt64Hi, OpInt64Lo: + w := v.Args[0] + switch w.Op { + case OpStructSelect, OpArraySelect, OpInt64Hi, OpInt64Lo, OpSelectN: + val2Preds[w] += 1 + if debug { + fmt.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w]) + } + } + fallthrough + case OpSelectN: + if _, ok := val2Preds[v]; !ok { + val2Preds[v] = 0 + if debug { + fmt.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) + } + } + case OpSelectNAddr: + // Do these directly, there are no chains of selectors. + call := v.Args[0] + which := v.AuxInt + aux := call.Aux.(*AuxCall) + pt := v.Type + off := f.ConstOffPtrSP(pt, aux.OffsetOfResult(which), sp) + v.copyOf(off) + } + } + } + + // Compilation must be deterministic + var ordered []*Value + less := func(i, j int) bool { return ordered[i].ID < ordered[j].ID } + + // Step 3: Rewrite in topological order. All chains of selectors end up in same block as the call. + for len(val2Preds) > 0 { + ordered = ordered[:0] + for v, n := range val2Preds { + if n == 0 { + ordered = append(ordered, v) + } + } + sort.Slice(ordered, less) + for _, v := range ordered { + for { + w := v.Args[0] + if debug { + fmt.Printf("About to rewrite %s, args[0]=%s\n", v.LongString(), w.LongString()) + } + delete(val2Preds, v) + rewriteSelect(v, v, 0) + v = w + n, ok := val2Preds[v] + if !ok { + break + } + if n != 1 { + val2Preds[v] = n - 1 + break + } + // Loop on new v; val2Preds[v] == 1 will be deleted in that iteration, no need to store zero. + } + } + } + + // Step 4: rewrite the calls themselves, correcting the type + for _, b := range f.Blocks { + for _, v := range b.Values { + switch v.Op { + case OpStaticLECall: + v.Op = OpStaticCall + v.Type = types.TypeMem + } + } + } + + // Step 5: elide any copies introduced. + for _, b := range f.Blocks { + for _, v := range b.Values { + for i, a := range v.Args { + if a.Op != OpCopy { + continue + } + aa := copySource(a) + v.SetArg(i, aa) + for a.Uses == 0 { + b := a.Args[0] + a.reset(OpInvalid) + a = b + } + } + } + } } diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index df54a45b0f..849c9e8967 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -527,7 +527,7 @@ func IsStackAddr(v *Value) bool { v = v.Args[0] } switch v.Op { - case OpSP, OpLocalAddr: + case OpSP, OpLocalAddr, OpSelectNAddr: return true } return false @@ -593,7 +593,7 @@ func IsSanitizerSafeAddr(v *Value) bool { v = v.Args[0] } switch v.Op { - case OpSP, OpLocalAddr: + case OpSP, OpLocalAddr, OpSelectNAddr: // Stack addresses are always safe. return true case OpITab, OpStringPtr, OpGetClosurePtr: @@ -609,7 +609,7 @@ func IsSanitizerSafeAddr(v *Value) bool { // isVolatile reports whether v is a pointer to argument region on stack which // will be clobbered by a function call. func isVolatile(v *Value) bool { - for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy { + for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy || v.Op == OpSelectNAddr { v = v.Args[0] } return v.Op == OpSP -- cgit v1.3 From 75ea9953a812dcb2f64ea949054e529d9748d553 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 5 Aug 2020 11:42:44 -0400 Subject: cmd/compile: enable late expansion for address-of static calls passes run.bash and race.bash (on Darwin-amd64) Change-Id: I2abda9636b681d050e85e88fc357ebe5220d2ba2 Reviewed-on: https://go-review.googlesource.com/c/go/+/246938 Trust: David Chase Run-TryBot: David Chase Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index aebb40568c..e2fbd6f096 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4376,7 +4376,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { case OCALLFUNC: if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC { sym = fn.Sym - if !returnResultAddr && ssa.LateCallExpansionEnabledWithin(s.f) { + if ssa.LateCallExpansionEnabledWithin(s.f) { testLateExpansion = true } break @@ -4393,7 +4393,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { } if k == callNormal { sym = fn.Sym - if !returnResultAddr && ssa.LateCallExpansionEnabledWithin(s.f) { + if ssa.LateCallExpansionEnabledWithin(s.f) { testLateExpansion = true } break @@ -4605,7 +4605,11 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { } fp := res.Field(0) if returnResultAddr { - return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()) + pt := types.NewPtr(fp.Type) + if testLateExpansion { + return s.newValue1I(ssa.OpSelectNAddr, pt, 0, call) + } + return s.constOffPtrSP(pt, fp.Offset+Ctxt.FixedFrameSize()) } if testLateExpansion { -- cgit v1.3 From adef4deeb85ede59201f37f5145763ed55a807f7 Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 7 Aug 2020 22:46:43 -0400 Subject: cmd/compile: enable late expansion for interface calls Includes a few tweaks to Value.copyOf(a) (make it a no-op for a self-copy) and new pattern hack "___" (3 underscores) is like ellipsis, except the replacement doesn't need to have matching ellipsis/underscores. Moved the arg-length check in generated pattern-matching code BEFORE the args are probed, because not all instances of variable length OpFoo will have all the args mentioned in some rule for OpFoo, and when that happens, the compiler panics without the early check. Change-Id: I66de40672b3794a6427890ff96c805a488d783f4 Reviewed-on: https://go-review.googlesource.com/c/go/+/247537 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 22 +++--- src/cmd/compile/internal/ssa/expand_calls.go | 93 ++++++++++++++++---------- src/cmd/compile/internal/ssa/gen/generic.rules | 7 ++ src/cmd/compile/internal/ssa/gen/genericOps.go | 10 +-- src/cmd/compile/internal/ssa/gen/rulegen.go | 22 ++++-- src/cmd/compile/internal/ssa/op.go | 11 +++ src/cmd/compile/internal/ssa/opGen.go | 16 +++++ src/cmd/compile/internal/ssa/rewrite.go | 30 +++++++++ src/cmd/compile/internal/ssa/rewritegeneric.go | 62 +++++++++++++++-- src/cmd/compile/internal/ssa/value.go | 3 + 10 files changed, 221 insertions(+), 55 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index e2fbd6f096..e01ebd6e89 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -2556,7 +2556,7 @@ func (s *state) expr(n *Node) *ssa.Value { return s.addr(n.Left) case ORESULT: - if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall { + if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall { // Do the old thing addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset) return s.rawLoad(n.Type, addr) @@ -4409,6 +4409,9 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { iclosure, rcvr = s.getClosureAndRcvr(fn) if k == callNormal { codeptr = s.load(types.Types[TUINTPTR], iclosure) + if ssa.LateCallExpansionEnabledWithin(s.f) { + testLateExpansion = true + } } else { closure = iclosure } @@ -4555,16 +4558,17 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { codeptr = s.rawLoad(types.Types[TUINTPTR], closure) call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, closure, s.mem()) case codeptr != nil: - call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem()) + if testLateExpansion { + aux := ssa.InterfaceAuxCall(ACArgs, ACResults) + call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr) + call.AddArgs(callArgs...) + } else { + call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem()) + } case sym != nil: if testLateExpansion { - var tys []*types.Type aux := ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults) - for i := int64(0); i < aux.NResults(); i++ { - tys = append(tys, aux.TypeOfResult(i)) - } - tys = append(tys, types.TypeMem) - call = s.newValue0A(ssa.OpStaticLECall, types.NewResults(tys), aux) + call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) call.AddArgs(callArgs...) } else { call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem()) @@ -4713,7 +4717,7 @@ func (s *state) addr(n *Node) *ssa.Value { } case ORESULT: // load return from callee - if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall { + if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall { return s.constOffPtrSP(t, n.Xoffset) } which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset) diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 8456dbab8d..7b1d656b64 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -38,6 +38,7 @@ func expandCalls(f *Func) { } else { hiOffset = 4 } + pairTypes := func(et types.EType) (tHi, tLo *types.Type) { tHi = tUint32 if et == types.TINT64 { @@ -231,46 +232,64 @@ func expandCalls(f *Func) { return x } + rewriteArgs := func(v *Value, firstArg int) *Value { + // Thread the stores on the memory arg + aux := v.Aux.(*AuxCall) + pos := v.Pos.WithNotStmt() + m0 := v.Args[len(v.Args)-1] + mem := m0 + for i, a := range v.Args { + if i < firstArg { + continue + } + if a == m0 { // mem is last. + break + } + auxI := int64(i - firstArg) + if a.Op == OpDereference { + if a.MemoryArg() != m0 { + f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString()) + } + // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move + // TODO this will be more complicated with registers in the picture. + src := a.Args[0] + dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(auxI), sp) + if a.Uses == 1 { + a.reset(OpMove) + a.Pos = pos + a.Type = types.TypeMem + a.Aux = aux.TypeOfArg(auxI) + a.AuxInt = aux.SizeOfArg(auxI) + a.SetArgs3(dst, src, mem) + mem = a + } else { + mem = a.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, src, mem) + mem.AuxInt = aux.SizeOfArg(auxI) + } + } else { + mem = storeArg(pos, v.Block, a, aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI), mem) + } + } + v.resetArgs() + return mem + } + // Step 0: rewrite the calls to convert incoming args to stores. for _, b := range f.Blocks { for _, v := range b.Values { switch v.Op { case OpStaticLECall: - // Thread the stores on the memory arg - m0 := v.MemoryArg() - mem := m0 - pos := v.Pos.WithNotStmt() - aux := v.Aux.(*AuxCall) - for i, a := range v.Args { - if a == m0 { // mem is last. - break - } - if a.Op == OpDereference { - // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move - // TODO this will be more complicated with registers in the picture. - if a.MemoryArg() != m0 { - f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString()) - } - src := a.Args[0] - dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(int64(i)), sp) - if a.Uses == 1 { - a.reset(OpMove) - a.Pos = pos - a.Type = types.TypeMem - a.Aux = aux.TypeOfArg(int64(i)) - a.AuxInt = aux.SizeOfArg(int64(i)) - a.SetArgs3(dst, src, mem) - mem = a - } else { - mem = a.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(int64(i)), dst, src, mem) - mem.AuxInt = aux.SizeOfArg(int64(i)) - } - } else { - mem = storeArg(pos, b, a, aux.TypeOfArg(int64(i)), aux.OffsetOfArg(int64(i)), mem) - } - } - v.resetArgs() + mem := rewriteArgs(v, 0) v.SetArgs1(mem) + case OpClosureLECall: + code := v.Args[0] + context := v.Args[1] + mem := rewriteArgs(v, 2) + v.SetArgs3(code, context, mem) + case OpInterLECall: + code := v.Args[0] + mem := rewriteArgs(v, 1) + v.SetArgs2(code, mem) } } } @@ -370,6 +389,12 @@ func expandCalls(f *Func) { case OpStaticLECall: v.Op = OpStaticCall v.Type = types.TypeMem + case OpClosureLECall: + v.Op = OpClosureCall + v.Type = types.TypeMem + case OpInterLECall: + v.Op = OpInterCall + v.Type = types.TypeMem } } } diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index 39f8cc8889..588077422c 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -2024,6 +2024,13 @@ (InterCall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) && devirt(v, auxCall, itab, off) != nil => (StaticCall [int32(argsize)] {devirt(v, auxCall, itab, off)} mem) +// De-virtualize late-expanded interface calls into late-expanded static calls. +// Note that (ITab (IMake)) doesn't get rewritten until after the first opt pass, +// so this rule should trigger reliably. +// devirtLECall removes the first argument, adds the devirtualized symbol to the AuxCall, and changes the opcode +(InterLECall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) ___) && devirtLESym(v, auxCall, itab, off) != + nil => devirtLECall(v, devirtLESym(v, auxCall, itab, off)) + // Move and Zero optimizations. // Move source and destination may overlap. diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 95edff4c8c..3518dd1e3c 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -389,10 +389,12 @@ var genericOps = []opData{ // TODO(josharian): ClosureCall and InterCall should have Int32 aux // to match StaticCall's 32 bit arg size limit. // TODO(drchase,josharian): could the arg size limit be bundled into the rules for CallOff? - {name: "ClosureCall", argLength: 3, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. - {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. - {name: "InterCall", argLength: 2, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. - {name: "StaticLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. + {name: "ClosureCall", argLength: 3, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. + {name: "StaticCall", argLength: 1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0=memory. auxint=arg size. Returns memory. + {name: "InterCall", argLength: 2, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory. + {name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded closure call. arg0=code pointer, arg1=context ptr, arg2..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. + {name: "StaticLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. + {name: "InterLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded interface call. arg0=code pointer, arg1..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", argLength: 1, typ: "Int16"}, diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go index be51a7c5f8..504ee2bd0a 100644 --- a/src/cmd/compile/internal/ssa/gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -50,8 +50,12 @@ import ( // variable ::= some token // opcode ::= one of the opcodes from the *Ops.go files +// special rules: trailing ellipsis "..." (in the outermost sexpr?) must match on both sides of a rule. +// trailing three underscore "___" in the outermost match sexpr indicate the presence of +// extra ignored args that need not appear in the replacement + // extra conditions is just a chunk of Go that evaluates to a boolean. It may use -// variables declared in the matching sexpr. The variable "v" is predefined to be +// variables declared in the matching tsexpr. The variable "v" is predefined to be // the value matched by the entire rule. // If multiple rules match, the first one in file order is selected. @@ -1019,6 +1023,19 @@ func genMatch0(rr *RuleRewrite, arch arch, match, v string, cnt map[string]int, pos = v + ".Pos" } + // If the last argument is ___, it means "don't care about trailing arguments, really" + // The likely/intended use is for rewrites that are too tricky to express in the existing pattern language + // Do a length check early because long patterns fed short (ultimately not-matching) inputs will + // do an indexing error in pattern-matching. + if op.argLength == -1 { + l := len(args) + if l == 0 || args[l-1] != "___" { + rr.add(breakf("len(%s.Args) != %d", v, l)) + } else if l > 1 && args[l-1] == "___" { + rr.add(breakf("len(%s.Args) < %d", v, l-1)) + } + } + for _, e := range []struct { name, field, dclType string }{ @@ -1159,9 +1176,6 @@ func genMatch0(rr *RuleRewrite, arch arch, match, v string, cnt map[string]int, } } - if op.argLength == -1 { - rr.add(breakf("len(%s.Args) != %d", v, len(args))) - } return pos, checkOp } diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 9b45dd53c7..62f5cddcfc 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -127,6 +127,17 @@ func (a *AuxCall) NResults() int64 { return int64(len(a.results)) } +// LateExpansionResultType returns the result type (including trailing mem) +// for a call that will be expanded later in the SSA phase. +func (a *AuxCall) LateExpansionResultType() *types.Type { + var tys []*types.Type + for i := int64(0); i < a.NResults(); i++ { + tys = append(tys, a.TypeOfResult(i)) + } + tys = append(tys, types.TypeMem) + return types.NewResults(tys) +} + // NArgs returns the number of arguments func (a *AuxCall) NArgs() int64 { return int64(len(a.args)) diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 1fe00c7026..9fe943c2e0 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -2732,7 +2732,9 @@ const ( OpClosureCall OpStaticCall OpInterCall + OpClosureLECall OpStaticLECall + OpInterLECall OpSignExt8to16 OpSignExt8to32 OpSignExt8to64 @@ -34851,6 +34853,13 @@ var opcodeTable = [...]opInfo{ call: true, generic: true, }, + { + name: "ClosureLECall", + auxType: auxCallOff, + argLen: -1, + call: true, + generic: true, + }, { name: "StaticLECall", auxType: auxCallOff, @@ -34858,6 +34867,13 @@ var opcodeTable = [...]opInfo{ call: true, generic: true, }, + { + name: "InterLECall", + auxType: auxCallOff, + argLen: -1, + call: true, + generic: true, + }, { name: "SignExt8to16", argLen: 1, diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index d9c3e455a0..9f4de83a77 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -764,6 +764,36 @@ func devirt(v *Value, aux interface{}, sym Sym, offset int64) *AuxCall { return StaticAuxCall(lsym, va.args, va.results) } +// de-virtualize an InterLECall +// 'sym' is the symbol for the itab +func devirtLESym(v *Value, aux interface{}, sym Sym, offset int64) *obj.LSym { + n, ok := sym.(*obj.LSym) + if !ok { + return nil + } + + f := v.Block.Func + lsym := f.fe.DerefItab(n, offset) + if f.pass.debug > 0 { + if lsym != nil { + f.Warnl(v.Pos, "de-virtualizing call") + } else { + f.Warnl(v.Pos, "couldn't de-virtualize call") + } + } + if lsym == nil { + return nil + } + return lsym +} + +func devirtLECall(v *Value, sym *obj.LSym) *Value { + v.Op = OpStaticLECall + v.Aux.(*AuxCall).Fn = sym + v.RemoveArg(0) + return v +} + // isSamePtr reports whether p1 and p2 point to the same address. func isSamePtr(p1, p2 *Value) bool { if p1 == p2 { diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 925ff53fd1..ade0a69a10 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -124,6 +124,8 @@ func rewriteValuegeneric(v *Value) bool { return rewriteValuegeneric_OpIMake(v) case OpInterCall: return rewriteValuegeneric_OpInterCall(v) + case OpInterLECall: + return rewriteValuegeneric_OpInterLECall(v) case OpIsInBounds: return rewriteValuegeneric_OpIsInBounds(v) case OpIsNonNil: @@ -8522,6 +8524,46 @@ func rewriteValuegeneric_OpInterCall(v *Value) bool { } return false } +func rewriteValuegeneric_OpInterLECall(v *Value) bool { + // match: (InterLECall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) ___) + // cond: devirtLESym(v, auxCall, itab, off) != nil + // result: devirtLECall(v, devirtLESym(v, auxCall, itab, off)) + for { + if len(v.Args) < 1 { + break + } + auxCall := auxToCall(v.Aux) + v_0 := v.Args[0] + if v_0.Op != OpLoad { + break + } + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpOffPtr { + break + } + off := auxIntToInt64(v_0_0.AuxInt) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpITab { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpIMake { + break + } + v_0_0_0_0_0 := v_0_0_0_0.Args[0] + if v_0_0_0_0_0.Op != OpAddr { + break + } + itab := auxToSym(v_0_0_0_0_0.Aux) + v_0_0_0_0_0_0 := v_0_0_0_0_0.Args[0] + if v_0_0_0_0_0_0.Op != OpSB || !(devirtLESym(v, auxCall, itab, off) != nil) { + break + } + v.copyOf(devirtLECall(v, devirtLESym(v, auxCall, itab, off))) + return true + } + return false +} func rewriteValuegeneric_OpIsInBounds(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -18549,6 +18591,9 @@ func rewriteValuegeneric_OpPhi(v *Value) bool { // match: (Phi (Const8 [c]) (Const8 [c])) // result: (Const8 [c]) for { + if len(v.Args) != 2 { + break + } _ = v.Args[1] v_0 := v.Args[0] if v_0.Op != OpConst8 { @@ -18556,7 +18601,7 @@ func rewriteValuegeneric_OpPhi(v *Value) bool { } c := auxIntToInt8(v_0.AuxInt) v_1 := v.Args[1] - if v_1.Op != OpConst8 || auxIntToInt8(v_1.AuxInt) != c || len(v.Args) != 2 { + if v_1.Op != OpConst8 || auxIntToInt8(v_1.AuxInt) != c { break } v.reset(OpConst8) @@ -18566,6 +18611,9 @@ func rewriteValuegeneric_OpPhi(v *Value) bool { // match: (Phi (Const16 [c]) (Const16 [c])) // result: (Const16 [c]) for { + if len(v.Args) != 2 { + break + } _ = v.Args[1] v_0 := v.Args[0] if v_0.Op != OpConst16 { @@ -18573,7 +18621,7 @@ func rewriteValuegeneric_OpPhi(v *Value) bool { } c := auxIntToInt16(v_0.AuxInt) v_1 := v.Args[1] - if v_1.Op != OpConst16 || auxIntToInt16(v_1.AuxInt) != c || len(v.Args) != 2 { + if v_1.Op != OpConst16 || auxIntToInt16(v_1.AuxInt) != c { break } v.reset(OpConst16) @@ -18583,6 +18631,9 @@ func rewriteValuegeneric_OpPhi(v *Value) bool { // match: (Phi (Const32 [c]) (Const32 [c])) // result: (Const32 [c]) for { + if len(v.Args) != 2 { + break + } _ = v.Args[1] v_0 := v.Args[0] if v_0.Op != OpConst32 { @@ -18590,7 +18641,7 @@ func rewriteValuegeneric_OpPhi(v *Value) bool { } c := auxIntToInt32(v_0.AuxInt) v_1 := v.Args[1] - if v_1.Op != OpConst32 || auxIntToInt32(v_1.AuxInt) != c || len(v.Args) != 2 { + if v_1.Op != OpConst32 || auxIntToInt32(v_1.AuxInt) != c { break } v.reset(OpConst32) @@ -18600,6 +18651,9 @@ func rewriteValuegeneric_OpPhi(v *Value) bool { // match: (Phi (Const64 [c]) (Const64 [c])) // result: (Const64 [c]) for { + if len(v.Args) != 2 { + break + } _ = v.Args[1] v_0 := v.Args[0] if v_0.Op != OpConst64 { @@ -18607,7 +18661,7 @@ func rewriteValuegeneric_OpPhi(v *Value) bool { } c := auxIntToInt64(v_0.AuxInt) v_1 := v.Args[1] - if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || len(v.Args) != 2 { + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c { break } v.reset(OpConst64) diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 94b8763d5d..edc43aaae7 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -348,6 +348,9 @@ func (v *Value) reset(op Op) { // It modifies v to be (Copy a). //go:noinline func (v *Value) copyOf(a *Value) { + if v == a { + return + } if v.InCache { v.Block.Func.unCache(v) } -- cgit v1.3 From 8c84dcfe8c6a795ed6ae6be540ffc638841144ce Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 10 Aug 2020 18:30:11 -0400 Subject: cmd/compile: enable late expansion for closure calls This works for "normal" calls. Defer func() and Go func() still pending. RT calls still pending. Change-Id: I29cbdad8c877d12c08bbf7f3f0696611de877da9 Reviewed-on: https://go-review.googlesource.com/c/go/+/247771 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index e01ebd6e89..7e377f9b84 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4386,6 +4386,9 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { // Deferred nil function needs to panic when the function is invoked, // not the point of defer statement. s.maybeNilCheckClosure(closure, k) + if k == callNormal && ssa.LateCallExpansionEnabledWithin(s.f) { + testLateExpansion = true + } } case OCALLMETH: if fn.Op != ODOTMETH { @@ -4556,7 +4559,13 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { // critical that we not clobber any arguments already // stored onto the stack. codeptr = s.rawLoad(types.Types[TUINTPTR], closure) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, closure, s.mem()) + if testLateExpansion { + aux := ssa.ClosureAuxCall(ACArgs, ACResults) + call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, closure) + call.AddArgs(callArgs...) + } else { + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, closure, s.mem()) + } case codeptr != nil: if testLateExpansion { aux := ssa.InterfaceAuxCall(ACArgs, ACResults) -- cgit v1.3 From 4ad5dd63a7b5bc57312a95bd7dcdb6c209456a6f Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 11 Aug 2020 08:39:17 -0400 Subject: cmd/compile: late call expansion for go func and simple defer func Passes run.bash and race.bash on darwin/amd64. Change-Id: Icbccaa2f2e7c3eac7c328c5253f331e598e11542 Reviewed-on: https://go-review.googlesource.com/c/go/+/247898 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 7e377f9b84..7effa9bd4b 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4374,11 +4374,11 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { switch n.Op { case OCALLFUNC: + if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) { + testLateExpansion = true + } if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC { sym = fn.Sym - if ssa.LateCallExpansionEnabledWithin(s.f) { - testLateExpansion = true - } break } closure = s.expr(fn) @@ -4386,19 +4386,16 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { // Deferred nil function needs to panic when the function is invoked, // not the point of defer statement. s.maybeNilCheckClosure(closure, k) - if k == callNormal && ssa.LateCallExpansionEnabledWithin(s.f) { - testLateExpansion = true - } } case OCALLMETH: if fn.Op != ODOTMETH { s.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn) } + if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) { + testLateExpansion = true + } if k == callNormal { sym = fn.Sym - if ssa.LateCallExpansionEnabledWithin(s.f) { - testLateExpansion = true - } break } closure = s.getMethodClosure(fn) @@ -4408,13 +4405,13 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { if fn.Op != ODOTINTER { s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op) } + if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) { + testLateExpansion = true + } var iclosure *ssa.Value iclosure, rcvr = s.getClosureAndRcvr(fn) if k == callNormal { codeptr = s.load(types.Types[TUINTPTR], iclosure) - if ssa.LateCallExpansionEnabledWithin(s.f) { - testLateExpansion = true - } } else { closure = iclosure } @@ -4549,9 +4546,21 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { // call target switch { case k == callDefer: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferproc, ACArgs, ACResults), s.mem()) + aux := ssa.StaticAuxCall(deferproc, ACArgs, ACResults) + if testLateExpansion { + call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) + call.AddArgs(callArgs...) + } else { + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem()) + } case k == callGo: - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(newproc, ACArgs, ACResults), s.mem()) + aux := ssa.StaticAuxCall(newproc, ACArgs, ACResults) + if testLateExpansion { + call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) + call.AddArgs(callArgs...) + } else { + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem()) + } case closure != nil: // rawLoad because loading the code pointer from a // closure is always safe, but IsSanitizerSafeAddr -- cgit v1.3 From f4cbf3477f1456b4d28a7b74b31820ee60b7e6d1 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 31 Aug 2020 14:29:58 -0400 Subject: cmd/compile: allow directory specification for GOSSAFUNC output This was useful for debugging failures occurring during make.bash. The added flush also ensures that any hints in the GOSSAFUNC output are flushed before fatal exit. The environment variable GOSSADIR specifies where the SSA html debugging files should be placed. To avoid collisions, each one is written into the [package].[functionOrMethod].html, where [package] is the filepath separator separated package name, function is the function name, and method is either (*Type).Method, or Type.Method, as appropriate. Directories are created as necessary to make this work. Change-Id: I420927426b618b633bb1ffc51cf0f223b8f6d49c Reviewed-on: https://go-review.googlesource.com/c/go/+/252338 Trust: David Chase Run-TryBot: David Chase Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/main.go | 1 + src/cmd/compile/internal/gc/ssa.go | 10 +++++++++- src/cmd/compile/internal/ssa/compile.go | 10 ++++++++++ src/cmd/compile/internal/ssa/html.go | 13 +++++++++---- 4 files changed, 29 insertions(+), 5 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 7ad3bfe0c8..e4e4ce72fd 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -516,6 +516,7 @@ func Main(archInit func(*Arch)) { } ssaDump = os.Getenv("GOSSAFUNC") + ssaDir = os.Getenv("GOSSADIR") if ssaDump != "" { if strings.HasSuffix(ssaDump, "+") { ssaDump = ssaDump[:len(ssaDump)-1] diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 7effa9bd4b..1d50cefe54 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -9,6 +9,7 @@ import ( "fmt" "html" "os" + "path/filepath" "sort" "bufio" @@ -26,6 +27,7 @@ var ssaConfig *ssa.Config var ssaCaches []ssa.Cache var ssaDump string // early copy of $GOSSAFUNC; the func name to dump output for +var ssaDir string // optional destination for ssa dump file var ssaDumpStdout bool // whether to dump to stdout var ssaDumpCFG string // generate CFGs for these phases const ssaDumpFile = "ssa.html" @@ -346,7 +348,13 @@ func buildssa(fn *Node, worker int) *ssa.Func { s.f.Entry.Pos = fn.Pos if printssa { - s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDumpFile, s.f, ssaDumpCFG) + ssaDF := ssaDumpFile + if ssaDir != "" { + ssaDF = filepath.Join(ssaDir, myimportpath+"."+name+".html") + ssaD := filepath.Dir(ssaDF) + os.MkdirAll(ssaD, 0755) + } + s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDF, s.f, ssaDumpCFG) // TODO: generate and print a mapping from nodes to values and blocks dumpSourcesColumn(s.f.HTMLWriter, fn) s.f.HTMLWriter.WriteAST("AST", astBuf) diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index 3dec1cd85b..0664c0ba46 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -47,6 +47,9 @@ func Compile(f *Func) { stack := make([]byte, 16384) n := runtime.Stack(stack, false) stack = stack[:n] + if f.HTMLWriter != nil { + f.HTMLWriter.flushPhases() + } f.Fatalf("panic during %s while compiling %s:\n\n%v\n\n%s\n", phaseName, f.Name, err, stack) } }() @@ -201,6 +204,13 @@ func (p *pass) addDump(s string) { p.dump[s] = true } +func (p *pass) String() string { + if p == nil { + return "nil pass" + } + return p.name +} + // Run consistency checker between each phase var ( checkEnabled = false diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index c781ca92cc..a9d52fa4ee 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -28,18 +28,23 @@ type HTMLWriter struct { } func NewHTMLWriter(path string, f *Func, cfgMask string) *HTMLWriter { + path = strings.Replace(path, "/", string(filepath.Separator), -1) out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { f.Fatalf("%v", err) } - pwd, err := os.Getwd() - if err != nil { - f.Fatalf("%v", err) + reportPath := path + if !filepath.IsAbs(reportPath) { + pwd, err := os.Getwd() + if err != nil { + f.Fatalf("%v", err) + } + reportPath = filepath.Join(pwd, path) } html := HTMLWriter{ w: out, Func: f, - path: filepath.Join(pwd, path), + path: reportPath, dot: newDotWriter(cfgMask), } html.start() -- cgit v1.3 From fe2cfb74ba6352990f5b41260b99e80f78e4a90a Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 1 Oct 2020 14:49:33 -0700 Subject: all: drop 387 support My last 387 CL. So sad ... ... ... ... not! Fixes #40255 Change-Id: I8d4ddb744b234b8adc735db2f7c3c7b6d8bbdfa4 Reviewed-on: https://go-review.googlesource.com/c/go/+/258957 Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/asm/internal/asm/endtoend_test.go | 7 +- src/cmd/compile/internal/gc/float_test.go | 19 -- src/cmd/compile/internal/gc/go.go | 5 - src/cmd/compile/internal/gc/ssa.go | 15 +- src/cmd/compile/internal/ssa/config.go | 6 - src/cmd/compile/internal/ssa/gen/386.rules | 10 +- src/cmd/compile/internal/ssa/gen/386Ops.go | 14 - src/cmd/compile/internal/ssa/opGen.go | 13 - src/cmd/compile/internal/ssa/regalloc.go | 14 - src/cmd/compile/internal/ssa/rewrite386.go | 84 ++---- src/cmd/compile/internal/x86/387.go | 403 ----------------------------- src/cmd/compile/internal/x86/galign.go | 17 +- src/cmd/compile/internal/x86/ssa.go | 2 - src/cmd/dist/build.go | 15 -- src/cmd/dist/buildruntime.go | 2 - src/cmd/dist/cpuid_386.s | 16 -- src/cmd/dist/cpuid_amd64.s | 16 -- src/cmd/dist/cpuid_default.s | 10 - src/cmd/dist/util_gc.go | 12 - src/cmd/dist/util_gccgo.go | 13 - src/cmd/go/alldocs.go | 3 - src/cmd/go/internal/cfg/cfg.go | 3 - src/cmd/go/internal/envcmd/env.go | 5 +- src/cmd/go/internal/help/helpdoc.go | 3 - src/cmd/go/internal/work/exec.go | 4 +- src/cmd/internal/objabi/util.go | 9 +- src/internal/cfg/cfg.go | 1 - src/reflect/all_test.go | 18 -- src/runtime/mkpreempt.go | 33 +-- src/runtime/preempt_386.s | 45 ++-- src/runtime/vlrt.go | 5 +- test/codegen/arithmetic.go | 6 +- test/codegen/floats.go | 19 +- test/codegen/math.go | 2 +- test/codegen/memops.go | 32 +-- test/run.go | 12 +- 36 files changed, 97 insertions(+), 796 deletions(-) delete mode 100644 src/cmd/compile/internal/x86/387.go delete mode 100644 src/cmd/dist/cpuid_386.s delete mode 100644 src/cmd/dist/cpuid_amd64.s delete mode 100644 src/cmd/dist/cpuid_default.s (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index 0759b7d10f..15202dc5dc 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -353,12 +353,7 @@ func testErrors(t *testing.T, goarch, file string) { } func Test386EndToEnd(t *testing.T) { - defer func(old string) { objabi.GO386 = old }(objabi.GO386) - for _, go386 := range []string{"387", "sse2"} { - t.Logf("GO386=%v", go386) - objabi.GO386 = go386 - testEndToEnd(t, "386", "386") - } + testEndToEnd(t, "386", "386") } func TestARMEndToEnd(t *testing.T) { diff --git a/src/cmd/compile/internal/gc/float_test.go b/src/cmd/compile/internal/gc/float_test.go index 6ae363be22..c619d25705 100644 --- a/src/cmd/compile/internal/gc/float_test.go +++ b/src/cmd/compile/internal/gc/float_test.go @@ -6,17 +6,9 @@ package gc import ( "math" - "os" - "runtime" "testing" ) -// For GO386=387, make sure fucomi* opcodes are not used -// for comparison operations. -// Note that this test will fail only on a Pentium MMX -// processor (with GOARCH=386 GO386=387), as it just runs -// some code and looks for an unimplemented instruction fault. - //go:noinline func compare1(a, b float64) bool { return a < b @@ -137,9 +129,6 @@ func TestFloatCompareFolded(t *testing.T) { } } -// For GO386=387, make sure fucomi* opcodes are not used -// for float->int conversions. - //go:noinline func cvt1(a float64) uint64 { return uint64(a) @@ -370,14 +359,6 @@ func TestFloat32StoreToLoadConstantFold(t *testing.T) { // are not converted to quiet NaN (qNaN) values during compilation. // See issue #27193 for more information. - // TODO: this method for detecting 387 won't work if the compiler has been - // built using GOARCH=386 GO386=387 and either the target is a different - // architecture or the GO386=387 environment variable is not set when the - // test is run. - if runtime.GOARCH == "386" && os.Getenv("GO386") == "387" { - t.Skip("signaling NaNs are not propagated on 387 (issue #27516)") - } - // signaling NaNs { const nan = uint32(0x7f800001) // sNaN diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 9079ce2afc..2fbdf71055 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -259,7 +259,6 @@ type Arch struct { REGSP int MAXWIDTH int64 - Use387 bool // should 386 backend use 387 FP instructions instead of sse2. SoftFloat bool PadFrame func(int64) int64 @@ -328,10 +327,6 @@ var ( BoundsCheckFunc [ssa.BoundsKindCount]*obj.LSym ExtendCheckFunc [ssa.BoundsKindCount]*obj.LSym - // GO386=387 - ControlWord64trunc, - ControlWord32 *obj.LSym - // Wasm WasmMove, WasmZero, diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 1d50cefe54..32394c4b1a 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -62,9 +62,6 @@ func initssaconfig() { _ = types.NewPtr(types.Errortype) // *error types.NewPtrCacheEnabled = false ssaConfig = ssa.NewConfig(thearch.LinkArch.Name, *types_, Ctxt, Debug['N'] == 0) - if thearch.LinkArch.Name == "386" { - ssaConfig.Set387(thearch.Use387) - } ssaConfig.SoftFloat = thearch.SoftFloat ssaConfig.Race = flag_race ssaCaches = make([]ssa.Cache, nBackendWorkers) @@ -175,10 +172,6 @@ func initssaconfig() { ExtendCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicExtendSlice3CU") } - // GO386=387 runtime definitions - ControlWord64trunc = sysvar("controlWord64trunc") // uint16 - ControlWord32 = sysvar("controlWord32") // uint16 - // Wasm (all asm funcs with special ABIs) WasmMove = sysvar("wasmMove") WasmZero = sysvar("wasmZero") @@ -5946,9 +5939,7 @@ type SSAGenState struct { // bstart remembers where each block starts (indexed by block ID) bstart []*obj.Prog - // 387 port: maps from SSE registers (REG_X?) to 387 registers (REG_F?) - SSEto387 map[int16]int16 - // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include x86-387, PPC, and Sparc V8. + // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include PPC and Sparc V8. ScratchFpMem *Node maxarg int64 // largest frame size for arguments to calls made by the function @@ -6115,10 +6106,6 @@ func genssa(f *ssa.Func, pp *Progs) { progToBlock[s.pp.next] = f.Blocks[0] } - if thearch.Use387 { - s.SSEto387 = map[int16]int16{} - } - s.ScratchFpMem = e.scratchFpMem if Ctxt.Flag_locationlists { diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 88a406deb9..649b5ba820 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -38,7 +38,6 @@ type Config struct { useSSE bool // Use SSE for non-float operations useAvg bool // Use optimizations that need Avg* operations useHmul bool // Use optimizations that need Hmul* operations - use387 bool // GO386=387 SoftFloat bool // Race bool // race detector enabled NeedsFpScratch bool // No direct move between GP and FP register sets @@ -387,9 +386,4 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config return c } -func (c *Config) Set387(b bool) { - c.NeedsFpScratch = b - c.use387 = b -} - func (c *Config) Ctxt() *obj.Link { return c.ctxt } diff --git a/src/cmd/compile/internal/ssa/gen/386.rules b/src/cmd/compile/internal/ssa/gen/386.rules index 4a8244eb27..6a0b87cab4 100644 --- a/src/cmd/compile/internal/ssa/gen/386.rules +++ b/src/cmd/compile/internal/ssa/gen/386.rules @@ -38,10 +38,8 @@ (Xor(32|16|8) ...) => (XORL ...) (Neg(32|16|8) ...) => (NEGL ...) -(Neg32F x) && !config.use387 => (PXOR x (MOVSSconst [float32(math.Copysign(0, -1))])) -(Neg64F x) && !config.use387 => (PXOR x (MOVSDconst [math.Copysign(0, -1)])) -(Neg32F x) && config.use387 => (FCHS x) -(Neg64F x) && config.use387 => (FCHS x) +(Neg32F x) => (PXOR x (MOVSSconst [float32(math.Copysign(0, -1))])) +(Neg64F x) => (PXOR x (MOVSDconst [math.Copysign(0, -1)])) (Com(32|16|8) ...) => (NOTL ...) @@ -670,8 +668,8 @@ // Merge load/store to op ((ADD|AND|OR|XOR|SUB|MUL)L x l:(MOVLload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|AND|OR|XOR|SUB|MUL)Lload x [off] {sym} ptr mem) -((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) => ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem) -((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) => ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem) +((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem) +((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem) (MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lload x [off] {sym} ptr mem) mem) && y.Uses==1 && clobber(y) => ((ADD|AND|OR|XOR)Lmodify [off] {sym} ptr x mem) (MOVLstore {sym} [off] ptr y:((ADD|SUB|AND|OR|XOR)L l:(MOVLload [off] {sym} ptr mem) x) mem) && y.Uses==1 && l.Uses==1 && clobber(y, l) => ((ADD|SUB|AND|OR|XOR)Lmodify [off] {sym} ptr x mem) diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index ddabde7d3d..737b99c371 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -51,17 +51,6 @@ var regNames386 = []string{ "SB", } -// Notes on 387 support. -// - The 387 has a weird stack-register setup for floating-point registers. -// We use these registers when SSE registers are not available (when GO386=387). -// - We use the same register names (X0-X7) but they refer to the 387 -// floating-point registers. That way, most of the SSA backend is unchanged. -// - The instruction generation pass maintains an SSE->387 register mapping. -// This mapping is updated whenever the FP stack is pushed or popped so that -// we can always find a given SSE register even when the TOS pointer has changed. -// - To facilitate the mapping from SSE to 387, we enforce that -// every basic block starts and ends with an empty floating-point stack. - func init() { // Make map from reg names to reg integers. if len(regNames386) > 64 { @@ -552,9 +541,6 @@ func init() { {name: "FlagGT_UGT"}, // signed > and unsigned < {name: "FlagGT_ULT"}, // signed > and unsigned > - // Special op for -x on 387 - {name: "FCHS", argLength: 1, reg: fp11}, - // Special ops for PIC floating-point constants. // MOVSXconst1 loads the address of the constant-pool entry into a register. // MOVSXconst2 loads the constant from that address. diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 9fe943c2e0..d7d2b24a48 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -536,7 +536,6 @@ const ( Op386FlagLT_UGT Op386FlagGT_UGT Op386FlagGT_ULT - Op386FCHS Op386MOVSSconst1 Op386MOVSDconst1 Op386MOVSSconst2 @@ -6060,18 +6059,6 @@ var opcodeTable = [...]opInfo{ argLen: 0, reg: regInfo{}, }, - { - name: "FCHS", - argLen: 1, - reg: regInfo{ - inputs: []inputInfo{ - {0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7 - }, - outputs: []outputInfo{ - {0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7 - }, - }, - }, { name: "MOVSSconst1", auxType: auxFloat32, diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 64c6aed3e7..691530ec0b 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -625,9 +625,6 @@ func (s *regAllocState) init(f *Func) { s.f.fe.Fatalf(src.NoXPos, "arch %s not implemented", s.f.Config.arch) } } - if s.f.Config.use387 { - s.allocatable &^= 1 << 15 // X7 disallowed (one 387 register is used as scratch space during SSE->387 generation in ../x86/387.go) - } // Linear scan register allocation can be influenced by the order in which blocks appear. // Decouple the register allocation order from the generated block order. @@ -1024,9 +1021,6 @@ func (s *regAllocState) regalloc(f *Func) { if phiRegs[i] != noRegister { continue } - if s.f.Config.use387 && v.Type.IsFloat() { - continue // 387 can't handle floats in registers between blocks - } m := s.compatRegs(v.Type) &^ phiUsed &^ s.used if m != 0 { r := pickReg(m) @@ -1528,11 +1522,6 @@ func (s *regAllocState) regalloc(f *Func) { s.freeUseRecords = u } - // Spill any values that can't live across basic block boundaries. - if s.f.Config.use387 { - s.freeRegs(s.f.Config.fpRegMask) - } - // If we are approaching a merge point and we are the primary // predecessor of it, find live values that we use soon after // the merge point and promote them to registers now. @@ -1562,9 +1551,6 @@ func (s *regAllocState) regalloc(f *Func) { continue } v := s.orig[vid] - if s.f.Config.use387 && v.Type.IsFloat() { - continue // 387 can't handle floats in registers between blocks - } m := s.compatRegs(v.Type) &^ s.used if m&^desired.avoid != 0 { m &^= desired.avoid diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go index fc1e0541b2..0f08160f44 100644 --- a/src/cmd/compile/internal/ssa/rewrite386.go +++ b/src/cmd/compile/internal/ssa/rewrite386.go @@ -1310,10 +1310,8 @@ func rewriteValue386_Op386ADDLmodify(v *Value) bool { func rewriteValue386_Op386ADDSD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (ADDSD x l:(MOVSDload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (ADDSDload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -1326,7 +1324,7 @@ func rewriteValue386_Op386ADDSD(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } v.reset(Op386ADDSDload) @@ -1395,10 +1393,8 @@ func rewriteValue386_Op386ADDSDload(v *Value) bool { func rewriteValue386_Op386ADDSS(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (ADDSS x l:(MOVSSload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (ADDSSload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -1411,7 +1407,7 @@ func rewriteValue386_Op386ADDSS(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } v.reset(Op386ADDSSload) @@ -2640,10 +2636,8 @@ func rewriteValue386_Op386CMPWload(v *Value) bool { func rewriteValue386_Op386DIVSD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (DIVSD x l:(MOVSDload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (DIVSDload x [off] {sym} ptr mem) for { x := v_0 @@ -2655,7 +2649,7 @@ func rewriteValue386_Op386DIVSD(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { break } v.reset(Op386DIVSDload) @@ -2722,10 +2716,8 @@ func rewriteValue386_Op386DIVSDload(v *Value) bool { func rewriteValue386_Op386DIVSS(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (DIVSS x l:(MOVSSload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (DIVSSload x [off] {sym} ptr mem) for { x := v_0 @@ -2737,7 +2729,7 @@ func rewriteValue386_Op386DIVSS(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { break } v.reset(Op386DIVSSload) @@ -6104,10 +6096,8 @@ func rewriteValue386_Op386MULLload(v *Value) bool { func rewriteValue386_Op386MULSD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (MULSD x l:(MOVSDload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (MULSDload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -6120,7 +6110,7 @@ func rewriteValue386_Op386MULSD(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } v.reset(Op386MULSDload) @@ -6189,10 +6179,8 @@ func rewriteValue386_Op386MULSDload(v *Value) bool { func rewriteValue386_Op386MULSS(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (MULSS x l:(MOVSSload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (MULSSload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -6205,7 +6193,7 @@ func rewriteValue386_Op386MULSS(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } v.reset(Op386MULSSload) @@ -8187,10 +8175,8 @@ func rewriteValue386_Op386SUBLmodify(v *Value) bool { func rewriteValue386_Op386SUBSD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (SUBSD x l:(MOVSDload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (SUBSDload x [off] {sym} ptr mem) for { x := v_0 @@ -8202,7 +8188,7 @@ func rewriteValue386_Op386SUBSD(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { break } v.reset(Op386SUBSDload) @@ -8269,10 +8255,8 @@ func rewriteValue386_Op386SUBSDload(v *Value) bool { func rewriteValue386_Op386SUBSS(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - config := b.Func.Config // match: (SUBSS x l:(MOVSSload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (SUBSSload x [off] {sym} ptr mem) for { x := v_0 @@ -8284,7 +8268,7 @@ func rewriteValue386_Op386SUBSS(v *Value) bool { sym := auxToSym(l.Aux) mem := l.Args[1] ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) { + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { break } v.reset(Op386SUBSSload) @@ -10043,68 +10027,32 @@ func rewriteValue386_OpMove(v *Value) bool { func rewriteValue386_OpNeg32F(v *Value) bool { v_0 := v.Args[0] b := v.Block - config := b.Func.Config typ := &b.Func.Config.Types // match: (Neg32F x) - // cond: !config.use387 // result: (PXOR x (MOVSSconst [float32(math.Copysign(0, -1))])) for { x := v_0 - if !(!config.use387) { - break - } v.reset(Op386PXOR) v0 := b.NewValue0(v.Pos, Op386MOVSSconst, typ.Float32) v0.AuxInt = float32ToAuxInt(float32(math.Copysign(0, -1))) v.AddArg2(x, v0) return true } - // match: (Neg32F x) - // cond: config.use387 - // result: (FCHS x) - for { - x := v_0 - if !(config.use387) { - break - } - v.reset(Op386FCHS) - v.AddArg(x) - return true - } - return false } func rewriteValue386_OpNeg64F(v *Value) bool { v_0 := v.Args[0] b := v.Block - config := b.Func.Config typ := &b.Func.Config.Types // match: (Neg64F x) - // cond: !config.use387 // result: (PXOR x (MOVSDconst [math.Copysign(0, -1)])) for { x := v_0 - if !(!config.use387) { - break - } v.reset(Op386PXOR) v0 := b.NewValue0(v.Pos, Op386MOVSDconst, typ.Float64) v0.AuxInt = float64ToAuxInt(math.Copysign(0, -1)) v.AddArg2(x, v0) return true } - // match: (Neg64F x) - // cond: config.use387 - // result: (FCHS x) - for { - x := v_0 - if !(config.use387) { - break - } - v.reset(Op386FCHS) - v.AddArg(x) - return true - } - return false } func rewriteValue386_OpNeq16(v *Value) bool { v_1 := v.Args[1] diff --git a/src/cmd/compile/internal/x86/387.go b/src/cmd/compile/internal/x86/387.go deleted file mode 100644 index 594adb2cd5..0000000000 --- a/src/cmd/compile/internal/x86/387.go +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright 2016 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 x86 - -import ( - "cmd/compile/internal/gc" - "cmd/compile/internal/ssa" - "cmd/compile/internal/types" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "math" -) - -// Generates code for v using 387 instructions. -func ssaGenValue387(s *gc.SSAGenState, v *ssa.Value) { - // The SSA compiler pretends that it has an SSE backend. - // If we don't have one of those, we need to translate - // all the SSE ops to equivalent 387 ops. That's what this - // function does. - - switch v.Op { - case ssa.Op386MOVSSconst, ssa.Op386MOVSDconst: - iv := uint64(v.AuxInt) - if iv == 0x0000000000000000 { // +0.0 - s.Prog(x86.AFLDZ) - } else if iv == 0x3ff0000000000000 { // +1.0 - s.Prog(x86.AFLD1) - } else if iv == 0x8000000000000000 { // -0.0 - s.Prog(x86.AFLDZ) - s.Prog(x86.AFCHS) - } else if iv == 0xbff0000000000000 { // -1.0 - s.Prog(x86.AFLD1) - s.Prog(x86.AFCHS) - } else if iv == 0x400921fb54442d18 { // +pi - s.Prog(x86.AFLDPI) - } else if iv == 0xc00921fb54442d18 { // -pi - s.Prog(x86.AFLDPI) - s.Prog(x86.AFCHS) - } else { // others - p := s.Prog(loadPush(v.Type)) - p.From.Type = obj.TYPE_FCONST - p.From.Val = math.Float64frombits(iv) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - } - popAndSave(s, v) - - case ssa.Op386MOVSSconst2, ssa.Op386MOVSDconst2: - p := s.Prog(loadPush(v.Type)) - p.From.Type = obj.TYPE_MEM - p.From.Reg = v.Args[0].Reg() - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - popAndSave(s, v) - - case ssa.Op386MOVSSload, ssa.Op386MOVSDload, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1, ssa.Op386MOVSSloadidx4, ssa.Op386MOVSDloadidx8: - p := s.Prog(loadPush(v.Type)) - p.From.Type = obj.TYPE_MEM - p.From.Reg = v.Args[0].Reg() - gc.AddAux(&p.From, v) - switch v.Op { - case ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1: - p.From.Scale = 1 - p.From.Index = v.Args[1].Reg() - if p.From.Index == x86.REG_SP { - p.From.Reg, p.From.Index = p.From.Index, p.From.Reg - } - case ssa.Op386MOVSSloadidx4: - p.From.Scale = 4 - p.From.Index = v.Args[1].Reg() - case ssa.Op386MOVSDloadidx8: - p.From.Scale = 8 - p.From.Index = v.Args[1].Reg() - } - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - popAndSave(s, v) - - case ssa.Op386MOVSSstore, ssa.Op386MOVSDstore: - // Push to-be-stored value on top of stack. - push(s, v.Args[1]) - - // Pop and store value. - var op obj.As - switch v.Op { - case ssa.Op386MOVSSstore: - op = x86.AFMOVFP - case ssa.Op386MOVSDstore: - op = x86.AFMOVDP - } - p := s.Prog(op) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_MEM - p.To.Reg = v.Args[0].Reg() - gc.AddAux(&p.To, v) - - case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSSstoreidx4, ssa.Op386MOVSDstoreidx8: - push(s, v.Args[2]) - var op obj.As - switch v.Op { - case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSSstoreidx4: - op = x86.AFMOVFP - case ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSDstoreidx8: - op = x86.AFMOVDP - } - p := s.Prog(op) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_MEM - p.To.Reg = v.Args[0].Reg() - gc.AddAux(&p.To, v) - switch v.Op { - case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1: - p.To.Scale = 1 - p.To.Index = v.Args[1].Reg() - if p.To.Index == x86.REG_SP { - p.To.Reg, p.To.Index = p.To.Index, p.To.Reg - } - case ssa.Op386MOVSSstoreidx4: - p.To.Scale = 4 - p.To.Index = v.Args[1].Reg() - case ssa.Op386MOVSDstoreidx8: - p.To.Scale = 8 - p.To.Index = v.Args[1].Reg() - } - - case ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD, - ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD: - if v.Reg() != v.Args[0].Reg() { - v.Fatalf("input[0] and output not in same register %s", v.LongString()) - } - - // Push arg1 on top of stack - push(s, v.Args[1]) - - // Set precision if needed. 64 bits is the default. - switch v.Op { - case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS: - // Save AX so we can use it as scratch space. - p := s.Prog(x86.AMOVL) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_AX - s.AddrScratch(&p.To) - // Install a 32-bit version of the control word. - installControlWord(s, gc.ControlWord32, x86.REG_AX) - // Restore AX. - p = s.Prog(x86.AMOVL) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_AX - } - - var op obj.As - switch v.Op { - case ssa.Op386ADDSS, ssa.Op386ADDSD: - op = x86.AFADDDP - case ssa.Op386SUBSS, ssa.Op386SUBSD: - op = x86.AFSUBDP - case ssa.Op386MULSS, ssa.Op386MULSD: - op = x86.AFMULDP - case ssa.Op386DIVSS, ssa.Op386DIVSD: - op = x86.AFDIVDP - } - p := s.Prog(op) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_REG - p.To.Reg = s.SSEto387[v.Reg()] + 1 - - // Restore precision if needed. - switch v.Op { - case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS: - restoreControlWord(s) - } - - case ssa.Op386UCOMISS, ssa.Op386UCOMISD: - push(s, v.Args[0]) - - // Compare. - p := s.Prog(x86.AFUCOMP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_REG - p.To.Reg = s.SSEto387[v.Args[1].Reg()] + 1 - - // Save AX. - p = s.Prog(x86.AMOVL) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_AX - s.AddrScratch(&p.To) - - // Move status word into AX. - p = s.Prog(x86.AFSTSW) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_AX - - // Then move the flags we need to the integer flags. - s.Prog(x86.ASAHF) - - // Restore AX. - p = s.Prog(x86.AMOVL) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_AX - - case ssa.Op386SQRTSD: - push(s, v.Args[0]) - s.Prog(x86.AFSQRT) - popAndSave(s, v) - - case ssa.Op386FCHS: - push(s, v.Args[0]) - s.Prog(x86.AFCHS) - popAndSave(s, v) - - case ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD: - p := s.Prog(x86.AMOVL) - p.From.Type = obj.TYPE_REG - p.From.Reg = v.Args[0].Reg() - s.AddrScratch(&p.To) - p = s.Prog(x86.AFMOVL) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - popAndSave(s, v) - - case ssa.Op386CVTTSD2SL, ssa.Op386CVTTSS2SL: - push(s, v.Args[0]) - - // Load control word which truncates (rounds towards zero). - installControlWord(s, gc.ControlWord64trunc, v.Reg()) - - // Now do the conversion. - p := s.Prog(x86.AFMOVLP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - s.AddrScratch(&p.To) - p = s.Prog(x86.AMOVL) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = v.Reg() - - // Restore control word. - restoreControlWord(s) - - case ssa.Op386CVTSS2SD: - // float32 -> float64 is a nop - push(s, v.Args[0]) - popAndSave(s, v) - - case ssa.Op386CVTSD2SS: - // Round to nearest float32. - push(s, v.Args[0]) - p := s.Prog(x86.AFMOVFP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - s.AddrScratch(&p.To) - p = s.Prog(x86.AFMOVF) - s.AddrScratch(&p.From) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - popAndSave(s, v) - - case ssa.OpLoadReg: - if !v.Type.IsFloat() { - ssaGenValue(s, v) - return - } - // Load+push the value we need. - p := s.Prog(loadPush(v.Type)) - gc.AddrAuto(&p.From, v.Args[0]) - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - // Move the value to its assigned register. - popAndSave(s, v) - - case ssa.OpStoreReg: - if !v.Type.IsFloat() { - ssaGenValue(s, v) - return - } - push(s, v.Args[0]) - var op obj.As - switch v.Type.Size() { - case 4: - op = x86.AFMOVFP - case 8: - op = x86.AFMOVDP - } - p := s.Prog(op) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - gc.AddrAuto(&p.To, v) - - case ssa.OpCopy: - if !v.Type.IsFloat() { - ssaGenValue(s, v) - return - } - push(s, v.Args[0]) - popAndSave(s, v) - - case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter: - flush387(s) // Calls must empty the FP stack. - fallthrough // then issue the call as normal - default: - ssaGenValue(s, v) - } -} - -// push pushes v onto the floating-point stack. v must be in a register. -func push(s *gc.SSAGenState, v *ssa.Value) { - p := s.Prog(x86.AFMOVD) - p.From.Type = obj.TYPE_REG - p.From.Reg = s.SSEto387[v.Reg()] - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 -} - -// popAndSave pops a value off of the floating-point stack and stores -// it in the register assigned to v. -func popAndSave(s *gc.SSAGenState, v *ssa.Value) { - r := v.Reg() - if _, ok := s.SSEto387[r]; ok { - // Pop value, write to correct register. - p := s.Prog(x86.AFMOVDP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_REG - p.To.Reg = s.SSEto387[v.Reg()] + 1 - } else { - // Don't actually pop value. This 387 register is now the - // new home for the not-yet-assigned-a-home SSE register. - // Increase the register mapping of all other registers by one. - for rSSE, r387 := range s.SSEto387 { - s.SSEto387[rSSE] = r387 + 1 - } - s.SSEto387[r] = x86.REG_F0 - } -} - -// loadPush returns the opcode for load+push of the given type. -func loadPush(t *types.Type) obj.As { - if t.Size() == 4 { - return x86.AFMOVF - } - return x86.AFMOVD -} - -// flush387 removes all entries from the 387 floating-point stack. -func flush387(s *gc.SSAGenState) { - for k := range s.SSEto387 { - p := s.Prog(x86.AFMOVDP) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_F0 - p.To.Type = obj.TYPE_REG - p.To.Reg = x86.REG_F0 - delete(s.SSEto387, k) - } -} - -func ssaGenBlock387(s *gc.SSAGenState, b, next *ssa.Block) { - // Empty the 387's FP stack before the block ends. - flush387(s) - - ssaGenBlock(s, b, next) -} - -// installControlWord saves the current floating-point control -// word and installs a new one loaded from cw. -// scratchReg must be an unused register. -// This call must be paired with restoreControlWord. -// Bytes 4-5 of the scratch space (s.AddrScratch) are used between -// this call and restoreControlWord. -func installControlWord(s *gc.SSAGenState, cw *obj.LSym, scratchReg int16) { - // Save current control word. - p := s.Prog(x86.AFSTCW) - s.AddrScratch(&p.To) - p.To.Offset += 4 - - // Materialize address of new control word. - // Note: this must be a seperate instruction to handle PIE correctly. - // See issue 41503. - p = s.Prog(x86.ALEAL) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = cw - p.To.Type = obj.TYPE_REG - p.To.Reg = scratchReg - - // Load replacement control word. - p = s.Prog(x86.AFLDCW) - p.From.Type = obj.TYPE_MEM - p.From.Reg = scratchReg -} -func restoreControlWord(s *gc.SSAGenState) { - p := s.Prog(x86.AFLDCW) - s.AddrScratch(&p.From) - p.From.Offset += 4 -} diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go index 56c6989d93..2d20b6a6d0 100644 --- a/src/cmd/compile/internal/x86/galign.go +++ b/src/cmd/compile/internal/x86/galign.go @@ -7,26 +7,13 @@ package x86 import ( "cmd/compile/internal/gc" "cmd/internal/obj/x86" - "cmd/internal/objabi" - "fmt" - "os" ) func Init(arch *gc.Arch) { arch.LinkArch = &x86.Link386 arch.REGSP = x86.REGSP - switch v := objabi.GO386; v { - case "387": - arch.Use387 = true - arch.SSAGenValue = ssaGenValue387 - arch.SSAGenBlock = ssaGenBlock387 - case "sse2": - arch.SSAGenValue = ssaGenValue - arch.SSAGenBlock = ssaGenBlock - default: - fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v) - gc.Exit(1) - } + arch.SSAGenValue = ssaGenValue + arch.SSAGenBlock = ssaGenBlock arch.MAXWIDTH = (1 << 32) - 1 arch.ZeroRange = zerorange diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go index c21ac32297..74a4570770 100644 --- a/src/cmd/compile/internal/x86/ssa.go +++ b/src/cmd/compile/internal/x86/ssa.go @@ -852,8 +852,6 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers gc.Warnl(v.Pos, "generated nil check") } - case ssa.Op386FCHS: - v.Fatalf("FCHS in non-387 mode") case ssa.OpClobber: p := s.Prog(x86.AMOVL) p.From.Type = obj.TYPE_CONST diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 3ac742fa55..5d62c1e8fa 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -30,7 +30,6 @@ var ( gohostos string goos string goarm string - go386 string gomips string gomips64 string goppc64 string @@ -142,16 +141,6 @@ func xinit() { } goarm = b - b = os.Getenv("GO386") - if b == "" { - if cansse2() { - b = "sse2" - } else { - b = "387" - } - } - go386 = b - b = os.Getenv("GOMIPS") if b == "" { b = "hardfloat" @@ -223,7 +212,6 @@ func xinit() { defaultldso = os.Getenv("GO_LDSO") // For tools being invoked but also for os.ExpandEnv. - os.Setenv("GO386", go386) os.Setenv("GOARCH", goarch) os.Setenv("GOARM", goarm) os.Setenv("GOHOSTARCH", gohostarch) @@ -1165,9 +1153,6 @@ func cmdenv() { if goarch == "arm" { xprintf(format, "GOARM", goarm) } - if goarch == "386" { - xprintf(format, "GO386", go386) - } if goarch == "mips" || goarch == "mipsle" { xprintf(format, "GOMIPS", gomips) } diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go index 2744951597..67d1d72db4 100644 --- a/src/cmd/dist/buildruntime.go +++ b/src/cmd/dist/buildruntime.go @@ -41,7 +41,6 @@ func mkzversion(dir, file string) { // package objabi // // const defaultGOROOT = -// const defaultGO386 = // const defaultGOARM = // const defaultGOMIPS = // const defaultGOMIPS64 = @@ -70,7 +69,6 @@ func mkzbootstrap(file string) { fmt.Fprintln(&buf) fmt.Fprintf(&buf, "import \"runtime\"\n") fmt.Fprintln(&buf) - fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386) fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm) fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips) fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64) diff --git a/src/cmd/dist/cpuid_386.s b/src/cmd/dist/cpuid_386.s deleted file mode 100644 index 65fbb2dcb7..0000000000 --- a/src/cmd/dist/cpuid_386.s +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 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. - -// +build !gccgo - -TEXT ·cpuid(SB),$0-8 - MOVL ax+4(FP), AX - CPUID - MOVL info+0(FP), DI - MOVL AX, 0(DI) - MOVL BX, 4(DI) - MOVL CX, 8(DI) - MOVL DX, 12(DI) - RET - diff --git a/src/cmd/dist/cpuid_amd64.s b/src/cmd/dist/cpuid_amd64.s deleted file mode 100644 index ea0b9d4dc9..0000000000 --- a/src/cmd/dist/cpuid_amd64.s +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 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. - -// +build !gccgo - -TEXT ·cpuid(SB),$0-12 - MOVL ax+8(FP), AX - CPUID - MOVQ info+0(FP), DI - MOVL AX, 0(DI) - MOVL BX, 4(DI) - MOVL CX, 8(DI) - MOVL DX, 12(DI) - RET - diff --git a/src/cmd/dist/cpuid_default.s b/src/cmd/dist/cpuid_default.s deleted file mode 100644 index 6412a507a9..0000000000 --- a/src/cmd/dist/cpuid_default.s +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2015 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. - -// +build !386,!amd64,!gccgo - -#include "textflag.h" - -TEXT ·cpuid(SB),NOSPLIT,$0-0 - RET diff --git a/src/cmd/dist/util_gc.go b/src/cmd/dist/util_gc.go index 698beef704..17a0e6fbb5 100644 --- a/src/cmd/dist/util_gc.go +++ b/src/cmd/dist/util_gc.go @@ -6,18 +6,6 @@ package main -func cpuid(info *[4]uint32, ax uint32) - -func cansse2() bool { - if gohostarch != "386" && gohostarch != "amd64" { - return false - } - - var info [4]uint32 - cpuid(&info, 1) - return info[3]&(1<<26) != 0 // SSE2 -} - // useVFPv1 tries to execute one VFPv1 instruction on ARM. // It will crash the current process if VFPv1 is missing. func useVFPv1() diff --git a/src/cmd/dist/util_gccgo.go b/src/cmd/dist/util_gccgo.go index f9f01dc048..dc897236fb 100644 --- a/src/cmd/dist/util_gccgo.go +++ b/src/cmd/dist/util_gccgo.go @@ -6,19 +6,6 @@ package main -/* -int supports_sse2() { -#if defined(__i386__) || defined(__x86_64__) - return __builtin_cpu_supports("sse2"); -#else - return 0; -#endif -} -*/ -import "C" - -func cansse2() bool { return C.supports_sse2() != 0 } - func useVFPv1() {} func useVFPv3() {} diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 4bc87008ff..500682ed02 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1853,9 +1853,6 @@ // GOARM // For GOARCH=arm, the ARM architecture for which to compile. // Valid values are 5, 6, 7. -// GO386 -// For GOARCH=386, the floating point instruction set. -// Valid values are 387, sse2. // GOMIPS // For GOARCH=mips{,le}, whether to use floating point instructions. // Valid values are hardfloat (default), softfloat. diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index 9bf1db73ef..ebbaf04115 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -244,7 +244,6 @@ var ( // Used in envcmd.MkEnv and build ID computations. GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM)) - GO386 = envOr("GO386", objabi.GO386) GOMIPS = envOr("GOMIPS", objabi.GOMIPS) GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64) GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64)) @@ -268,8 +267,6 @@ func GetArchEnv() (key, val string) { switch Goarch { case "arm": return "GOARM", GOARM - case "386": - return "GO386", GO386 case "mips", "mipsle": return "GOMIPS", GOMIPS case "mips64", "mips64le": diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index 7bd75f7305..ee0bb0d0b2 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.go @@ -497,7 +497,10 @@ func lineToKey(line string) string { } // sortKeyValues sorts a sequence of lines by key. -// It differs from sort.Strings in that GO386= sorts after GO=. +// It differs from sort.Strings in that keys which are GOx where x is an ASCII +// character smaller than = sort after GO=. +// (There are no such keys currently. It used to matter for GO386 which was +// removed in Go 1.16.) func sortKeyValues(lines []string) { sort.Slice(lines, func(i, j int) bool { return lineToKey(lines[i]) < lineToKey(lines[j]) diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 0ae5fd7ca9..befa10a0e4 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -581,9 +581,6 @@ Architecture-specific environment variables: GOARM For GOARCH=arm, the ARM architecture for which to compile. Valid values are 5, 6, 7. - GO386 - For GOARCH=386, the floating point instruction set. - Valid values are 387, sse2. GOMIPS For GOARCH=mips{,le}, whether to use floating point instructions. Valid values are hardfloat (default), softfloat. diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 51fc2b588d..e68b322c7d 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -271,7 +271,7 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { fmt.Fprintf(h, "asm %q %q %q\n", b.toolID("asm"), forcedAsmflags, p.Internal.Asmflags) } - // GO386, GOARM, GOMIPS, etc. + // GOARM, GOMIPS, etc. key, val := cfg.GetArchEnv() fmt.Fprintf(h, "%s=%s\n", key, val) @@ -1175,7 +1175,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) { fmt.Fprintf(h, "linkflags %q\n", p.Internal.Ldflags) } - // GO386, GOARM, GOMIPS, etc. + // GOARM, GOMIPS, etc. key, val := cfg.GetArchEnv() fmt.Fprintf(h, "%s=%s\n", key, val) diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go index b81b73a022..cedb2d0a26 100644 --- a/src/cmd/internal/objabi/util.go +++ b/src/cmd/internal/objabi/util.go @@ -24,7 +24,6 @@ var ( GOROOT = envOr("GOROOT", defaultGOROOT) GOARCH = envOr("GOARCH", defaultGOARCH) GOOS = envOr("GOOS", defaultGOOS) - GO386 = envOr("GO386", defaultGO386) GOAMD64 = goamd64() GOARM = goarm() GOMIPS = gomips() @@ -136,6 +135,14 @@ func init() { if GOARCH != "amd64" { Regabi_enabled = 0 } + + if v := os.Getenv("GO386"); v != "" && v != "sse2" { + msg := fmt.Sprintf("unsupported setting GO386=%s", v) + if v == "387" { + msg += ". 387 support was dropped in Go 1.16. Consider using gccgo instead." + } + log.Fatal(msg) + } } // Note: must agree with runtime.framepointer_enabled. diff --git a/src/internal/cfg/cfg.go b/src/internal/cfg/cfg.go index bdbe9df3e7..023429e441 100644 --- a/src/internal/cfg/cfg.go +++ b/src/internal/cfg/cfg.go @@ -32,7 +32,6 @@ const KnownEnv = ` FC GCCGO GO111MODULE - GO386 GOARCH GOARM GOBIN diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index ec87ec0c8a..0684eab973 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -4265,24 +4265,6 @@ var gFloat32 float32 func TestConvertNaNs(t *testing.T) { const snan uint32 = 0x7f800001 - - // Test to see if a store followed by a load of a signaling NaN - // maintains the signaling bit. The only platform known to fail - // this test is 386,GO386=387. The real test below will always fail - // if the platform can't even store+load a float without mucking - // with the bits. - gFloat32 = math.Float32frombits(snan) - runtime.Gosched() // make sure we don't optimize the store/load away - r := math.Float32bits(gFloat32) - if r != snan { - // This should only happen on 386,GO386=387. We have no way to - // test for 387, so we just make sure we're at least on 386. - if runtime.GOARCH != "386" { - t.Errorf("store/load of sNaN not faithful") - } - t.Skip("skipping test, float store+load not faithful") - } - type myFloat32 float32 x := V(myFloat32(math.Float32frombits(snan))) y := x.Convert(TypeOf(float32(0))) diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index c2e14cdcd6..c5bfb0f207 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -190,40 +190,25 @@ func (l *layout) restore() { func gen386() { p("PUSHFL") - // Save general purpose registers. + // Assign stack offsets. var l = layout{sp: "SP"} for _, reg := range regNames386 { - if reg == "SP" || strings.HasPrefix(reg, "X") { + if reg == "SP" { continue } - l.add("MOVL", reg, 4) - } - - // Save the 387 state. - l.addSpecial( - "FSAVE %d(SP)\nFLDCW runtime·controlWord64(SB)", - "FRSTOR %d(SP)", - 108) - - // Save SSE state only if supported. - lSSE := layout{stack: l.stack, sp: "SP"} - for i := 0; i < 8; i++ { - lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16) + if strings.HasPrefix(reg, "X") { + l.add("MOVUPS", reg, 16) + } else { + l.add("MOVL", reg, 4) + } } - p("ADJSP $%d", lSSE.stack) + p("ADJSP $%d", l.stack) p("NOP SP") l.save() - p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse") - lSSE.save() - label("nosse:") p("CALL ·asyncPreempt2(SB)") - p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse2") - lSSE.restore() - label("nosse2:") l.restore() - p("ADJSP $%d", -lSSE.stack) - + p("ADJSP $%d", -l.stack) p("POPFL") p("RET") } diff --git a/src/runtime/preempt_386.s b/src/runtime/preempt_386.s index a00ac8f385..5c9b8ea224 100644 --- a/src/runtime/preempt_386.s +++ b/src/runtime/preempt_386.s @@ -5,7 +5,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHFL - ADJSP $264 + ADJSP $156 NOP SP MOVL AX, 0(SP) MOVL CX, 4(SP) @@ -14,32 +14,23 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVL BP, 16(SP) MOVL SI, 20(SP) MOVL DI, 24(SP) - FSAVE 28(SP) - FLDCW runtime·controlWord64(SB) - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE nosse - MOVUPS X0, 136(SP) - MOVUPS X1, 152(SP) - MOVUPS X2, 168(SP) - MOVUPS X3, 184(SP) - MOVUPS X4, 200(SP) - MOVUPS X5, 216(SP) - MOVUPS X6, 232(SP) - MOVUPS X7, 248(SP) -nosse: + MOVUPS X0, 28(SP) + MOVUPS X1, 44(SP) + MOVUPS X2, 60(SP) + MOVUPS X3, 76(SP) + MOVUPS X4, 92(SP) + MOVUPS X5, 108(SP) + MOVUPS X6, 124(SP) + MOVUPS X7, 140(SP) CALL ·asyncPreempt2(SB) - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE nosse2 - MOVUPS 248(SP), X7 - MOVUPS 232(SP), X6 - MOVUPS 216(SP), X5 - MOVUPS 200(SP), X4 - MOVUPS 184(SP), X3 - MOVUPS 168(SP), X2 - MOVUPS 152(SP), X1 - MOVUPS 136(SP), X0 -nosse2: - FRSTOR 28(SP) + MOVUPS 140(SP), X7 + MOVUPS 124(SP), X6 + MOVUPS 108(SP), X5 + MOVUPS 92(SP), X4 + MOVUPS 76(SP), X3 + MOVUPS 60(SP), X2 + MOVUPS 44(SP), X1 + MOVUPS 28(SP), X0 MOVL 24(SP), DI MOVL 20(SP), SI MOVL 16(SP), BP @@ -47,6 +38,6 @@ nosse2: MOVL 8(SP), DX MOVL 4(SP), CX MOVL 0(SP), AX - ADJSP $-264 + ADJSP $-156 POPFL RET diff --git a/src/runtime/vlrt.go b/src/runtime/vlrt.go index 38e0b32801..996c0611fd 100644 --- a/src/runtime/vlrt.go +++ b/src/runtime/vlrt.go @@ -263,7 +263,7 @@ func slowdodiv(n, d uint64) (q, r uint64) { return q, n } -// Floating point control word values for GOARCH=386 GO386=387. +// Floating point control word values. // Bits 0-5 are bits to disable floating-point exceptions. // Bits 8-9 are the precision control: // 0 = single precision a.k.a. float32 @@ -273,6 +273,5 @@ func slowdodiv(n, d uint64) (q, r uint64) { // 3 = round toward zero var ( controlWord64 uint16 = 0x3f + 2<<8 + 0<<10 - controlWord32 = 0x3f + 0<<8 + 0<<10 - controlWord64trunc = 0x3f + 2<<8 + 3<<10 + controlWord64trunc uint16 = 0x3f + 2<<8 + 3<<10 ) diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go index 0bdb66a376..30f39a8da1 100644 --- a/test/codegen/arithmetic.go +++ b/test/codegen/arithmetic.go @@ -125,7 +125,7 @@ func Mul_n120(n int) int { func MulMemSrc(a []uint32, b []float32) { // 386:`IMULL\s4\([A-Z]+\),\s[A-Z]+` a[0] *= a[1] - // 386/sse2:`MULSS\s4\([A-Z]+\),\sX[0-9]+` + // 386:`MULSS\s4\([A-Z]+\),\sX[0-9]+` // amd64:`MULSS\s4\([A-Z]+\),\sX[0-9]+` b[0] *= b[1] } @@ -167,7 +167,7 @@ func MergeMuls5(a, n int) int { // -------------- // func DivMemSrc(a []float64) { - // 386/sse2:`DIVSD\s8\([A-Z]+\),\sX[0-9]+` + // 386:`DIVSD\s8\([A-Z]+\),\sX[0-9]+` // amd64:`DIVSD\s8\([A-Z]+\),\sX[0-9]+` a[0] /= a[1] } @@ -211,7 +211,7 @@ func ConstDivs(n1 uint, n2 int) (uint, int) { func FloatDivs(a []float32) float32 { // amd64:`DIVSS\s8\([A-Z]+\),\sX[0-9]+` - // 386/sse2:`DIVSS\s8\([A-Z]+\),\sX[0-9]+` + // 386:`DIVSS\s8\([A-Z]+\),\sX[0-9]+` return a[1] / a[2] } diff --git a/test/codegen/floats.go b/test/codegen/floats.go index 3fae1a327c..d115800a67 100644 --- a/test/codegen/floats.go +++ b/test/codegen/floats.go @@ -6,8 +6,6 @@ package codegen -import "math" - // This file contains codegen tests related to arithmetic // simplifications and optimizations on float types. // For codegen tests on integer types, see arithmetic.go. @@ -17,8 +15,7 @@ import "math" // --------------------- // func Mul2(f float64) float64 { - // 386/sse2:"ADDSD",-"MULSD" - // 386/387:"FADDDP",-"FMULDP" + // 386:"ADDSD",-"MULSD" // amd64:"ADDSD",-"MULSD" // arm/7:"ADDD",-"MULD" // arm64:"FADDD",-"FMULD" @@ -28,8 +25,7 @@ func Mul2(f float64) float64 { } func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { - // 386/sse2:"MULSD",-"DIVSD" - // 386/387:"FMULDP",-"FDIVDP" + // 386:"MULSD",-"DIVSD" // amd64:"MULSD",-"DIVSD" // arm/7:"MULD",-"DIVD" // arm64:"FMULD",-"FDIVD" @@ -37,8 +33,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { // ppc64le:"FMUL",-"FDIV" x := f1 / 16.0 - // 386/sse2:"MULSD",-"DIVSD" - // 386/387:"FMULDP",-"FDIVDP" + // 386:"MULSD",-"DIVSD" // amd64:"MULSD",-"DIVSD" // arm/7:"MULD",-"DIVD" // arm64:"FMULD",-"FDIVD" @@ -46,8 +41,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { // ppc64le:"FMUL",-"FDIVD" y := f2 / 0.125 - // 386/sse2:"ADDSD",-"DIVSD",-"MULSD" - // 386/387:"FADDDP",-"FDIVDP",-"FMULDP" + // 386:"ADDSD",-"DIVSD",-"MULSD" // amd64:"ADDSD",-"DIVSD",-"MULSD" // arm/7:"ADDD",-"MULD",-"DIVD" // arm64:"FADDD",-"FMULD",-"FDIVD" @@ -58,11 +52,6 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) { return x, y, z } -func getPi() float64 { - // 386/387:"FLDPI" - return math.Pi -} - func indexLoad(b0 []float32, b1 float32, idx int) float32 { // arm64:`FMOVS\s\(R[0-9]+\)\(R[0-9]+\),\sF[0-9]+` return b0[idx] * b1 diff --git a/test/codegen/math.go b/test/codegen/math.go index 1ebfda0405..fe678eea23 100644 --- a/test/codegen/math.go +++ b/test/codegen/math.go @@ -46,7 +46,7 @@ func approx(x float64) { func sqrt(x float64) float64 { // amd64:"SQRTSD" - // 386/387:"FSQRT" 386/sse2:"SQRTSD" + // 386:"SQRTSD" // arm64:"FSQRTD" // arm/7:"SQRTD" // mips/hardfloat:"SQRTD" mips/softfloat:-"SQRTD" diff --git a/test/codegen/memops.go b/test/codegen/memops.go index a234283146..4b003ad861 100644 --- a/test/codegen/memops.go +++ b/test/codegen/memops.go @@ -175,33 +175,33 @@ func idxInt64(x, y []int64, i int) { func idxFloat32(x, y []float32, i int) { var t float32 - // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` - // 386/sse2: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` + // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` + // 386: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` t = x[i+1] - // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)` - // 386/sse2: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)` + // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)` + // 386: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)` y[i+1] = t - // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+` - // 386/sse2: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+` + // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+` + // 386: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+` t = x[16*i+1] - // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)` - // 386/sse2: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)` + // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)` + // 386: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)` y[16*i+1] = t } func idxFloat64(x, y []float64, i int) { var t float64 - // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+` - // 386/sse2: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+` + // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+` + // 386: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+` t = x[i+1] - // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)` - // 386/sse2: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)` + // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)` + // 386: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)` y[i+1] = t - // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+` - // 386/sse2: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+` + // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+` + // 386: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+` t = x[16*i+1] - // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)` - // 386/sse2: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)` + // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)` + // 386: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)` y[16*i+1] = t } diff --git a/test/run.go b/test/run.go index 95b94b7277..77710fd89a 100644 --- a/test/run.go +++ b/test/run.go @@ -1489,7 +1489,7 @@ var ( // value[0] is the variant-changing environment variable, and values[1:] // are the supported variants. archVariants = map[string][]string{ - "386": {"GO386", "387", "sse2"}, + "386": {}, "amd64": {}, "arm": {"GOARM", "5", "6", "7"}, "arm64": {}, @@ -1511,12 +1511,12 @@ type wantedAsmOpcode struct { found bool // true if the opcode check matched at least one in the output } -// A build environment triplet separated by slashes (eg: linux/386/sse2). +// A build environment triplet separated by slashes (eg: linux/arm/7). // The third field can be empty if the arch does not support variants (eg: "plan9/amd64/") type buildEnv string // Environ returns the environment it represents in cmd.Environ() "key=val" format -// For instance, "linux/386/sse2".Environ() returns {"GOOS=linux", "GOARCH=386", "GO386=sse2"} +// For instance, "linux/arm/7".Environ() returns {"GOOS=linux", "GOARCH=arm", "GOARM=7"} func (b buildEnv) Environ() []string { fields := strings.Split(string(b), "/") if len(fields) != 3 { @@ -1571,11 +1571,11 @@ func (t *test) wantedAsmOpcodes(fn string) asmChecks { var arch, subarch, os string switch { - case archspec[2] != "": // 3 components: "linux/386/sse2" + case archspec[2] != "": // 3 components: "linux/arm/7" os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:] - case archspec[1] != "": // 2 components: "386/sse2" + case archspec[1] != "": // 2 components: "arm/7" os, arch, subarch = "linux", archspec[0], archspec[1][1:] - default: // 1 component: "386" + default: // 1 component: "arm" os, arch, subarch = "linux", archspec[0], "" if arch == "wasm" { os = "js" -- cgit v1.3 From 095e0f48a19fa3bd7901f79420374b9cb50940e9 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Thu, 1 Oct 2020 12:03:27 +0200 Subject: cmd/compile: change mustHeapAlloc to return a reason why This change renames mustHeapAlloc to heapAllocReason, and changes it to return the reason why the argument must escape, so we don't have to re-deduce it in its callers just to print the escape reason. It also embeds isSmallMakeSlice body in heapAllocReason, since the former was only used by the latter, and deletes isSmallMakeSlice. An outdated TODO to remove smallintconst, which the TODO claimed was only used in one place, was also removed, since grepping shows we currently call smallintconst in 11 different places. Change-Id: I0bd11bf29b92c4126f5bb455877ff73217d5a155 Reviewed-on: https://go-review.googlesource.com/c/go/+/258678 Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Trust: Alberto Donizetti Trust: Cuong Manh Le Reviewed-by: Cuong Manh Le Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/const.go | 1 - src/cmd/compile/internal/gc/esc.go | 31 +++++++++++++++++++++---------- src/cmd/compile/internal/gc/escape.go | 6 +----- src/cmd/compile/internal/gc/walk.go | 17 ++--------------- test/fixedbugs/issue41635.go | 11 +++++------ 5 files changed, 29 insertions(+), 37 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index c0ed8192d9..d881be485e 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -1134,7 +1134,6 @@ func strlit(n *Node) string { return n.Val().U.(string) } -// TODO(gri) smallintconst is only used in one place - can we used indexconst? func smallintconst(n *Node) bool { if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil { switch simtype[n.Type.Etype] { diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index 375331d1f5..d7aa72b450 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -169,36 +169,47 @@ func mayAffectMemory(n *Node) bool { } } -func mustHeapAlloc(n *Node) bool { +// heapAllocReason returns the reason the given Node must be heap +// allocated, or the empty string if it doesn't. +func heapAllocReason(n *Node) string { if n.Type == nil { - return false + return "" } // Parameters are always passed via the stack. if n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) { - return false + return "" } if n.Type.Width > maxStackVarSize { - return true + return "too large for stack" } if (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize { - return true + return "too large for stack" } if n.Op == OCLOSURE && closureType(n).Size() >= maxImplicitStackVarSize { - return true + return "too large for stack" } if n.Op == OCALLPART && partialCallType(n).Size() >= maxImplicitStackVarSize { - return true + return "too large for stack" } - if n.Op == OMAKESLICE && !isSmallMakeSlice(n) { - return true + if n.Op == OMAKESLICE { + r := n.Right + if r == nil { + r = n.Left + } + if !smallintconst(r) { + return "non-constant size" + } + if t := n.Type; t.Elem().Width != 0 && r.Int64() >= maxImplicitStackVarSize/t.Elem().Width { + return "too large for stack" + } } - return false + return "" } // addrescapes tags node n as having had its address taken diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index d79d32ec48..79df584ab1 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -1051,11 +1051,7 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation { } n.SetOpt(loc) - if mustHeapAlloc(n) { - why := "too large for stack" - if n.Op == OMAKESLICE && (!Isconst(n.Left, CTINT) || (n.Right != nil && !Isconst(n.Right, CTINT))) { - why = "non-constant size" - } + if why := heapAllocReason(n); why != "" { e.flow(e.heapHole().addr(n, why), loc) } } diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 8e45059eab..3fe7c3e089 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -336,19 +336,6 @@ func walkstmt(n *Node) *Node { return n } -func isSmallMakeSlice(n *Node) bool { - if n.Op != OMAKESLICE { - return false - } - r := n.Right - if r == nil { - r = n.Left - } - t := n.Type - - return smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < maxImplicitStackVarSize/t.Elem().Width) -} - // walk the whole tree of the body of an // expression or simple statement. // the types expressions are calculated. @@ -1339,8 +1326,8 @@ opswitch: yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem()) } if n.Esc == EscNone { - if !isSmallMakeSlice(n) { - Fatalf("non-small OMAKESLICE with EscNone: %v", n) + if why := heapAllocReason(n); why != "" { + Fatalf("%v has EscNone, but %v", n, why) } // var arr [r]T // n = arr[:l] diff --git a/test/fixedbugs/issue41635.go b/test/fixedbugs/issue41635.go index b33c1a07e7..35c0034cdd 100644 --- a/test/fixedbugs/issue41635.go +++ b/test/fixedbugs/issue41635.go @@ -7,12 +7,11 @@ package p func f() { // ERROR "" - b1 := make([]byte, 1<<17) // ERROR "too large for stack" "" - b2 := make([]byte, 100, 1<<17) // ERROR "too large for stack" "" - n, m := 100, 200 - b1 = make([]byte, n) // ERROR "non-constant size" "" - b2 = make([]byte, 100, m) // ERROR "non-constant size" "" + _ = make([]byte, 1<<17) // ERROR "too large for stack" "" + _ = make([]byte, 100, 1<<17) // ERROR "too large for stack" "" + _ = make([]byte, n, 1<<17) // ERROR "too large for stack" "" - _, _ = b1, b2 + _ = make([]byte, n) // ERROR "non-constant size" "" + _ = make([]byte, 100, m) // ERROR "non-constant size" "" } -- cgit v1.3 From a9c75ecd3da2d87ce08b2e75bd4f332185cd7fc8 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Fri, 2 Oct 2020 15:48:50 -0700 Subject: cmd/compile: export notinheap annotation to object file In the rare case when a cgo type makes it into an object file, we need the go:notinheap annotation to go with it. Fixes #41761 Change-Id: I541500cb1a03de954881aef659f96fc0b7738848 Reviewed-on: https://go-review.googlesource.com/c/go/+/259297 Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- misc/cgo/test/testdata/issue41761.go | 20 ++++++++++++++++++++ misc/cgo/test/testdata/issue41761a/a.go | 14 ++++++++++++++ src/cmd/compile/internal/gc/iexport.go | 2 ++ src/cmd/compile/internal/gc/iimport.go | 2 +- src/cmd/compile/internal/gc/lex.go | 2 +- 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 misc/cgo/test/testdata/issue41761.go create mode 100644 misc/cgo/test/testdata/issue41761a/a.go (limited to 'src/cmd/compile/internal/gc') diff --git a/misc/cgo/test/testdata/issue41761.go b/misc/cgo/test/testdata/issue41761.go new file mode 100644 index 0000000000..919c749251 --- /dev/null +++ b/misc/cgo/test/testdata/issue41761.go @@ -0,0 +1,20 @@ +// Copyright 2020 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 cgotest + +/* + typedef struct S S; +*/ +import "C" + +import ( + "cgotest/issue41761a" + "testing" +) + +func test41761(t *testing.T) { + var x issue41761a.T + _ = (*C.struct_S)(x.X) +} diff --git a/misc/cgo/test/testdata/issue41761a/a.go b/misc/cgo/test/testdata/issue41761a/a.go new file mode 100644 index 0000000000..ca5c18191e --- /dev/null +++ b/misc/cgo/test/testdata/issue41761a/a.go @@ -0,0 +1,14 @@ +// Copyright 2020 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 issue41761a + +/* + typedef struct S S; +*/ +import "C" + +type T struct { + X *C.S +} diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index b3f50b63af..3be3b0a213 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -1017,6 +1017,8 @@ func (w *exportWriter) symIdx(s *types.Sym) { } func (w *exportWriter) typeExt(t *types.Type) { + // Export whether this type is marked notinheap. + w.bool(t.NotInHeap()) // For type T, export the index of type descriptor symbols of T and *T. if i, ok := typeSymIdx[t]; ok { w.int64(i[0]) diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index 4169222c14..0c5e469c57 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -596,7 +596,6 @@ func (r *importReader) typ1() *types.Type { // Ensure we expand the interface in the frontend (#25055). checkwidth(t) - return t } } @@ -711,6 +710,7 @@ func (r *importReader) symIdx(s *types.Sym) { } func (r *importReader) typeExt(t *types.Type) { + t.SetNotInHeap(r.bool()) i, pi := r.int64(), r.int64() if i != -1 && pi != -1 { typeSymIdx[t] = [2]int64{i, pi} diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 1a344c6566..25bc0399ce 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -48,7 +48,7 @@ const ( Nowritebarrierrec // error on write barrier in this or recursive callees Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees - // Runtime-only type pragmas + // Runtime and cgo type pragmas NotInHeap // values of this type must not be heap allocated ) -- cgit v1.3 From bdab5df40f474c7768a945ef4fcf5aab634f7af5 Mon Sep 17 00:00:00 2001 From: Lynn Boger Date: Fri, 2 Oct 2020 17:51:13 -0400 Subject: cmd/compile,cmd/internal/obj/ppc64: use mulli where possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support to allow the use of mulli when one of the multiply operands is a constant that fits in 16 bits. This especially helps in the case where this instruction appears in a loop since the load of the constant is not being moved out of the loop. Some improvements seen in compress/flate on power9: Decode/Digits/Huffman/1e4 259µs ± 0% 261µs ± 0% +0.57% (p=1.000 n=1+1) Decode/Digits/Huffman/1e5 2.43ms ± 0% 2.45ms ± 0% +0.79% (p=1.000 n=1+1) Decode/Digits/Huffman/1e6 23.9ms ± 0% 24.2ms ± 0% +0.86% (p=1.000 n=1+1) Decode/Digits/Speed/1e4 278µs ± 0% 279µs ± 0% +0.34% (p=1.000 n=1+1) Decode/Digits/Speed/1e5 2.80ms ± 0% 2.81ms ± 0% +0.29% (p=1.000 n=1+1) Decode/Digits/Speed/1e6 28.0ms ± 0% 28.1ms ± 0% +0.28% (p=1.000 n=1+1) Decode/Digits/Default/1e4 278µs ± 0% 278µs ± 0% +0.28% (p=1.000 n=1+1) Decode/Digits/Default/1e5 2.68ms ± 0% 2.69ms ± 0% +0.19% (p=1.000 n=1+1) Decode/Digits/Default/1e6 26.6ms ± 0% 26.6ms ± 0% +0.21% (p=1.000 n=1+1) Decode/Digits/Compression/1e4 278µs ± 0% 278µs ± 0% +0.00% (p=1.000 n=1+1) Decode/Digits/Compression/1e5 2.68ms ± 0% 2.69ms ± 0% +0.21% (p=1.000 n=1+1) Decode/Digits/Compression/1e6 26.6ms ± 0% 26.6ms ± 0% +0.07% (p=1.000 n=1+1) Decode/Newton/Huffman/1e4 322µs ± 0% 312µs ± 0% -2.84% (p=1.000 n=1+1) Decode/Newton/Huffman/1e5 3.11ms ± 0% 2.91ms ± 0% -6.41% (p=1.000 n=1+1) Decode/Newton/Huffman/1e6 31.4ms ± 0% 29.3ms ± 0% -6.85% (p=1.000 n=1+1) Decode/Newton/Speed/1e4 282µs ± 0% 269µs ± 0% -4.69% (p=1.000 n=1+1) Decode/Newton/Speed/1e5 2.29ms ± 0% 2.20ms ± 0% -4.13% (p=1.000 n=1+1) Decode/Newton/Speed/1e6 22.7ms ± 0% 21.3ms ± 0% -6.06% (p=1.000 n=1+1) Decode/Newton/Default/1e4 254µs ± 0% 237µs ± 0% -6.60% (p=1.000 n=1+1) Decode/Newton/Default/1e5 1.86ms ± 0% 1.75ms ± 0% -5.99% (p=1.000 n=1+1) Decode/Newton/Default/1e6 18.1ms ± 0% 17.4ms ± 0% -4.10% (p=1.000 n=1+1) Decode/Newton/Compression/1e4 254µs ± 0% 244µs ± 0% -3.91% (p=1.000 n=1+1) Decode/Newton/Compression/1e5 1.85ms ± 0% 1.79ms ± 0% -3.10% (p=1.000 n=1+1) Decode/Newton/Compression/1e6 18.0ms ± 0% 17.3ms ± 0% -3.88% (p=1.000 n=1+1) Change-Id: I840320fab1c4bf64c76b001c2651ab79f23df4eb Reviewed-on: https://go-review.googlesource.com/c/go/+/259444 Run-TryBot: Lynn Boger TryBot-Result: Go Bot Reviewed-by: Paul Murphy Reviewed-by: Carlos Eduardo Seo Trust: Lynn Boger --- src/cmd/asm/internal/asm/testdata/ppc64enc.s | 4 +++ src/cmd/compile/internal/gc/bench_test.go | 12 +++++++ src/cmd/compile/internal/ppc64/ssa.go | 3 +- src/cmd/compile/internal/ssa/gen/PPC64.rules | 2 ++ src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 2 ++ src/cmd/compile/internal/ssa/opGen.go | 30 ++++++++++++++++ src/cmd/compile/internal/ssa/rewritePPC64.go | 54 ++++++++++++++++++++++++++++ src/cmd/internal/obj/ppc64/asm9.go | 9 ++--- 8 files changed, 111 insertions(+), 5 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/asm/internal/asm/testdata/ppc64enc.s b/src/cmd/asm/internal/asm/testdata/ppc64enc.s index 869f8c2d4f..c6d7b59aad 100644 --- a/src/cmd/asm/internal/asm/testdata/ppc64enc.s +++ b/src/cmd/asm/internal/asm/testdata/ppc64enc.s @@ -204,12 +204,16 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 MULLW R3, R4 // 7c8419d6 MULLW R3, R4, R5 // 7ca419d6 + MULLW $10, R3 // 1c63000a + MULLW $10000000, R3 // 641f009863ff96807c7f19d6 MULLWCC R3, R4, R5 // 7ca419d7 MULHW R3, R4, R5 // 7ca41896 MULHWU R3, R4, R5 // 7ca41816 MULLD R3, R4 // 7c8419d2 MULLD R4, R4, R5 // 7ca421d2 + MULLD $20, R4 // 1c840014 + MULLD $200000000, R4 // 641f0beb63ffc2007c9f21d2 MULLDCC R3, R4, R5 // 7ca419d3 MULHD R3, R4, R5 // 7ca41892 MULHDCC R3, R4, R5 // 7ca41893 diff --git a/src/cmd/compile/internal/gc/bench_test.go b/src/cmd/compile/internal/gc/bench_test.go index a2887f2f7b..8c4288128f 100644 --- a/src/cmd/compile/internal/gc/bench_test.go +++ b/src/cmd/compile/internal/gc/bench_test.go @@ -7,6 +7,7 @@ package gc import "testing" var globl int64 +var globl32 int32 func BenchmarkLoadAdd(b *testing.B) { x := make([]int64, 1024) @@ -42,6 +43,17 @@ func BenchmarkModify(b *testing.B) { } } +func BenchmarkMullImm(b *testing.B) { + x := make([]int32, 1024) + for i := 0; i < b.N; i++ { + var s int32 + for i := range x { + s += x[i] * 100 + } + globl32 = s + } +} + func BenchmarkConstModify(b *testing.B) { a := make([]int64, 1024) for i := 0; i < b.N; i++ { diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go index d83b2df379..1ece4d999f 100644 --- a/src/cmd/compile/internal/ppc64/ssa.go +++ b/src/cmd/compile/internal/ppc64/ssa.go @@ -677,7 +677,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.From.Reg = v.Args[0].Reg() case ssa.OpPPC64ADDconst, ssa.OpPPC64ANDconst, ssa.OpPPC64ORconst, ssa.OpPPC64XORconst, - ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst, ssa.OpPPC64EXTSWSLconst: + ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, + ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst, ssa.OpPPC64EXTSWSLconst, ssa.OpPPC64MULLWconst, ssa.OpPPC64MULLDconst: p := s.Prog(v.Op.Asm()) p.Reg = v.Args[0].Reg() p.From.Type = obj.TYPE_CONST diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules index 83ee4c499b..a05cfee654 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64.rules +++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules @@ -821,6 +821,8 @@ (ADDconst [c] (MOVDaddr [d] {sym} x)) && is32Bit(c+int64(d)) => (MOVDaddr [int32(c+int64(d))] {sym} x) +(MULL(W|D) x (MOVDconst [c])) && is16Bit(c) => (MULL(W|D)const [int32(c)] x) + // Subtract from (with carry, but ignored) constant. // Note, these clobber the carry bit. (SUB (MOVDconst [c]) x) && is32Bit(c) => (SUBFCconst [c] x) diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index 28317928a8..5885660597 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -181,6 +181,8 @@ func init() { {name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true}, // arg0*arg1 (signed 64-bit) {name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true}, // arg0*arg1 (signed 32-bit) + {name: "MULLDconst", argLength: 1, reg: gp11, asm: "MULLD", aux: "Int32", typ: "Int64"}, // arg0*auxInt (signed 64-bit) + {name: "MULLWconst", argLength: 1, reg: gp11, asm: "MULLW", aux: "Int32", typ: "Int64"}, // arg0*auxInt (signed 64-bit) {name: "MADDLD", argLength: 3, reg: gp31, asm: "MADDLD", typ: "Int64"}, // (arg0*arg1)+arg2 (signed 64-bit) {name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", commutative: true}, // (arg0 * arg1) >> 64, signed diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index d7d2b24a48..051550fb17 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1832,6 +1832,8 @@ const ( OpPPC64FSUBS OpPPC64MULLD OpPPC64MULLW + OpPPC64MULLDconst + OpPPC64MULLWconst OpPPC64MADDLD OpPPC64MULHD OpPPC64MULHW @@ -24377,6 +24379,34 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "MULLDconst", + auxType: auxInt32, + argLen: 1, + asm: ppc64.AMULLD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "MULLWconst", + auxType: auxInt32, + argLen: 1, + asm: ppc64.AMULLW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "MADDLD", argLen: 3, diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go index 9822637b05..1b8a5a78ca 100644 --- a/src/cmd/compile/internal/ssa/rewritePPC64.go +++ b/src/cmd/compile/internal/ssa/rewritePPC64.go @@ -568,6 +568,10 @@ func rewriteValuePPC64(v *Value) bool { return rewriteValuePPC64_OpPPC64MOVWstorezero(v) case OpPPC64MTVSRD: return rewriteValuePPC64_OpPPC64MTVSRD(v) + case OpPPC64MULLD: + return rewriteValuePPC64_OpPPC64MULLD(v) + case OpPPC64MULLW: + return rewriteValuePPC64_OpPPC64MULLW(v) case OpPPC64NEG: return rewriteValuePPC64_OpPPC64NEG(v) case OpPPC64NOR: @@ -11003,6 +11007,56 @@ func rewriteValuePPC64_OpPPC64MTVSRD(v *Value) bool { } return false } +func rewriteValuePPC64_OpPPC64MULLD(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MULLD x (MOVDconst [c])) + // cond: is16Bit(c) + // result: (MULLDconst [int32(c)] x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpPPC64MOVDconst { + continue + } + c := auxIntToInt64(v_1.AuxInt) + if !(is16Bit(c)) { + continue + } + v.reset(OpPPC64MULLDconst) + v.AuxInt = int32ToAuxInt(int32(c)) + v.AddArg(x) + return true + } + break + } + return false +} +func rewriteValuePPC64_OpPPC64MULLW(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MULLW x (MOVDconst [c])) + // cond: is16Bit(c) + // result: (MULLWconst [int32(c)] x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpPPC64MOVDconst { + continue + } + c := auxIntToInt64(v_1.AuxInt) + if !(is16Bit(c)) { + continue + } + v.reset(OpPPC64MULLWconst) + v.AuxInt = int32ToAuxInt(int32(c)) + v.AddArg(x) + return true + } + break + } + return false +} func rewriteValuePPC64_OpPPC64NEG(v *Value) bool { v_0 := v.Args[0] // match: (NEG (ADDconst [c] x)) diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go index 928e299f43..c2e8e9e9d0 100644 --- a/src/cmd/internal/obj/ppc64/asm9.go +++ b/src/cmd/internal/obj/ppc64/asm9.go @@ -1279,6 +1279,9 @@ func buildop(ctxt *obj.Link) { case AREMD: opset(AREMDU, r0) + case AMULLW: + opset(AMULLD, r0) + case ADIVW: /* op Rb[,Ra],Rd */ opset(AMULHW, r0) @@ -1312,7 +1315,6 @@ func buildop(ctxt *obj.Link) { opset(AMULHDCC, r0) opset(AMULHDU, r0) opset(AMULHDUCC, r0) - opset(AMULLD, r0) opset(AMULLDCC, r0) opset(AMULLDVCC, r0) opset(AMULLDV, r0) @@ -1996,7 +1998,6 @@ func buildop(ctxt *obj.Link) { AMOVB, /* macro: move byte with sign extension */ AMOVBU, /* macro: move byte with sign extension & update */ AMOVFL, - AMULLW, /* op $s[,r2],r3; op r1[,r2],r3; no cc/v */ ASUBC, /* op r1,$s,r3; op r1[,r2],r3 */ ASTSW, @@ -4990,8 +4991,8 @@ func (c *ctxt9) opirr(a obj.As) uint32 { case ADARN: return OPVCC(31, 755, 0, 0) /* darn - v3.00 */ - case AMULLW: - return OPVCC(7, 0, 0, 0) + case AMULLW, AMULLD: + return OPVCC(7, 0, 0, 0) /* mulli works with MULLW or MULLD */ case AOR: return OPVCC(24, 0, 0, 0) -- cgit v1.3 From f3b58edd036e082c210b11bb3aee8a40aa8fbcf2 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 8 Oct 2020 19:51:57 +0700 Subject: cmd/compile: use types.IdealFoo directly in predecl Instead of using untype(Ctype) to get corresponding untyped type. Passes toolstash-check. Change-Id: I311fe6c94b1f8eb2e1615101a379cd06dcab835b Reviewed-on: https://go-review.googlesource.com/c/go/+/260698 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/bexport.go | 38 +++++++--------------------------- src/cmd/compile/internal/gc/iexport.go | 4 ++-- 2 files changed, 9 insertions(+), 33 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 5ced66c0da..f82925347c 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -126,30 +126,6 @@ const ( aliasTag ) -// untype returns the "pseudo" untyped type for a Ctype (import/export use only). -// (we can't use a pre-initialized array because we must be sure all types are -// set up) -func untype(ctype Ctype) *types.Type { - switch ctype { - case CTINT: - return types.Idealint - case CTRUNE: - return types.Idealrune - case CTFLT: - return types.Idealfloat - case CTCPLX: - return types.Idealcomplex - case CTSTR: - return types.Idealstring - case CTBOOL: - return types.Idealbool - case CTNIL: - return types.Types[TNIL] - } - Fatalf("exporter: unknown Ctype") - return nil -} - var predecl []*types.Type // initialized lazily func predeclared() []*types.Type { @@ -184,13 +160,13 @@ func predeclared() []*types.Type { types.Errortype, // untyped types - untype(CTBOOL), - untype(CTINT), - untype(CTRUNE), - untype(CTFLT), - untype(CTCPLX), - untype(CTSTR), - untype(CTNIL), + types.Idealbool, + types.Idealint, + types.Idealrune, + types.Idealfloat, + types.Idealcomplex, + types.Idealstring, + types.Types[TNIL], // package unsafe types.Types[TUNSAFEPTR], diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index 3be3b0a213..3ccaf60f40 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -780,8 +780,8 @@ func constTypeOf(typ *types.Type) Ctype { } func (w *exportWriter) value(typ *types.Type, v Val) { - if typ.IsUntyped() { - typ = untype(v.Ctype()) + if vt := idealType(v.Ctype()); typ.IsUntyped() && typ != vt { + Fatalf("exporter: untyped type mismatch, have: %v, want: %v", typ, vt) } w.typ(typ) -- cgit v1.3 From 46ab0c0c0474d38d9b924b2428f20c6da58c85fa Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 8 Oct 2020 20:33:36 +0700 Subject: cmd/compile: rename types.IdealFoo to types.UntypedFoo To be consistent with go/types. Passes toolstash-check. Change-Id: I5e02f529064a904310a164f8765082aa533cc799 Reviewed-on: https://go-review.googlesource.com/c/go/+/260699 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/bexport.go | 12 +++++------ src/cmd/compile/internal/gc/const.go | 36 ++++++++++++++++---------------- src/cmd/compile/internal/gc/fmt.go | 14 ++++++------- src/cmd/compile/internal/gc/iexport.go | 6 +++--- src/cmd/compile/internal/gc/iimport.go | 2 +- src/cmd/compile/internal/gc/ssa.go | 4 ++-- src/cmd/compile/internal/gc/subr.go | 2 +- src/cmd/compile/internal/gc/typecheck.go | 16 +++++++------- src/cmd/compile/internal/gc/universe.go | 10 ++++----- src/cmd/compile/internal/types/type.go | 14 ++++++------- 10 files changed, 58 insertions(+), 58 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index f82925347c..0cb9fe9e62 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -160,12 +160,12 @@ func predeclared() []*types.Type { types.Errortype, // untyped types - types.Idealbool, - types.Idealint, - types.Idealrune, - types.Idealfloat, - types.Idealcomplex, - types.Idealstring, + types.UntypedBool, + types.UntypedInt, + types.UntypedRune, + types.UntypedFloat, + types.UntypedComplex, + types.UntypedString, types.Types[TNIL], // package unsafe diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index d881be485e..b28c0fc8d0 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -1019,17 +1019,17 @@ func nodlit(v Val) *Node { func idealType(ct Ctype) *types.Type { switch ct { case CTSTR: - return types.Idealstring + return types.UntypedString case CTBOOL: - return types.Idealbool + return types.UntypedBool case CTINT: - return types.Idealint + return types.UntypedInt case CTRUNE: - return types.Idealrune + return types.UntypedRune case CTFLT: - return types.Idealfloat + return types.UntypedFloat case CTCPLX: - return types.Idealcomplex + return types.UntypedComplex case CTNIL: return types.Types[TNIL] } @@ -1080,17 +1080,17 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) { func ctype(t *types.Type) Ctype { switch t { - case types.Idealbool: + case types.UntypedBool: return CTBOOL - case types.Idealstring: + case types.UntypedString: return CTSTR - case types.Idealint: + case types.UntypedInt: return CTINT - case types.Idealrune: + case types.UntypedRune: return CTRUNE - case types.Idealfloat: + case types.UntypedFloat: return CTFLT - case types.Idealcomplex: + case types.UntypedComplex: return CTCPLX } Fatalf("bad type %v", t) @@ -1111,17 +1111,17 @@ func defaultType(t *types.Type) *types.Type { } switch t { - case types.Idealbool: + case types.UntypedBool: return types.Types[TBOOL] - case types.Idealstring: + case types.UntypedString: return types.Types[TSTRING] - case types.Idealint: + case types.UntypedInt: return types.Types[TINT] - case types.Idealrune: + case types.UntypedRune: return types.Runetype - case types.Idealfloat: + case types.UntypedFloat: return types.Types[TFLOAT64] - case types.Idealcomplex: + case types.UntypedComplex: return types.Types[TCOMPLEX128] } diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index d4af451506..36b596338f 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -773,17 +773,17 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" { var name string switch t { - case types.Idealbool: + case types.UntypedBool: name = "untyped bool" - case types.Idealstring: + case types.UntypedString: name = "untyped string" - case types.Idealint: + case types.UntypedInt: name = "untyped int" - case types.Idealrune: + case types.UntypedRune: name = "untyped rune" - case types.Idealfloat: + case types.UntypedFloat: name = "untyped float" - case types.Idealcomplex: + case types.UntypedComplex: name = "untyped complex" default: name = basicnames[t.Etype] @@ -1333,7 +1333,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) { n.Orig.exprfmt(s, prec, mode) return } - if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != types.Idealbool && n.Type != types.Idealstring { + if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != types.UntypedBool && n.Type != types.UntypedString { // Need parens when type begins with what might // be misinterpreted as a unary operator: * or <-. if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == types.Crecv) { diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index 3ccaf60f40..df08a4a6c2 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -751,11 +751,11 @@ func (w *exportWriter) param(f *types.Field) { func constTypeOf(typ *types.Type) Ctype { switch typ { - case types.Idealint, types.Idealrune: + case types.UntypedInt, types.UntypedRune: return CTINT - case types.Idealfloat: + case types.UntypedFloat: return CTFLT - case types.Idealcomplex: + case types.UntypedComplex: return CTCPLX } diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index 0c5e469c57..5f107eeec7 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -375,7 +375,7 @@ func (p *importReader) value() (typ *types.Type, v Val) { v.U = p.string() case CTINT: x := new(Mpint) - x.Rune = typ == types.Idealrune + x.Rune = typ == types.UntypedRune p.mpint(&x.Val, typ) v.U = x case CTFLT: diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 32394c4b1a..7bde7f7c65 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -50,12 +50,12 @@ func initssaconfig() { // Caching is disabled in the backend, so generating these here avoids allocations. _ = types.NewPtr(types.Types[TINTER]) // *interface{} _ = types.NewPtr(types.NewPtr(types.Types[TSTRING])) // **string - _ = types.NewPtr(types.NewPtr(types.Idealstring)) // **string + _ = types.NewPtr(types.NewPtr(types.UntypedString)) // **string _ = types.NewPtr(types.NewSlice(types.Types[TINTER])) // *[]interface{} _ = types.NewPtr(types.NewPtr(types.Bytetype)) // **byte _ = types.NewPtr(types.NewSlice(types.Bytetype)) // *[]byte _ = types.NewPtr(types.NewSlice(types.Types[TSTRING])) // *[]string - _ = types.NewPtr(types.NewSlice(types.Idealstring)) // *[]string + _ = types.NewPtr(types.NewSlice(types.UntypedString)) // *[]string _ = types.NewPtr(types.NewPtr(types.NewPtr(types.Types[TUINT8]))) // ***uint8 _ = types.NewPtr(types.Types[TINT16]) // *int16 _ = types.NewPtr(types.Types[TINT64]) // *int64 diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 07547df36e..0242832322 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -825,7 +825,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node { // Convert ideal bool from comparison to plain bool // if the next step is non-bool (like interface{}). - if n.Type == types.Idealbool && !t.IsBoolean() { + if n.Type == types.UntypedBool && !t.IsBoolean() { if n.Op == ONAME || n.Op == OLITERAL { r := nod(OCONVNOP, n, nil) r.Type = types.Types[TBOOL] diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 0eb0dae373..769341ee04 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -361,7 +361,7 @@ func typecheck1(n *Node, top int) (res *Node) { ok |= ctxExpr if n.Type == nil && n.Val().Ctype() == CTSTR { - n.Type = types.Idealstring + n.Type = types.UntypedString } case ONONAME: @@ -623,8 +623,8 @@ func typecheck1(n *Node, top int) (res *Node) { // no defaultlit for left // the outer context gives the type n.Type = l.Type - if (l.Type == types.Idealfloat || l.Type == types.Idealcomplex) && r.Op == OLITERAL { - n.Type = types.Idealint + if (l.Type == types.UntypedFloat || l.Type == types.UntypedComplex) && r.Op == OLITERAL { + n.Type = types.UntypedInt } break @@ -777,7 +777,7 @@ func typecheck1(n *Node, top int) (res *Node) { if iscmp[n.Op] { evconst(n) - t = types.Idealbool + t = types.UntypedBool if n.Op != OLITERAL { l, r = defaultlit2(l, r, true) n.Left = l @@ -1458,7 +1458,7 @@ func typecheck1(n *Node, top int) (res *Node) { // Determine result type. switch t.Etype { case TIDEAL: - n.Type = types.Idealfloat + n.Type = types.UntypedFloat case TCOMPLEX64: n.Type = types.Types[TFLOAT32] case TCOMPLEX128: @@ -1504,7 +1504,7 @@ func typecheck1(n *Node, top int) (res *Node) { return n case TIDEAL: - t = types.Idealcomplex + t = types.UntypedComplex case TFLOAT32: t = types.Types[TCOMPLEX64] @@ -2724,9 +2724,9 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string { // e.g in error messages about wrong arguments to return. func sigrepr(t *types.Type, isddd bool) string { switch t { - case types.Idealstring: + case types.UntypedString: return "string" - case types.Idealbool: + case types.UntypedBool: return "bool" } diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go index 04861c8dd4..ff8cabd8e3 100644 --- a/src/cmd/compile/internal/gc/universe.go +++ b/src/cmd/compile/internal/gc/universe.go @@ -123,21 +123,21 @@ func lexinit() { asNode(s2.Def).SetSubOp(s.op) } - types.Idealstring = types.New(TSTRING) - types.Idealbool = types.New(TBOOL) + types.UntypedString = types.New(TSTRING) + types.UntypedBool = types.New(TBOOL) types.Types[TANY] = types.New(TANY) s := builtinpkg.Lookup("true") s.Def = asTypesNode(nodbool(true)) asNode(s.Def).Sym = lookup("true") asNode(s.Def).Name = new(Name) - asNode(s.Def).Type = types.Idealbool + asNode(s.Def).Type = types.UntypedBool s = builtinpkg.Lookup("false") s.Def = asTypesNode(nodbool(false)) asNode(s.Def).Sym = lookup("false") asNode(s.Def).Name = new(Name) - asNode(s.Def).Type = types.Idealbool + asNode(s.Def).Type = types.UntypedBool s = lookup("_") s.Block = -100 @@ -351,7 +351,7 @@ func typeinit() { sizeofString = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr)) dowidth(types.Types[TSTRING]) - dowidth(types.Idealstring) + dowidth(types.UntypedString) } func makeErrorInterface() *types.Type { diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 5d1d5d4008..023ab9af88 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -105,14 +105,14 @@ var ( Errortype *Type // Types to represent untyped string and boolean constants. - Idealstring *Type - Idealbool *Type + UntypedString *Type + UntypedBool *Type // Types to represent untyped numeric constants. - Idealint = New(TIDEAL) - Idealrune = New(TIDEAL) - Idealfloat = New(TIDEAL) - Idealcomplex = New(TIDEAL) + UntypedInt = New(TIDEAL) + UntypedRune = New(TIDEAL) + UntypedFloat = New(TIDEAL) + UntypedComplex = New(TIDEAL) ) // A Type represents a Go type. @@ -1436,7 +1436,7 @@ func (t *Type) IsUntyped() bool { if t == nil { return false } - if t == Idealstring || t == Idealbool { + if t == UntypedString || t == UntypedBool { return true } switch t.Etype { -- cgit v1.3 From 8f26b57f9afc238bdecb9b7030bc2f4364093885 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 3 Oct 2020 01:23:47 +0700 Subject: cmd/compile: split exported/non-exported methods for interface type Currently, mhdr/methods is emitted with the same len/cap. There's no way to distinguish between exported and non-exported methods statically. This CL splits mhdr/methods into two parts, use "len" for number of exported methods, and "cap" for all methods. This fixes the bug in issue #22075, which intends to return the number of exported methods but currently return all methods. Note that with this encoding, we still can access either all/exported-only/non-exported-only methods: mhdr[:cap(mhdr)] // all methods mhdr // exported methods mhdr[len(mhdr):cap(mhdr)] // non-exported methods Thank to Matthew Dempsky (@mdempsky) for suggesting this encoding. Fixes #22075 Change-Id: If662adb03ccff27407d55a5578a0ed05a15e7cdd Reviewed-on: https://go-review.googlesource.com/c/go/+/259237 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Cherry Zhang Reviewed-by: Matthew Dempsky --- doc/go1.16.html | 8 +++++ src/cmd/compile/internal/gc/reflect.go | 3 +- src/internal/reflectlite/type.go | 35 +++++++++++++++------- src/internal/reflectlite/value.go | 4 +-- src/reflect/all_test.go | 18 +++++++---- src/reflect/type.go | 55 ++++++++++++++++++++-------------- src/reflect/value.go | 21 +++++++------ src/runtime/alg.go | 2 +- src/runtime/iface.go | 12 ++++---- src/runtime/mfinal.go | 4 +-- src/runtime/type.go | 26 ++++++++++++---- 11 files changed, 124 insertions(+), 64 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/doc/go1.16.html b/doc/go1.16.html index 720acc757a..509956fbf2 100644 --- a/doc/go1.16.html +++ b/doc/go1.16.html @@ -213,6 +213,14 @@ Do not send CLs removing the interior tags from such phrases. with "use of closed network connection".

+

reflect

+ +

+ For interface types and values, Method, + MethodByName, and + NumMethod now + operate on the interface's exported method set, rather than its full method set. +

text/template/parse

diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 21429af782..229fcfeaee 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -1275,8 +1275,9 @@ func dtypesym(t *types.Type) *obj.LSym { } ot = dgopkgpath(lsym, ot, tpkg) + xcount := sort.Search(n, func(i int) bool { return !types.IsExported(m[i].name.Name) }) ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t)) - ot = duintptr(lsym, ot, uint64(n)) + ot = duintptr(lsym, ot, uint64(xcount)) ot = duintptr(lsym, ot, uint64(n)) dataAdd := imethodSize() * n ot = dextratype(lsym, ot, t, dataAdd) diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go index 15ba30da36..37cf03594f 100644 --- a/src/internal/reflectlite/type.go +++ b/src/internal/reflectlite/type.go @@ -234,10 +234,13 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { rtype - pkgPath name // import path - methods []imethod // sorted by hash + pkgPath name // import path + expMethods []imethod // sorted by name, see runtime/type.go:interfacetype to see how it is encoded. } +func (t *interfaceType) methods() []imethod { return t.expMethods[:cap(t.expMethods)] } +func (t *interfaceType) isEmpty() bool { return cap(t.expMethods) == 0 } + // mapType represents a map type. type mapType struct { rtype @@ -695,7 +698,7 @@ func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { } // NumMethod returns the number of interface methods in the type's method set. -func (t *interfaceType) NumMethod() int { return len(t.methods) } +func (t *interfaceType) NumMethod() int { return len(t.expMethods) } // TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. @@ -732,9 +735,10 @@ func implements(T, V *rtype) bool { return false } t := (*interfaceType)(unsafe.Pointer(T)) - if len(t.methods) == 0 { + if t.isEmpty() { return true } + tmethods := t.methods() // The same algorithm applies in both cases, but the // method tables for an interface type and a concrete type @@ -751,10 +755,11 @@ func implements(T, V *rtype) bool { if V.Kind() == Interface { v := (*interfaceType)(unsafe.Pointer(V)) i := 0 - for j := 0; j < len(v.methods); j++ { - tm := &t.methods[i] + vmethods := v.methods() + for j := 0; j < len(vmethods); j++ { + tm := &tmethods[i] tmName := t.nameOff(tm.name) - vm := &v.methods[j] + vm := &vmethods[j] vmName := V.nameOff(vm.name) if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if !tmName.isExported() { @@ -770,7 +775,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(t.methods) { + if i++; i >= len(tmethods) { return true } } @@ -785,7 +790,7 @@ func implements(T, V *rtype) bool { i := 0 vmethods := v.methods() for j := 0; j < int(v.mcount); j++ { - tm := &t.methods[i] + tm := &tmethods[i] tmName := t.nameOff(tm.name) vm := vmethods[j] vmName := V.nameOff(vm.name) @@ -803,7 +808,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(t.methods) { + if i++; i >= len(tmethods) { return true } } @@ -897,7 +902,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { case Interface: t := (*interfaceType)(unsafe.Pointer(T)) v := (*interfaceType)(unsafe.Pointer(V)) - if len(t.methods) == 0 && len(v.methods) == 0 { + if t.isEmpty() && v.isEmpty() { return true } // Might have the same methods but still @@ -962,3 +967,11 @@ func toType(t *rtype) Type { func ifaceIndir(t *rtype) bool { return t.kind&kindDirectIface == 0 } + +func isEmptyIface(t *rtype) bool { + if t.Kind() != Interface { + return false + } + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.isEmpty() +} diff --git a/src/internal/reflectlite/value.go b/src/internal/reflectlite/value.go index 0365eeeabf..fb0ec77b58 100644 --- a/src/internal/reflectlite/value.go +++ b/src/internal/reflectlite/value.go @@ -228,7 +228,7 @@ func (v Value) Elem() Value { switch k { case Interface: var eface interface{} - if v.typ.NumMethod() == 0 { + if isEmptyIface(v.typ) { eface = *(*interface{})(v.ptr) } else { eface = (interface{})(*(*interface { @@ -433,7 +433,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value return Value{dst, nil, flag(Interface)} } x := valueInterface(v) - if dst.NumMethod() == 0 { + if isEmptyIface(dst) { *(*interface{})(target) = x } else { ifaceE2I(dst, x, target) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index a12712d254..be15362aae 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -2995,6 +2995,14 @@ func TestUnexportedMethods(t *testing.T) { if got := typ.NumMethod(); got != 0 { t.Errorf("NumMethod=%d, want 0 satisfied methods", got) } + + var i unexpI + if got := TypeOf(&i).Elem().NumMethod(); got != 0 { + t.Errorf("NumMethod=%d, want 0 satisfied methods", got) + } + if got := ValueOf(&i).Elem().NumMethod(); got != 0 { + t.Errorf("NumMethod=%d, want 0 satisfied methods", got) + } } type InnerInt struct { @@ -3648,21 +3656,21 @@ func TestCallPanic(t *testing.T) { v := ValueOf(T{i, i, i, i, T2{i, i}, i, i, T2{i, i}}) badCall(func() { call(v.Field(0).Method(0)) }) // .t0.W badCall(func() { call(v.Field(0).Elem().Method(0)) }) // .t0.W - badCall(func() { call(v.Field(0).Method(1)) }) // .t0.w + badMethod(func() { call(v.Field(0).Method(1)) }) // .t0.w badMethod(func() { call(v.Field(0).Elem().Method(2)) }) // .t0.w ok(func() { call(v.Field(1).Method(0)) }) // .T1.Y ok(func() { call(v.Field(1).Elem().Method(0)) }) // .T1.Y - badCall(func() { call(v.Field(1).Method(1)) }) // .T1.y + badMethod(func() { call(v.Field(1).Method(1)) }) // .T1.y badMethod(func() { call(v.Field(1).Elem().Method(2)) }) // .T1.y ok(func() { call(v.Field(2).Method(0)) }) // .NamedT0.W ok(func() { call(v.Field(2).Elem().Method(0)) }) // .NamedT0.W - badCall(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w + badMethod(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w badMethod(func() { call(v.Field(2).Elem().Method(2)) }) // .NamedT0.w ok(func() { call(v.Field(3).Method(0)) }) // .NamedT1.Y ok(func() { call(v.Field(3).Elem().Method(0)) }) // .NamedT1.Y - badCall(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y + badMethod(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y badMethod(func() { call(v.Field(3).Elem().Method(3)) }) // .NamedT1.y ok(func() { call(v.Field(4).Field(0).Method(0)) }) // .NamedT2.T1.Y @@ -3672,7 +3680,7 @@ func TestCallPanic(t *testing.T) { badCall(func() { call(v.Field(5).Method(0)) }) // .namedT0.W badCall(func() { call(v.Field(5).Elem().Method(0)) }) // .namedT0.W - badCall(func() { call(v.Field(5).Method(1)) }) // .namedT0.w + badMethod(func() { call(v.Field(5).Method(1)) }) // .namedT0.w badMethod(func() { call(v.Field(5).Elem().Method(2)) }) // .namedT0.w badCall(func() { call(v.Field(6).Method(0)) }) // .namedT1.Y diff --git a/src/reflect/type.go b/src/reflect/type.go index a3a616701b..0b34ca0c94 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -386,10 +386,14 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { rtype - pkgPath name // import path - methods []imethod // sorted by hash + pkgPath name // import path + expMethods []imethod // sorted by name, see runtime/type.go:interfacetype to see how it is encoded. } +// methods returns t's full method set, both exported and non-exported. +func (t *interfaceType) methods() []imethod { return t.expMethods[:cap(t.expMethods)] } +func (t *interfaceType) isEmpty() bool { return cap(t.expMethods) == 0 } + // mapType represents a map type. type mapType struct { rtype @@ -1049,25 +1053,22 @@ func (d ChanDir) String() string { // Method returns the i'th method in the type's method set. func (t *interfaceType) Method(i int) (m Method) { - if i < 0 || i >= len(t.methods) { - return + if i < 0 || i >= len(t.expMethods) { + panic("reflect: Method index out of range") } - p := &t.methods[i] + p := &t.expMethods[i] pname := t.nameOff(p.name) m.Name = pname.name() if !pname.isExported() { - m.PkgPath = pname.pkgPath() - if m.PkgPath == "" { - m.PkgPath = t.pkgPath.name() - } + panic("reflect: unexported method: " + pname.name()) } m.Type = toType(t.typeOff(p.typ)) m.Index = i return } -// NumMethod returns the number of interface methods in the type's method set. -func (t *interfaceType) NumMethod() int { return len(t.methods) } +// NumMethod returns the number of exported interface methods in the type's method set. +func (t *interfaceType) NumMethod() int { return len(t.expMethods) } // MethodByName method with the given name in the type's method set. func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { @@ -1075,8 +1076,8 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { return } var p *imethod - for i := range t.methods { - p = &t.methods[i] + for i := range t.expMethods { + p = &t.expMethods[i] if t.nameOff(p.name).name() == name { return t.Method(i), true } @@ -1485,9 +1486,10 @@ func implements(T, V *rtype) bool { return false } t := (*interfaceType)(unsafe.Pointer(T)) - if len(t.methods) == 0 { + if t.isEmpty() { return true } + tmethods := t.methods() // The same algorithm applies in both cases, but the // method tables for an interface type and a concrete type @@ -1504,10 +1506,11 @@ func implements(T, V *rtype) bool { if V.Kind() == Interface { v := (*interfaceType)(unsafe.Pointer(V)) i := 0 - for j := 0; j < len(v.methods); j++ { - tm := &t.methods[i] + vmethods := v.methods() + for j := 0; j < len(vmethods); j++ { + tm := &tmethods[i] tmName := t.nameOff(tm.name) - vm := &v.methods[j] + vm := &vmethods[j] vmName := V.nameOff(vm.name) if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if !tmName.isExported() { @@ -1523,7 +1526,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(t.methods) { + if i++; i >= len(tmethods) { return true } } @@ -1538,7 +1541,7 @@ func implements(T, V *rtype) bool { i := 0 vmethods := v.methods() for j := 0; j < int(v.mcount); j++ { - tm := &t.methods[i] + tm := &tmethods[i] tmName := t.nameOff(tm.name) vm := vmethods[j] vmName := V.nameOff(vm.name) @@ -1556,7 +1559,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(t.methods) { + if i++; i >= len(tmethods) { return true } } @@ -1658,7 +1661,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { case Interface: t := (*interfaceType)(unsafe.Pointer(T)) v := (*interfaceType)(unsafe.Pointer(V)) - if len(t.methods) == 0 && len(v.methods) == 0 { + if t.isEmpty() && v.isEmpty() { return true } // Might have the same methods but still @@ -2442,7 +2445,7 @@ func StructOf(fields []StructField) Type { switch f.typ.Kind() { case Interface: ift := (*interfaceType)(unsafe.Pointer(ft)) - for im, m := range ift.methods { + for im, m := range ift.methods() { if ift.nameOff(m.name).pkgPath() != "" { // TODO(sbinet). Issue 15924. panic("reflect: embedded interface with unexported method(s) not implemented") @@ -3149,3 +3152,11 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) { } } } + +func isEmptyIface(rt *rtype) bool { + if rt.Kind() != Interface { + return false + } + tt := (*interfaceType)(unsafe.Pointer(rt)) + return len(tt.methods()) == 0 +} diff --git a/src/reflect/value.go b/src/reflect/value.go index a14131e1f8..bb6371b867 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -635,10 +635,11 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *rtype, t *fu i := methodIndex if v.typ.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(v.typ)) - if uint(i) >= uint(len(tt.methods)) { + ttmethods := tt.methods() + if uint(i) >= uint(len(ttmethods)) { panic("reflect: internal error: invalid method index") } - m := &tt.methods[i] + m := &ttmethods[i] if !tt.nameOff(m.name).isExported() { panic("reflect: " + op + " of unexported method") } @@ -812,7 +813,7 @@ func (v Value) Elem() Value { switch k { case Interface: var eface interface{} - if v.typ.NumMethod() == 0 { + if isEmptyIface(v.typ) { eface = *(*interface{})(v.ptr) } else { eface = (interface{})(*(*interface { @@ -1033,7 +1034,7 @@ func valueInterface(v Value, safe bool) interface{} { // Special case: return the element inside the interface. // Empty interface has one layout, all interfaces with // methods have a second layout. - if v.NumMethod() == 0 { + if isEmptyIface(v.typ) { return *(*interface{})(v.ptr) } return *(*interface { @@ -1908,10 +1909,11 @@ func (v Value) Type() Type { if v.typ.Kind() == Interface { // Method on interface. tt := (*interfaceType)(unsafe.Pointer(v.typ)) - if uint(i) >= uint(len(tt.methods)) { + ttmethods := tt.methods() + if uint(i) >= uint(len(ttmethods)) { panic("reflect: internal error: invalid method index") } - m := &tt.methods[i] + m := &ttmethods[i] return v.typ.typeOff(m.typ) } // Method on concrete type. @@ -2429,7 +2431,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value return Value{dst, nil, flag(Interface)} } x := valueInterface(v, false) - if dst.NumMethod() == 0 { + if isEmptyIface(dst) { *(*interface{})(target) = x } else { ifaceE2I(dst, x, target) @@ -2718,10 +2720,11 @@ func cvtDirect(v Value, typ Type) Value { func cvtT2I(v Value, typ Type) Value { target := unsafe_New(typ.common()) x := valueInterface(v, false) - if typ.NumMethod() == 0 { + rt := typ.(*rtype) + if isEmptyIface(rt) { *(*interface{})(target) = x } else { - ifaceE2I(typ.(*rtype), x, target) + ifaceE2I(rt, x, target) } return Value{typ.common(), target, v.flag.ro() | flagIndir | flag(Interface)} } diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 0af48ab25c..4a98b84e4a 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -185,7 +185,7 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr { return strhash(p, h) case kindInterface: i := (*interfacetype)(unsafe.Pointer(t)) - if len(i.mhdr) == 0 { + if i.isEmpty() { return nilinterhash(p, h) } return interhash(p, h) diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 0504b89363..f8b7d429a3 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -31,16 +31,17 @@ func itabHashFunc(inter *interfacetype, typ *_type) uintptr { } func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { - if len(inter.mhdr) == 0 { + if inter.isEmpty() { throw("internal error - misuse of itab") } + imethods := inter.methods() // easy case if typ.tflag&tflagUncommon == 0 { if canfail { return nil } - name := inter.typ.nameOff(inter.mhdr[0].name) + name := inter.typ.nameOff(imethods[0].name) panic(&TypeAssertionError{nil, typ, &inter.typ, name.name()}) } @@ -63,7 +64,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { } // Entry doesn't exist yet. Make a new entry & add it. - m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*sys.PtrSize, 0, &memstats.other_sys)) + m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(imethods)-1)*sys.PtrSize, 0, &memstats.other_sys)) m.inter = inter m._type = typ // The hash is used in type switches. However, compiler statically generates itab's @@ -197,7 +198,8 @@ func (m *itab) init() string { // and interface names are unique, // so can iterate over both in lock step; // the loop is O(ni+nt) not O(ni*nt). - ni := len(inter.mhdr) + imethods := inter.methods() + ni := len(imethods) nt := int(x.mcount) xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt] j := 0 @@ -205,7 +207,7 @@ func (m *itab) init() string { var fun0 unsafe.Pointer imethods: for k := 0; k < ni; k++ { - i := &inter.mhdr[k] + i := &imethods[k] itype := inter.typ.typeOff(i.ityp) name := inter.typ.nameOff(i.name) iname := name.name() diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index cd6196dcab..6676ae6736 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -210,7 +210,7 @@ func runfinq() { // set up with empty interface (*eface)(frame)._type = &f.ot.typ (*eface)(frame).data = f.arg - if len(ityp.mhdr) != 0 { + if !ityp.isEmpty() { // convert to interface with methods // this conversion is guaranteed to succeed - we checked in SetFinalizer *(*iface)(frame) = assertE2I(ityp, *(*eface)(frame)) @@ -394,7 +394,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { } case fint.kind&kindMask == kindInterface: ityp := (*interfacetype)(unsafe.Pointer(fint)) - if len(ityp.mhdr) == 0 { + if ityp.isEmpty() { // ok - satisfies empty interface goto okarg } diff --git a/src/runtime/type.go b/src/runtime/type.go index 81455f3532..36492619e1 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -366,7 +366,19 @@ type imethod struct { type interfacetype struct { typ _type pkgpath name - mhdr []imethod + // expMethods contains all interface methods. + // + // - len(expMethods) returns number of exported methods. + // - cap(expMethods) returns all interface methods, including both exported/non-exported methods. + expMethods []imethod +} + +func (it *interfacetype) methods() []imethod { + return it.expMethods[:cap(it.expMethods)] +} + +func (it *interfacetype) isEmpty() bool { + return cap(it.expMethods) == 0 } type maptype struct { @@ -664,13 +676,15 @@ func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool { if it.pkgpath.name() != iv.pkgpath.name() { return false } - if len(it.mhdr) != len(iv.mhdr) { + itmethods := it.methods() + ivmethods := iv.methods() + if len(itmethods) != len(ivmethods) { return false } - for i := range it.mhdr { - tm := &it.mhdr[i] - vm := &iv.mhdr[i] - // Note the mhdr array can be relocated from + for i := range itmethods { + tm := &itmethods[i] + vm := &ivmethods[i] + // Note the expMethods array can be relocated from // another module. See #17724. tname := resolveNameOff(unsafe.Pointer(tm), tm.name) vname := resolveNameOff(unsafe.Pointer(vm), vm.name) -- cgit v1.3 From 51690f777c9e842e7693f8713e9686aab380ef31 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 10 Aug 2020 13:33:15 -0400 Subject: cmd/compile: some minor cleanups Change-Id: Icdf3320814ad4a86a5ae532f4fcb899da3f46ae3 Reviewed-on: https://go-review.googlesource.com/c/go/+/248186 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 12 +++--------- src/cmd/compile/internal/ssa/expand_calls.go | 15 ++++++++++----- 2 files changed, 13 insertions(+), 14 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 7bde7f7c65..90ce9e54f8 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4375,9 +4375,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { switch n.Op { case OCALLFUNC: - if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) { - testLateExpansion = true - } + testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC { sym = fn.Sym break @@ -4392,9 +4390,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { if fn.Op != ODOTMETH { s.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn) } - if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) { - testLateExpansion = true - } + testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) if k == callNormal { sym = fn.Sym break @@ -4406,9 +4402,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { if fn.Op != ODOTINTER { s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op) } - if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) { - testLateExpansion = true - } + testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) var iclosure *ssa.Value iclosure, rcvr = s.getClosureAndRcvr(fn) if k == callNormal { diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 8c06040542..bbd9aeee51 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -39,7 +39,9 @@ func expandCalls(f *Func) { hiOffset = 4 } - pairTypes := func(et types.EType) (tHi, tLo *types.Type) { + // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target + // that has no 64-bit integer registers. + intPairTypes := func(et types.EType) (tHi, tLo *types.Type) { tHi = tUint32 if et == types.TINT64 { tHi = tInt32 @@ -147,8 +149,8 @@ func expandCalls(f *Func) { } } - // storeArg converts stores of SSA-able aggregates into a series of stores of smaller types into - // individual parameter slots. + // storeArg converts stores of SSA-able aggregate arguments (passed to a call) into a series of stores of + // smaller types into individual parameter slots. // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. var storeArg func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value storeArg = func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value { @@ -165,7 +167,7 @@ func expandCalls(f *Func) { return storeArg(pos, b, a.Args[0], t.Elem(), offset, mem) case OpInt64Make: - tHi, tLo := pairTypes(t.Etype) + tHi, tLo := intPairTypes(t.Etype) mem = storeArg(pos, b, a.Args[0], tHi, offset+hiOffset, mem) return storeArg(pos, b, a.Args[1], tLo, offset+lowOffset, mem) } @@ -207,7 +209,7 @@ func expandCalls(f *Func) { if t.Width == regSize { break } - tHi, tLo := pairTypes(t.Etype) + tHi, tLo := intPairTypes(t.Etype) sel := src.Block.NewValue1(pos, OpInt64Hi, tHi, src) mem = splitStore(dst, sel, mem, v, tHi, offset+hiOffset, firstStorePos) firstStorePos = firstStorePos.WithNotStmt() @@ -261,6 +263,9 @@ func expandCalls(f *Func) { return x } + // rewriteArgs removes all the Args from a call and converts the call args into appropriate + // stores (or later, register movement). Extra args for interface and closure calls are ignored, + // but removed. rewriteArgs := func(v *Value, firstArg int) *Value { // Thread the stores on the memory arg aux := v.Aux.(*AuxCall) -- cgit v1.3 From 68bc950583221c972a132a2aa2813b6a1f5cd955 Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 11 Aug 2020 12:52:01 -0400 Subject: cmd/compile: enable late call lowering for "callDeferStack" Change-Id: I773fce43d43f6e19180531e7bd1cc50bd8f31f75 Reviewed-on: https://go-review.googlesource.com/c/go/+/248187 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 90ce9e54f8..77c9282401 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4421,6 +4421,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { var call *ssa.Value if k == callDeferStack { + testLateExpansion = ssa.LateCallExpansionEnabledWithin(s.f) // Make a defer struct d on the stack. t := deferstruct(stksize) d := tempAt(n.Pos, s.curfn, t) @@ -4471,10 +4472,17 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { } // Call runtime.deferprocStack with pointer to _defer record. - arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) - s.store(types.Types[TUINTPTR], arg0, addr) ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())}) - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults), s.mem()) + aux := ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults) + if testLateExpansion { + callArgs = append(callArgs, addr, s.mem()) + call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) + call.AddArgs(callArgs...) + } else { + arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) + s.store(types.Types[TUINTPTR], arg0, addr) + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem()) + } if stksize < int64(Widthptr) { // We need room for both the call to deferprocStack and the call to // the deferred function. -- cgit v1.3 From bb46b60d294e8759de19da6d824af315dafca22c Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 12 Aug 2020 12:58:07 -0400 Subject: cmd/compile: enable late expansion in openDeferExit Change-Id: I4ce9e914d22b519b00d7e19d93091f6ac0b60938 Reviewed-on: https://go-review.googlesource.com/c/go/+/248188 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 57 ++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 11 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 77c9282401..96add2d738 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4251,6 +4251,7 @@ func (s *state) openDeferExit() { s.lastDeferExit = deferExit s.lastDeferCount = len(s.openDefers) zeroval := s.constInt8(types.Types[TUINT8], 0) + testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f) // Test for and run defers in reverse order for i := len(s.openDefers) - 1; i >= 0; i-- { r := s.openDefers[i] @@ -4288,23 +4289,38 @@ func (s *state) openDeferExit() { stksize := fn.Type.ArgWidth() var ACArgs []ssa.Param var ACResults []ssa.Param + var callArgs []*ssa.Value if r.rcvr != nil { // rcvr in case of OCALLINTER v := s.load(r.rcvr.Type.Elem(), r.rcvr) addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart) ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)}) - s.store(types.Types[TUINTPTR], addr, v) + if testLateExpansion { + callArgs = append(callArgs, v) + } else { + s.store(types.Types[TUINTPTR], addr, v) + } } for j, argAddrVal := range r.argVals { f := getParam(r.n, j) pt := types.NewPtr(f.Type) - addr := s.constOffPtrSP(pt, argStart+f.Offset) - ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart + f.Offset)}) - if !canSSAType(f.Type) { - s.move(f.Type, addr, argAddrVal) + ACArgs = append(ACArgs, ssa.Param{Type: f.Type, Offset: int32(argStart + f.Offset)}) + if testLateExpansion { + var a *ssa.Value + if !canSSAType(f.Type) { + a = s.newValue2(ssa.OpDereference, f.Type, argAddrVal, s.mem()) + } else { + a = s.load(f.Type, argAddrVal) + } + callArgs = append(callArgs, a) } else { - argVal := s.load(f.Type, argAddrVal) - s.storeType(f.Type, addr, argVal, 0, false) + addr := s.constOffPtrSP(pt, argStart+f.Offset) + if !canSSAType(f.Type) { + s.move(f.Type, addr, argAddrVal) + } else { + argVal := s.load(f.Type, argAddrVal) + s.storeType(f.Type, addr, argVal, 0, false) + } } } var call *ssa.Value @@ -4312,13 +4328,32 @@ func (s *state) openDeferExit() { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[TUINTPTR], v) - call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, v, s.mem()) + aux := ssa.ClosureAuxCall(ACArgs, ACResults) + if testLateExpansion { + callArgs = append(callArgs, s.mem()) + call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v) + call.AddArgs(callArgs...) + } else { + call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, aux, codeptr, v, s.mem()) + } } else { - // Do a static call if the original call was a static function or method - call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults), s.mem()) + aux := ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults) + if testLateExpansion { + callArgs = append(callArgs, s.mem()) + call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) + call.AddArgs(callArgs...) + } else { + // Do a static call if the original call was a static function or method + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem()) + } } call.AuxInt = stksize - s.vars[&memVar] = call + if testLateExpansion { + s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call) + } else { + s.vars[&memVar] = call + } + // Make sure that the stack slots with pointers are kept live // through the call (which is a pre-emption point). Also, we will // use the first call of the last defer exit to compute liveness -- cgit v1.3 From eb67eab861442e6275deb584ecf08c95a639cedb Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 12 Aug 2020 15:46:19 -0400 Subject: cmd/compile: late call expansion for rtcall Change-Id: I0708c9d649d8a579857330b68d9fbcbbeced29e5 Reviewed-on: https://go-review.googlesource.com/c/go/+/248189 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 48 ++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 10 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 96add2d738..08144bace3 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4353,7 +4353,6 @@ func (s *state) openDeferExit() { } else { s.vars[&memVar] = call } - // Make sure that the stack slots with pointers are kept live // through the call (which is a pre-emption point). Also, we will // use the first call of the last defer exit to compute liveness @@ -5076,15 +5075,22 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . s.prevCall = nil // Write args to the stack off := Ctxt.FixedFrameSize() + testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f) var ACArgs []ssa.Param var ACResults []ssa.Param + var callArgs []*ssa.Value + for _, arg := range args { t := arg.Type off = Rnd(off, t.Alignment()) - ptr := s.constOffPtrSP(t.PtrTo(), off) size := t.Size() ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)}) - s.store(t, ptr, arg) + if testLateExpansion { + callArgs = append(callArgs, arg) + } else { + ptr := s.constOffPtrSP(t.PtrTo(), off) + s.store(t, ptr, arg) + } off += size } off = Rnd(off, int64(Widthreg)) @@ -5098,8 +5104,17 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . } // Issue call - call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn, ACArgs, ACResults), s.mem()) - s.vars[&memVar] = call + var call *ssa.Value + aux := ssa.StaticAuxCall(fn, ACArgs, ACResults) + if testLateExpansion { + callArgs = append(callArgs, s.mem()) + call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) + call.AddArgs(callArgs...) + s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call) + } else { + call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem()) + s.vars[&memVar] = call + } if !returns { // Finish block @@ -5115,11 +5130,24 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . // Load results res := make([]*ssa.Value, len(results)) - for i, t := range results { - off = Rnd(off, t.Alignment()) - ptr := s.constOffPtrSP(types.NewPtr(t), off) - res[i] = s.load(t, ptr) - off += t.Size() + if testLateExpansion { + for i, t := range results { + off = Rnd(off, t.Alignment()) + if canSSAType(t) { + res[i] = s.newValue1I(ssa.OpSelectN, t, int64(i), call) + } else { + addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(t), int64(i), call) + res[i] = s.rawLoad(t, addr) + } + off += t.Size() + } + } else { + for i, t := range results { + off = Rnd(off, t.Alignment()) + ptr := s.constOffPtrSP(types.NewPtr(t), off) + res[i] = s.load(t, ptr) + off += t.Size() + } } off = Rnd(off, int64(Widthptr)) -- cgit v1.3 From d317ba5d4489c1ef53d3077afbff30eb72d7d3b0 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 9 Oct 2020 23:26:30 +0700 Subject: cmd/compile: remove NewPtr/NewSlice calls with untyped string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is follow up of CL 260699, NewPtr/NewSlice should not be called with untyped string. No significal change in performance. name old time/op new time/op delta Template 167ms ± 5% 168ms ± 6% ~ (p=0.549 n=9+10) Unicode 73.9ms ±21% 69.5ms ± 5% ~ (p=0.182 n=10+9) GoTypes 611ms ± 4% 603ms ± 2% ~ (p=0.356 n=10+9) Compiler 3.07s ± 4% 3.05s ± 2% ~ (p=0.356 n=9+10) SSA 7.61s ± 3% 7.49s ± 1% -1.67% (p=0.017 n=10+9) Flate 114ms ±10% 110ms ±12% ~ (p=0.165 n=10+10) GoParser 148ms ±23% 138ms ±13% ~ (p=0.436 n=9+9) Reflect 377ms ± 9% 364ms ± 8% ~ (p=0.105 n=10+10) Tar 149ms ±12% 151ms ±12% ~ (p=0.315 n=10+10) XML 201ms ± 3% 206ms ± 8% ~ (p=0.237 n=8+10) LinkCompiler 308ms ± 3% 325ms ± 9% +5.38% (p=0.017 n=9+10) [Geo mean] 352ms 348ms -1.19% Passes toolstash-check. Change-Id: I1ca8e9635f1926e53e457bc06648fa08a5473bf3 Reviewed-on: https://go-review.googlesource.com/c/go/+/260859 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/ssa.go | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 08144bace3..e363f4f723 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -50,12 +50,10 @@ func initssaconfig() { // Caching is disabled in the backend, so generating these here avoids allocations. _ = types.NewPtr(types.Types[TINTER]) // *interface{} _ = types.NewPtr(types.NewPtr(types.Types[TSTRING])) // **string - _ = types.NewPtr(types.NewPtr(types.UntypedString)) // **string _ = types.NewPtr(types.NewSlice(types.Types[TINTER])) // *[]interface{} _ = types.NewPtr(types.NewPtr(types.Bytetype)) // **byte _ = types.NewPtr(types.NewSlice(types.Bytetype)) // *[]byte _ = types.NewPtr(types.NewSlice(types.Types[TSTRING])) // *[]string - _ = types.NewPtr(types.NewSlice(types.UntypedString)) // *[]string _ = types.NewPtr(types.NewPtr(types.NewPtr(types.Types[TUINT8]))) // ***uint8 _ = types.NewPtr(types.Types[TINT16]) // *int16 _ = types.NewPtr(types.Types[TINT64]) // *int64 -- cgit v1.3 From 9449a125e8b25d21c0d522435be44a4a6e7af2d3 Mon Sep 17 00:00:00 2001 From: hk Date: Sun, 11 Oct 2020 03:44:09 +0000 Subject: cmd/compile/internal/gc: fix wrong function name in the comment Change-Id: I2fc5cff7495b5db4eb8f286a5335787241f1a850 GitHub-Last-Rev: 1d226f14c8edbcb189fd1a37b11a82b9642f19ab GitHub-Pull-Request: golang/go#41917 Reviewed-on: https://go-review.googlesource.com/c/go/+/261317 Reviewed-by: Ian Lance Taylor Trust: Robert Griesemer --- src/cmd/compile/internal/gc/export.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 44bea2b1fd..839c2c2c75 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -96,7 +96,7 @@ func importsym(ipkg *types.Pkg, s *types.Sym, op Op) *Node { return n } -// pkgtype returns the named type declared by symbol s. +// importtype returns the named type declared by symbol s. // If no such type has been declared yet, a forward declaration is returned. // ipkg is the package being imported func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type { -- cgit v1.3 From 349a2876467cdae58a772424f044f882fd4e2f6b Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Mon, 12 Oct 2020 14:35:24 +0200 Subject: cmd/compile: use Bool accessor in place of Val.U.(bool) We have a Bool() accessor for the value in boolean nodes, that we use elsewhere for n.Val().U.(bool), use it here too. Noticed while reading the code. Change-Id: Ie42e014970099a05fe9f02f378af77b63e7e6b13 Reviewed-on: https://go-review.googlesource.com/c/go/+/261360 Trust: Alberto Donizetti Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/gc/swt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go index 138b0acc53..bf0410900f 100644 --- a/src/cmd/compile/internal/gc/swt.go +++ b/src/cmd/compile/internal/gc/swt.go @@ -440,7 +440,7 @@ func (c *exprClause) test(exprname *Node) *Node { // Optimize "switch true { ...}" and "switch false { ... }". if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() { - if exprname.Val().U.(bool) { + if exprname.Bool() { return c.lo } else { return nodl(c.pos, ONOT, c.lo, nil) -- cgit v1.3 From 2c6df2e35dd95621d025306a49b863efd77ebf3a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 22 May 2020 15:22:52 -0400 Subject: cmd/compile: reject misplaced go:build comments We are converting from using error-prone ad-hoc syntax // +build lines to less error-prone, standard boolean syntax //go:build lines. The timeline is: Go 1.16: prepare for transition - Builds still use // +build for file selection. - Source files may not contain //go:build without // +build. - Builds fail when a source file contains //go:build lines without // +build lines. <<< Go 1.17: start transition - Builds prefer //go:build for file selection, falling back to // +build for files containing only // +build. - Source files may contain //go:build without // +build (but they won't build with Go 1.16). - Gofmt moves //go:build and // +build lines to proper file locations. - Gofmt introduces //go:build lines into files with only // +build lines. - Go vet rejects files with mismatched //go:build and // +build lines. Go 1.18: complete transition - Go fix removes // +build lines, leaving behind equivalent // +build lines. This CL provides part of the <<< marked line above in the Go 1.16 step: rejecting files containing //go:build but not // +build. The standard go command checks only consider the top of the file. This compiler check, along with a separate go vet check for ignored files, handles the remainder of the file. For #41184. Change-Id: I014006eebfc84ab5943de18bc90449e534f150a2 Reviewed-on: https://go-review.googlesource.com/c/go/+/240601 Trust: Russ Cox Run-TryBot: Russ Cox TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/cmd/compile/internal/gc/lex.go | 5 +++++ src/cmd/compile/internal/gc/noder.go | 1 + test/directive.go | 7 +++++++ 3 files changed, 13 insertions(+) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 25bc0399ce..7cce371408 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -50,6 +50,9 @@ const ( // Runtime and cgo type pragmas NotInHeap // values of this type must not be heap allocated + + // Go command pragmas + GoBuildPragma ) const ( @@ -71,6 +74,8 @@ const ( func pragmaFlag(verb string) PragmaFlag { switch verb { + case "go:build": + return GoBuildPragma case "go:nointerface": if objabi.Fieldtrack_enabled != 0 { return Nointerface diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 5dce533e4b..c63c80dd36 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -242,6 +242,7 @@ func (p *noder) node() { mkpackage(p.file.PkgName.Value) if pragma, ok := p.file.Pragma.(*Pragma); ok { + pragma.Flag &^= GoBuildPragma p.checkUnused(pragma) } diff --git a/test/directive.go b/test/directive.go index 6167cd6279..37781c30d5 100644 --- a/test/directive.go +++ b/test/directive.go @@ -6,11 +6,16 @@ // Verify that misplaced directives are diagnosed. +// ok +//go:build !ignore + //go:noinline // ERROR "misplaced compiler directive" //go:noinline // ERROR "misplaced compiler directive" package main +//go:build bad // ERROR "misplaced compiler directive" + //go:nosplit func f1() {} @@ -93,3 +98,5 @@ type T6 = int // EOF //go:noinline // ERROR "misplaced compiler directive" + +//go:build bad // ERROR "misplaced compiler directive" -- cgit v1.3 From 85bb4294c07cc63fb21743594f3c7872387ff0f4 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 12 Oct 2020 14:02:36 -0700 Subject: cmd/compile: fix and improve alias detection "aliased" is the function responsible for detecting whether we can turn "a, b = x, y" into just "a = x; b = y", or we need to pre-compute y and save it in a temporary variable because it might depend on a. It currently has two issues: 1. It suboptimally treats assignments to blank as writes to heap memory. Users generally won't write "_, b = x, y" directly, but it comes up a lot in generated code within the compiler. This CL changes it to ignore blank assignments. 2. When deciding whether the assigned variable might be referenced by pointers, it mistakenly checks Class() and Name.Addrtaken() on "n" (the *value* expression being assigned) rather than "a" (the destination expression). It doesn't appear to result in correctness issues (i.e., incorrectly reporting no aliasing when there is potential aliasing), due to all the (overly conservative) rewrite passes before code reaches here. But it generates unnecessary code and could have correctness issues if we improve those other passes to be more aggressive. This CL fixes the misuse of "n" for "a" by renaming the variables to "r" and "l", respectively, to make their meaning clearer. Improving these two cases shaves 4.6kB of text from cmd/go, and 93kB from k8s.io/kubernetes/cmd/kubelet: text data bss dec hex filename 9732136 290072 231552 10253760 9c75c0 go.before 9727542 290072 231552 10249166 9c63ce go.after 97977637 1007051 301344 99286032 5eafc10 kubelet.before 97884549 1007051 301344 99192944 5e99070 kubelet.after While here, this CL also collapses "memwrite" and "varwrite" into a single variable. Logically, they're detecting the same thing: are we assigning to a memory location that a pointer might alias. There's no need for two variables. Updates #6853. Updates #23017. Change-Id: I5a307b8e20bcd2196e85c55eb025d3f01e303008 Reviewed-on: https://go-review.googlesource.com/c/go/+/261677 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/walk.go | 62 +++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 30 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 3fe7c3e089..0388662a4f 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -2157,7 +2157,7 @@ func reorder3(all []*Node) []*Node { // The result of reorder3save MUST be assigned back to n, e.g. // n.Left = reorder3save(n.Left, all, i, early) func reorder3save(n *Node, all []*Node, i int, early *[]*Node) *Node { - if !aliased(n, all, i) { + if !aliased(n, all[:i]) { return n } @@ -2189,73 +2189,75 @@ func outervalue(n *Node) *Node { } } -// Is it possible that the computation of n might be -// affected by writes in as up to but not including the ith element? -func aliased(n *Node, all []*Node, i int) bool { - if n == nil { +// Is it possible that the computation of r might be +// affected by assignments in all? +func aliased(r *Node, all []*Node) bool { + if r == nil { return false } // Treat all fields of a struct as referring to the whole struct. // We could do better but we would have to keep track of the fields. - for n.Op == ODOT { - n = n.Left + for r.Op == ODOT { + r = r.Left } // Look for obvious aliasing: a variable being assigned // during the all list and appearing in n. - // Also record whether there are any writes to main memory. - // Also record whether there are any writes to variables - // whose addresses have been taken. + // Also record whether there are any writes to addressable + // memory (either main memory or variables whose addresses + // have been taken). memwrite := false - varwrite := false - for _, an := range all[:i] { - a := outervalue(an.Left) - - for a.Op == ODOT { - a = a.Left + for _, as := range all { + // We can ignore assignments to blank. + if as.Left.isBlank() { + continue } - if a.Op != ONAME { + l := outervalue(as.Left) + if l.Op != ONAME { memwrite = true continue } - switch n.Class() { + switch l.Class() { default: - varwrite = true + Fatalf("unexpected class: %v, %v", l, l.Class()) + + case PAUTOHEAP, PEXTERN: + memwrite = true continue case PAUTO, PPARAM, PPARAMOUT: - if n.Name.Addrtaken() { - varwrite = true + if l.Name.Addrtaken() { + memwrite = true continue } - if vmatch2(a, n) { - // Direct hit. + if vmatch2(l, r) { + // Direct hit: l appears in r. return true } } } - // The variables being written do not appear in n. - // However, n might refer to computed addresses + // The variables being written do not appear in r. + // However, r might refer to computed addresses // that are being written. // If no computed addresses are affected by the writes, no aliasing. - if !memwrite && !varwrite { + if !memwrite { return false } - // If n does not refer to computed addresses - // (that is, if n only refers to variables whose addresses + // If r does not refer to computed addresses + // (that is, if r only refers to variables whose addresses // have not been taken), no aliasing. - if varexpr(n) { + if varexpr(r) { return false } - // Otherwise, both the writes and n refer to computed memory addresses. + // Otherwise, both the writes and r refer to computed memory addresses. // Assume that they might conflict. return true } -- cgit v1.3 From e2931612b04e2ea6be337872c6f4a31c7d7dec54 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Mon, 12 Oct 2020 15:02:59 +0200 Subject: cmd/compile: rename strlit, Bool, and Int64 *Node accessors The Node type has shortcuts to access bool and int Values: func (n *Node) Int64() int64 for n.Val().U.(*Mpint).Int64() func (n *Node) Bool() bool for n.Val().U.(bool) I was convinced we didn't have one for string literal nodes, until I noticed that we do, it's just called strlit, it's not a method, and it's later in the file: func strlit(n *Node) string This change, for consistency: - Renames strlit to StringVal and makes it a *Node method - Renames Bool and Int64 to BoolVal and Int64Val - Moves StringVal near the other two Change-Id: I18e635384c35eb3a238fd52b1ccd322b1a74d733 Reviewed-on: https://go-review.googlesource.com/c/go/+/261361 Trust: Alberto Donizetti Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/const.go | 34 ++++++++++++++++++-------------- src/cmd/compile/internal/gc/esc.go | 2 +- src/cmd/compile/internal/gc/noder.go | 6 +++--- src/cmd/compile/internal/gc/obj.go | 2 +- src/cmd/compile/internal/gc/order.go | 4 ++-- src/cmd/compile/internal/gc/sinit.go | 14 ++++++------- src/cmd/compile/internal/gc/ssa.go | 8 ++++---- src/cmd/compile/internal/gc/swt.go | 10 +++++----- src/cmd/compile/internal/gc/typecheck.go | 31 ++++++++++++++--------------- src/cmd/compile/internal/gc/walk.go | 30 ++++++++++++++-------------- 10 files changed, 72 insertions(+), 69 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index b28c0fc8d0..b92c8d66b5 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -114,16 +114,16 @@ func (v Val) Interface() interface{} { type NilVal struct{} -// Int64 returns n as an int64. +// Int64Val returns n as an int64. // n must be an integer or rune constant. -func (n *Node) Int64() int64 { +func (n *Node) Int64Val() int64 { if !Isconst(n, CTINT) { - Fatalf("Int64(%v)", n) + Fatalf("Int64Val(%v)", n) } return n.Val().U.(*Mpint).Int64() } -// CanInt64 reports whether it is safe to call Int64() on n. +// CanInt64 reports whether it is safe to call Int64Val() on n. func (n *Node) CanInt64() bool { if !Isconst(n, CTINT) { return false @@ -131,18 +131,27 @@ func (n *Node) CanInt64() bool { // if the value inside n cannot be represented as an int64, the // return value of Int64 is undefined - return n.Val().U.(*Mpint).CmpInt64(n.Int64()) == 0 + return n.Val().U.(*Mpint).CmpInt64(n.Int64Val()) == 0 } -// Bool returns n as a bool. +// BoolVal returns n as a bool. // n must be a boolean constant. -func (n *Node) Bool() bool { +func (n *Node) BoolVal() bool { if !Isconst(n, CTBOOL) { - Fatalf("Bool(%v)", n) + Fatalf("BoolVal(%v)", n) } return n.Val().U.(bool) } +// StringVal returns the value of a literal string Node as a string. +// n must be a string constant. +func (n *Node) StringVal() string { + if !Isconst(n, CTSTR) { + Fatalf("StringVal(%v)", n) + } + return n.Val().U.(string) +} + // truncate float literal fv to 32-bit or 64-bit precision // according to type; return truncated value. func truncfltlit(oldv *Mpflt, t *types.Type) *Mpflt { @@ -612,7 +621,7 @@ func evconst(n *Node) { var strs []string i2 := i1 for i2 < len(s) && Isconst(s[i2], CTSTR) { - strs = append(strs, strlit(s[i2])) + strs = append(strs, s[i2].StringVal()) i2++ } @@ -635,7 +644,7 @@ func evconst(n *Node) { switch nl.Type.Etype { case TSTRING: if Isconst(nl, CTSTR) { - setintconst(n, int64(len(strlit(nl)))) + setintconst(n, int64(len(nl.StringVal()))) } case TARRAY: if !hascallchan(nl) { @@ -1129,11 +1138,6 @@ func defaultType(t *types.Type) *types.Type { return nil } -// strlit returns the value of a literal string Node as a string. -func strlit(n *Node) string { - return n.Val().U.(string) -} - func smallintconst(n *Node) bool { if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil { switch simtype[n.Type.Etype] { diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index d7aa72b450..c11066a62f 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -204,7 +204,7 @@ func heapAllocReason(n *Node) string { if !smallintconst(r) { return "non-constant size" } - if t := n.Type; t.Elem().Width != 0 && r.Int64() >= maxImplicitStackVarSize/t.Elem().Width { + if t := n.Type; t.Elem().Width != 0 && r.Int64Val() >= maxImplicitStackVarSize/t.Elem().Width { return "too large for stack" } } diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index c63c80dd36..68d0327cdb 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -774,7 +774,7 @@ func (p *noder) sum(x syntax.Expr) *Node { n := p.expr(x) if Isconst(n, CTSTR) && n.Sym == nil { nstr = n - chunks = append(chunks, strlit(nstr)) + chunks = append(chunks, nstr.StringVal()) } for i := len(adds) - 1; i >= 0; i-- { @@ -784,12 +784,12 @@ func (p *noder) sum(x syntax.Expr) *Node { if Isconst(r, CTSTR) && r.Sym == nil { if nstr != nil { // Collapse r into nstr instead of adding to n. - chunks = append(chunks, strlit(r)) + chunks = append(chunks, r.StringVal()) continue } nstr = r - chunks = append(chunks, strlit(nstr)) + chunks = append(chunks, nstr.StringVal()) } else { if len(chunks) > 1 { nstr.SetVal(Val{U: strings.Join(chunks, "")}) diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index b55331a948..d7f4a94041 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -272,7 +272,7 @@ func dumpGlobalConst(n *Node) { default: return } - Ctxt.DwarfIntConst(myimportpath, n.Sym.Name, typesymname(t), n.Int64()) + Ctxt.DwarfIntConst(myimportpath, n.Sym.Name, typesymname(t), n.Int64Val()) } func dumpglobls() { diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 75da154fe2..e562ab7556 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -1102,7 +1102,7 @@ func (o *Order) expr(n, lhs *Node) *Node { haslit := false for _, n1 := range n.List.Slice() { hasbyte = hasbyte || n1.Op == OBYTES2STR - haslit = haslit || n1.Op == OLITERAL && len(strlit(n1)) != 0 + haslit = haslit || n1.Op == OLITERAL && len(n1.StringVal()) != 0 } if haslit && hasbyte { @@ -1274,7 +1274,7 @@ func (o *Order) expr(n, lhs *Node) *Node { var t *types.Type switch n.Op { case OSLICELIT: - t = types.NewArray(n.Type.Elem(), n.Right.Int64()) + t = types.NewArray(n.Type.Elem(), n.Right.Int64Val()) case OCALLPART: t = partialCallType(n) } diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index af19a96bbc..fda33534b6 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -128,7 +128,7 @@ func (s *InitSchedule) staticcopy(l *Node, r *Node) bool { case OSLICELIT: // copy slice a := s.inittemps[r] - slicesym(l, a, r.Right.Int64()) + slicesym(l, a, r.Right.Int64Val()) return true case OARRAYLIT, OSTRUCTLIT: @@ -205,7 +205,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool { case OSTR2BYTES: if l.Class() == PEXTERN && r.Left.Op == OLITERAL { - sval := strlit(r.Left) + sval := r.Left.StringVal() slicebytes(l, sval) return true } @@ -213,7 +213,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool { case OSLICELIT: s.initplan(r) // Init slice. - bound := r.Right.Int64() + bound := r.Right.Int64Val() ta := types.NewArray(r.Type.Elem(), bound) ta.SetNoalg(true) a := staticname(ta) @@ -413,7 +413,7 @@ func getdyn(n *Node, top bool) initGenType { if !top { return initDynamic } - if n.Right.Int64()/4 > int64(n.List.Len()) { + if n.Right.Int64Val()/4 > int64(n.List.Len()) { // <25% of entries have explicit values. // Very rough estimation, it takes 4 bytes of instructions // to initialize 1 byte of result. So don't use a static @@ -589,12 +589,12 @@ func isSmallSliceLit(n *Node) bool { r := n.Right - return smallintconst(r) && (n.Type.Elem().Width == 0 || r.Int64() <= smallArrayBytes/n.Type.Elem().Width) + return smallintconst(r) && (n.Type.Elem().Width == 0 || r.Int64Val() <= smallArrayBytes/n.Type.Elem().Width) } func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) { // make an array type corresponding the number of elements we have - t := types.NewArray(n.Type.Elem(), n.Right.Int64()) + t := types.NewArray(n.Type.Elem(), n.Right.Int64Val()) dowidth(t) if ctxt == inNonInitFunction { @@ -993,7 +993,7 @@ func oaslit(n *Node, init *Nodes) bool { func getlit(lit *Node) int { if smallintconst(lit) { - return int(lit.Int64()) + return int(lit.Int64Val()) } return -1 } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index e363f4f723..3d5fa4cd0a 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -1271,7 +1271,7 @@ func (s *state) stmt(n *Node) { // We're assigning a slicing operation back to its source. // Don't write back fields we aren't changing. See issue #14855. i, j, k := rhs.SliceBounds() - if i != nil && (i.Op == OLITERAL && i.Val().Ctype() == CTINT && i.Int64() == 0) { + if i != nil && (i.Op == OLITERAL && i.Val().Ctype() == CTINT && i.Int64Val() == 0) { // [0:...] is the same as [:...] i = nil } @@ -1301,7 +1301,7 @@ func (s *state) stmt(n *Node) { case OIF: if Isconst(n.Left, CTBOOL) { s.stmtList(n.Left.Ninit) - if n.Left.Bool() { + if n.Left.BoolVal() { s.stmtList(n.Nbody) } else { s.stmtList(n.Rlist) @@ -2610,7 +2610,7 @@ func (s *state) expr(n *Node) *ssa.Value { // Replace "abc"[1] with 'b'. // Delayed until now because "abc"[1] is not an ideal constant. // See test/fixedbugs/issue11370.go. - return s.newValue0I(ssa.OpConst8, types.Types[TUINT8], int64(int8(strlit(n.Left)[n.Right.Int64()]))) + return s.newValue0I(ssa.OpConst8, types.Types[TUINT8], int64(int8(n.Left.StringVal()[n.Right.Int64Val()]))) } a := s.expr(n.Left) i := s.expr(n.Right) @@ -2619,7 +2619,7 @@ func (s *state) expr(n *Node) *ssa.Value { ptrtyp := s.f.Config.Types.BytePtr ptr := s.newValue1(ssa.OpStringPtr, ptrtyp, a) if Isconst(n.Right, CTINT) { - ptr = s.newValue1I(ssa.OpOffPtr, ptrtyp, n.Right.Int64(), ptr) + ptr = s.newValue1I(ssa.OpOffPtr, ptrtyp, n.Right.Int64Val(), ptr) } else { ptr = s.newValue2(ssa.OpAddPtr, ptrtyp, ptr, i) } diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go index bf0410900f..bfbedb2aa5 100644 --- a/src/cmd/compile/internal/gc/swt.go +++ b/src/cmd/compile/internal/gc/swt.go @@ -358,8 +358,8 @@ func (s *exprSwitch) flush() { // all we need here is consistency. We respect this // sorting below. sort.Slice(cc, func(i, j int) bool { - si := strlit(cc[i].lo) - sj := strlit(cc[j].lo) + si := cc[i].lo.StringVal() + sj := cc[j].lo.StringVal() if len(si) != len(sj) { return len(si) < len(sj) } @@ -368,7 +368,7 @@ func (s *exprSwitch) flush() { // runLen returns the string length associated with a // particular run of exprClauses. - runLen := func(run []exprClause) int64 { return int64(len(strlit(run[0].lo))) } + runLen := func(run []exprClause) int64 { return int64(len(run[0].lo.StringVal())) } // Collapse runs of consecutive strings with the same length. var runs [][]exprClause @@ -405,7 +405,7 @@ func (s *exprSwitch) flush() { merged := cc[:1] for _, c := range cc[1:] { last := &merged[len(merged)-1] - if last.jmp == c.jmp && last.hi.Int64()+1 == c.lo.Int64() { + if last.jmp == c.jmp && last.hi.Int64Val()+1 == c.lo.Int64Val() { last.hi = c.lo } else { merged = append(merged, c) @@ -440,7 +440,7 @@ func (c *exprClause) test(exprname *Node) *Node { // Optimize "switch true { ...}" and "switch false { ... }". if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() { - if exprname.Bool() { + if exprname.BoolVal() { return c.lo } else { return nodl(c.pos, ONOT, c.lo, nil) diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 769341ee04..75ce95832e 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -1046,13 +1046,13 @@ func typecheck1(n *Node, top int) (res *Node) { } if !n.Bounded() && Isconst(n.Right, CTINT) { - x := n.Right.Int64() + x := n.Right.Int64Val() if x < 0 { yyerror("invalid %s index %v (index must be non-negative)", why, n.Right) } else if t.IsArray() && x >= t.NumElem() { yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.NumElem()) - } else if Isconst(n.Left, CTSTR) && x >= int64(len(strlit(n.Left))) { - yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(strlit(n.Left))) + } else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.StringVal())) { + yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.StringVal())) } else if n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 { yyerror("invalid %s index %v (index too large)", why, n.Right) } @@ -1148,11 +1148,11 @@ func typecheck1(n *Node, top int) (res *Node) { l = defaultlit(l, types.Types[TINT]) c = defaultlit(c, types.Types[TINT]) - if Isconst(l, CTINT) && l.Int64() < 0 { + if Isconst(l, CTINT) && l.Int64Val() < 0 { Fatalf("len for OSLICEHEADER must be non-negative") } - if Isconst(c, CTINT) && c.Int64() < 0 { + if Isconst(c, CTINT) && c.Int64Val() < 0 { Fatalf("cap for OSLICEHEADER must be non-negative") } @@ -1201,7 +1201,7 @@ func typecheck1(n *Node, top int) (res *Node) { if n.Left.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 { Fatalf("len for OMAKESLICECOPY too large") } - if n.Left.Int64() < 0 { + if n.Left.Int64Val() < 0 { Fatalf("len for OMAKESLICECOPY must be non-negative") } } @@ -2187,14 +2187,14 @@ func checksliceindex(l *Node, r *Node, tp *types.Type) bool { } if r.Op == OLITERAL { - if r.Int64() < 0 { + if r.Int64Val() < 0 { yyerror("invalid slice index %v (index must be non-negative)", r) return false - } else if tp != nil && tp.NumElem() >= 0 && r.Int64() > tp.NumElem() { + } else if tp != nil && tp.NumElem() >= 0 && r.Int64Val() > tp.NumElem() { yyerror("invalid slice index %v (out of bounds for %d-element array)", r, tp.NumElem()) return false - } else if Isconst(l, CTSTR) && r.Int64() > int64(len(strlit(l))) { - yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(strlit(l))) + } else if Isconst(l, CTSTR) && r.Int64Val() > int64(len(l.StringVal())) { + yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.StringVal())) return false } else if r.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 { yyerror("invalid slice index %v (index too large)", r) @@ -3450,9 +3450,8 @@ func stringtoruneslit(n *Node) *Node { } var l []*Node - s := strlit(n.Left) i := 0 - for _, r := range s { + for _, r := range n.Left.StringVal() { l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(r)))) i++ } @@ -3904,7 +3903,7 @@ func deadcodefn(fn *Node) { return } case OFOR: - if !Isconst(n.Left, CTBOOL) || n.Left.Bool() { + if !Isconst(n.Left, CTBOOL) || n.Left.BoolVal() { return } default: @@ -3934,7 +3933,7 @@ func deadcodeslice(nn Nodes) { n.Left = deadcodeexpr(n.Left) if Isconst(n.Left, CTBOOL) { var body Nodes - if n.Left.Bool() { + if n.Left.BoolVal() { n.Rlist = Nodes{} body = n.Nbody } else { @@ -3977,7 +3976,7 @@ func deadcodeexpr(n *Node) *Node { n.Left = deadcodeexpr(n.Left) n.Right = deadcodeexpr(n.Right) if Isconst(n.Left, CTBOOL) { - if n.Left.Bool() { + if n.Left.BoolVal() { return n.Right // true && x => x } else { return n.Left // false && x => false @@ -3987,7 +3986,7 @@ func deadcodeexpr(n *Node) *Node { n.Left = deadcodeexpr(n.Left) n.Right = deadcodeexpr(n.Right) if Isconst(n.Left, CTBOOL) { - if n.Left.Bool() { + if n.Left.BoolVal() { return n.Left // true || x => true } else { return n.Right // false || x => x diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 0388662a4f..05a049b3cc 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -1001,7 +1001,7 @@ opswitch: // The SSA backend will handle those. switch et { case TINT64: - c := n.Right.Int64() + c := n.Right.Int64Val() if c < 0 { c = -c } @@ -1009,7 +1009,7 @@ opswitch: break opswitch } case TUINT64: - c := uint64(n.Right.Int64()) + c := uint64(n.Right.Int64Val()) if c != 0 && c&(c-1) == 0 { break opswitch } @@ -1056,7 +1056,7 @@ opswitch: yyerror("index out of bounds") } } else if Isconst(n.Left, CTSTR) { - n.SetBounded(bounded(r, int64(len(strlit(n.Left))))) + n.SetBounded(bounded(r, int64(len(n.Left.StringVal())))) if Debug['m'] != 0 && n.Bounded() && !Isconst(n.Right, CTINT) { Warn("index bounds check elided") } @@ -1491,7 +1491,7 @@ opswitch: case OSTR2BYTES: s := n.Left if Isconst(s, CTSTR) { - sc := strlit(s) + sc := s.StringVal() // Allocate a [n]byte of the right size. t := types.NewArray(types.Types[TUINT8], int64(len(sc))) @@ -1919,7 +1919,7 @@ func walkprint(nn *Node, init *Nodes) *Node { for i := 0; i < len(s); { var strs []string for i < len(s) && Isconst(s[i], CTSTR) { - strs = append(strs, strlit(s[i])) + strs = append(strs, s[i].StringVal()) i++ } if len(strs) > 0 { @@ -1988,7 +1988,7 @@ func walkprint(nn *Node, init *Nodes) *Node { case TSTRING: cs := "" if Isconst(n, CTSTR) { - cs = strlit(n) + cs = n.StringVal() } switch cs { case " ": @@ -2645,7 +2645,7 @@ func addstr(n *Node, init *Nodes) *Node { sz := int64(0) for _, n1 := range n.List.Slice() { if n1.Op == OLITERAL { - sz += int64(len(strlit(n1))) + sz += int64(len(n1.StringVal())) } } @@ -3439,7 +3439,7 @@ func walkcompare(n *Node, init *Nodes) *Node { func tracecmpArg(n *Node, t *types.Type, init *Nodes) *Node { // Ugly hack to avoid "constant -1 overflows uintptr" errors, etc. - if n.Op == OLITERAL && n.Type.IsSigned() && n.Int64() < 0 { + if n.Op == OLITERAL && n.Type.IsSigned() && n.Int64Val() < 0 { n = copyexpr(n, n.Type, init) } @@ -3509,7 +3509,7 @@ func walkcompareString(n *Node, init *Nodes) *Node { // Length-only checks are ok, though. maxRewriteLen = 0 } - if s := strlit(cs); len(s) <= maxRewriteLen { + if s := cs.StringVal(); len(s) <= maxRewriteLen { if len(s) > 0 { ncs = safeexpr(ncs, init) } @@ -3604,7 +3604,7 @@ func bounded(n *Node, max int64) bool { bits := int32(8 * n.Type.Width) if smallintconst(n) { - v := n.Int64() + v := n.Int64Val() return 0 <= v && v < max } @@ -3612,9 +3612,9 @@ func bounded(n *Node, max int64) bool { case OAND: v := int64(-1) if smallintconst(n.Left) { - v = n.Left.Int64() + v = n.Left.Int64Val() } else if smallintconst(n.Right) { - v = n.Right.Int64() + v = n.Right.Int64Val() } if 0 <= v && v < max { @@ -3623,7 +3623,7 @@ func bounded(n *Node, max int64) bool { case OMOD: if !sign && smallintconst(n.Right) { - v := n.Right.Int64() + v := n.Right.Int64Val() if 0 <= v && v <= max { return true } @@ -3631,7 +3631,7 @@ func bounded(n *Node, max int64) bool { case ODIV: if !sign && smallintconst(n.Right) { - v := n.Right.Int64() + v := n.Right.Int64Val() for bits > 0 && v >= 2 { bits-- v >>= 1 @@ -3640,7 +3640,7 @@ func bounded(n *Node, max int64) bool { case ORSH: if !sign && smallintconst(n.Right) { - v := n.Right.Int64() + v := n.Right.Int64Val() if v > int64(bits) { return true } -- cgit v1.3 From 2517f4946b42b8deedb864c884f1b41311d45850 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Wed, 14 Oct 2020 17:18:27 -0400 Subject: runtime: remove debugCachedWork debugCachedWork and all of its dependent fields and code were added to aid in debugging issue #27993. Now that the source of the problem is known and mitigated (via the extra work check after STW in gcMarkDone), these extra checks are no longer required and simply make the code more difficult to follow. Remove it all. Updates #27993 Change-Id: I594beedd5ca61733ba9cc9eaad8f80ea92df1a0d Reviewed-on: https://go-review.googlesource.com/c/go/+/262350 Trust: Michael Pratt Run-TryBot: Michael Pratt TryBot-Result: Go Bot Reviewed-by: Austin Clements --- src/cmd/compile/internal/gc/inl_test.go | 2 +- src/runtime/mgc.go | 121 +++++++------------------------- src/runtime/mgcwork.go | 74 ------------------- src/runtime/mwbbuf.go | 32 +-------- src/runtime/panic.go | 9 --- 5 files changed, 27 insertions(+), 211 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl_test.go b/src/cmd/compile/internal/gc/inl_test.go index 9d3b8c59fd..547a602ee0 100644 --- a/src/cmd/compile/internal/gc/inl_test.go +++ b/src/cmd/compile/internal/gc/inl_test.go @@ -83,7 +83,7 @@ func TestIntendedInlining(t *testing.T) { "puintptr.ptr", "spanOf", "spanOfUnchecked", - //"(*gcWork).putFast", // TODO(austin): For debugging #27993 + "(*gcWork).putFast", "(*gcWork).tryGetFast", "(*guintptr).set", "(*markBits).advance", diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index bd87144355..0a4d5616a5 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1407,19 +1407,6 @@ func gcStart(trigger gcTrigger) { // This is protected by markDoneSema. var gcMarkDoneFlushed uint32 -// debugCachedWork enables extra checks for debugging premature mark -// termination. -// -// For debugging issue #27993. -const debugCachedWork = false - -// gcWorkPauseGen is for debugging the mark completion algorithm. -// gcWork put operations spin while gcWork.pauseGen == gcWorkPauseGen. -// Only used if debugCachedWork is true. -// -// For debugging issue #27993. -var gcWorkPauseGen uint32 = 1 - // gcMarkDone transitions the GC from mark to mark termination if all // reachable objects have been marked (that is, there are no grey // objects and can be no more in the future). Otherwise, it flushes @@ -1475,15 +1462,7 @@ top: // Flush the write barrier buffer, since this may add // work to the gcWork. wbBufFlush1(_p_) - // For debugging, shrink the write barrier - // buffer so it flushes immediately. - // wbBuf.reset will keep it at this size as - // long as throwOnGCWork is set. - if debugCachedWork { - b := &_p_.wbBuf - b.end = uintptr(unsafe.Pointer(&b.buf[wbBufEntryPointers])) - b.debugGen = gcWorkPauseGen - } + // Flush the gcWork, since this may create global work // and set the flushedWork flag. // @@ -1494,29 +1473,12 @@ top: if _p_.gcw.flushedWork { atomic.Xadd(&gcMarkDoneFlushed, 1) _p_.gcw.flushedWork = false - } else if debugCachedWork { - // For debugging, freeze the gcWork - // until we know whether we've reached - // completion or not. If we think - // we've reached completion, but - // there's a paused gcWork, then - // that's a bug. - _p_.gcw.pauseGen = gcWorkPauseGen - // Capture the G's stack. - for i := range _p_.gcw.pauseStack { - _p_.gcw.pauseStack[i] = 0 - } - callers(1, _p_.gcw.pauseStack[:]) } }) casgstatus(gp, _Gwaiting, _Grunning) }) if gcMarkDoneFlushed != 0 { - if debugCachedWork { - // Release paused gcWorks. - atomic.Xadd(&gcWorkPauseGen, 1) - } // More grey objects were discovered since the // previous termination check, so there may be more // work to do. Keep going. It's possible the @@ -1526,13 +1488,6 @@ top: goto top } - if debugCachedWork { - throwOnGCWork = true - // Release paused gcWorks. If there are any, they - // should now observe throwOnGCWork and panic. - atomic.Xadd(&gcWorkPauseGen, 1) - } - // There was no global work, no local work, and no Ps // communicated work since we took markDoneSema. Therefore // there are no grey objects and no more objects can be @@ -1549,59 +1504,33 @@ top: // below. The important thing is that the wb remains active until // all marking is complete. This includes writes made by the GC. - if debugCachedWork { - // For debugging, double check that no work was added after we - // went around above and disable write barrier buffering. + // There is sometimes work left over when we enter mark termination due + // to write barriers performed after the completion barrier above. + // Detect this and resume concurrent mark. This is obviously + // unfortunate. + // + // See issue #27993 for details. + // + // Switch to the system stack to call wbBufFlush1, though in this case + // it doesn't matter because we're non-preemptible anyway. + restart := false + systemstack(func() { for _, p := range allp { - gcw := &p.gcw - if !gcw.empty() { - printlock() - print("runtime: P ", p.id, " flushedWork ", gcw.flushedWork) - if gcw.wbuf1 == nil { - print(" wbuf1=") - } else { - print(" wbuf1.n=", gcw.wbuf1.nobj) - } - if gcw.wbuf2 == nil { - print(" wbuf2=") - } else { - print(" wbuf2.n=", gcw.wbuf2.nobj) - } - print("\n") - if gcw.pauseGen == gcw.putGen { - println("runtime: checkPut already failed at this generation") - } - throw("throwOnGCWork") + wbBufFlush1(p) + if !p.gcw.empty() { + restart = true + break } } - } else { - // For unknown reasons (see issue #27993), there is - // sometimes work left over when we enter mark - // termination. Detect this and resume concurrent - // mark. This is obviously unfortunate. - // - // Switch to the system stack to call wbBufFlush1, - // though in this case it doesn't matter because we're - // non-preemptible anyway. - restart := false + }) + if restart { + getg().m.preemptoff = "" systemstack(func() { - for _, p := range allp { - wbBufFlush1(p) - if !p.gcw.empty() { - restart = true - break - } - } + now := startTheWorldWithSema(true) + work.pauseNS += now - work.pauseStart }) - if restart { - getg().m.preemptoff = "" - systemstack(func() { - now := startTheWorldWithSema(true) - work.pauseNS += now - work.pauseStart - }) - semrelease(&worldsema) - goto top - } + semrelease(&worldsema) + goto top } // Disable assists and background workers. We must do @@ -2085,7 +2014,7 @@ func gcMark(start_time int64) { // ensured all reachable objects were marked, all of // these must be pointers to black objects. Hence we // can just discard the write barrier buffer. - if debug.gccheckmark > 0 || throwOnGCWork { + if debug.gccheckmark > 0 { // For debugging, flush the buffer and make // sure it really was all marked. wbBufFlush1(p) @@ -2117,8 +2046,6 @@ func gcMark(start_time int64) { gcw.dispose() } - throwOnGCWork = false - cachestats() // Update the marked heap stat. diff --git a/src/runtime/mgcwork.go b/src/runtime/mgcwork.go index 46101657d5..51e0fe9219 100644 --- a/src/runtime/mgcwork.go +++ b/src/runtime/mgcwork.go @@ -22,13 +22,6 @@ const ( workbufAlloc = 32 << 10 ) -// throwOnGCWork causes any operations that add pointers to a gcWork -// buffer to throw. -// -// TODO(austin): This is a temporary debugging measure for issue -// #27993. To be removed before release. -var throwOnGCWork bool - func init() { if workbufAlloc%pageSize != 0 || workbufAlloc%_WorkbufSize != 0 { throw("bad workbufAlloc") @@ -93,17 +86,6 @@ type gcWork struct { // termination check. Specifically, this indicates that this // gcWork may have communicated work to another gcWork. flushedWork bool - - // pauseGen causes put operations to spin while pauseGen == - // gcWorkPauseGen if debugCachedWork is true. - pauseGen uint32 - - // putGen is the pauseGen of the last putGen. - putGen uint32 - - // pauseStack is the stack at which this P was paused if - // debugCachedWork is true. - pauseStack [16]uintptr } // Most of the methods of gcWork are go:nowritebarrierrec because the @@ -122,60 +104,10 @@ func (w *gcWork) init() { w.wbuf2 = wbuf2 } -func (w *gcWork) checkPut(ptr uintptr, ptrs []uintptr) { - if debugCachedWork { - alreadyFailed := w.putGen == w.pauseGen - w.putGen = w.pauseGen - if !canPreemptM(getg().m) { - // If we were to spin, the runtime may - // deadlock. Since we can't be preempted, the - // spin could prevent gcMarkDone from - // finishing the ragged barrier, which is what - // releases us from the spin. - return - } - for atomic.Load(&gcWorkPauseGen) == w.pauseGen { - } - if throwOnGCWork { - printlock() - if alreadyFailed { - println("runtime: checkPut already failed at this generation") - } - println("runtime: late gcWork put") - if ptr != 0 { - gcDumpObject("ptr", ptr, ^uintptr(0)) - } - for _, ptr := range ptrs { - gcDumpObject("ptrs", ptr, ^uintptr(0)) - } - println("runtime: paused at") - for _, pc := range w.pauseStack { - if pc == 0 { - break - } - f := findfunc(pc) - if f.valid() { - // Obviously this doesn't - // relate to ancestor - // tracebacks, but this - // function prints what we - // want. - printAncestorTracebackFuncInfo(f, pc) - } else { - println("\tunknown PC ", hex(pc), "\n") - } - } - throw("throwOnGCWork") - } - } -} - // put enqueues a pointer for the garbage collector to trace. // obj must point to the beginning of a heap object or an oblet. //go:nowritebarrierrec func (w *gcWork) put(obj uintptr) { - w.checkPut(obj, nil) - flushed := false wbuf := w.wbuf1 // Record that this may acquire the wbufSpans or heap lock to @@ -214,8 +146,6 @@ func (w *gcWork) put(obj uintptr) { // otherwise it returns false and the caller needs to call put. //go:nowritebarrierrec func (w *gcWork) putFast(obj uintptr) bool { - w.checkPut(obj, nil) - wbuf := w.wbuf1 if wbuf == nil { return false @@ -237,8 +167,6 @@ func (w *gcWork) putBatch(obj []uintptr) { return } - w.checkPut(0, obj) - flushed := false wbuf := w.wbuf1 if wbuf == nil { @@ -360,12 +288,10 @@ func (w *gcWork) balance() { return } if wbuf := w.wbuf2; wbuf.nobj != 0 { - w.checkPut(0, wbuf.obj[:wbuf.nobj]) putfull(wbuf) w.flushedWork = true w.wbuf2 = getempty() } else if wbuf := w.wbuf1; wbuf.nobj > 4 { - w.checkPut(0, wbuf.obj[:wbuf.nobj]) w.wbuf1 = handoff(wbuf) w.flushedWork = true // handoff did putfull } else { diff --git a/src/runtime/mwbbuf.go b/src/runtime/mwbbuf.go index 632769c114..6efc00007d 100644 --- a/src/runtime/mwbbuf.go +++ b/src/runtime/mwbbuf.go @@ -57,12 +57,6 @@ type wbBuf struct { // on. This must be a multiple of wbBufEntryPointers because // the write barrier only checks for overflow once per entry. buf [wbBufEntryPointers * wbBufEntries]uintptr - - // debugGen causes the write barrier buffer to flush after - // every write barrier if equal to gcWorkPauseGen. This is for - // debugging #27993. This is only set if debugCachedWork is - // set. - debugGen uint32 } const ( @@ -86,7 +80,7 @@ const ( func (b *wbBuf) reset() { start := uintptr(unsafe.Pointer(&b.buf[0])) b.next = start - if writeBarrier.cgo || (debugCachedWork && (throwOnGCWork || b.debugGen == atomic.Load(&gcWorkPauseGen))) { + if writeBarrier.cgo { // Effectively disable the buffer by forcing a flush // on every barrier. b.end = uintptr(unsafe.Pointer(&b.buf[wbBufEntryPointers])) @@ -204,32 +198,10 @@ func wbBufFlush(dst *uintptr, src uintptr) { // Switch to the system stack so we don't have to worry about // the untyped stack slots or safe points. systemstack(func() { - if debugCachedWork { - // For debugging, include the old value of the - // slot and some other data in the traceback. - wbBuf := &getg().m.p.ptr().wbBuf - var old uintptr - if dst != nil { - // dst may be nil in direct calls to wbBufFlush. - old = *dst - } - wbBufFlush1Debug(old, wbBuf.buf[0], wbBuf.buf[1], &wbBuf.buf[0], wbBuf.next) - } else { - wbBufFlush1(getg().m.p.ptr()) - } + wbBufFlush1(getg().m.p.ptr()) }) } -// wbBufFlush1Debug is a temporary function for debugging issue -// #27993. It exists solely to add some context to the traceback. -// -//go:nowritebarrierrec -//go:systemstack -//go:noinline -func wbBufFlush1Debug(old, buf1, buf2 uintptr, start *uintptr, next uintptr) { - wbBufFlush1(getg().m.p.ptr()) -} - // wbBufFlush1 flushes p's write barrier buffer to the GC work queue. // // This must not have write barriers because it is part of the write diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 6050a34d29..aed17d6fc6 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -421,15 +421,6 @@ func newdefer(siz int32) *_defer { total := roundupsize(totaldefersize(uintptr(siz))) d = (*_defer)(mallocgc(total, deferType, true)) }) - if debugCachedWork { - // Duplicate the tail below so if there's a - // crash in checkPut we can tell if d was just - // allocated or came from the pool. - d.siz = siz - d.link = gp._defer - gp._defer = d - return d - } } d.siz = siz d.heap = true -- cgit v1.3 From 50b7171af05531df6744cf33dabfceaa98e986e0 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 15 Oct 2020 22:49:30 +0700 Subject: cmd/compile: simplify exprformat untyped condition checking L1337 in fmt.go can be checked just by using "!n.Type.IsUntyped". Passes toolstash-check. Change-Id: I5b0c81543bc929367f70713d0ca40b289f905b48 Reviewed-on: https://go-review.googlesource.com/c/go/+/262637 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/fmt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 36b596338f..9ba1789633 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -1333,7 +1333,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) { n.Orig.exprfmt(s, prec, mode) return } - if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != types.UntypedBool && n.Type != types.UntypedString { + if n.Type != nil && !n.Type.IsUntyped() { // Need parens when type begins with what might // be misinterpreted as a unary operator: * or <-. if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == types.Crecv) { -- cgit v1.3 From 3c9488edff40f9837dfef94554ac638433708a65 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Thu, 15 Oct 2020 14:48:26 +0200 Subject: cmd/compile: clean up C->Go translation artifacts in badtype Change-Id: I576a596ed8e9ce14e3750031d0e338e9276eff1e Reviewed-on: https://go-review.googlesource.com/c/go/+/262537 Trust: Alberto Donizetti Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/subr.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 0242832322..c5ef707cb7 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1040,25 +1040,24 @@ func calcHasCall(n *Node) bool { return false } -func badtype(op Op, tl *types.Type, tr *types.Type) { - fmt_ := "" +func badtype(op Op, tl, tr *types.Type) { + var s string if tl != nil { - fmt_ += fmt.Sprintf("\n\t%v", tl) + s += fmt.Sprintf("\n\t%v", tl) } if tr != nil { - fmt_ += fmt.Sprintf("\n\t%v", tr) + s += fmt.Sprintf("\n\t%v", tr) } // common mistake: *struct and *interface. if tl != nil && tr != nil && tl.IsPtr() && tr.IsPtr() { if tl.Elem().IsStruct() && tr.Elem().IsInterface() { - fmt_ += "\n\t(*struct vs *interface)" + s += "\n\t(*struct vs *interface)" } else if tl.Elem().IsInterface() && tr.Elem().IsStruct() { - fmt_ += "\n\t(*interface vs *struct)" + s += "\n\t(*interface vs *struct)" } } - s := fmt_ yyerror("illegal types for operand: %v%s", op, s) } -- cgit v1.3 From 8773d141641708574654c617b686a7fd687c3f70 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Tue, 13 Oct 2020 15:58:10 +0200 Subject: cmd/compile: make assignop/convertop reason a return param On a negative answer, the assignop and convertop functions write the reason why to a string pointer passed as an argument, likely a C-ism leftover since the compiler's machine assisted translation to Go. This change makes why a return parameter. It also fixes a few places where the assignop/convertop result was compared to 0. While OXXX's value may be zero now, using the named constant is more robust. Change-Id: Id9147ed4c1b97d658d30a2f778f876b7867006b4 Reviewed-on: https://go-review.googlesource.com/c/go/+/261857 Reviewed-by: Matthew Dempsky Trust: Alberto Donizetti --- src/cmd/compile/internal/gc/range.go | 13 +-- src/cmd/compile/internal/gc/subr.go | 150 ++++++++++++++----------------- src/cmd/compile/internal/gc/swt.go | 19 ++-- src/cmd/compile/internal/gc/typecheck.go | 14 ++- 4 files changed, 92 insertions(+), 104 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go index 5434b0167a..6d22964dcd 100644 --- a/src/cmd/compile/internal/gc/range.go +++ b/src/cmd/compile/internal/gc/range.go @@ -112,12 +112,13 @@ func typecheckrangeExpr(n *Node) { v2 = nil } - var why string if v1 != nil { if v1.Name != nil && v1.Name.Defn == n { v1.Type = t1 - } else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 { - yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t1, v1, why) + } else if v1.Type != nil { + if op, why := assignop(t1, v1.Type); op == OXXX { + yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t1, v1, why) + } } checkassign(n, v1) } @@ -125,8 +126,10 @@ func typecheckrangeExpr(n *Node) { if v2 != nil { if v2.Name != nil && v2.Name.Defn == n { v2.Type = t2 - } else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 { - yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t2, v2, why) + } else if v2.Type != nil { + if op, why := assignop(t2, v2.Type); op == OXXX { + yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t2, v2, why) + } } checkassign(n, v2) } diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index c5ef707cb7..f4b0c0fae0 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -546,22 +546,19 @@ func methtype(t *types.Type) *types.Type { // Is type src assignment compatible to type dst? // If so, return op code to use in conversion. -// If not, return OXXX. -func assignop(src, dst *types.Type, why *string) Op { - if why != nil { - *why = "" - } - +// If not, return OXXX. In this case, the string return parameter may +// hold a reason why. In all other cases, it'll be the empty string. +func assignop(src, dst *types.Type) (Op, string) { if src == dst { - return OCONVNOP + return OCONVNOP, "" } if src == nil || dst == nil || src.Etype == TFORW || dst.Etype == TFORW || src.Orig == nil || dst.Orig == nil { - return OXXX + return OXXX, "" } // 1. src type is identical to dst. if types.Identical(src, dst) { - return OCONVNOP + return OCONVNOP, "" } // 2. src and dst have identical underlying types @@ -575,13 +572,13 @@ func assignop(src, dst *types.Type, why *string) Op { if src.IsEmptyInterface() { // Conversion between two empty interfaces // requires no code. - return OCONVNOP + return OCONVNOP, "" } if (src.Sym == nil || dst.Sym == nil) && !src.IsInterface() { // Conversion between two types, at least one unnamed, // needs no conversion. The exception is nonempty interfaces // which need to have their itab updated. - return OCONVNOP + return OCONVNOP, "" } } @@ -590,49 +587,47 @@ func assignop(src, dst *types.Type, why *string) Op { var missing, have *types.Field var ptr int if implements(src, dst, &missing, &have, &ptr) { - return OCONVIFACE + return OCONVIFACE, "" } // we'll have complained about this method anyway, suppress spurious messages. if have != nil && have.Sym == missing.Sym && (have.Type.Broke() || missing.Type.Broke()) { - return OCONVIFACE - } - - if why != nil { - if isptrto(src, TINTER) { - *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src) - } else if have != nil && have.Sym == missing.Sym && have.Nointerface() { - *why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym) - } else if have != nil && have.Sym == missing.Sym { - *why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+ - "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) - } else if ptr != 0 { - *why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym) - } else if have != nil { - *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+ - "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) - } else { - *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym) - } + return OCONVIFACE, "" + } + + var why string + if isptrto(src, TINTER) { + why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src) + } else if have != nil && have.Sym == missing.Sym && have.Nointerface() { + why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym) + } else if have != nil && have.Sym == missing.Sym { + why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+ + "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) + } else if ptr != 0 { + why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym) + } else if have != nil { + why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+ + "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) + } else { + why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym) } - return OXXX + return OXXX, why } if isptrto(dst, TINTER) { - if why != nil { - *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst) - } - return OXXX + why := fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst) + return OXXX, why } if src.IsInterface() && dst.Etype != TBLANK { var missing, have *types.Field var ptr int - if why != nil && implements(dst, src, &missing, &have, &ptr) { - *why = ": need type assertion" + var why string + if implements(dst, src, &missing, &have, &ptr) { + why = ": need type assertion" } - return OXXX + return OXXX, why } // 4. src is a bidirectional channel value, dst is a channel type, @@ -640,7 +635,7 @@ func assignop(src, dst *types.Type, why *string) Op { // either src or dst is not a named type. if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() { if types.Identical(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) { - return OCONVNOP + return OCONVNOP, "" } } @@ -653,7 +648,7 @@ func assignop(src, dst *types.Type, why *string) Op { TCHAN, TINTER, TSLICE: - return OCONVNOP + return OCONVNOP, "" } } @@ -661,26 +656,23 @@ func assignop(src, dst *types.Type, why *string) Op { // 7. Any typed value can be assigned to the blank identifier. if dst.Etype == TBLANK { - return OCONVNOP + return OCONVNOP, "" } - return OXXX + return OXXX, "" } // Can we convert a value of type src to a value of type dst? // If so, return op code to use in conversion (maybe OCONVNOP). -// If not, return OXXX. +// If not, return OXXX. In this case, the string return parameter may +// hold a reason why. In all other cases, it'll be the empty string. // srcConstant indicates whether the value of type src is a constant. -func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { - if why != nil { - *why = "" - } - +func convertop(srcConstant bool, src, dst *types.Type) (Op, string) { if src == dst { - return OCONVNOP + return OCONVNOP, "" } if src == nil || dst == nil { - return OXXX + return OXXX, "" } // Conversions from regular to go:notinheap are not allowed @@ -688,23 +680,19 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { // rules. // (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't. if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() { - if why != nil { - *why = fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem()) - } - return OXXX + why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem()) + return OXXX, why } // (b) Disallow string to []T where T is go:notinheap. if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Etype == types.Bytetype.Etype || dst.Elem().Etype == types.Runetype.Etype) { - if why != nil { - *why = fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem()) - } - return OXXX + why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem()) + return OXXX, why } // 1. src can be assigned to dst. - op := assignop(src, dst, why) + op, why := assignop(src, dst) if op != OXXX { - return op + return op, why } // The rules for interfaces are no different in conversions @@ -712,60 +700,57 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { // with the good message from assignop. // Otherwise clear the error. if src.IsInterface() || dst.IsInterface() { - return OXXX - } - if why != nil { - *why = "" + return OXXX, why } // 2. Ignoring struct tags, src and dst have identical underlying types. if types.IdenticalIgnoreTags(src.Orig, dst.Orig) { - return OCONVNOP + return OCONVNOP, "" } // 3. src and dst are unnamed pointer types and, ignoring struct tags, // their base types have identical underlying types. if src.IsPtr() && dst.IsPtr() && src.Sym == nil && dst.Sym == nil { if types.IdenticalIgnoreTags(src.Elem().Orig, dst.Elem().Orig) { - return OCONVNOP + return OCONVNOP, "" } } // 4. src and dst are both integer or floating point types. if (src.IsInteger() || src.IsFloat()) && (dst.IsInteger() || dst.IsFloat()) { if simtype[src.Etype] == simtype[dst.Etype] { - return OCONVNOP + return OCONVNOP, "" } - return OCONV + return OCONV, "" } // 5. src and dst are both complex types. if src.IsComplex() && dst.IsComplex() { if simtype[src.Etype] == simtype[dst.Etype] { - return OCONVNOP + return OCONVNOP, "" } - return OCONV + return OCONV, "" } // Special case for constant conversions: any numeric // conversion is potentially okay. We'll validate further // within evconst. See #38117. if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) { - return OCONV + return OCONV, "" } // 6. src is an integer or has type []byte or []rune // and dst is a string type. if src.IsInteger() && dst.IsString() { - return ORUNESTR + return ORUNESTR, "" } if src.IsSlice() && dst.IsString() { if src.Elem().Etype == types.Bytetype.Etype { - return OBYTES2STR + return OBYTES2STR, "" } if src.Elem().Etype == types.Runetype.Etype { - return ORUNES2STR + return ORUNES2STR, "" } } @@ -773,21 +758,21 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { // String to slice. if src.IsString() && dst.IsSlice() { if dst.Elem().Etype == types.Bytetype.Etype { - return OSTR2BYTES + return OSTR2BYTES, "" } if dst.Elem().Etype == types.Runetype.Etype { - return OSTR2RUNES + return OSTR2RUNES, "" } } // 8. src is a pointer or uintptr and dst is unsafe.Pointer. if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() { - return OCONVNOP + return OCONVNOP, "" } // 9. src is unsafe.Pointer and dst is a pointer or uintptr. if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) { - return OCONVNOP + return OCONVNOP, "" } // src is map and dst is a pointer to corresponding hmap. @@ -795,10 +780,10 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op { // go gc maps are implemented as a pointer to a hmap struct. if src.Etype == TMAP && dst.IsPtr() && src.MapType().Hmap == dst.Elem() { - return OCONVNOP + return OCONVNOP, "" } - return OXXX + return OXXX, "" } func assignconv(n *Node, t *types.Type, context string) *Node { @@ -839,8 +824,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node { return n } - var why string - op := assignop(n.Type, t, &why) + op, why := assignop(n.Type, t) if op == OXXX { yyerror("cannot use %L as type %v in %s%s", n, t, context(), why) op = OCONV diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go index bfbedb2aa5..8d9fbe300e 100644 --- a/src/cmd/compile/internal/gc/swt.go +++ b/src/cmd/compile/internal/gc/swt.go @@ -189,16 +189,19 @@ func typecheckExprSwitch(n *Node) { continue } - switch { - case nilonly != "" && !n1.isNil(): + if nilonly != "" && !n1.isNil() { yyerrorl(ncase.Pos, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left) - case t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type): + } else if t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type) { yyerrorl(ncase.Pos, "invalid case %L in switch (incomparable type)", n1) - case assignop(n1.Type, t, nil) == 0 && assignop(t, n1.Type, nil) == 0: - if n.Left != nil { - yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t) - } else { - yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type) + } else { + op1, _ := assignop(n1.Type, t) + op2, _ := assignop(t, n1.Type) + if op1 == OXXX && op2 == OXXX { + if n.Left != nil { + yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t) + } else { + yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type) + } } } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 75ce95832e..a4b462da1d 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -674,8 +674,8 @@ func typecheck1(n *Node, top int) (res *Node) { // The conversion allocates, so only do it if the concrete type is huge. converted := false if r.Type.Etype != TBLANK { - aop = assignop(l.Type, r.Type, nil) - if aop != 0 { + aop, _ = assignop(l.Type, r.Type) + if aop != OXXX { if r.Type.IsInterface() && !l.Type.IsInterface() && !IsComparable(l.Type) { yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type)) n.Type = nil @@ -696,8 +696,8 @@ func typecheck1(n *Node, top int) (res *Node) { } if !converted && l.Type.Etype != TBLANK { - aop = assignop(r.Type, l.Type, nil) - if aop != 0 { + aop, _ = assignop(r.Type, l.Type) + if aop != OXXX { if l.Type.IsInterface() && !r.Type.IsInterface() && !IsComparable(r.Type) { yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type)) n.Type = nil @@ -1691,7 +1691,7 @@ func typecheck1(n *Node, top int) (res *Node) { return n } var why string - n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why) + n.Op, why = convertop(n.Left.Op == OLITERAL, t, n.Type) if n.Op == OXXX { if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() { yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why) @@ -3267,9 +3267,7 @@ func typecheckas(n *Node) { } func checkassignto(src *types.Type, dst *Node) { - var why string - - if assignop(src, dst.Type, &why) == 0 { + if op, why := assignop(src, dst.Type); op == OXXX { yyerror("cannot assign %v to %L in multiple assignment%s", src, dst, why) return } -- cgit v1.3 From cced777026e1fc094ed21d99ae1efa4cf19146d2 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 21 Sep 2020 21:24:00 -0700 Subject: cmd/compile: set n.Name.Defn for inlined parameters Normally, when variables are declared and initialized using ":=", we set the variable's n.Name.Defn to point to the initialization assignment node (i.e., OAS or OAS2). Further, some frontend optimizations look for variables that are initialized but never reassigned. However, when inl.go inlines calls, it was declaring the inlined variables, and then separately assigning to them. This CL changes inl.go tweaks the AST to fit the combined declaration+initialization pattern. This isn't terribly useful by itself, but it allows further followup optimizations. Updates #41474. Change-Id: I62a9752c60414305679e0ed15a6563baa0224efa Reviewed-on: https://go-review.googlesource.com/c/go/+/256457 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Trust: Matthew Dempsky Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/gc/inl.go | 74 +++++++++++--------------- src/cmd/compile/internal/logopt/logopt_test.go | 2 +- 2 files changed, 33 insertions(+), 43 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index fa5b3ec698..5740864b12 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -831,16 +831,19 @@ func (v *reassignVisitor) visitList(l Nodes) *Node { return nil } -func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node { - if n := asNode(t.Nname); n != nil && !n.isBlank() { - inlvar := inlvars[n] - if inlvar == nil { - Fatalf("missing inlvar for %v\n", n) - } - return inlvar +func inlParam(t *types.Field, as *Node, inlvars map[*Node]*Node) *Node { + n := asNode(t.Nname) + if n == nil || n.isBlank() { + return nblank } - return typecheck(nblank, ctxExpr|ctxAssign) + inlvar := inlvars[n] + if inlvar == nil { + Fatalf("missing inlvar for %v", n) + } + as.Ninit.Append(nod(ODCL, inlvar, nil)) + inlvar.Name.Defn = as + return inlvar } var inlgen int @@ -970,14 +973,15 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { continue } if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap - continue - } - inlvars[ln] = typecheck(inlvar(ln), ctxExpr) - if ln.Class() == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class() == PPARAM { - ninit.Append(nod(ODCL, inlvars[ln], nil)) + // TODO(mdempsky): Remove once I'm confident + // this never actually happens. We currently + // perform inlining before escape analysis, so + // nothing should have moved to the heap yet. + Fatalf("impossible: %v", ln) } + inlf := typecheck(inlvar(ln), ctxExpr) + inlvars[ln] = inlf if genDwarfInline > 0 { - inlf := inlvars[ln] if ln.Class() == PPARAM { inlf.Name.SetInlFormal(true) } else { @@ -1019,56 +1023,42 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { // Assign arguments to the parameters' temp names. as := nod(OAS2, nil, nil) - as.Rlist.Set(n.List.Slice()) + as.SetColas(true) + if n.Op == OCALLMETH { + if n.Left.Left == nil { + Fatalf("method call without receiver: %+v", n) + } + as.Rlist.Append(n.Left.Left) + } + as.Rlist.Append(n.List.Slice()...) // For non-dotted calls to variadic functions, we assign the // variadic parameter's temp name separately. var vas *Node - if fn.IsMethod() { - rcv := fn.Type.Recv() - - if n.Left.Op == ODOTMETH { - // For x.M(...), assign x directly to the - // receiver parameter. - if n.Left.Left == nil { - Fatalf("method call without receiver: %+v", n) - } - ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left) - ras = typecheck(ras, ctxStmt) - ninit.Append(ras) - } else { - // For T.M(...), add the receiver parameter to - // as.List, so it's assigned by the normal - // arguments. - if as.Rlist.Len() == 0 { - Fatalf("non-method call to method without first arg: %+v", n) - } - as.List.Append(tinlvar(rcv, inlvars)) - } + if recv := fn.Type.Recv(); recv != nil { + as.List.Append(inlParam(recv, as, inlvars)) } - for _, param := range fn.Type.Params().Fields().Slice() { // For ordinary parameters or variadic parameters in // dotted calls, just add the variable to the // assignment list, and we're done. if !param.IsDDD() || n.IsDDD() { - as.List.Append(tinlvar(param, inlvars)) + as.List.Append(inlParam(param, as, inlvars)) continue } // Otherwise, we need to collect the remaining values // to pass as a slice. - numvals := n.List.Len() - x := as.List.Len() - for as.List.Len() < numvals { + for as.List.Len() < as.Rlist.Len() { as.List.Append(argvar(param.Type, as.List.Len())) } varargs := as.List.Slice()[x:] - vas = nod(OAS, tinlvar(param, inlvars), nil) + vas = nod(OAS, nil, nil) + vas.Left = inlParam(param, vas, inlvars) if len(varargs) == 0 { vas.Right = nodnil() vas.Right.Type = param.Type diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go index b57a07f12c..fb71e142e3 100644 --- a/src/cmd/compile/internal/logopt/logopt_test.go +++ b/src/cmd/compile/internal/logopt/logopt_test.go @@ -213,7 +213,7 @@ func s15a8(x *[15]int64) [15]int64 { want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r2 with derefs=0",`+ `"relatedInformation":[`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+ - `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y = \u003cN\u003e (assign-pair)"},`+ + `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~r1 = y:"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y.b (dot of pointer)"},`+ -- cgit v1.3 From c0417df15664a84c3cc6de8292f78debce111def Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 22 Sep 2020 02:12:03 -0700 Subject: cmd/compile: improve escape analysis of known calls Escape analysis is currently very naive about identifying calls to known functions: it only recognizes direct calls to a declared function, or direct calls to a closure. This CL adds a new "staticValue" helper function that can trace back through local variables that were initialized and never reassigned based on a similar optimization already used by inlining. (And to be used by inlining in a followup CL.) Updates #41474. Change-Id: I8204fd3b1e150ab77a27f583985cf099a8572b2e Reviewed-on: https://go-review.googlesource.com/c/go/+/256458 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Trust: Matthew Dempsky Reviewed-by: Cuong Manh Le Reviewed-by: David Chase --- src/cmd/compile/internal/gc/escape.go | 9 ++++--- src/cmd/compile/internal/gc/inl.go | 49 +++++++++++++++++++++++++++++++++++ test/escape_closure.go | 17 ++++++++++-- 3 files changed, 69 insertions(+), 6 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index 79df584ab1..93965d4fac 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -771,10 +771,11 @@ func (e *Escape) call(ks []EscHole, call, where *Node) { var fn *Node switch call.Op { case OCALLFUNC: - if call.Left.Op == ONAME && call.Left.Class() == PFUNC { - fn = call.Left - } else if call.Left.Op == OCLOSURE { - fn = call.Left.Func.Closure.Func.Nname + switch v := staticValue(call.Left); { + case v.Op == ONAME && v.Class() == PFUNC: + fn = v + case v.Op == OCLOSURE: + fn = v.Func.Closure.Func.Nname } case OCALLMETH: fn = asNode(call.Left.Type.FuncType().Nname) diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 5740864b12..cac51685df 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -751,6 +751,55 @@ func inlinableClosure(n *Node) *Node { return f } +func staticValue(n *Node) *Node { + for { + n1 := staticValue1(n) + if n1 == nil { + return n + } + n = n1 + } +} + +// staticValue1 implements a simple SSA-like optimization. +func staticValue1(n *Node) *Node { + if n.Op != ONAME || n.Class() != PAUTO || n.Name.Addrtaken() { + return nil + } + + defn := n.Name.Defn + if defn == nil { + return nil + } + + var rhs *Node +FindRHS: + switch defn.Op { + case OAS: + rhs = defn.Right + case OAS2: + for i, lhs := range defn.List.Slice() { + if lhs == n { + rhs = defn.Rlist.Index(i) + break FindRHS + } + } + Fatalf("%v missing from LHS of %v", n, defn) + default: + return nil + } + if rhs == nil { + Fatalf("RHS is nil: %v", defn) + } + + unsafe, _ := reassigned(n) + if unsafe { + return nil + } + + return rhs +} + // reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean // indicating whether the name has any assignments other than its declaration. // The second return value is the first such assignment encountered in the walk, if any. It is mostly diff --git a/test/escape_closure.go b/test/escape_closure.go index 3b14027fa4..9152319fe0 100644 --- a/test/escape_closure.go +++ b/test/escape_closure.go @@ -50,7 +50,7 @@ func ClosureCallArgs4() { } func ClosureCallArgs5() { - x := 0 // ERROR "moved to heap: x" + x := 0 // ERROR "moved to heap: x" // TODO(mdempsky): We get "leaking param: p" here because the new escape analysis pass // can tell that p flows directly to sink, but it's a little weird. Re-evaluate. sink = func(p *int) *int { // ERROR "leaking param: p" "func literal does not escape" @@ -132,7 +132,7 @@ func ClosureCallArgs14() { } func ClosureCallArgs15() { - x := 0 // ERROR "moved to heap: x" + x := 0 // ERROR "moved to heap: x" p := &x sink = func(p **int) *int { // ERROR "leaking param content: p" "func literal does not escape" return *p @@ -164,3 +164,16 @@ func ClosureLeak2a(a ...string) string { // ERROR "leaking param content: a" func ClosureLeak2b(f func() string) string { // ERROR "f does not escape" return f() } + +func ClosureIndirect() { + f := func(p *int) {} // ERROR "p does not escape" "func literal does not escape" + f(new(int)) // ERROR "new\(int\) does not escape" + + g := f + g(new(int)) // ERROR "new\(int\) does not escape" + + h := nopFunc + h(new(int)) // ERROR "new\(int\) does not escape" +} + +func nopFunc(p *int) {} // ERROR "p does not escape" -- cgit v1.3 From 497ea0610ea3757c6171cae3a85627459b572e5d Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 21 Sep 2020 20:20:00 -0700 Subject: cmd/compile: allow inlining of "for" loops We already allow inlining "if" and "goto" statements, so we might as well allow "for" loops too. The majority of frontend support is already there too. The critical missing feature at the moment is that inlining doesn't properly reassociate OLABEL nodes with their control statement (e.g., OFOR) after inlining. This eventually causes SSA construction to fail. As a workaround, this CL only enables inlining for unlabeled "for" loops. It's left to a (yet unplanned) future CL to add support for labeled "for" loops. The increased opportunity for inlining leads to a small growth in binary size. For example: $ size go.old go.new text data bss dec hex filename 9740163 320064 230656 10290883 9d06c3 go.old 9793399 320064 230656 10344119 9dd6b7 go.new Updates #14768. Fixes #41474. Change-Id: I827db0b2b9d9fa2934db05caf6baa463f0cd032a Reviewed-on: https://go-review.googlesource.com/c/go/+/256459 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Trust: Matthew Dempsky Reviewed-by: David Chase Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/gc/inl.go | 18 ++++++++++++++---- test/closure3.dir/main.go | 3 +-- test/inline.go | 23 +++++++++++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index cac51685df..8630560a9a 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -385,14 +385,11 @@ func (v *hairyVisitor) visit(n *Node) bool { case OCLOSURE, OCALLPART, ORANGE, - OFOR, - OFORUNTIL, OSELECT, OTYPESW, OGO, ODEFER, ODCLTYPE, // can't print yet - OBREAK, ORETJMP: v.reason = "unhandled op " + n.Op.String() return true @@ -400,10 +397,23 @@ func (v *hairyVisitor) visit(n *Node) bool { case OAPPEND: v.budget -= inlineExtraAppendCost - case ODCLCONST, OEMPTY, OFALL, OLABEL: + case ODCLCONST, OEMPTY, OFALL: // These nodes don't produce code; omit from inlining budget. return false + case OLABEL: + // TODO(mdempsky): Add support for inlining labeled control statements. + if n.labeledControl() != nil { + v.reason = "labeled control" + return true + } + + case OBREAK, OCONTINUE: + if n.Sym != nil { + // Should have short-circuited due to labeledControl above. + Fatalf("unexpected labeled break/continue: %v", n) + } + case OIF: if Isconst(n.Left, CTBOOL) { // This if and the condition cost nothing. diff --git a/test/closure3.dir/main.go b/test/closure3.dir/main.go index 3ec90139a3..5694673f1e 100644 --- a/test/closure3.dir/main.go +++ b/test/closure3.dir/main.go @@ -238,8 +238,7 @@ func main() { if c != 4 { ppanic("c != 4") } - for i := 0; i < 10; i++ { // prevent inlining - } + recover() // prevent inlining }() }() if c != 4 { diff --git a/test/inline.go b/test/inline.go index 3edcf2edfd..2f6fc0fe88 100644 --- a/test/inline.go +++ b/test/inline.go @@ -197,3 +197,26 @@ func gg(x int) { // ERROR "can inline gg" func hh(x int) { // ERROR "can inline hh" ff(x - 1) // ERROR "inlining call to ff" // ERROR "inlining call to gg" } + +// Issue #14768 - make sure we can inline for loops. +func for1(fn func() bool) { // ERROR "can inline for1" "fn does not escape" + for { + if fn() { + break + } else { + continue + } + } +} + +// BAD: for2 should be inlineable too. +func for2(fn func() bool) { // ERROR "fn does not escape" +Loop: + for { + if fn() { + break Loop + } else { + continue Loop + } + } +} -- cgit v1.3 From 1bcf6beec53ae811490fcd0ac29328b12b53702c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 22 Sep 2020 02:19:14 -0700 Subject: cmd/compile: use staticValue for inlining logic This CL replaces the ad hoc and duplicated logic for detecting inlinable calls with a single "inlCallee" function, which uses the "staticValue" helper function introduced in an earlier commit. Updates #41474. Change-Id: I103d4091b10366fce1344ef2501222b7df68f21d Reviewed-on: https://go-review.googlesource.com/c/go/+/256460 Reviewed-by: David Chase Reviewed-by: Cuong Manh Le Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/inl.go | 92 +++++++------------------- src/cmd/compile/internal/logopt/logopt_test.go | 1 - test/inline.go | 9 +++ 3 files changed, 34 insertions(+), 68 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 8630560a9a..ba12cf40b5 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -325,18 +325,10 @@ func (v *hairyVisitor) visit(n *Node) bool { break } - if fn := n.Left.Func; fn != nil && fn.Inl != nil { - v.budget -= fn.Inl.Cost + if fn := inlCallee(n.Left); fn != nil && fn.Func.Inl != nil { + v.budget -= fn.Func.Inl.Cost break } - if n.Left.isMethodExpression() { - if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl != nil { - v.budget -= d.Func.Inl.Cost - break - } - } - // TODO(mdempsky): Budget for OCLOSURE calls if we - // ever allow that. See #15561 and #23093. // Call cost for non-leaf inlining. v.budget -= v.extraCallCost @@ -679,53 +671,11 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node { if Debug['m'] > 3 { fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left) } - if n.Left.Func != nil && n.Left.Func.Inl != nil && !isIntrinsicCall(n) { // normal case - n = mkinlcall(n, n.Left, maxCost, inlMap) - } else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil { - n = mkinlcall(n, asNode(n.Left.Sym.Def), maxCost, inlMap) - } else if n.Left.Op == OCLOSURE { - if f := inlinableClosure(n.Left); f != nil { - n = mkinlcall(n, f, maxCost, inlMap) - } - } else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil { - if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE { - if f := inlinableClosure(d.Right); f != nil { - // NB: this check is necessary to prevent indirect re-assignment of the variable - // having the address taken after the invocation or only used for reads is actually fine - // but we have no easy way to distinguish the safe cases - if d.Left.Name.Addrtaken() { - if Debug['m'] > 1 { - fmt.Printf("%v: cannot inline escaping closure variable %v\n", n.Line(), n.Left) - } - if logopt.Enabled() { - logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(), - fmt.Sprintf("%v cannot be inlined (escaping closure variable)", n.Left)) - } - break - } - - // ensure the variable is never re-assigned - if unsafe, a := reassigned(n.Left); unsafe { - if Debug['m'] > 1 { - if a != nil { - fmt.Printf("%v: cannot inline re-assigned closure variable at %v: %v\n", n.Line(), a.Line(), a) - if logopt.Enabled() { - logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(), - fmt.Sprintf("%v cannot be inlined (re-assigned closure variable)", a)) - } - } else { - fmt.Printf("%v: cannot inline global closure variable %v\n", n.Line(), n.Left) - if logopt.Enabled() { - logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(), - fmt.Sprintf("%v cannot be inlined (global closure variable)", n.Left)) - } - } - } - break - } - n = mkinlcall(n, f, maxCost, inlMap) - } - } + if isIntrinsicCall(n) { + break + } + if fn := inlCallee(n.Left); fn != nil && fn.Func.Inl != nil { + n = mkinlcall(n, fn, maxCost, inlMap) } case OCALLMETH: @@ -749,16 +699,22 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node { return n } -// inlinableClosure takes an OCLOSURE node and follows linkage to the matching ONAME with -// the inlinable body. Returns nil if the function is not inlinable. -func inlinableClosure(n *Node) *Node { - c := n.Func.Closure - caninl(c) - f := c.Func.Nname - if f == nil || f.Func.Inl == nil { - return nil +// inlCallee takes a function-typed expression and returns the underlying function ONAME +// that it refers to if statically known. Otherwise, it returns nil. +func inlCallee(fn *Node) *Node { + fn = staticValue(fn) + switch { + case fn.Op == ONAME && fn.Class() == PFUNC: + if fn.isMethodExpression() { + return asNode(fn.Sym.Def) + } + return fn + case fn.Op == OCLOSURE: + c := fn.Func.Closure + caninl(c) + return c.Func.Nname } - return f + return nil } func staticValue(n *Node) *Node { @@ -771,7 +727,9 @@ func staticValue(n *Node) *Node { } } -// staticValue1 implements a simple SSA-like optimization. +// staticValue1 implements a simple SSA-like optimization. If n is a local variable +// that is initialized and never reassigned, staticValue1 returns the initializer +// expression. Otherwise, it returns nil. func staticValue1(n *Node) *Node { if n.Op != ONAME || n.Class() != PAUTO || n.Name.Addrtaken() { return nil diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go index fb71e142e3..fca85c10fb 100644 --- a/src/cmd/compile/internal/logopt/logopt_test.go +++ b/src/cmd/compile/internal/logopt/logopt_test.go @@ -208,7 +208,6 @@ func s15a8(x *[15]int64) [15]int64 { `"relatedInformation":[{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"}]}`) want(t, slogged, `{"range":{"start":{"line":11,"character":6},"end":{"line":11,"character":6}},"severity":3,"code":"isInBounds","source":"go compiler","message":""}`) want(t, slogged, `{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":6}},"severity":3,"code":"canInlineFunction","source":"go compiler","message":"cost: 35"}`) - want(t, slogged, `{"range":{"start":{"line":21,"character":21},"end":{"line":21,"character":21}},"severity":3,"code":"cannotInlineCall","source":"go compiler","message":"foo cannot be inlined (escaping closure variable)"}`) // escape analysis explanation want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r2 with derefs=0",`+ `"relatedInformation":[`+ diff --git a/test/inline.go b/test/inline.go index 2f6fc0fe88..0e41873de4 100644 --- a/test/inline.go +++ b/test/inline.go @@ -49,6 +49,12 @@ func j(x int) int { // ERROR "can inline j" } } +func _() int { // ERROR "can inline _" + tmp1 := h + tmp2 := tmp1 + return tmp2(0) // ERROR "inlining call to h" +} + var somethingWrong error // local closures can be inlined @@ -58,6 +64,9 @@ func l(x, y int) (int, int, error) { } if x == y { e(somethingWrong) // ERROR "inlining call to l.func1" + } else { + f := e + f(nil) // ERROR "inlining call to l.func1" } return y, x, nil } -- cgit v1.3 From 912262b806a432a29302e0cee45e4f42ef7038a2 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sun, 19 Jul 2020 00:30:12 -0400 Subject: cmd/internal/obj: move LSym.Func into LSym.Extra This creates space for a different kind of extension field in LSym without making the struct any larger. (There are many LSym, so we care about keeping the struct small.) Change-Id: Ib16edb9e15f54c2a7351c8b875e19684058711e5 Reviewed-on: https://go-review.googlesource.com/c/go/+/243943 Trust: Russ Cox Run-TryBot: Russ Cox TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/asm/internal/asm/asm.go | 2 +- src/cmd/compile/internal/gc/dwinl.go | 4 +- src/cmd/compile/internal/gc/gsubr.go | 2 +- src/cmd/compile/internal/gc/obj.go | 9 ++-- src/cmd/compile/internal/gc/pgen.go | 18 ++++---- src/cmd/compile/internal/gc/plive.go | 9 ++-- src/cmd/compile/internal/gc/scope.go | 4 +- src/cmd/compile/internal/gc/ssa.go | 10 ++-- src/cmd/internal/obj/arm/asm5.go | 8 ++-- src/cmd/internal/obj/arm/obj5.go | 34 +++++++------- src/cmd/internal/obj/arm64/asm7.go | 12 ++--- src/cmd/internal/obj/arm64/obj7.go | 36 +++++++-------- src/cmd/internal/obj/dwarf.go | 31 +++++++------ src/cmd/internal/obj/ld.go | 2 +- src/cmd/internal/obj/link.go | 23 ++++++++- src/cmd/internal/obj/mips/asm0.go | 10 ++-- src/cmd/internal/obj/mips/obj0.go | 42 ++++++++--------- src/cmd/internal/obj/objfile.go | 90 ++++++++++++++++++------------------ src/cmd/internal/obj/pass.go | 6 +-- src/cmd/internal/obj/pcln.go | 28 +++++------ src/cmd/internal/obj/plist.go | 14 +++--- src/cmd/internal/obj/ppc64/asm9.go | 10 ++-- src/cmd/internal/obj/ppc64/obj9.go | 28 +++++------ src/cmd/internal/obj/riscv/obj.go | 38 +++++++-------- src/cmd/internal/obj/s390x/asmz.go | 6 +-- src/cmd/internal/obj/s390x/objz.go | 28 +++++------ src/cmd/internal/obj/sym.go | 6 ++- src/cmd/internal/obj/wasm/wasmobj.go | 34 +++++++------- src/cmd/internal/obj/x86/asm6.go | 8 ++-- src/cmd/internal/obj/x86/obj6.go | 26 +++++------ 30 files changed, 304 insertions(+), 274 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go index 7878d74549..b9efa454ed 100644 --- a/src/cmd/asm/internal/asm/asm.go +++ b/src/cmd/asm/internal/asm/asm.go @@ -181,7 +181,7 @@ func (p *Parser) asmText(operands [][]lex.Token) { // Argsize set below. }, } - nameAddr.Sym.Func.Text = prog + nameAddr.Sym.Func().Text = prog prog.To.Val = int32(argSize) p.append(prog, "", true) } diff --git a/src/cmd/compile/internal/gc/dwinl.go b/src/cmd/compile/internal/gc/dwinl.go index 27e2cbcd98..5120fa1166 100644 --- a/src/cmd/compile/internal/gc/dwinl.go +++ b/src/cmd/compile/internal/gc/dwinl.go @@ -34,7 +34,7 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls { // Walk progs to build up the InlCalls data structure var prevpos src.XPos - for p := fnsym.Func.Text; p != nil; p = p.Link { + for p := fnsym.Func().Text; p != nil; p = p.Link { if p.Pos == prevpos { continue } @@ -150,7 +150,7 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls { start := int64(-1) curii := -1 var prevp *obj.Prog - for p := fnsym.Func.Text; p != nil; prevp, p = p, p.Link { + for p := fnsym.Func().Text; p != nil; prevp, p = p, p.Link { if prevp != nil && p.Pos == prevp.Pos { continue } diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index 480d411f49..14c217ff3b 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -199,7 +199,7 @@ func (pp *Progs) settext(fn *Node) { ptxt := pp.Prog(obj.ATEXT) pp.Text = ptxt - fn.Func.lsym.Func.Text = ptxt + fn.Func.lsym.Func().Text = ptxt ptxt.From.Type = obj.TYPE_MEM ptxt.From.Name = obj.NAME_EXTERN ptxt.From.Sym = fn.Func.lsym diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index d7f4a94041..f6557e2d15 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -305,20 +305,21 @@ func dumpglobls() { // global symbols can't be declared during parallel compilation. func addGCLocals() { for _, s := range Ctxt.Text { - if s.Func == nil { + fn := s.Func() + if fn == nil { continue } - for _, gcsym := range []*obj.LSym{s.Func.GCArgs, s.Func.GCLocals, s.Func.GCRegs} { + for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals, fn.GCRegs} { if gcsym != nil && !gcsym.OnList() { ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK) } } - if x := s.Func.StackObjects; x != nil { + if x := fn.StackObjects; x != nil { attr := int16(obj.RODATA) ggloblsym(x, int32(len(x.P)), attr) x.Set(obj.AttrStatic, true) } - if x := s.Func.OpenCodedDeferInfo; x != nil { + if x := fn.OpenCodedDeferInfo; x != nil { ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) } } diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 52b1ed351d..353f4b08c9 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -266,8 +266,8 @@ func compile(fn *Node) { dtypesym(n.Type) // Also make sure we allocate a linker symbol // for the stack object data, for the same reason. - if fn.Func.lsym.Func.StackObjects == nil { - fn.Func.lsym.Func.StackObjects = Ctxt.Lookup(fn.Func.lsym.Name + ".stkobj") + if fn.Func.lsym.Func().StackObjects == nil { + fn.Func.lsym.Func().StackObjects = Ctxt.Lookup(fn.Func.lsym.Name + ".stkobj") } } } @@ -415,7 +415,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S case PAUTO: if !n.Name.Used() { // Text == nil -> generating abstract function - if fnsym.Func.Text != nil { + if fnsym.Func().Text != nil { Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)") } continue @@ -425,7 +425,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S continue } apdecls = append(apdecls, n) - fnsym.Func.RecordAutoType(ngotype(n).Linksym()) + fnsym.Func().RecordAutoType(ngotype(n).Linksym()) } decls, dwarfVars := createDwarfVars(fnsym, fn.Func, apdecls) @@ -435,7 +435,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S // the function symbol to insure that the type included in DWARF // processing during linking. typesyms := []*obj.LSym{} - for t, _ := range fnsym.Func.Autot { + for t, _ := range fnsym.Func().Autot { typesyms = append(typesyms, t) } sort.Sort(obj.BySymName(typesyms)) @@ -444,7 +444,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S r.Sym = sym r.Type = objabi.R_USETYPE } - fnsym.Func.Autot = nil + fnsym.Func().Autot = nil var varScopes []ScopeID for _, decl := range decls { @@ -522,7 +522,7 @@ func createSimpleVar(fnsym *obj.LSym, n *Node) *dwarf.Var { } typename := dwarf.InfoPrefix + typesymname(n.Type) - delete(fnsym.Func.Autot, ngotype(n).Linksym()) + delete(fnsym.Func().Autot, ngotype(n).Linksym()) inlIndex := 0 if genDwarfInline > 1 { if n.Name.InlFormal() || n.Name.InlLocal() { @@ -667,7 +667,7 @@ func createDwarfVars(fnsym *obj.LSym, fn *Func, apDecls []*Node) ([]*Node, []*dw ChildIndex: -1, }) // Record go type of to insure that it gets emitted by the linker. - fnsym.Func.RecordAutoType(ngotype(n).Linksym()) + fnsym.Func().RecordAutoType(ngotype(n).Linksym()) } return decls, vars @@ -731,7 +731,7 @@ func createComplexVar(fnsym *obj.LSym, fn *Func, varID ssa.VarID) *dwarf.Var { } gotype := ngotype(n).Linksym() - delete(fnsym.Func.Autot, gotype) + delete(fnsym.Func().Autot, gotype) typename := dwarf.InfoPrefix + gotype.Name[len("type."):] inlIndex := 0 if genDwarfInline > 1 { diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go index a9ea37701e..b471accb65 100644 --- a/src/cmd/compile/internal/gc/plive.go +++ b/src/cmd/compile/internal/gc/plive.go @@ -1552,26 +1552,27 @@ func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap { // Emit the live pointer map data structures ls := e.curfn.Func.lsym - ls.Func.GCArgs, ls.Func.GCLocals, ls.Func.GCRegs = lv.emit() + fninfo := ls.Func() + fninfo.GCArgs, fninfo.GCLocals, fninfo.GCRegs = lv.emit() p := pp.Prog(obj.AFUNCDATA) Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN - p.To.Sym = ls.Func.GCArgs + p.To.Sym = fninfo.GCArgs p = pp.Prog(obj.AFUNCDATA) Addrconst(&p.From, objabi.FUNCDATA_LocalsPointerMaps) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN - p.To.Sym = ls.Func.GCLocals + p.To.Sym = fninfo.GCLocals if !go115ReduceLiveness { p = pp.Prog(obj.AFUNCDATA) Addrconst(&p.From, objabi.FUNCDATA_RegPointerMaps) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN - p.To.Sym = ls.Func.GCRegs + p.To.Sym = fninfo.GCRegs } return lv.livenessMap diff --git a/src/cmd/compile/internal/gc/scope.go b/src/cmd/compile/internal/gc/scope.go index d7239d5693..e66b859e10 100644 --- a/src/cmd/compile/internal/gc/scope.go +++ b/src/cmd/compile/internal/gc/scope.go @@ -62,9 +62,9 @@ func scopePCs(fnsym *obj.LSym, marks []Mark, dwarfScopes []dwarf.Scope) { if len(marks) == 0 { return } - p0 := fnsym.Func.Text + p0 := fnsym.Func().Text scope := findScope(marks, p0.Pos) - for p := fnsym.Func.Text; p != nil; p = p.Link { + for p := p0; p != nil; p = p.Link { if p.Pos == p0.Pos { continue } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 3d5fa4cd0a..d8f627c213 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -240,7 +240,7 @@ func dvarint(x *obj.LSym, off int, v int64) int { // - Offset of where argument should be placed in the args frame when making call func (s *state) emitOpenDeferInfo() { x := Ctxt.Lookup(s.curfn.Func.lsym.Name + ".opendefer") - s.curfn.Func.lsym.Func.OpenCodedDeferInfo = x + s.curfn.Func.lsym.Func().OpenCodedDeferInfo = x off := 0 // Compute maxargsize (max size of arguments for all defers) @@ -6108,7 +6108,7 @@ func emitStackObjects(e *ssafn, pp *Progs) { // Populate the stack object data. // Format must match runtime/stack.go:stackObjectRecord. - x := e.curfn.Func.lsym.Func.StackObjects + x := e.curfn.Func.lsym.Func().StackObjects off := 0 off = duintptr(x, off, uint64(len(vars))) for _, v := range vars { @@ -6145,7 +6145,7 @@ func genssa(f *ssa.Func, pp *Progs) { s.livenessMap = liveness(e, f, pp) emitStackObjects(e, pp) - openDeferInfo := e.curfn.Func.lsym.Func.OpenCodedDeferInfo + openDeferInfo := e.curfn.Func.lsym.Func().OpenCodedDeferInfo if openDeferInfo != nil { // This function uses open-coded defers -- write out the funcdata // info that we computed at the end of genssa. @@ -6350,7 +6350,7 @@ func genssa(f *ssa.Func, pp *Progs) { // some of the inline marks. // Use this instruction instead. p.Pos = p.Pos.WithIsStmt() // promote position to a statement - pp.curfn.Func.lsym.Func.AddInlMark(p, inlMarks[m]) + pp.curfn.Func.lsym.Func().AddInlMark(p, inlMarks[m]) // Make the inline mark a real nop, so it doesn't generate any code. m.As = obj.ANOP m.Pos = src.NoXPos @@ -6362,7 +6362,7 @@ func genssa(f *ssa.Func, pp *Progs) { // Any unmatched inline marks now need to be added to the inlining tree (and will generate a nop instruction). for _, p := range inlMarkList { if p.As != obj.ANOP { - pp.curfn.Func.lsym.Func.AddInlMark(p, inlMarks[p]) + pp.curfn.Func.lsym.Func().AddInlMark(p, inlMarks[p]) } } } diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go index 269a4223d5..ebb98b4859 100644 --- a/src/cmd/internal/obj/arm/asm5.go +++ b/src/cmd/internal/obj/arm/asm5.go @@ -390,7 +390,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { var p *obj.Prog var op *obj.Prog - p = cursym.Func.Text + p = cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -482,8 +482,8 @@ func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { bflag = 0 pc = 0 times++ - c.cursym.Func.Text.Pc = 0 // force re-layout the code. - for p = c.cursym.Func.Text; p != nil; p = p.Link { + c.cursym.Func().Text.Pc = 0 // force re-layout the code. + for p = c.cursym.Func().Text; p != nil; p = p.Link { o = c.oplook(p) if int64(pc) > p.Pc { p.Pc = int64(pc) @@ -558,7 +558,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { * perhaps we'd be able to parallelize the span loop above. */ - p = c.cursym.Func.Text + p = c.cursym.Func().Text c.autosize = p.To.Offset + 4 c.cursym.Grow(c.cursym.Size) diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go index 4d9187b530..f2bfb9679f 100644 --- a/src/cmd/internal/obj/arm/obj5.go +++ b/src/cmd/internal/obj/arm/obj5.go @@ -249,13 +249,13 @@ const ( func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize := int32(0) - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog} - p := c.cursym.Func.Text + p := c.cursym.Func().Text autoffset := int32(p.To.Offset) if autoffset == -4 { // Historical way to mark NOFRAME. @@ -271,30 +271,30 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - cursym.Func.Locals = autoffset - cursym.Func.Args = p.To.Val.(int32) + cursym.Func().Locals = autoffset + cursym.Func().Args = p.To.Val.(int32) /* * find leaf subroutines */ - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.ATEXT: p.Mark |= LEAF case ADIV, ADIVU, AMOD, AMODU: - cursym.Func.Text.Mark &^= LEAF + cursym.Func().Text.Mark &^= LEAF case ABL, ABX, obj.ADUFFZERO, obj.ADUFFCOPY: - cursym.Func.Text.Mark &^= LEAF + cursym.Func().Text.Mark &^= LEAF } } var q2 *obj.Prog - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: @@ -311,20 +311,20 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize += 4 } - if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 { + if autosize == 0 && cursym.Func().Text.Mark&LEAF == 0 { // A very few functions that do not return to their caller // are not identified as leaves but still have no frame. if ctxt.Debugvlog { ctxt.Logf("save suppressed in: %s\n", cursym.Name) } - cursym.Func.Text.Mark |= LEAF + cursym.Func().Text.Mark |= LEAF } // FP offsets need an updated p.To.Offset. p.To.Offset = int64(autosize) - 4 - if cursym.Func.Text.Mark&LEAF != 0 { + if cursym.Func().Text.Mark&LEAF != 0 { cursym.Set(obj.AttrLeaf, true) if p.From.Sym.NoFrame() { break @@ -347,7 +347,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Reg = REGSP p.Spadj = autosize - if cursym.Func.Text.From.Sym.Wrapper() { + if cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVW g_panic(g), R1 @@ -460,7 +460,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case obj.ARET: nocache(p) - if cursym.Func.Text.Mark&LEAF != 0 { + if cursym.Func().Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AB p.From = obj.Addr{} @@ -508,7 +508,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } case ADIV, ADIVU, AMOD, AMODU: - if cursym.Func.Text.From.Sym.NoSplit() { + if cursym.Func().Text.From.Sym.NoSplit() { ctxt.Diag("cannot divide in NOSPLIT function") } const debugdivmod = false @@ -720,7 +720,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1) var last *obj.Prog - for last = c.cursym.Func.Text; last.Link != nil; last = last.Link { + for last = c.cursym.Func().Text; last.Link != nil; last = last.Link { } // Now we are at the end of the function, but logically @@ -751,7 +751,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { switch { case c.cursym.CFunc(): morestack = "runtime.morestackc" - case !c.cursym.Func.Text.From.Sym.NeedCtxt(): + case !c.cursym.Func().Text.From.Sym.NeedCtxt(): morestack = "runtime.morestack_noctxt" } call.To.Sym = c.ctxt.Lookup(morestack) @@ -762,7 +762,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { b := obj.Appendp(pcdata, c.newprog) b.As = obj.AJMP b.To.Type = obj.TYPE_BRANCH - b.To.SetTarget(c.cursym.Func.Text.Link) + b.To.SetTarget(c.cursym.Func().Text.Link) b.Spadj = +framesize return end diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index c46066313e..6b9fe27c05 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -913,7 +913,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Retpoline = false // don't keep printing } - p := cursym.Func.Text + p := cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -943,8 +943,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { alignedValue := p.From.Offset m = pcAlignPadLength(pc, alignedValue, ctxt) // Update the current text symbol alignment value. - if int32(alignedValue) > cursym.Func.Align { - cursym.Func.Align = int32(alignedValue) + if int32(alignedValue) > cursym.Func().Align { + cursym.Func().Align = int32(alignedValue) } break case obj.ANOP, obj.AFUNCDATA, obj.APCDATA: @@ -983,7 +983,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { for bflag != 0 { bflag = 0 pc = 0 - for p = c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p = c.cursym.Func().Text.Link; p != nil; p = p.Link { if p.As == ADWORD && (pc&7) != 0 { pc += 4 } @@ -1047,7 +1047,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { psz := int32(0) var i int var out [6]uint32 - for p := c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p := c.cursym.Func().Text.Link; p != nil; p = p.Link { c.pc = p.Pc o = c.oplook(p) @@ -1088,7 +1088,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // We use REGTMP as a scratch register during call injection, // so instruction sequences that use REGTMP are unsafe to // preempt asynchronously. - obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, c.isRestartable) + obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable) } // isUnsafePoint returns whether p is an unsafe point. diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go index f1bc2583cb..0baf51973a 100644 --- a/src/cmd/internal/obj/arm64/obj7.go +++ b/src/cmd/internal/obj/arm64/obj7.go @@ -166,7 +166,7 @@ func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1) var last *obj.Prog - for last = c.cursym.Func.Text; last.Link != nil; last = last.Link { + for last = c.cursym.Func().Text; last.Link != nil; last = last.Link { } // Now we are at the end of the function, but logically @@ -209,7 +209,7 @@ func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { switch { case c.cursym.CFunc(): morestack = "runtime.morestackc" - case !c.cursym.Func.Text.From.Sym.NeedCtxt(): + case !c.cursym.Func().Text.From.Sym.NeedCtxt(): morestack = "runtime.morestack_noctxt" } call.To.Sym = c.ctxt.Lookup(morestack) @@ -220,7 +220,7 @@ func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { jmp := obj.Appendp(pcdata, c.newprog) jmp.As = AB jmp.To.Type = obj.TYPE_BRANCH - jmp.To.SetTarget(c.cursym.Func.Text.Link) + jmp.To.SetTarget(c.cursym.Func().Text.Link) jmp.Spadj = +framesize return end @@ -441,13 +441,13 @@ func (c *ctxt7) rewriteToUseGot(p *obj.Prog) { } func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym} - p := c.cursym.Func.Text + p := c.cursym.Func().Text textstksiz := p.To.Offset if textstksiz == -8 { // Historical way to mark NOFRAME. @@ -463,13 +463,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - c.cursym.Func.Args = p.To.Val.(int32) - c.cursym.Func.Locals = int32(textstksiz) + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) /* * find leaf subroutines */ - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.ATEXT: p.Mark |= LEAF @@ -477,18 +477,18 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case ABL, obj.ADUFFZERO, obj.ADUFFCOPY: - c.cursym.Func.Text.Mark &^= LEAF + c.cursym.Func().Text.Mark &^= LEAF } } var q *obj.Prog var q1 *obj.Prog var retjmp *obj.LSym - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: - c.cursym.Func.Text = p + c.cursym.Func().Text = p c.autosize = int32(textstksiz) if p.Mark&LEAF != 0 && c.autosize == 0 { @@ -514,7 +514,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8) } c.autosize += extrasize - c.cursym.Func.Locals += extrasize + c.cursym.Func().Locals += extrasize // low 32 bits for autosize // high 32 bits for extrasize @@ -524,14 +524,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Offset = 0 } - if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 { + if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 { if c.ctxt.Debugvlog { - c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name) + c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name) } - c.cursym.Func.Text.Mark |= LEAF + c.cursym.Func().Text.Mark |= LEAF } - if cursym.Func.Text.Mark&LEAF != 0 { + if cursym.Func().Text.Mark&LEAF != 0 { cursym.Set(obj.AttrLeaf, true) if p.From.Sym.NoFrame() { break @@ -641,7 +641,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q1.To.Reg = REGFP } - if c.cursym.Func.Text.From.Sym.Wrapper() { + if c.cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOV g_panic(g), R1 @@ -755,7 +755,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { retjmp = p.To.Sym p.To = obj.Addr{} - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { if c.autosize != 0 { p.As = AADD p.From.Type = obj.TYPE_CONST diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go index 9abb31b558..328fb03b24 100644 --- a/src/cmd/internal/obj/dwarf.go +++ b/src/cmd/internal/obj/dwarf.go @@ -46,12 +46,12 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) { // we expect at the start of a new sequence. stmt := true line := int64(1) - pc := s.Func.Text.Pc + pc := s.Func().Text.Pc var lastpc int64 // last PC written to line table, not last PC in func name := "" prologue, wrotePrologue := false, false // Walk the progs, generating the DWARF table. - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { prologue = prologue || (p.Pos.Xlogue() == src.PosPrologueEnd) // If we're not at a real instruction, keep looping! if p.Pos.Line() == 0 || (p.Link != nil && p.Link.Pc == p.Pc) { @@ -103,7 +103,7 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) { // text address before the end sequence op. If this isn't done, // GDB will assign a line number of zero the last row in the line // table, which we don't want. - lastlen := uint64(s.Size - (lastpc - s.Func.Text.Pc)) + lastlen := uint64(s.Size - (lastpc - s.Func().Text.Pc)) putpclcdelta(ctxt, dctxt, lines, lastlen, 0) dctxt.AddUint8(lines, 0) // start extended opcode dwarf.Uleb128put(dctxt, lines, 1) @@ -301,26 +301,27 @@ func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, if s.Type != objabi.STEXT { ctxt.Diag("dwarfSym of non-TEXT %v", s) } - if s.Func.dwarfInfoSym == nil { - s.Func.dwarfInfoSym = &LSym{ + fn := s.Func() + if fn.dwarfInfoSym == nil { + fn.dwarfInfoSym = &LSym{ Type: objabi.SDWARFFCN, } if ctxt.Flag_locationlists { - s.Func.dwarfLocSym = &LSym{ + fn.dwarfLocSym = &LSym{ Type: objabi.SDWARFLOC, } } - s.Func.dwarfRangesSym = &LSym{ + fn.dwarfRangesSym = &LSym{ Type: objabi.SDWARFRANGE, } - s.Func.dwarfDebugLinesSym = &LSym{ + fn.dwarfDebugLinesSym = &LSym{ Type: objabi.SDWARFLINES, } if s.WasInlined() { - s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s) + fn.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s) } } - return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym + return fn.dwarfInfoSym, fn.dwarfLocSym, fn.dwarfRangesSym, fn.dwarfAbsFnSym, fn.dwarfDebugLinesSym } func (s *LSym) Length(dwarfContext interface{}) int64 { @@ -331,7 +332,7 @@ func (s *LSym) Length(dwarfContext interface{}) int64 { // first instruction (prog) of the specified function. This will // presumably be the file in which the function is defined. func (ctxt *Link) fileSymbol(fn *LSym) *LSym { - p := fn.Func.Text + p := fn.Func().Text if p != nil { f, _ := linkgetlineFromPos(ctxt, p.Pos) fsym := ctxt.Lookup(f) @@ -405,8 +406,8 @@ func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath str if absfn.Size != 0 { ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s) } - if s.Func == nil { - s.Func = new(FuncInfo) + if s.Func() == nil { + s.NewFuncInfo() } scopes, _ := ctxt.DebugInfo(s, absfn, curfn) dwctxt := dwCtxt{ctxt} @@ -527,8 +528,8 @@ func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) { // wrapper generation as opposed to the main inlining phase) it's // possible that we didn't cache the abstract function sym for the // text symbol -- do so now if needed. See issue 38068. - if s.Func != nil && s.Func.dwarfAbsFnSym == nil { - s.Func.dwarfAbsFnSym = absfn + if fn := s.Func(); fn != nil && fn.dwarfAbsFnSym == nil { + fn.dwarfAbsFnSym = absfn } ft.precursor[s] = fnState{precursor: fn, absfn: absfn} diff --git a/src/cmd/internal/obj/ld.go b/src/cmd/internal/obj/ld.go index 4ba52c7785..5d6c000dc6 100644 --- a/src/cmd/internal/obj/ld.go +++ b/src/cmd/internal/obj/ld.go @@ -59,7 +59,7 @@ func mkfwd(sym *LSym) { } i := 0 - for p := sym.Func.Text; p != nil && p.Link != nil; p = p.Link { + for p := sym.Func().Text; p != nil && p.Link != nil; p = p.Link { i-- if i < 0 { i = LOG - 1 diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index f14b691802..ae85dbbe4e 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -38,6 +38,7 @@ import ( "cmd/internal/src" "cmd/internal/sys" "fmt" + "log" "sync" ) @@ -400,7 +401,7 @@ type LSym struct { P []byte R []Reloc - Func *FuncInfo + Extra *interface{} // *FuncInfo if present Pkg string PkgIdx int32 @@ -433,6 +434,26 @@ type FuncInfo struct { FuncInfoSym *LSym } +// NewFuncInfo allocates and returns a FuncInfo for LSym. +func (s *LSym) NewFuncInfo() *FuncInfo { + if s.Extra != nil { + log.Fatalf("invalid use of LSym - NewFuncInfo with Extra of type %T", *s.Extra) + } + f := new(FuncInfo) + s.Extra = new(interface{}) + *s.Extra = f + return f +} + +// Func returns the *FuncInfo associated with s, or else nil. +func (s *LSym) Func() *FuncInfo { + if s.Extra == nil { + return nil + } + f, _ := (*s.Extra).(*FuncInfo) + return f +} + type InlMark struct { // When unwinding from an instruction in an inlined body, mark // where we should unwind to. diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go index 6107974745..fd29f9fa21 100644 --- a/src/cmd/internal/obj/mips/asm0.go +++ b/src/cmd/internal/obj/mips/asm0.go @@ -410,7 +410,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Retpoline = false // don't keep printing } - p := cursym.Func.Text + p := cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -455,7 +455,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { for bflag != 0 { bflag = 0 pc = 0 - for p = c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p = c.cursym.Func().Text.Link; p != nil; p = p.Link { p.Pc = pc o = c.oplook(p) @@ -512,7 +512,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { bp := c.cursym.P var i int32 var out [4]uint32 - for p := c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p := c.cursym.Func().Text.Link; p != nil; p = p.Link { c.pc = p.Pc o = c.oplook(p) if int(o.size) > 4*len(out) { @@ -529,7 +529,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // We use REGTMP as a scratch register during call injection, // so instruction sequences that use REGTMP are unsafe to // preempt asynchronously. - obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, c.isRestartable) + obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable) } // isUnsafePoint returns whether p is an unsafe point. @@ -1302,7 +1302,7 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { } o1 = OP_JMP(c.opirr(p.As), uint32(v)) if p.To.Sym == nil { - p.To.Sym = c.cursym.Func.Text.From.Sym + p.To.Sym = c.cursym.Func().Text.From.Sym p.To.Offset = p.To.Target().Pc } rel := obj.Addrel(c.cursym) diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go index f19facc00c..135a8df3aa 100644 --- a/src/cmd/internal/obj/mips/obj0.go +++ b/src/cmd/internal/obj/mips/obj0.go @@ -133,11 +133,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // a switch for enabling/disabling instruction scheduling nosched := true - if c.cursym.Func.Text == nil || c.cursym.Func.Text.Link == nil { + if c.cursym.Func().Text == nil || c.cursym.Func().Text.Link == nil { return } - p := c.cursym.Func.Text + p := c.cursym.Func().Text textstksiz := p.To.Offset if textstksiz == -ctxt.FixedFrameSize() { // Historical way to mark NOFRAME. @@ -153,8 +153,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - c.cursym.Func.Args = p.To.Val.(int32) - c.cursym.Func.Locals = int32(textstksiz) + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) /* * find leaf subroutines @@ -162,7 +162,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { * expand BECOME pseudo */ - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case obj.ATEXT: @@ -203,7 +203,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { AJAL, obj.ADUFFZERO, obj.ADUFFCOPY: - c.cursym.Func.Text.Mark &^= LEAF + c.cursym.Func().Text.Mark &^= LEAF fallthrough case AJMP, @@ -267,7 +267,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize := int32(0) var p1 *obj.Prog var p2 *obj.Prog - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: @@ -288,19 +288,19 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize += 4 } - if autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 { - if c.cursym.Func.Text.From.Sym.NoSplit() { + if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 { + if c.cursym.Func().Text.From.Sym.NoSplit() { if ctxt.Debugvlog { ctxt.Logf("save suppressed in: %s\n", c.cursym.Name) } - c.cursym.Func.Text.Mark |= LEAF + c.cursym.Func().Text.Mark |= LEAF } } p.To.Offset = int64(autosize) - ctxt.FixedFrameSize() - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { c.cursym.Set(obj.AttrLeaf, true) if p.From.Sym.NoFrame() { break @@ -344,7 +344,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) } - if c.cursym.Func.Text.From.Sym.Wrapper() && c.cursym.Func.Text.Mark&LEAF == 0 { + if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOV g_panic(g), R1 @@ -438,7 +438,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction p.To.Sym = nil - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AJMP p.From = obj.Addr{} @@ -540,7 +540,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { if c.ctxt.Arch.Family == sys.MIPS { // rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access - for p = c.cursym.Func.Text; p != nil; p = p1 { + for p = c.cursym.Func().Text; p != nil; p = p1 { p1 = p.Link if p.As != AMOVD { @@ -580,7 +580,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { if nosched { // if we don't do instruction scheduling, simply add // NOP after each branch instruction. - for p = c.cursym.Func.Text; p != nil; p = p.Link { + for p = c.cursym.Func().Text; p != nil; p = p.Link { if p.Mark&BRANCH != 0 { c.addnop(p) } @@ -589,10 +589,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } // instruction scheduling - q = nil // p - 1 - q1 = c.cursym.Func.Text // top of block - o := 0 // count of instructions - for p = c.cursym.Func.Text; p != nil; p = p1 { + q = nil // p - 1 + q1 = c.cursym.Func().Text // top of block + o := 0 // count of instructions + for p = c.cursym.Func().Text; p != nil; p = p1 { p1 = p.Link o++ if p.Mark&NOSCHED != 0 { @@ -791,7 +791,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.To.Type = obj.TYPE_BRANCH if c.cursym.CFunc() { p.To.Sym = c.ctxt.Lookup("runtime.morestackc") - } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() { + } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt") } else { p.To.Sym = c.ctxt.Lookup("runtime.morestack") @@ -805,7 +805,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.As = AJMP p.To.Type = obj.TYPE_BRANCH - p.To.SetTarget(c.cursym.Func.Text.Link) + p.To.SetTarget(c.cursym.Func().Text.Link) p.Mark |= BRANCH // placeholder for q1's jump target diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index fa60c9ad6d..a08de891d3 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -189,8 +189,8 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) { // object file, and the Pcln variables haven't been filled in. As such, we // need to check that Pcsp exists, and assume the other pcln variables exist // as well. Tests like test/fixedbugs/issue22200.go demonstrate this issue. - if s.Func != nil && s.Func.Pcln.Pcsp != nil { - pc := &s.Func.Pcln + if fn := s.Func(); fn != nil && fn.Pcln.Pcsp != nil { + pc := &fn.Pcln w.Bytes(pc.Pcsp.P) w.Bytes(pc.Pcfile.P) w.Bytes(pc.Pcline.P) @@ -303,8 +303,8 @@ func (w *writer) Sym(s *LSym) { name = filepath.ToSlash(name) } var align uint32 - if s.Func != nil { - align = uint32(s.Func.Align) + if fn := s.Func(); fn != nil { + align = uint32(fn.Align) } if s.ContentAddressable() { // We generally assume data symbols are natually aligned, @@ -470,38 +470,38 @@ func (w *writer) Aux(s *LSym) { if s.Gotype != nil { w.aux1(goobj.AuxGotype, s.Gotype) } - if s.Func != nil { - w.aux1(goobj.AuxFuncInfo, s.Func.FuncInfoSym) + if fn := s.Func(); fn != nil { + w.aux1(goobj.AuxFuncInfo, fn.FuncInfoSym) - for _, d := range s.Func.Pcln.Funcdata { + for _, d := range fn.Pcln.Funcdata { w.aux1(goobj.AuxFuncdata, d) } - if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { - w.aux1(goobj.AuxDwarfInfo, s.Func.dwarfInfoSym) + if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { + w.aux1(goobj.AuxDwarfInfo, fn.dwarfInfoSym) } - if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { - w.aux1(goobj.AuxDwarfLoc, s.Func.dwarfLocSym) + if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { + w.aux1(goobj.AuxDwarfLoc, fn.dwarfLocSym) } - if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { - w.aux1(goobj.AuxDwarfRanges, s.Func.dwarfRangesSym) + if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { + w.aux1(goobj.AuxDwarfRanges, fn.dwarfRangesSym) } - if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { - w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym) + if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { + w.aux1(goobj.AuxDwarfLines, fn.dwarfDebugLinesSym) } - if s.Func.Pcln.Pcsp != nil && s.Func.Pcln.Pcsp.Size != 0 { - w.aux1(goobj.AuxPcsp, s.Func.Pcln.Pcsp) + if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { + w.aux1(goobj.AuxPcsp, fn.Pcln.Pcsp) } - if s.Func.Pcln.Pcfile != nil && s.Func.Pcln.Pcfile.Size != 0 { - w.aux1(goobj.AuxPcfile, s.Func.Pcln.Pcfile) + if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { + w.aux1(goobj.AuxPcfile, fn.Pcln.Pcfile) } - if s.Func.Pcln.Pcline != nil && s.Func.Pcln.Pcline.Size != 0 { - w.aux1(goobj.AuxPcline, s.Func.Pcln.Pcline) + if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { + w.aux1(goobj.AuxPcline, fn.Pcln.Pcline) } - if s.Func.Pcln.Pcinline != nil && s.Func.Pcln.Pcinline.Size != 0 { - w.aux1(goobj.AuxPcinline, s.Func.Pcln.Pcinline) + if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { + w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline) } - for _, pcSym := range s.Func.Pcln.Pcdata { + for _, pcSym := range fn.Pcln.Pcdata { w.aux1(goobj.AuxPcdata, pcSym) } @@ -571,34 +571,34 @@ func nAuxSym(s *LSym) int { if s.Gotype != nil { n++ } - if s.Func != nil { + if fn := s.Func(); fn != nil { // FuncInfo is an aux symbol, each Funcdata is an aux symbol - n += 1 + len(s.Func.Pcln.Funcdata) - if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { + n += 1 + len(fn.Pcln.Funcdata) + if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { n++ } - if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { + if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { n++ } - if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { + if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { n++ } - if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { + if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { n++ } - if s.Func.Pcln.Pcsp != nil && s.Func.Pcln.Pcsp.Size != 0 { + if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { n++ } - if s.Func.Pcln.Pcfile != nil && s.Func.Pcln.Pcfile.Size != 0 { + if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { n++ } - if s.Func.Pcln.Pcline != nil && s.Func.Pcln.Pcline.Size != 0 { + if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { n++ } - if s.Func.Pcln.Pcinline != nil && s.Func.Pcln.Pcinline.Size != 0 { + if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { n++ } - n += len(s.Func.Pcln.Pcdata) + n += len(fn.Pcln.Pcdata) } return n } @@ -620,15 +620,16 @@ func genFuncInfoSyms(ctxt *Link) { var b bytes.Buffer symidx := int32(len(ctxt.defs)) for _, s := range ctxt.Text { - if s.Func == nil { + fn := s.Func() + if fn == nil { continue } o := goobj.FuncInfo{ - Args: uint32(s.Func.Args), - Locals: uint32(s.Func.Locals), - FuncID: objabi.FuncID(s.Func.FuncID), + Args: uint32(fn.Args), + Locals: uint32(fn.Locals), + FuncID: objabi.FuncID(fn.FuncID), } - pc := &s.Func.Pcln + pc := &fn.Pcln o.Pcsp = makeSymRef(preparePcSym(pc.Pcsp)) o.Pcfile = makeSymRef(preparePcSym(pc.Pcfile)) o.Pcline = makeSymRef(preparePcSym(pc.Pcline)) @@ -670,10 +671,10 @@ func genFuncInfoSyms(ctxt *Link) { isym.Set(AttrIndexed, true) symidx++ infosyms = append(infosyms, isym) - s.Func.FuncInfoSym = isym + fn.FuncInfoSym = isym b.Reset() - dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym, s.Func.dwarfInfoSym} + dwsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym} for _, s := range dwsyms { if s == nil || s.Size == 0 { continue @@ -744,14 +745,15 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { } fmt.Fprintf(ctxt.Bso, "size=%d", s.Size) if s.Type == objabi.STEXT { - fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(s.Func.Args), uint64(s.Func.Locals), uint64(s.Func.FuncID)) + fn := s.Func() + fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID)) if s.Leaf() { fmt.Fprintf(ctxt.Bso, " leaf") } } fmt.Fprintf(ctxt.Bso, "\n") if s.Type == objabi.STEXT { - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc))) if ctxt.Debugasm > 1 { io.WriteString(ctxt.Bso, p.String()) diff --git a/src/cmd/internal/obj/pass.go b/src/cmd/internal/obj/pass.go index 09d520b4e9..01657dd4f6 100644 --- a/src/cmd/internal/obj/pass.go +++ b/src/cmd/internal/obj/pass.go @@ -118,7 +118,7 @@ func checkaddr(ctxt *Link, p *Prog, a *Addr) { } func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) { - for p := sym.Func.Text; p != nil; p = p.Link { + for p := sym.Func().Text; p != nil; p = p.Link { checkaddr(ctxt, p, &p.From) if p.GetFrom3() != nil { checkaddr(ctxt, p, p.GetFrom3()) @@ -138,7 +138,7 @@ func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) { if p.To.Sym != nil { continue } - q := sym.Func.Text + q := sym.Func().Text for q != nil && p.To.Offset != q.Pc { if q.Forwd != nil && p.To.Offset >= q.Forwd.Pc { q = q.Forwd @@ -164,7 +164,7 @@ func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) { } // Collapse series of jumps to jumps. - for p := sym.Func.Text; p != nil; p = p.Link { + for p := sym.Func().Text; p != nil; p = p.Link { if p.To.Target() == nil { continue } diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go index ce0d3714c0..67c4f9a62b 100644 --- a/src/cmd/internal/obj/pcln.go +++ b/src/cmd/internal/obj/pcln.go @@ -35,20 +35,21 @@ func funcpctab(ctxt *Link, func_ *LSym, desc string, valfunc func(*Link, *LSym, val := int32(-1) oldval := val - if func_.Func.Text == nil { + fn := func_.Func() + if fn.Text == nil { // Return the emtpy symbol we've built so far. return sym } - pc := func_.Func.Text.Pc + pc := fn.Text.Pc if dbg { - ctxt.Logf("%6x %6d %v\n", uint64(pc), val, func_.Func.Text) + ctxt.Logf("%6x %6d %v\n", uint64(pc), val, fn.Text) } buf := make([]byte, binary.MaxVarintLen32) started := false - for p := func_.Func.Text; p != nil; p = p.Link { + for p := fn.Text; p != nil; p = p.Link { // Update val. If it's not changing, keep going. val = valfunc(ctxt, func_, val, p, 0, arg) @@ -107,7 +108,7 @@ func funcpctab(ctxt *Link, func_ *LSym, desc string, valfunc func(*Link, *LSym, if started { if dbg { - ctxt.Logf("%6x done\n", uint64(func_.Func.Text.Pc+func_.Size)) + ctxt.Logf("%6x done\n", uint64(fn.Text.Pc+func_.Size)) } v := (func_.Size - pc) / int64(ctxt.Arch.MinLC) if v < 0 { @@ -257,12 +258,12 @@ func pctopcdata(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg i } func linkpcln(ctxt *Link, cursym *LSym) { - pcln := &cursym.Func.Pcln + pcln := &cursym.Func().Pcln pcln.UsedFiles = make(map[goobj.CUFileIndex]struct{}) npcdata := 0 nfuncdata := 0 - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { // Find the highest ID of any used PCDATA table. This ignores PCDATA table // that consist entirely of "-1", since that's the assumed default value. // From.Offset is table ID @@ -288,11 +289,12 @@ func linkpcln(ctxt *Link, cursym *LSym) { // Check that all the Progs used as inline markers are still reachable. // See issue #40473. - inlMarkProgs := make(map[*Prog]struct{}, len(cursym.Func.InlMarks)) - for _, inlMark := range cursym.Func.InlMarks { + fn := cursym.Func() + inlMarkProgs := make(map[*Prog]struct{}, len(fn.InlMarks)) + for _, inlMark := range fn.InlMarks { inlMarkProgs[inlMark.p] = struct{}{} } - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := fn.Text; p != nil; p = p.Link { if _, ok := inlMarkProgs[p]; ok { delete(inlMarkProgs, p) } @@ -303,7 +305,7 @@ func linkpcln(ctxt *Link, cursym *LSym) { pcinlineState := new(pcinlineState) pcln.Pcinline = funcpctab(ctxt, cursym, "pctoinline", pcinlineState.pctoinline, nil) - for _, inlMark := range cursym.Func.InlMarks { + for _, inlMark := range fn.InlMarks { pcinlineState.setParentPC(ctxt, int(inlMark.id), int32(inlMark.p.Pc)) } pcln.InlTree = pcinlineState.localTree @@ -316,7 +318,7 @@ func linkpcln(ctxt *Link, cursym *LSym) { // tabulate which pc and func data we have. havepc := make([]uint32, (npcdata+31)/32) havefunc := make([]uint32, (nfuncdata+31)/32) - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := fn.Text; p != nil; p = p.Link { if p.As == AFUNCDATA { if (havefunc[p.From.Offset/32]>>uint64(p.From.Offset%32))&1 != 0 { ctxt.Diag("multiple definitions for FUNCDATA $%d", p.From.Offset) @@ -344,7 +346,7 @@ func linkpcln(ctxt *Link, cursym *LSym) { // funcdata if nfuncdata > 0 { - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := fn.Text; p != nil; p = p.Link { if p.As != AFUNCDATA { continue } diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go index 6e33f29959..eb54c67f6a 100644 --- a/src/cmd/internal/obj/plist.go +++ b/src/cmd/internal/obj/plist.go @@ -81,7 +81,7 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string continue } found := false - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == objabi.FUNCDATA_ArgsPointerMaps { found = true break @@ -89,7 +89,7 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string } if !found { - p := Appendp(s.Func.Text, newprog) + p := Appendp(s.Func().Text, newprog) p.As = AFUNCDATA p.From.Type = TYPE_CONST p.From.Offset = objabi.FUNCDATA_ArgsPointerMaps @@ -120,15 +120,15 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) { // func _() { } return } - if s.Func != nil { + if s.Func() != nil { ctxt.Diag("InitTextSym double init for %s", s.Name) } - s.Func = new(FuncInfo) + s.NewFuncInfo() if s.OnList() { ctxt.Diag("symbol %s listed multiple times", s.Name) } name := strings.Replace(s.Name, "\"\"", ctxt.Pkgpath, -1) - s.Func.FuncID = objabi.GetFuncID(name, flag&WRAPPER != 0) + s.Func().FuncID = objabi.GetFuncID(name, flag&WRAPPER != 0) s.Set(AttrOnList, true) s.Set(AttrDuplicateOK, flag&DUPOK != 0) s.Set(AttrNoSplit, flag&NOSPLIT != 0) @@ -185,7 +185,7 @@ func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog { // Similar to EmitEntryLiveness, but just emit stack map. func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { pcdata := Appendp(p, newprog) - pcdata.Pos = s.Func.Text.Pos + pcdata.Pos = s.Func().Text.Pos pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST pcdata.From.Offset = objabi.PCDATA_StackMapIndex @@ -198,7 +198,7 @@ func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { // Similar to EmitEntryLiveness, but just emit register map. func (ctxt *Link) EmitEntryRegMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { pcdata := Appendp(p, newprog) - pcdata.Pos = s.Func.Text.Pos + pcdata.Pos = s.Func().Text.Pos pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST pcdata.From.Offset = objabi.PCDATA_RegMapIndex diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go index c2e8e9e9d0..dcabb3cd6a 100644 --- a/src/cmd/internal/obj/ppc64/asm9.go +++ b/src/cmd/internal/obj/ppc64/asm9.go @@ -663,8 +663,8 @@ func addpad(pc, a int64, ctxt *obj.Link, cursym *obj.LSym) int { // the function alignment is not changed which might // result in 16 byte alignment but that is still fine. // TODO: alignment on AIX - if ctxt.Headtype != objabi.Haix && cursym.Func.Align < 32 { - cursym.Func.Align = 32 + if ctxt.Headtype != objabi.Haix && cursym.Func().Align < 32 { + cursym.Func().Align = 32 } default: ctxt.Diag("Unexpected alignment: %d for PCALIGN directive\n", a) @@ -673,7 +673,7 @@ func addpad(pc, a int64, ctxt *obj.Link, cursym *obj.LSym) int { } func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { - p := cursym.Func.Text + p := cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -722,7 +722,7 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { for bflag != 0 { bflag = 0 pc = 0 - for p = c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p = c.cursym.Func().Text.Link; p != nil; p = p.Link { p.Pc = pc o = c.oplook(p) @@ -784,7 +784,7 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { bp := c.cursym.P var i int32 var out [6]uint32 - for p := c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p := c.cursym.Func().Text.Link; p != nil; p = p.Link { c.pc = p.Pc o = c.oplook(p) if int(o.size) > 4*len(out) { diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go index c012762a18..3ab19de602 100644 --- a/src/cmd/internal/obj/ppc64/obj9.go +++ b/src/cmd/internal/obj/ppc64/obj9.go @@ -402,13 +402,13 @@ func (c *ctxt9) rewriteToUseGot(p *obj.Prog) { func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // TODO(minux): add morestack short-cuts with small fixed frame-size. - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog} - p := c.cursym.Func.Text + p := c.cursym.Func().Text textstksiz := p.To.Offset if textstksiz == -8 { // Compatibility hack. @@ -424,8 +424,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - c.cursym.Func.Args = p.To.Val.(int32) - c.cursym.Func.Locals = int32(textstksiz) + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) /* * find leaf subroutines @@ -435,7 +435,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { var q *obj.Prog var q1 *obj.Prog - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case obj.ATEXT: @@ -541,7 +541,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ABCL, obj.ADUFFZERO, obj.ADUFFCOPY: - c.cursym.Func.Text.Mark &^= LEAF + c.cursym.Func().Text.Mark &^= LEAF fallthrough case ABC, @@ -598,7 +598,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize := int32(0) var p1 *obj.Prog var p2 *obj.Prog - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: @@ -664,7 +664,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { rel.Type = objabi.R_ADDRPOWER_PCREL } - if !c.cursym.Func.Text.From.Sym.NoSplit() { + if !c.cursym.Func().Text.From.Sym.NoSplit() { q = c.stacksplit(q, autosize) // emit split check } @@ -732,14 +732,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) } - } else if c.cursym.Func.Text.Mark&LEAF == 0 { + } else if c.cursym.Func().Text.Mark&LEAF == 0 { // A very few functions that do not return to their caller // (e.g. gogo) are not identified as leaves but still have // no frame. - c.cursym.Func.Text.Mark |= LEAF + c.cursym.Func().Text.Mark |= LEAF } - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { c.cursym.Set(obj.AttrLeaf, true) break } @@ -755,7 +755,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.To.Offset = 24 } - if c.cursym.Func.Text.From.Sym.Wrapper() { + if c.cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVD g_panic(g), R3 @@ -853,7 +853,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { retTarget := p.To.Sym - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { if autosize == 0 || c.cursym.Name == "runtime.racecallbackthunk" { p.As = ABR p.From = obj.Addr{} @@ -1161,7 +1161,7 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { var morestacksym *obj.LSym if c.cursym.CFunc() { morestacksym = c.ctxt.Lookup("runtime.morestackc") - } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() { + } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt") } else { morestacksym = c.ctxt.Lookup("runtime.morestack") diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 841b30d85c..045c2250b5 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -427,7 +427,7 @@ func InvertBranch(as obj.As) obj.As { // instruction. Must be called after progedit. func containsCall(sym *obj.LSym) bool { // CALLs are CALL or JAL(R) with link register LR. - for p := sym.Func.Text; p != nil; p = p.Link { + for p := sym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.ACALL: return true @@ -499,12 +499,12 @@ func stackOffset(a *obj.Addr, stacksize int64) { // concrete, real RISC-V instructions or directive pseudo-ops like TEXT, // PCDATA, and FUNCDATA. func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } // Generate the prologue. - text := cursym.Func.Text + text := cursym.Func().Text if text.As != obj.ATEXT { ctxt.Diag("preprocess: found symbol that does not start with TEXT directive") return @@ -538,12 +538,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { stacksize += ctxt.FixedFrameSize() } - cursym.Func.Args = text.To.Val.(int32) - cursym.Func.Locals = int32(stacksize) + cursym.Func().Args = text.To.Val.(int32) + cursym.Func().Locals = int32(stacksize) prologue := text - if !cursym.Func.Text.From.Sym.NoSplit() { + if !cursym.Func().Text.From.Sym.NoSplit() { prologue = stacksplit(ctxt, prologue, cursym, newprog, stacksize) // emit split check } @@ -567,7 +567,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { prologue = ctxt.EndUnsafePoint(prologue, newprog, -1) } - if cursym.Func.Text.From.Sym.Wrapper() { + if cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOV g_panic(g), X11 @@ -647,13 +647,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } // Update stack-based offsets. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { stackOffset(&p.From, stacksize) stackOffset(&p.To, stacksize) } // Additional instruction rewriting. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.AGETCALLERPC: if cursym.Leaf() { @@ -733,7 +733,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // Rewrite MOV pseudo-instructions. This cannot be done in // progedit, as SP offsets need to be applied before we split // up some of the Addrs. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD: rewriteMOV(ctxt, newprog, p) @@ -741,7 +741,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } // Split immediates larger than 12-bits. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { // $imm, REG, TO case AADDI, AANDI, AORI, AXORI: @@ -858,9 +858,9 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // a fixed point will be reached). No attempt to handle functions > 2GiB. for { rescan := false - setPCs(cursym.Func.Text, 0) + setPCs(cursym.Func().Text, 0) - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ: if p.To.Type != obj.TYPE_BRANCH { @@ -917,7 +917,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // Now that there are no long branches, resolve branch and jump targets. // At this point, instruction rewriting which changes the number of // instructions will break everything--don't do it! - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, AJAL: switch p.To.Type { @@ -940,7 +940,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } // Validate all instructions - this provides nice error messages. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { for _, ins := range instructionsForProg(p) { ins.validate(ctxt) } @@ -1068,7 +1068,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA p.To.Type = obj.TYPE_BRANCH if cursym.CFunc() { p.To.Sym = ctxt.Lookup("runtime.morestackc") - } else if !cursym.Func.Text.From.Sym.NeedCtxt() { + } else if !cursym.Func().Text.From.Sym.NeedCtxt() { p.To.Sym = ctxt.Lookup("runtime.morestack_noctxt") } else { p.To.Sym = ctxt.Lookup("runtime.morestack") @@ -1083,7 +1083,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA p.As = AJAL p.To = obj.Addr{Type: obj.TYPE_BRANCH} p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO} - p.To.SetTarget(cursym.Func.Text.Link) + p.To.SetTarget(cursym.Func().Text.Link) // placeholder for to_done's jump target p = obj.Appendp(p, newprog) @@ -1926,7 +1926,7 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } var symcode []uint32 - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case AJALR: if p.To.Sym != nil { @@ -1981,7 +1981,7 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Arch.ByteOrder.PutUint32(p, symcode[i]) } - obj.MarkUnsafePoints(ctxt, cursym.Func.Text, newprog, isUnsafePoint, nil) + obj.MarkUnsafePoints(ctxt, cursym.Func().Text, newprog, isUnsafePoint, nil) } func isUnsafePoint(p *obj.Prog) bool { diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go index cb3a2c3196..da14dd3c41 100644 --- a/src/cmd/internal/obj/s390x/asmz.go +++ b/src/cmd/internal/obj/s390x/asmz.go @@ -447,7 +447,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Retpoline = false // don't keep printing } - p := cursym.Func.Text + p := cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -473,7 +473,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { c.cursym.R[nrelocs0+i] = obj.Reloc{} } c.cursym.R = c.cursym.R[:nrelocs0] // preserve marker relocations generated by the compiler - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { pc := int64(len(buffer)) if pc != p.Pc { changed = true @@ -504,7 +504,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // We use REGTMP as a scratch register during call injection, // so instruction sequences that use REGTMP are unsafe to // preempt asynchronously. - obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, nil) + obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, nil) } // Return whether p is an unsafe point. diff --git a/src/cmd/internal/obj/s390x/objz.go b/src/cmd/internal/obj/s390x/objz.go index 625bb0f7b4..3af5425b36 100644 --- a/src/cmd/internal/obj/s390x/objz.go +++ b/src/cmd/internal/obj/s390x/objz.go @@ -205,13 +205,13 @@ func (c *ctxtz) rewriteToUseGot(p *obj.Prog) { func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // TODO(minux): add morestack short-cuts with small fixed frame-size. - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } c := ctxtz{ctxt: ctxt, cursym: cursym, newprog: newprog} - p := c.cursym.Func.Text + p := c.cursym.Func().Text textstksiz := p.To.Offset if textstksiz == -8 { // Compatibility hack. @@ -227,8 +227,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - c.cursym.Func.Args = p.To.Val.(int32) - c.cursym.Func.Locals = int32(textstksiz) + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) /* * find leaf subroutines @@ -237,7 +237,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { */ var q *obj.Prog - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.ATEXT: q = p @@ -245,7 +245,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case ABL, ABCL: q = p - c.cursym.Func.Text.Mark &^= LEAF + c.cursym.Func().Text.Mark &^= LEAF fallthrough case ABC, @@ -294,7 +294,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { var pPre *obj.Prog var pPreempt *obj.Prog wasSplit := false - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { pLast = p switch p.As { case obj.ATEXT: @@ -356,19 +356,19 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.Spadj = autosize q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) - } else if c.cursym.Func.Text.Mark&LEAF == 0 { + } else if c.cursym.Func().Text.Mark&LEAF == 0 { // A very few functions that do not return to their caller // (e.g. gogo) are not identified as leaves but still have // no frame. - c.cursym.Func.Text.Mark |= LEAF + c.cursym.Func().Text.Mark |= LEAF } - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { c.cursym.Set(obj.AttrLeaf, true) break } - if c.cursym.Func.Text.From.Sym.Wrapper() { + if c.cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVD g_panic(g), R3 @@ -461,7 +461,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case obj.ARET: retTarget := p.To.Sym - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { if autosize == 0 { p.As = ABR p.From = obj.Addr{} @@ -696,7 +696,7 @@ func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, p.To.Type = obj.TYPE_BRANCH if c.cursym.CFunc() { p.To.Sym = c.ctxt.Lookup("runtime.morestackc") - } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() { + } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt") } else { p.To.Sym = c.ctxt.Lookup("runtime.morestack") @@ -709,7 +709,7 @@ func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, p.As = ABR p.To.Type = obj.TYPE_BRANCH - p.To.SetTarget(c.cursym.Func.Text.Link) + p.To.SetTarget(c.cursym.Func().Text.Link) return p } diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index e5d7b2cbfd..0182773f8e 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -358,7 +358,8 @@ func (ctxt *Link) traverseSyms(flag traverseFlag, fn func(*LSym)) { } func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent *LSym, aux *LSym)) { - pc := &fsym.Func.Pcln + fninfo := fsym.Func() + pc := &fninfo.Pcln if flag&traverseAux == 0 { // NB: should it become necessary to walk aux sym reloc references // without walking the aux syms themselves, this can be changed. @@ -389,7 +390,8 @@ func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent fn(fsym, filesym) } } - dwsyms := []*LSym{fsym.Func.dwarfRangesSym, fsym.Func.dwarfLocSym, fsym.Func.dwarfDebugLinesSym, fsym.Func.dwarfInfoSym} + + dwsyms := []*LSym{fninfo.dwarfRangesSym, fninfo.dwarfLocSym, fninfo.dwarfDebugLinesSym, fninfo.dwarfInfoSym} for _, dws := range dwsyms { if dws == nil || dws.Size == 0 { continue diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index a9e093a8ad..f7f66a1255 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -182,14 +182,14 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { return p } - framesize := s.Func.Text.To.Offset + framesize := s.Func().Text.To.Offset if framesize < 0 { panic("bad framesize") } - s.Func.Args = s.Func.Text.To.Val.(int32) - s.Func.Locals = int32(framesize) + s.Func().Args = s.Func().Text.To.Val.(int32) + s.Func().Locals = int32(framesize) - if s.Func.Text.From.Sym.Wrapper() { + if s.Func().Text.From.Sym.Wrapper() { // if g._panic != nil && g._panic.argp == FP { // g._panic.argp = bottom-of-frame // } @@ -222,7 +222,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { Offset: 0, // panic.argp } - p := s.Func.Text + p := s.Func().Text p = appendp(p, AMOVD, gpanic, regAddr(REG_R0)) p = appendp(p, AGet, regAddr(REG_R0)) @@ -245,7 +245,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } if framesize > 0 { - p := s.Func.Text + p := s.Func().Text p = appendp(p, AGet, regAddr(REG_SP)) p = appendp(p, AI32Const, constAddr(framesize)) p = appendp(p, AI32Sub) @@ -260,8 +260,8 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { pc := int64(0) // pc is only incremented when necessary, this avoids bloat of the BrTable instruction var tableIdxs []uint64 tablePC := int64(0) - base := ctxt.PosTable.Pos(s.Func.Text.Pos).Base() - for p := s.Func.Text; p != nil; p = p.Link { + base := ctxt.PosTable.Pos(s.Func().Text.Pos).Base() + for p := s.Func().Text; p != nil; p = p.Link { prevBase := base base = ctxt.PosTable.Pos(p.Pos).Base() switch p.As { @@ -313,8 +313,8 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { tableIdxs = append(tableIdxs, uint64(numResumePoints)) s.Size = pc + 1 - if !s.Func.Text.From.Sym.NoSplit() { - p := s.Func.Text + if !s.Func().Text.From.Sym.NoSplit() { + p := s.Func().Text if framesize <= objabi.StackSmall { // small stack: SP <= stackguard @@ -352,7 +352,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { p = appendp(p, AIf) p = appendp(p, obj.ACALL, constAddr(0)) - if s.Func.Text.From.Sym.NeedCtxt() { + if s.Func().Text.From.Sym.NeedCtxt() { p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestack} } else { p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestackNoCtxt} @@ -365,7 +365,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { var entryPointLoopBranches []*obj.Prog var unwindExitBranches []*obj.Prog currentDepth := 0 - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { switch p.As { case ABlock, ALoop, AIf: currentDepth++ @@ -562,7 +562,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } } - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { switch p.From.Name { case obj.NAME_AUTO: p.From.Offset += int64(framesize) @@ -712,7 +712,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } { - p := s.Func.Text + p := s.Func().Text if len(unwindExitBranches) > 0 { p = appendp(p, ABlock) // unwindExit, used to return 1 when unwinding the stack for _, b := range unwindExitBranches { @@ -749,7 +749,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { currentDepth = 0 blockDepths := make(map[*obj.Prog]int) - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { switch p.As { case ABlock, ALoop, AIf: currentDepth++ @@ -850,7 +850,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { hasLocalSP = true var regUsed [MAXREG - MINREG]bool - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { if p.From.Reg != 0 { regUsed[p.From.Reg-MINREG] = true } @@ -896,7 +896,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { updateLocalSP(w) } - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { switch p.As { case AGet: if p.From.Type != obj.TYPE_REG { diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index 4940c79eaa..c412f4945d 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -2050,7 +2050,7 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { ctxt.Diag("x86 tables not initialized, call x86.instinit first") } - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { if p.To.Type == obj.TYPE_BRANCH && p.To.Target() == nil { p.To.SetTarget(p) } @@ -2085,7 +2085,7 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } var count int64 // rough count of number of instructions - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { count++ p.Back = branchShort // use short branches first time through if q := p.To.Target(); q != nil && (q.Back&branchShort != 0) { @@ -2113,7 +2113,7 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { c = 0 var pPrev *obj.Prog nops = nops[:0] - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { c0 := c c = pjc.padJump(ctxt, s, p, c) @@ -2227,7 +2227,7 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { // the first instruction.) return p.From.Index == REG_TLS } - obj.MarkUnsafePoints(ctxt, s.Func.Text, newprog, useTLS, nil) + obj.MarkUnsafePoints(ctxt, s.Func().Text, newprog, useTLS, nil) } } diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go index 18a6afcd77..e11fa13f65 100644 --- a/src/cmd/internal/obj/x86/obj6.go +++ b/src/cmd/internal/obj/x86/obj6.go @@ -563,11 +563,11 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { } func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } - p := cursym.Func.Text + p := cursym.Func().Text autoffset := int32(p.To.Offset) if autoffset < 0 { autoffset = 0 @@ -602,12 +602,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } textarg := int64(p.To.Val.(int32)) - cursym.Func.Args = int32(textarg) - cursym.Func.Locals = int32(p.To.Offset) + cursym.Func().Args = int32(textarg) + cursym.Func().Locals = int32(p.To.Offset) // TODO(rsc): Remove. - if ctxt.Arch.Family == sys.I386 && cursym.Func.Locals < 0 { - cursym.Func.Locals = 0 + if ctxt.Arch.Family == sys.I386 && cursym.Func().Locals < 0 { + cursym.Func().Locals = 0 } // TODO(rsc): Remove 'ctxt.Arch.Family == sys.AMD64 &&'. @@ -642,7 +642,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p = load_g_cx(ctxt, p, newprog) // load g into CX } - if !cursym.Func.Text.From.Sym.NoSplit() { + if !cursym.Func().Text.From.Sym.NoSplit() { p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg)) // emit split check } @@ -690,7 +690,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Reg = REG_BP } - if cursym.Func.Text.From.Sym.Wrapper() { + if cursym.Func().Text.From.Sym.Wrapper() { // if g._panic != nil && g._panic.argp == FP { // g._panic.argp = bottom-of-frame // } @@ -808,7 +808,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } var deltasp int32 - for p = cursym.Func.Text; p != nil; p = p.Link { + for p = cursym.Func().Text; p != nil; p = p.Link { pcsize := ctxt.Arch.RegSize switch p.From.Name { case obj.NAME_AUTO: @@ -1103,7 +1103,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA end := ctxt.EndUnsafePoint(jls, newprog, -1) var last *obj.Prog - for last = cursym.Func.Text; last.Link != nil; last = last.Link { + for last = cursym.Func().Text; last.Link != nil; last = last.Link { } // Now we are at the end of the function, but logically @@ -1117,7 +1117,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA pcdata = ctxt.StartUnsafePoint(pcdata, newprog) call := obj.Appendp(pcdata, newprog) - call.Pos = cursym.Func.Text.Pos + call.Pos = cursym.Func().Text.Pos call.As = obj.ACALL call.To.Type = obj.TYPE_BRANCH call.To.Name = obj.NAME_EXTERN @@ -1125,7 +1125,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA switch { case cursym.CFunc(): morestack = "runtime.morestackc" - case !cursym.Func.Text.From.Sym.NeedCtxt(): + case !cursym.Func().Text.From.Sym.NeedCtxt(): morestack = "runtime.morestack_noctxt" } call.To.Sym = ctxt.Lookup(morestack) @@ -1144,7 +1144,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA jmp := obj.Appendp(pcdata, newprog) jmp.As = obj.AJMP jmp.To.Type = obj.TYPE_BRANCH - jmp.To.SetTarget(cursym.Func.Text.Link) + jmp.To.SetTarget(cursym.Func().Text.Link) jmp.Spadj = +framesize jls.To.SetTarget(call) -- cgit v1.3 From 771f5f2e487db4eb2bcf6fa1660dc8cef1feaf14 Mon Sep 17 00:00:00 2001 From: Daniel Martí Date: Fri, 16 Oct 2020 15:05:04 +0100 Subject: compress/flate: revert a goto for-loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In https://golang.org/cl/16528, a goto loop was chosen over a regular for loop since that would make the function inlinable. Thanks to the recent https://golang.org/cl/256459, for loops without a label can now be inlined. So we can undo the workaround and simplify the code. Also add the function to TestIntendedInlining, which passes both before and after the change, as expected. For #14768. Change-Id: Ie5df55a6bcb07c538ca331eef2f908807ff0b516 Reviewed-on: https://go-review.googlesource.com/c/go/+/263037 Trust: Daniel Martí Run-TryBot: Daniel Martí Reviewed-by: Brad Fitzpatrick TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/inl_test.go | 1 + src/compress/flate/dict_decoder.go | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl_test.go b/src/cmd/compile/internal/gc/inl_test.go index 547a602ee0..afa6b98315 100644 --- a/src/cmd/compile/internal/gc/inl_test.go +++ b/src/cmd/compile/internal/gc/inl_test.go @@ -115,6 +115,7 @@ func TestIntendedInlining(t *testing.T) { "byLiteral.Len", "byLiteral.Less", "byLiteral.Swap", + "(*dictDecoder).tryWriteCopy", }, "encoding/base64": { "assemble32", diff --git a/src/compress/flate/dict_decoder.go b/src/compress/flate/dict_decoder.go index 71c75a065e..3b59d48351 100644 --- a/src/compress/flate/dict_decoder.go +++ b/src/compress/flate/dict_decoder.go @@ -160,10 +160,8 @@ func (dd *dictDecoder) tryWriteCopy(dist, length int) int { srcPos := dstPos - dist // Copy possibly overlapping section before destination position. -loop: - dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos]) - if dstPos < endPos { - goto loop // Avoid for-loop so that this function can be inlined + for dstPos < endPos { + dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos]) } dd.wrPos = dstPos -- cgit v1.3 From c06a699fb69e17274905bc8d9942de4a4ab8b31b Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 16 Oct 2020 22:39:51 +0700 Subject: cmd/compile: remove deltaNewFile CL 196963 removed last usages of deltaNewFile, this CL remove it. While at it, move the comment to go/internal/gcimporter. Change-Id: Ieea47db405cf43744689f50b79be8ca710e21c85 Reviewed-on: https://go-review.googlesource.com/c/go/+/263077 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/bexport.go | 5 ----- src/go/internal/gcimporter/support.go | 5 ++++- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 0cb9fe9e62..10f21f86df 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -81,11 +81,6 @@ func (p *exporter) markType(t *types.Type) { } } -// deltaNewFile is a magic line delta offset indicating a new file. -// We use -64 because it is rare; see issue 20080 and CL 41619. -// -64 is the smallest int that fits in a single byte as a varint. -const deltaNewFile = -64 - // ---------------------------------------------------------------------------- // Export format diff --git a/src/go/internal/gcimporter/support.go b/src/go/internal/gcimporter/support.go index 2de7cacd2d..b8bb14dc49 100644 --- a/src/go/internal/gcimporter/support.go +++ b/src/go/internal/gcimporter/support.go @@ -17,7 +17,10 @@ func errorf(format string, args ...interface{}) { panic(fmt.Sprintf(format, args...)) } -const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go +// deltaNewFile is a magic line delta offset indicating a new file. +// We use -64 because it is rare; see issue 20080 and CL 41619. +// -64 is the smallest int that fits in a single byte as a varint. +const deltaNewFile = -64 // Synthesize a token.Pos type fakeFileSet struct { -- cgit v1.3 From 76a615b20a943b7255ac1cb3944df62a1dbc882c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Sat, 17 Oct 2020 01:10:06 -0700 Subject: cmd/compile: fix defer/go calls to variadic unsafe-uintptr functions Before generating wrapper function, turn any f(a, b, []T{c, d, e}...) calls back into f(a, b, c, d, e). This allows the existing code for recognizing and specially handling unsafe.Pointer->uintptr conversions to correctly handle variadic arguments too. Fixes #41460. Change-Id: I0a1255abdd1bd5dafd3e89547aedd4aec878394c Reviewed-on: https://go-review.googlesource.com/c/go/+/263297 Trust: Matthew Dempsky Trust: Cuong Manh Le Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/gc/walk.go | 10 ++++++++++ test/fixedbugs/issue24491a.go | 3 --- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 05a049b3cc..9df288ea65 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -3881,6 +3881,16 @@ func wrapCall(n *Node, init *Nodes) *Node { } isBuiltinCall := n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER + + // Turn f(a, b, []T{c, d, e}...) back into f(a, b, c, d, e). + if !isBuiltinCall && n.IsDDD() { + last := n.List.Len() - 1 + if va := n.List.Index(last); va.Op == OSLICELIT { + n.List.Set(append(n.List.Slice()[:last], va.List.Slice()...)) + n.SetIsDDD(false) + } + } + // origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion. origArgs := make([]*Node, n.List.Len()) t := nod(OTFUNC, nil, nil) diff --git a/test/fixedbugs/issue24491a.go b/test/fixedbugs/issue24491a.go index 3c595798b5..8accf8c0a3 100644 --- a/test/fixedbugs/issue24491a.go +++ b/test/fixedbugs/issue24491a.go @@ -34,9 +34,6 @@ func test(s string, p, q uintptr, rest ...uintptr) int { panic(s + ": q failed") } for _, r := range rest { - // TODO(mdempsky): Remove. - break - if *(*string)(unsafe.Pointer(r)) != "ok" { panic(s + ": r[i] failed") } -- cgit v1.3 From dd58239dd20f7002b3e219a477514b91dd0cc5fc Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Wed, 7 Oct 2020 12:31:05 -0400 Subject: cmd/asm: allow def/ref of func ABI when compiling runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function symbols defined and referenced by assembly source currently always default to ABI0; this patch adds preliminary support for accepting an explicit ABI selector clause for func defs/refs. This functionality is currently only enabled when compiling runtime-related packages (runtime, syscall, reflect). Examples: TEXT ·DefinedAbi0Symbol(SB),NOSPLIT,$0 RET TEXT ·DefinedAbi1Symbol(SB),NOSPLIT,$0 CALL ·AbiZerolSym(SB) ... JMP ·AbiInternalSym(SB) RET Also included is a small change to the code in the compiler that reads the symabis file emitted by the assembler. New behavior is currently gated under GOEXPERIMENT=regabi. Updates #27539, #40724. Change-Id: Ia22221fe26df0fa002191cfb13bdfaaa38d7df38 Reviewed-on: https://go-review.googlesource.com/c/go/+/260477 Run-TryBot: Than McIntosh TryBot-Result: Go Bot Reviewed-by: Cherry Zhang Trust: Than McIntosh --- src/cmd/asm/internal/asm/endtoend_test.go | 4 +- src/cmd/asm/internal/asm/expr_test.go | 4 +- src/cmd/asm/internal/asm/line_test.go | 2 +- src/cmd/asm/internal/asm/operand_test.go | 24 ++++- src/cmd/asm/internal/asm/parse.go | 148 ++++++++++++++++++++---------- src/cmd/asm/internal/asm/pseudo_test.go | 1 + src/cmd/asm/main.go | 3 +- src/cmd/compile/internal/gc/main.go | 11 ++- src/cmd/internal/obj/link.go | 14 +++ src/cmd/internal/obj/util.go | 39 +++++++- src/cmd/internal/objabi/path.go | 22 +++++ 11 files changed, 207 insertions(+), 65 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index decf5391db..989b7a5405 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -31,7 +31,7 @@ func testEndToEnd(t *testing.T, goarch, file string) { architecture, ctxt := setArch(goarch) architecture.Init(ctxt) lexer := lex.NewLexer(input) - parser := NewParser(ctxt, architecture, lexer) + parser := NewParser(ctxt, architecture, lexer, false) pList := new(obj.Plist) var ok bool testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. @@ -273,7 +273,7 @@ func testErrors(t *testing.T, goarch, file string) { input := filepath.Join("testdata", file+".s") architecture, ctxt := setArch(goarch) lexer := lex.NewLexer(input) - parser := NewParser(ctxt, architecture, lexer) + parser := NewParser(ctxt, architecture, lexer, false) pList := new(obj.Plist) var ok bool testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. diff --git a/src/cmd/asm/internal/asm/expr_test.go b/src/cmd/asm/internal/asm/expr_test.go index 1251594349..e9c92df1f3 100644 --- a/src/cmd/asm/internal/asm/expr_test.go +++ b/src/cmd/asm/internal/asm/expr_test.go @@ -57,7 +57,7 @@ var exprTests = []exprTest{ } func TestExpr(t *testing.T) { - p := NewParser(nil, nil, nil) // Expression evaluation uses none of these fields of the parser. + p := NewParser(nil, nil, nil, false) // Expression evaluation uses none of these fields of the parser. for i, test := range exprTests { p.start(lex.Tokenize(test.input)) result := int64(p.expr()) @@ -113,7 +113,7 @@ func TestBadExpr(t *testing.T) { } func runBadTest(i int, test badExprTest, t *testing.T) (err error) { - p := NewParser(nil, nil, nil) // Expression evaluation uses none of these fields of the parser. + p := NewParser(nil, nil, nil, false) // Expression evaluation uses none of these fields of the parser. p.start(lex.Tokenize(test.input)) return tryParse(t, func() { p.expr() diff --git a/src/cmd/asm/internal/asm/line_test.go b/src/cmd/asm/internal/asm/line_test.go index 01b058bd95..da857ced3a 100644 --- a/src/cmd/asm/internal/asm/line_test.go +++ b/src/cmd/asm/internal/asm/line_test.go @@ -39,7 +39,7 @@ func testBadInstParser(t *testing.T, goarch string, tests []badInstTest) { for i, test := range tests { arch, ctxt := setArch(goarch) tokenizer := lex.NewTokenizer("", strings.NewReader(test.input+"\n"), nil) - parser := NewParser(ctxt, arch, tokenizer) + parser := NewParser(ctxt, arch, tokenizer, false) err := tryParse(t, func() { parser.Parse() diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go index f187d0b166..2e83e176b2 100644 --- a/src/cmd/asm/internal/asm/operand_test.go +++ b/src/cmd/asm/internal/asm/operand_test.go @@ -28,7 +28,7 @@ func setArch(goarch string) (*arch.Arch, *obj.Link) { func newParser(goarch string) *Parser { architecture, ctxt := setArch(goarch) - return NewParser(ctxt, architecture, nil) + return NewParser(ctxt, architecture, nil, false) } // tryParse executes parse func in panicOnError=true context. @@ -75,7 +75,12 @@ func testOperandParser(t *testing.T, parser *Parser, tests []operandTest) { parser.start(lex.Tokenize(test.input)) addr := obj.Addr{} parser.operand(&addr) - result := obj.Dconv(&emptyProg, &addr) + var result string + if parser.compilingRuntime { + result = obj.DconvWithABIDetail(&emptyProg, &addr) + } else { + result = obj.Dconv(&emptyProg, &addr) + } if result != test.output { t.Errorf("fail at %s: got %s; expected %s\n", test.input, result, test.output) } @@ -86,6 +91,9 @@ func TestAMD64OperandParser(t *testing.T) { parser := newParser("amd64") testOperandParser(t, parser, amd64OperandTests) testBadOperandParser(t, parser, amd64BadOperandTests) + parser.compilingRuntime = true + testOperandParser(t, parser, amd64RuntimeOperandTests) + testBadOperandParser(t, parser, amd64BadOperandRuntimeTests) } func Test386OperandParser(t *testing.T) { @@ -141,7 +149,7 @@ func TestFuncAddress(t *testing.T) { parser := newParser(sub.arch) for _, test := range sub.tests { parser.start(lex.Tokenize(test.input)) - name, ok := parser.funcAddress() + name, _, ok := parser.funcAddress() isFuncSym := strings.HasSuffix(test.input, "(SB)") && // Ignore static symbols. @@ -298,6 +306,11 @@ var amd64OperandTests = []operandTest{ {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms. } +var amd64RuntimeOperandTests = []operandTest{ + {"$bar(SB)", "$bar(SB)"}, + {"$foo(SB)", "$foo(SB)"}, +} + var amd64BadOperandTests = []badOperandTest{ {"[", "register list: expected ']', found EOF"}, {"[4", "register list: bad low register in `[4`"}, @@ -311,6 +324,11 @@ var amd64BadOperandTests = []badOperandTest{ {"[X0-X1-X2]", "register list: expected ']' after `[X0-X1`, found '-'"}, {"[X0,X3]", "register list: expected '-' after `[X0`, found ','"}, {"[X0,X1,X2,X3]", "register list: expected '-' after `[X0`, found ','"}, + {"$foo", "ABI selector only permitted when compiling runtime, reference was to \"foo\""}, +} + +var amd64BadOperandRuntimeTests = []badOperandTest{ + {"$foo", "malformed ABI selector \"bletch\" in reference to \"foo\""}, } var x86OperandTests = []operandTest{ diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go index d9dbd92cb0..154cf9c7a7 100644 --- a/src/cmd/asm/internal/asm/parse.go +++ b/src/cmd/asm/internal/asm/parse.go @@ -25,25 +25,26 @@ import ( ) type Parser struct { - lex lex.TokenReader - lineNum int // Line number in source file. - errorLine int // Line number of last error. - errorCount int // Number of errors. - sawCode bool // saw code in this file (as opposed to comments and blank lines) - pc int64 // virtual PC; count of Progs; doesn't advance for GLOBL or DATA. - input []lex.Token - inputPos int - pendingLabels []string // Labels to attach to next instruction. - labels map[string]*obj.Prog - toPatch []Patch - addr []obj.Addr - arch *arch.Arch - ctxt *obj.Link - firstProg *obj.Prog - lastProg *obj.Prog - dataAddr map[string]int64 // Most recent address for DATA for this symbol. - isJump bool // Instruction being assembled is a jump. - errorWriter io.Writer + lex lex.TokenReader + lineNum int // Line number in source file. + errorLine int // Line number of last error. + errorCount int // Number of errors. + sawCode bool // saw code in this file (as opposed to comments and blank lines) + pc int64 // virtual PC; count of Progs; doesn't advance for GLOBL or DATA. + input []lex.Token + inputPos int + pendingLabels []string // Labels to attach to next instruction. + labels map[string]*obj.Prog + toPatch []Patch + addr []obj.Addr + arch *arch.Arch + ctxt *obj.Link + firstProg *obj.Prog + lastProg *obj.Prog + dataAddr map[string]int64 // Most recent address for DATA for this symbol. + isJump bool // Instruction being assembled is a jump. + compilingRuntime bool + errorWriter io.Writer } type Patch struct { @@ -51,14 +52,15 @@ type Patch struct { label string } -func NewParser(ctxt *obj.Link, ar *arch.Arch, lexer lex.TokenReader) *Parser { +func NewParser(ctxt *obj.Link, ar *arch.Arch, lexer lex.TokenReader, compilingRuntime bool) *Parser { return &Parser{ - ctxt: ctxt, - arch: ar, - lex: lexer, - labels: make(map[string]*obj.Prog), - dataAddr: make(map[string]int64), - errorWriter: os.Stderr, + ctxt: ctxt, + arch: ar, + lex: lexer, + labels: make(map[string]*obj.Prog), + dataAddr: make(map[string]int64), + errorWriter: os.Stderr, + compilingRuntime: compilingRuntime, } } @@ -310,8 +312,8 @@ func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) { // Defines text symbol in operands[0]. if len(operands) > 0 { p.start(operands[0]) - if name, ok := p.funcAddress(); ok { - fmt.Fprintf(w, "def %s ABI0\n", name) + if name, abi, ok := p.funcAddress(); ok { + fmt.Fprintf(w, "def %s %s\n", name, abi) } } return @@ -329,8 +331,8 @@ func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) { // Search for symbol references. for _, op := range operands { p.start(op) - if name, ok := p.funcAddress(); ok { - fmt.Fprintf(w, "ref %s ABI0\n", name) + if name, abi, ok := p.funcAddress(); ok { + fmt.Fprintf(w, "ref %s %s\n", name, abi) } } } @@ -765,20 +767,19 @@ func (p *Parser) symbolReference(a *obj.Addr, name string, prefix rune) { case '*': a.Type = obj.TYPE_INDIR } - // Weirdness with statics: Might now have "<>". - isStatic := false - if p.peek() == '<' { - isStatic = true - p.next() - p.get('>') - } + + // Parse optional <> (indicates a static symbol) or + // (selecting text symbol with specific ABI). + doIssueError := true + isStatic, abi := p.symRefAttrs(name, doIssueError) + if p.peek() == '+' || p.peek() == '-' { a.Offset = int64(p.expr()) } if isStatic { a.Sym = p.ctxt.LookupStatic(name) } else { - a.Sym = p.ctxt.Lookup(name) + a.Sym = p.ctxt.LookupABI(name, abi) } if p.peek() == scanner.EOF { if prefix == 0 && p.isJump { @@ -823,12 +824,60 @@ func (p *Parser) setPseudoRegister(addr *obj.Addr, reg string, isStatic bool, pr } } +// symRefAttrs parses an optional function symbol attribute clause for +// the function symbol 'name', logging an error for a malformed +// attribute clause if 'issueError' is true. The return value is a +// (boolean, ABI) pair indicating that the named symbol is either +// static or a particular ABI specification. +// +// The expected form of the attribute clause is: +// +// empty, yielding (false, obj.ABI0) +// "<>", yielding (true, obj.ABI0) +// "" yielding (false, obj.ABI0) +// "" yielding (false, obj.ABIInternal) +// +// Anything else beginning with "<" logs an error if issueError is +// true, otherwise returns (false, obj.ABI0). +// +func (p *Parser) symRefAttrs(name string, issueError bool) (bool, obj.ABI) { + abi := obj.ABI0 + isStatic := false + if p.peek() != '<' { + return isStatic, abi + } + p.next() + tok := p.peek() + if tok == '>' { + isStatic = true + } else if tok == scanner.Ident { + abistr := p.get(scanner.Ident).String() + if !p.compilingRuntime { + if issueError { + p.errorf("ABI selector only permitted when compiling runtime, reference was to %q", name) + } + } else { + theabi, valid := obj.ParseABI(abistr) + if !valid { + if issueError { + p.errorf("malformed ABI selector %q in reference to %q", + abistr, name) + } + } else { + abi = theabi + } + } + } + p.get('>') + return isStatic, abi +} + // funcAddress parses an external function address. This is a // constrained form of the operand syntax that's always SB-based, // non-static, and has at most a simple integer offset: // -// [$|*]sym[+Int](SB) -func (p *Parser) funcAddress() (string, bool) { +// [$|*]sym[][+Int](SB) +func (p *Parser) funcAddress() (string, obj.ABI, bool) { switch p.peek() { case '$', '*': // Skip prefix. @@ -838,25 +887,32 @@ func (p *Parser) funcAddress() (string, bool) { tok := p.next() name := tok.String() if tok.ScanToken != scanner.Ident || p.atStartOfRegister(name) { - return "", false + return "", obj.ABI0, false + } + // Parse optional <> (indicates a static symbol) or + // (selecting text symbol with specific ABI). + noErrMsg := false + isStatic, abi := p.symRefAttrs(name, noErrMsg) + if isStatic { + return "", obj.ABI0, false // This function rejects static symbols. } tok = p.next() if tok.ScanToken == '+' { if p.next().ScanToken != scanner.Int { - return "", false + return "", obj.ABI0, false } tok = p.next() } if tok.ScanToken != '(' { - return "", false + return "", obj.ABI0, false } if reg := p.next(); reg.ScanToken != scanner.Ident || reg.String() != "SB" { - return "", false + return "", obj.ABI0, false } if p.next().ScanToken != ')' || p.peek() != scanner.EOF { - return "", false + return "", obj.ABI0, false } - return name, true + return name, abi, true } // registerIndirect parses the general form of a register indirection. diff --git a/src/cmd/asm/internal/asm/pseudo_test.go b/src/cmd/asm/internal/asm/pseudo_test.go index 100bef91cf..622ee25ce7 100644 --- a/src/cmd/asm/internal/asm/pseudo_test.go +++ b/src/cmd/asm/internal/asm/pseudo_test.go @@ -37,6 +37,7 @@ func TestErroneous(t *testing.T) { {"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467. {"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468. {"TEXT", "@B(SB),0,$0", "expected '(', found B"}, // Issue 23580. + {"TEXT", "foo(SB),0", "ABI selector only permitted when compiling runtime, reference was to \"foo\""}, {"FUNCDATA", "", "expect two operands for FUNCDATA"}, {"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"}, {"DATA", "", "expect two operands for DATA"}, diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index fd079a2ccd..01c963ac72 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -52,6 +52,7 @@ func main() { case "all", "ret": ctxt.Retpoline = true } + compilingRuntime := objabi.IsRuntimePackagePath(*flags.Importpath) ctxt.Bso = bufio.NewWriter(os.Stdout) defer ctxt.Bso.Flush() @@ -74,7 +75,7 @@ func main() { var failedFile string for _, f := range flag.Args() { lexer := lex.NewLexer(f) - parser := asm.NewParser(ctxt, architecture, lexer) + parser := asm.NewParser(ctxt, architecture, lexer, compilingRuntime) ctxt.DiagFunc = func(format string, args ...interface{}) { diag = true log.Printf(format, args...) diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index e4e4ce72fd..2fffe625cd 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -968,9 +968,10 @@ func readSymABIs(file, myimportpath string) { if len(parts) != 3 { log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0]) } - sym, abi := parts[1], parts[2] - if abi != "ABI0" { // Only supported external ABI right now - log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abi) + sym, abistr := parts[1], parts[2] + abi, valid := obj.ParseABI(abistr) + if !valid { + log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr) } // If the symbol is already prefixed with @@ -983,9 +984,9 @@ func readSymABIs(file, myimportpath string) { // Record for later. if parts[0] == "def" { - symabiDefs[sym] = obj.ABI0 + symabiDefs[sym] = abi } else { - symabiRefs[sym] = obj.ABI0 + symabiRefs[sym] = abi } default: log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0]) diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index ae85dbbe4e..ad4708138f 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -502,6 +502,20 @@ const ( ABICount ) +// ParseABI converts from a string representation in 'abistr' to the +// corresponding ABI value. Second return value is TRUE if the +// abi string is recognized, FALSE otherwise. +func ParseABI(abistr string) (ABI, bool) { + switch abistr { + default: + return ABI0, false + case "ABI0": + return ABI0, true + case "ABIInternal": + return ABIInternal, true + } +} + // Attribute is a set of symbol attributes. type Attribute uint32 diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index a30ccf0564..21e28807a6 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -210,13 +210,30 @@ func (ctxt *Link) CanReuseProgs() bool { return ctxt.Debugasm == 0 } +// Dconv accepts an argument 'a' within a prog 'p' and returns a string +// with a formatted version of the argument. func Dconv(p *Prog, a *Addr) string { buf := new(bytes.Buffer) - WriteDconv(buf, p, a) + writeDconv(buf, p, a, false) return buf.String() } +// DconvDconvWithABIDetail accepts an argument 'a' within a prog 'p' +// and returns a string with a formatted version of the argument, in +// which text symbols are rendered with explicit ABI selectors. +func DconvWithABIDetail(p *Prog, a *Addr) string { + buf := new(bytes.Buffer) + writeDconv(buf, p, a, true) + return buf.String() +} + +// WriteDconv accepts an argument 'a' within a prog 'p' +// and writes a formatted version of the arg to the writer. func WriteDconv(w io.Writer, p *Prog, a *Addr) { + writeDconv(w, p, a, false) +} + +func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) { switch a.Type { default: fmt.Fprintf(w, "type=%d", a.Type) @@ -250,7 +267,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) { case TYPE_BRANCH: if a.Sym != nil { - fmt.Fprintf(w, "%s(SB)", a.Sym.Name) + fmt.Fprintf(w, "%s%s(SB)", a.Sym.Name, abiDecorate(a, abiDetail)) } else if a.Target() != nil { fmt.Fprint(w, a.Target().Pc) } else { @@ -259,7 +276,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) { case TYPE_INDIR: io.WriteString(w, "*") - a.WriteNameTo(w) + a.writeNameTo(w, abiDetail) case TYPE_MEM: a.WriteNameTo(w) @@ -299,7 +316,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) { case TYPE_ADDR: io.WriteString(w, "$") - a.WriteNameTo(w) + a.writeNameTo(w, abiDetail) case TYPE_SHIFT: v := int(a.Offset) @@ -335,6 +352,11 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) { } func (a *Addr) WriteNameTo(w io.Writer) { + a.writeNameTo(w, false) +} + +func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) { + switch a.Name { default: fmt.Fprintf(w, "name=%d", a.Name) @@ -356,7 +378,7 @@ func (a *Addr) WriteNameTo(w io.Writer) { reg = Rconv(int(a.Reg)) } if a.Sym != nil { - fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) + fmt.Fprintf(w, "%s%s%s(%s)", a.Sym.Name, abiDecorate(a, abiDetail), offConv(a.Offset), reg) } else { fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) } @@ -596,3 +618,10 @@ func Bool2int(b bool) int { } return i } + +func abiDecorate(a *Addr, abiDetail bool) string { + if !abiDetail || a.Sym == nil { + return "" + } + return fmt.Sprintf("<%s>", a.Sym.ABI()) +} diff --git a/src/cmd/internal/objabi/path.go b/src/cmd/internal/objabi/path.go index 2a42179a36..fd1c9981c6 100644 --- a/src/cmd/internal/objabi/path.go +++ b/src/cmd/internal/objabi/path.go @@ -39,3 +39,25 @@ func PathToPrefix(s string) string { return string(p) } + +// IsRuntimePackagePath examines 'pkgpath' and returns TRUE if it +// belongs to the collection of "runtime-related" packages, including +// "runtime" itself, "reflect", "syscall", and the +// "runtime/internal/*" packages. The compiler and/or assembler in +// some cases need to be aware of when they are building such a +// package, for example to enable features such as ABI selectors in +// assembly sources. +func IsRuntimePackagePath(pkgpath string) bool { + rval := false + switch pkgpath { + case "runtime": + rval = true + case "reflect": + rval = true + case "syscall": + rval = true + default: + rval = strings.HasPrefix(pkgpath, "runtime/internal") + } + return rval +} -- cgit v1.3 From 178b0f59ff0d8be6d41d0f4099d16bfd3bc21ea7 Mon Sep 17 00:00:00 2001 From: hk Date: Mon, 19 Oct 2020 12:13:25 +0000 Subject: cmd/compile/internal/gc: remove an unused method Change-Id: Ib496d1e9a47d2cf69509ffd0009038b7d34a0149 GitHub-Last-Rev: 5b5589dd03e1d781e4cedc643d5e8a7f8bda2139 GitHub-Pull-Request: golang/go#42060 Reviewed-on: https://go-review.googlesource.com/c/go/+/263618 Run-TryBot: Robert Griesemer Reviewed-by: Matthew Dempsky Trust: Matthew Dempsky Trust: Robert Griesemer TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/noder.go | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 68d0327cdb..85e710086a 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -1438,11 +1438,6 @@ func (p *noder) mkname(name *syntax.Name) *Node { return mkname(p.name(name)) } -func (p *noder) newname(name *syntax.Name) *Node { - // TODO(mdempsky): Set line number? - return newname(p.name(name)) -} - func (p *noder) wrapname(n syntax.Node, x *Node) *Node { // These nodes do not carry line numbers. // Introduce a wrapper node to give them the correct line. -- cgit v1.3 From c216ae80c965acb9641d94d5f58c206bd0cf7d66 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 19 Oct 2020 14:14:40 -0700 Subject: cmd/compile: fix ICE in reporting of invalid recursive types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit asNode(t.Nod).Name.Param will be nil for builtin types (i.e., the universal predeclared types and unsafe.Pointer). These types can't be part of a cycle anyway, so we can just skip them. Fixes #42075. Change-Id: Ic7a44de65c6bfd16936545dee25e36de8850acf3 Reviewed-on: https://go-review.googlesource.com/c/go/+/263717 Trust: Matthew Dempsky Trust: Daniel Martí Reviewed-by: Daniel Martí --- src/cmd/compile/internal/gc/align.go | 2 +- test/fixedbugs/issue42075.go | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/fixedbugs/issue42075.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index 5af403afa3..4bc454df22 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -199,7 +199,7 @@ func findTypeLoop(t *types.Type, path *[]*types.Type) bool { } *path = append(*path, t) - if findTypeLoop(asNode(t.Nod).Name.Param.Ntype.Type, path) { + if p := asNode(t.Nod).Name.Param; p != nil && findTypeLoop(p.Ntype.Type, path) { return true } *path = (*path)[:len(*path)-1] diff --git a/test/fixedbugs/issue42075.go b/test/fixedbugs/issue42075.go new file mode 100644 index 0000000000..af85fb281d --- /dev/null +++ b/test/fixedbugs/issue42075.go @@ -0,0 +1,16 @@ +// errorcheck + +// Copyright 2020 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 p + +import "unsafe" + +type T struct { // ERROR "recursive type" + x int + p unsafe.Pointer + + f T +} -- cgit v1.3 From 70cad2744b57691962645681722ce756e81791c1 Mon Sep 17 00:00:00 2001 From: hk Date: Mon, 19 Oct 2020 12:11:13 +0000 Subject: cmd/compile/internal/gc: use plural nouns in comments Change-Id: I99a5b5b88495cfaef16d41ee4b962c5d0b3488aa GitHub-Last-Rev: fa0d895b3a87e45f499f02651a5f0e4946124765 GitHub-Pull-Request: golang/go#42059 Reviewed-on: https://go-review.googlesource.com/c/go/+/263617 Reviewed-by: Keith Randall Trust: Keith Randall Trust: Robert Griesemer --- src/cmd/compile/internal/gc/go.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 2fbdf71055..ee2add3733 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -61,12 +61,12 @@ type Class uint8 //go:generate stringer -type=Class const ( Pxxx Class = iota // no class; used during ssa conversion to indicate pseudo-variables - PEXTERN // global variable + PEXTERN // global variables PAUTO // local variables - PAUTOHEAP // local variable or parameter moved to heap + PAUTOHEAP // local variables or parameters moved to heap PPARAM // input arguments PPARAMOUT // output results - PFUNC // global function + PFUNC // global functions // Careful: Class is stored in three bits in Node.flags. _ = uint((1 << 3) - iota) // static assert for iota <= (1 << 3) -- cgit v1.3 From 8fe372c7b36b4d078c871a26e10b427c41275ecd Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Mon, 19 Oct 2020 13:09:55 -0700 Subject: cmd/compile: allowing inlining of functions with OCALLPART OCALLPART is exported in its original form, which is as an OXDOT. The body of the method value wrapper created in makepartialcall() was not being typechecked, and that was causing a problem during escape analysis, so I added code to typecheck the body. The go executable got slightly bigger with this change (13598111 -> 13598905), because of extra exported methods with OCALLPART (I believe), while the text size got slightly smaller (9686964 -> 9686643). This is mainly part of the work to make sure all function bodies can be exported (for purposes of generics), but might as well fix the OCALLPART inlining bug as well. Fixes #18493 Change-Id: If7aa055ff78ed7a6330c6a1e22f836ec567d04fd Reviewed-on: https://go-review.googlesource.com/c/go/+/263620 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/closure.go | 6 ++++++ src/cmd/compile/internal/gc/iexport.go | 9 +++++++-- src/cmd/compile/internal/gc/iimport.go | 2 +- src/cmd/compile/internal/gc/inl.go | 6 ++++-- test/inline.go | 17 +++++++++++++++++ 5 files changed, 35 insertions(+), 5 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index 250be38e5b..5d1012111f 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -434,6 +434,8 @@ func typecheckpartialcall(fn *Node, sym *types.Sym) { fn.Type = xfunc.Type } +// makepartialcall returns a DCLFUNC node representing the wrapper function (*-fm) needed +// for partial calls. func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node { rcvrtype := fn.Left.Type sym := methodSymSuffix(rcvrtype, meth, "-fm") @@ -500,6 +502,10 @@ func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node { funcbody() xfunc = typecheck(xfunc, ctxStmt) + // Need to typecheck the body of the just-generated wrapper. + // typecheckslice() requires that Curfn is set when processing an ORETURN. + Curfn = xfunc + typecheckslice(xfunc.Nbody.Slice(), ctxStmt) sym.Def = asTypesNode(xfunc) xtop = append(xtop, xfunc) Curfn = savecurfn diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index df08a4a6c2..9bc1f64600 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -1266,8 +1266,13 @@ func (w *exportWriter) expr(n *Node) { // case OSTRUCTKEY: // unreachable - handled in case OSTRUCTLIT by elemList - // case OCALLPART: - // unimplemented - handled by default case + case OCALLPART: + // An OCALLPART is an OXDOT before type checking. + w.op(OXDOT) + w.pos(n.Pos) + w.expr(n.Left) + // Right node should be ONAME + w.selector(n.Right.Sym) case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH: w.op(OXDOT) diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index 5f107eeec7..107e96cc6a 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -866,7 +866,7 @@ func (r *importReader) node() *Node { // unreachable - handled in case OSTRUCTLIT by elemList // case OCALLPART: - // unimplemented + // unreachable - mapped to case OXDOT below by exporter // case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH: // unreachable - mapped to case OXDOT below by exporter diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index ba12cf40b5..55a14d378e 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -374,8 +374,10 @@ func (v *hairyVisitor) visit(n *Node) bool { v.reason = "call to recover" return true + case OCALLPART: + // OCALLPART is inlineable, but no extra cost to the budget + case OCLOSURE, - OCALLPART, ORANGE, OSELECT, OTYPESW, @@ -454,7 +456,7 @@ func inlcopy(n *Node) *Node { } m := n.copy() - if m.Func != nil { + if n.Op != OCALLPART && m.Func != nil { Fatalf("unexpected Func: %v", m) } m.Left = inlcopy(n.Left) diff --git a/test/inline.go b/test/inline.go index 0e41873de4..9b75bc5065 100644 --- a/test/inline.go +++ b/test/inline.go @@ -229,3 +229,20 @@ Loop: } } } + +// Issue #18493 - make sure we can do inlining of functions with a method value +type T1 struct{} + +func (a T1) meth(val int) int { // ERROR "can inline T1.meth" "inlining call to T1.meth" + return val + 5 +} + +func getMeth(t1 T1) func(int) int { // ERROR "can inline getMeth" + return t1.meth // ERROR "t1.meth escapes to heap" +} + +func ii() { // ERROR "can inline ii" + var t1 T1 + f := getMeth(t1) // ERROR "inlining call to getMeth" "t1.meth does not escape" + _ = f(3) +} -- cgit v1.3 From bccdd31252c9771ef2e8dae0402251163a081b56 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 19 Oct 2020 18:32:15 +0700 Subject: cmd/compile: use type position for error message in align.go This helps the compiler reports the right place where the type declared, instead of relying on global lineno, which maybe set to wrong value at the time the error is reported. Fixes #42058 Change-Id: I06d34aa9b0236d122f4a0d72e66675ded022baac Reviewed-on: https://go-review.googlesource.com/c/go/+/263597 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le Reviewed-by: Matthew Dempsky TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/align.go | 10 +++++----- src/go/types/stdlib_test.go | 2 ++ test/fixedbugs/issue42058a.go | 13 +++++++++++++ test/fixedbugs/issue42058b.go | 13 +++++++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 test/fixedbugs/issue42058a.go create mode 100644 test/fixedbugs/issue42058b.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index 4bc454df22..a3a0c8fce8 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -86,7 +86,7 @@ func expandiface(t *types.Type) { sort.Sort(methcmp(methods)) if int64(len(methods)) >= thearch.MAXWIDTH/int64(Widthptr) { - yyerror("interface too large") + yyerrorl(typePos(t), "interface too large") } for i, m := range methods { m.Offset = int64(i) * int64(Widthptr) @@ -150,7 +150,7 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 { maxwidth = 1<<31 - 1 } if o >= maxwidth { - yyerror("type %L too large", errtype) + yyerrorl(typePos(errtype), "type %L too large", errtype) o = 8 // small but nonzero } } @@ -381,7 +381,7 @@ func dowidth(t *types.Type) { t1 := t.ChanArgs() dowidth(t1) // just in case if t1.Elem().Width >= 1<<16 { - yyerror("channel element type too large (>64kB)") + yyerrorl(typePos(t1), "channel element type too large (>64kB)") } w = 1 // anything will do @@ -414,7 +414,7 @@ func dowidth(t *types.Type) { if t.Elem().Width != 0 { cap := (uint64(thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width) if uint64(t.NumElem()) > cap { - yyerror("type %L larger than address space", t) + yyerrorl(typePos(t), "type %L larger than address space", t) } } w = t.NumElem() * t.Elem().Width @@ -456,7 +456,7 @@ func dowidth(t *types.Type) { } if Widthptr == 4 && w != int64(int32(w)) { - yyerror("type %v too large", t) + yyerrorl(typePos(t), "type %v too large", t) } t.Width = w diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go index f5a3273fa1..669e7bec20 100644 --- a/src/go/types/stdlib_test.go +++ b/src/go/types/stdlib_test.go @@ -183,6 +183,8 @@ func TestStdFixed(t *testing.T) { "issue31747.go", // go/types does not have constraints on language level (-lang=go1.12) (see #31793) "issue34329.go", // go/types does not have constraints on language level (-lang=go1.13) (see #31793) "bug251.go", // issue #34333 which was exposed with fix for #34151 + "issue42058a.go", // go/types does not have constraints on channel element size + "issue42058b.go", // go/types does not have constraints on channel element size ) } diff --git a/test/fixedbugs/issue42058a.go b/test/fixedbugs/issue42058a.go new file mode 100644 index 0000000000..67751a1b0c --- /dev/null +++ b/test/fixedbugs/issue42058a.go @@ -0,0 +1,13 @@ +// errorcheck + +// Copyright 2020 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 p + +var c chan [2 << 16]byte // ERROR "channel element type too large" + +type T [1 << 17]byte + +var x chan T // ERROR "channel element type too large" diff --git a/test/fixedbugs/issue42058b.go b/test/fixedbugs/issue42058b.go new file mode 100644 index 0000000000..03f86ee1b1 --- /dev/null +++ b/test/fixedbugs/issue42058b.go @@ -0,0 +1,13 @@ +// errorcheck + +// Copyright 2020 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 p + +var c chan [2 << 16]byte // ERROR "channel element type too large" + +func f() { + _ = 42 +} -- cgit v1.3 From f121e0eddd64aa0084b916e94c9a540edf859539 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Tue, 20 Oct 2020 15:03:33 -0700 Subject: cmd/compile: fix nodedump output for types of nodes The Dbg dumping of complex types was broken, because (I think) of a recent change to handle recursive types correctly. Before this fix, the Dump output of a closure node (where the last thing on the line is the type of the node) was: . . CLOSURE l(8) esc(h) tc(1) FUNC-@0 after this change it is: . . CLOSURE l(8) esc(h) tc(1) FUNC-func(int) int The problem is that that the 'mode == Fdbg' code was immediately aborting the descent into tconv2, since it was calling down with the same node that was just entered into the hash table. Change-Id: Iee106b967cea1856dd92d4350681401dd34a23b3 Reviewed-on: https://go-review.googlesource.com/c/go/+/264025 Trust: Dan Scales Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/fmt.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 9ba1789633..d7ed1d2ff0 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -792,6 +792,13 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited return } + if mode == FDbg { + b.WriteString(t.Etype.String()) + b.WriteByte('-') + tconv2(b, t, flag, FErr, visited) + return + } + // At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't // try to print it recursively. // We record the offset in the result buffer where the type's text starts. This offset serves as a reference @@ -805,12 +812,6 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited visited[t] = b.Len() defer delete(visited, t) - if mode == FDbg { - b.WriteString(t.Etype.String()) - b.WriteByte('-') - tconv2(b, t, flag, FErr, visited) - return - } switch t.Etype { case TPTR: b.WriteByte('*') -- cgit v1.3 From 15ead857dbc638b9d83a7686acf0dc746fc45918 Mon Sep 17 00:00:00 2001 From: "Paul E. Murphy" Date: Wed, 9 Sep 2020 17:24:23 -0500 Subject: cmd/compiler,cmd/go,sync: add internal {LoadAcq,StoreRel}64 on ppc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an internal atomic intrinsic for load with acquire semantics (extending LoadAcq to 64b) and add LoadAcquintptr for internal use within the sync package. For other arches, this remaps to the appropriate atomic.Load{,64} intrinsic which should not alter code generation. Similarly, add StoreRel{uintptr,64} for consistency, and inline. Finally, add an exception to allow sync to directly use the runtime/internal/atomic package which avoids more convoluted workarounds (contributed by Lynn Boger). In an extreme example, sync.(*Pool).pin consumes 20% of wall time during fmt tests. This is reduced to 5% on ppc64le/power9. From the fmt benchmarks on ppc64le: name old time/op new time/op delta SprintfPadding 468ns ± 0% 451ns ± 0% -3.63% SprintfEmpty 73.3ns ± 0% 51.9ns ± 0% -29.20% SprintfString 135ns ± 0% 122ns ± 0% -9.63% SprintfTruncateString 232ns ± 0% 214ns ± 0% -7.76% SprintfTruncateBytes 216ns ± 0% 202ns ± 0% -6.48% SprintfSlowParsingPath 162ns ± 0% 142ns ± 0% -12.35% SprintfQuoteString 1.00µs ± 0% 0.99µs ± 0% -1.39% SprintfInt 117ns ± 0% 104ns ± 0% -11.11% SprintfIntInt 190ns ± 0% 175ns ± 0% -7.89% SprintfPrefixedInt 232ns ± 0% 212ns ± 0% -8.62% SprintfFloat 270ns ± 0% 255ns ± 0% -5.56% SprintfComplex 1.01µs ± 0% 0.99µs ± 0% -1.68% SprintfBoolean 127ns ± 0% 111ns ± 0% -12.60% SprintfHexString 220ns ± 0% 198ns ± 0% -10.00% SprintfHexBytes 261ns ± 0% 252ns ± 0% -3.45% SprintfBytes 600ns ± 0% 590ns ± 0% -1.67% SprintfStringer 684ns ± 0% 658ns ± 0% -3.80% SprintfStructure 2.57µs ± 0% 2.57µs ± 0% -0.12% ManyArgs 669ns ± 0% 646ns ± 0% -3.44% FprintInt 140ns ± 0% 136ns ± 0% -2.86% FprintfBytes 184ns ± 0% 181ns ± 0% -1.63% FprintIntNoAlloc 140ns ± 0% 136ns ± 0% -2.86% ScanInts 929µs ± 0% 921µs ± 0% -0.79% ScanRecursiveInt 122ms ± 0% 121ms ± 0% -0.11% ScanRecursiveIntReaderWrapper 122ms ± 0% 122ms ± 0% -0.18% Change-Id: I4d66780261b57b06ef600229e475462e7313f0d6 Reviewed-on: https://go-review.googlesource.com/c/go/+/253748 Run-TryBot: Lynn Boger Reviewed-by: Lynn Boger Reviewed-by: Keith Randall Trust: Lynn Boger TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/ssa.go | 19 ++++++++++++++ src/cmd/compile/internal/ssa/branchelim.go | 2 +- src/cmd/compile/internal/ssa/gen/PPC64.rules | 4 +-- src/cmd/compile/internal/ssa/gen/genericOps.go | 2 ++ src/cmd/compile/internal/ssa/opGen.go | 13 ++++++++++ src/cmd/compile/internal/ssa/rewritePPC64.go | 34 ++++++++++++++++++++++++++ src/cmd/go/internal/load/pkg.go | 5 ++++ src/runtime/internal/atomic/asm_386.s | 3 +++ src/runtime/internal/atomic/asm_amd64.s | 6 +++++ src/runtime/internal/atomic/asm_arm.s | 6 +++++ src/runtime/internal/atomic/asm_mips64x.s | 6 +++++ src/runtime/internal/atomic/asm_mipsx.s | 3 +++ src/runtime/internal/atomic/asm_ppc64x.s | 13 ++++++++++ src/runtime/internal/atomic/atomic_386.go | 9 +++++++ src/runtime/internal/atomic/atomic_amd64.go | 18 ++++++++++++++ src/runtime/internal/atomic/atomic_arm.go | 6 +++++ src/runtime/internal/atomic/atomic_arm64.go | 12 +++++++++ src/runtime/internal/atomic/atomic_arm64.s | 14 +++++++++++ src/runtime/internal/atomic/atomic_mips64x.go | 12 +++++++++ src/runtime/internal/atomic/atomic_mips64x.s | 8 ++++++ src/runtime/internal/atomic/atomic_mipsx.go | 6 +++++ src/runtime/internal/atomic/atomic_ppc64x.go | 12 +++++++++ src/runtime/internal/atomic/atomic_ppc64x.s | 20 +++++++++++++++ src/runtime/internal/atomic/atomic_riscv64.go | 12 +++++++++ src/runtime/internal/atomic/atomic_riscv64.s | 12 +++++++++ src/runtime/internal/atomic/atomic_s390x.go | 24 ++++++++++++++++++ src/runtime/internal/atomic/atomic_wasm.go | 24 ++++++++++++++++++ src/sync/pool.go | 15 ++++++------ 28 files changed, 310 insertions(+), 10 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index d8f627c213..e1455d2c3f 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3389,6 +3389,13 @@ func init() { return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v) }, sys.PPC64, sys.S390X) + addF("runtime/internal/atomic", "LoadAcq64", + func(s *state, n *Node, args []*ssa.Value) *ssa.Value { + v := s.newValue2(ssa.OpAtomicLoadAcq64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], s.mem()) + s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) + return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v) + }, + sys.PPC64) addF("runtime/internal/atomic", "Loadp", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { v := s.newValue2(ssa.OpAtomicLoadPtr, types.NewTuple(s.f.Config.Types.BytePtr, types.TypeMem), args[0], s.mem()) @@ -3427,6 +3434,12 @@ func init() { return nil }, sys.PPC64, sys.S390X) + addF("runtime/internal/atomic", "StoreRel64", + func(s *state, n *Node, args []*ssa.Value) *ssa.Value { + s.vars[&memVar] = s.newValue3(ssa.OpAtomicStoreRel64, types.TypeMem, args[0], args[1], s.mem()) + return nil + }, + sys.PPC64) addF("runtime/internal/atomic", "Xchg", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { @@ -3542,9 +3555,15 @@ func init() { alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load", p4...) alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load64", p8...) alias("runtime/internal/atomic", "LoadAcq", "runtime/internal/atomic", "Load", lwatomics...) + alias("runtime/internal/atomic", "LoadAcq64", "runtime/internal/atomic", "Load64", lwatomics...) + alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...) + alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...) alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store", p4...) alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store64", p8...) alias("runtime/internal/atomic", "StoreRel", "runtime/internal/atomic", "Store", lwatomics...) + alias("runtime/internal/atomic", "StoreRel64", "runtime/internal/atomic", "Store64", lwatomics...) + alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...) + alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...) alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg", p4...) alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg64", p8...) alias("runtime/internal/atomic", "Xadduintptr", "runtime/internal/atomic", "Xadd", p4...) diff --git a/src/cmd/compile/internal/ssa/branchelim.go b/src/cmd/compile/internal/ssa/branchelim.go index 4f9fd8e22e..1d34f8160b 100644 --- a/src/cmd/compile/internal/ssa/branchelim.go +++ b/src/cmd/compile/internal/ssa/branchelim.go @@ -35,7 +35,7 @@ func branchelim(f *Func) { for _, b := range f.Blocks { for _, v := range b.Values { switch v.Op { - case OpLoad, OpAtomicLoad8, OpAtomicLoad32, OpAtomicLoad64, OpAtomicLoadPtr, OpAtomicLoadAcq32: + case OpLoad, OpAtomicLoad8, OpAtomicLoad32, OpAtomicLoad64, OpAtomicLoadPtr, OpAtomicLoadAcq32, OpAtomicLoadAcq64: loadAddr.add(v.Args[0].ID) case OpMove: loadAddr.add(v.Args[1].ID) diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules index a05cfee654..11b1a318fe 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64.rules +++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules @@ -967,10 +967,10 @@ // atomic intrinsics (AtomicLoad(8|32|64|Ptr) ptr mem) => (LoweredAtomicLoad(8|32|64|Ptr) [1] ptr mem) -(AtomicLoadAcq32 ptr mem) => (LoweredAtomicLoad32 [0] ptr mem) +(AtomicLoadAcq(32|64) ptr mem) => (LoweredAtomicLoad(32|64) [0] ptr mem) (AtomicStore(8|32|64) ptr val mem) => (LoweredAtomicStore(8|32|64) [1] ptr val mem) -(AtomicStoreRel32 ptr val mem) => (LoweredAtomicStore32 [0] ptr val mem) +(AtomicStoreRel(32|64) ptr val mem) => (LoweredAtomicStore(32|64) [0] ptr val mem) //(AtomicStorePtrNoWB ptr val mem) => (STLR ptr val mem) (AtomicExchange(32|64) ...) => (LoweredAtomicExchange(32|64) ...) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 85839303c5..12ba9f1fc9 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -550,11 +550,13 @@ var genericOps = []opData{ {name: "AtomicLoad64", argLength: 2, typ: "(UInt64,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory. {name: "AtomicLoadPtr", argLength: 2, typ: "(BytePtr,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory. {name: "AtomicLoadAcq32", argLength: 2, typ: "(UInt32,Mem)"}, // Load from arg0. arg1=memory. Lock acquisition, returns loaded value and new memory. + {name: "AtomicLoadAcq64", argLength: 2, typ: "(UInt64,Mem)"}, // Load from arg0. arg1=memory. Lock acquisition, returns loaded value and new memory. {name: "AtomicStore8", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns memory. {name: "AtomicStore32", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns memory. {name: "AtomicStore64", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns memory. {name: "AtomicStorePtrNoWB", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns memory. {name: "AtomicStoreRel32", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Lock release, returns memory. + {name: "AtomicStoreRel64", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Lock release, returns memory. {name: "AtomicExchange32", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory. {name: "AtomicExchange64", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory. {name: "AtomicAdd32", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory. diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 051550fb17..eae30c79ba 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -2839,11 +2839,13 @@ const ( OpAtomicLoad64 OpAtomicLoadPtr OpAtomicLoadAcq32 + OpAtomicLoadAcq64 OpAtomicStore8 OpAtomicStore32 OpAtomicStore64 OpAtomicStorePtrNoWB OpAtomicStoreRel32 + OpAtomicStoreRel64 OpAtomicExchange32 OpAtomicExchange64 OpAtomicAdd32 @@ -35429,6 +35431,11 @@ var opcodeTable = [...]opInfo{ argLen: 2, generic: true, }, + { + name: "AtomicLoadAcq64", + argLen: 2, + generic: true, + }, { name: "AtomicStore8", argLen: 3, @@ -35459,6 +35466,12 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, generic: true, }, + { + name: "AtomicStoreRel64", + argLen: 3, + hasSideEffects: true, + generic: true, + }, { name: "AtomicExchange32", argLen: 3, diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go index 1b8a5a78ca..a820bc0c4e 100644 --- a/src/cmd/compile/internal/ssa/rewritePPC64.go +++ b/src/cmd/compile/internal/ssa/rewritePPC64.go @@ -82,6 +82,8 @@ func rewriteValuePPC64(v *Value) bool { return rewriteValuePPC64_OpAtomicLoad8(v) case OpAtomicLoadAcq32: return rewriteValuePPC64_OpAtomicLoadAcq32(v) + case OpAtomicLoadAcq64: + return rewriteValuePPC64_OpAtomicLoadAcq64(v) case OpAtomicLoadPtr: return rewriteValuePPC64_OpAtomicLoadPtr(v) case OpAtomicOr8: @@ -95,6 +97,8 @@ func rewriteValuePPC64(v *Value) bool { return rewriteValuePPC64_OpAtomicStore8(v) case OpAtomicStoreRel32: return rewriteValuePPC64_OpAtomicStoreRel32(v) + case OpAtomicStoreRel64: + return rewriteValuePPC64_OpAtomicStoreRel64(v) case OpAvg64u: return rewriteValuePPC64_OpAvg64u(v) case OpBitLen32: @@ -930,6 +934,20 @@ func rewriteValuePPC64_OpAtomicLoadAcq32(v *Value) bool { return true } } +func rewriteValuePPC64_OpAtomicLoadAcq64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (AtomicLoadAcq64 ptr mem) + // result: (LoweredAtomicLoad64 [0] ptr mem) + for { + ptr := v_0 + mem := v_1 + v.reset(OpPPC64LoweredAtomicLoad64) + v.AuxInt = int64ToAuxInt(0) + v.AddArg2(ptr, mem) + return true + } +} func rewriteValuePPC64_OpAtomicLoadPtr(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -1008,6 +1026,22 @@ func rewriteValuePPC64_OpAtomicStoreRel32(v *Value) bool { return true } } +func rewriteValuePPC64_OpAtomicStoreRel64(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (AtomicStoreRel64 ptr val mem) + // result: (LoweredAtomicStore64 [0] ptr val mem) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpPPC64LoweredAtomicStore64) + v.AuxInt = int64ToAuxInt(0) + v.AddArg3(ptr, val, mem) + return true + } +} func rewriteValuePPC64_OpAvg64u(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index 2bdc08ba36..c9665265e9 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -1337,6 +1337,11 @@ func disallowInternal(srcDir string, importer *Package, importerPath string, p * return p } + // Allow sync package to access lightweight atomic functions limited to the runtime. + if p.Standard && strings.HasPrefix(importerPath, "sync") && p.ImportPath == "runtime/internal/atomic" { + return p + } + // Internal is present. // Map import path back to directory corresponding to parent of internal. if i > 0 { diff --git a/src/runtime/internal/atomic/asm_386.s b/src/runtime/internal/atomic/asm_386.s index bcefff373f..7ebf675ac5 100644 --- a/src/runtime/internal/atomic/asm_386.s +++ b/src/runtime/internal/atomic/asm_386.s @@ -189,6 +189,9 @@ TEXT ·Store(SB), NOSPLIT, $0-8 TEXT ·StoreRel(SB), NOSPLIT, $0-8 JMP ·Store(SB) +TEXT runtime∕internal∕atomic·StoreReluintptr(SB), NOSPLIT, $0-8 + JMP runtime∕internal∕atomic·Store(SB) + // uint64 atomicload64(uint64 volatile* addr); TEXT ·Load64(SB), NOSPLIT, $0-12 NO_LOCAL_POINTERS diff --git a/src/runtime/internal/atomic/asm_amd64.s b/src/runtime/internal/atomic/asm_amd64.s index 90c56424c9..80fb31285d 100644 --- a/src/runtime/internal/atomic/asm_amd64.s +++ b/src/runtime/internal/atomic/asm_amd64.s @@ -136,6 +136,12 @@ TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12 TEXT runtime∕internal∕atomic·StoreRel(SB), NOSPLIT, $0-12 JMP runtime∕internal∕atomic·Store(SB) +TEXT runtime∕internal∕atomic·StoreRel64(SB), NOSPLIT, $0-16 + JMP runtime∕internal∕atomic·Store64(SB) + +TEXT runtime∕internal∕atomic·StoreReluintptr(SB), NOSPLIT, $0-16 + JMP runtime∕internal∕atomic·Store64(SB) + TEXT runtime∕internal∕atomic·Store8(SB), NOSPLIT, $0-9 MOVQ ptr+0(FP), BX MOVB val+8(FP), AX diff --git a/src/runtime/internal/atomic/asm_arm.s b/src/runtime/internal/atomic/asm_arm.s index c3d1d9025d..274925ed60 100644 --- a/src/runtime/internal/atomic/asm_arm.s +++ b/src/runtime/internal/atomic/asm_arm.s @@ -57,6 +57,9 @@ TEXT ·Loadp(SB),NOSPLIT|NOFRAME,$0-8 TEXT ·LoadAcq(SB),NOSPLIT|NOFRAME,$0-8 B ·Load(SB) +TEXT ·LoadAcquintptr(SB),NOSPLIT|NOFRAME,$0-8 + B ·Load(SB) + TEXT ·Casuintptr(SB),NOSPLIT,$0-13 B ·Cas(SB) @@ -81,6 +84,9 @@ TEXT ·StorepNoWB(SB),NOSPLIT,$0-8 TEXT ·StoreRel(SB),NOSPLIT,$0-8 B ·Store(SB) +TEXT ·StoreReluintptr(SB),NOSPLIT,$0-8 + B ·Store(SB) + TEXT ·Xadduintptr(SB),NOSPLIT,$0-12 B ·Xadd(SB) diff --git a/src/runtime/internal/atomic/asm_mips64x.s b/src/runtime/internal/atomic/asm_mips64x.s index 3290fb726a..03fb822929 100644 --- a/src/runtime/internal/atomic/asm_mips64x.s +++ b/src/runtime/internal/atomic/asm_mips64x.s @@ -158,6 +158,12 @@ TEXT ·StorepNoWB(SB), NOSPLIT, $0-16 TEXT ·StoreRel(SB), NOSPLIT, $0-12 JMP ·Store(SB) +TEXT ·StoreRel64(SB), NOSPLIT, $0-16 + JMP ·Store64(SB) + +TEXT ·StoreReluintptr(SB), NOSPLIT, $0-16 + JMP ·Store64(SB) + TEXT ·Store(SB), NOSPLIT, $0-12 MOVV ptr+0(FP), R1 MOVW val+8(FP), R2 diff --git a/src/runtime/internal/atomic/asm_mipsx.s b/src/runtime/internal/atomic/asm_mipsx.s index 62811a6599..63bb548825 100644 --- a/src/runtime/internal/atomic/asm_mipsx.s +++ b/src/runtime/internal/atomic/asm_mipsx.s @@ -122,6 +122,9 @@ TEXT ·StorepNoWB(SB),NOSPLIT,$0-8 TEXT ·StoreRel(SB),NOSPLIT,$0-8 JMP ·Store(SB) +TEXT ·StoreReluintptr(SB),NOSPLIT,$0-8 + JMP ·Store(SB) + // void Or8(byte volatile*, byte); TEXT ·Or8(SB),NOSPLIT,$0-5 MOVW ptr+0(FP), R1 diff --git a/src/runtime/internal/atomic/asm_ppc64x.s b/src/runtime/internal/atomic/asm_ppc64x.s index 06dc931bf4..c0237de4d0 100644 --- a/src/runtime/internal/atomic/asm_ppc64x.s +++ b/src/runtime/internal/atomic/asm_ppc64x.s @@ -83,12 +83,18 @@ TEXT runtime∕internal∕atomic·Casuintptr(SB), NOSPLIT, $0-25 TEXT runtime∕internal∕atomic·Loaduintptr(SB), NOSPLIT|NOFRAME, $0-16 BR runtime∕internal∕atomic·Load64(SB) +TEXT runtime∕internal∕atomic·LoadAcquintptr(SB), NOSPLIT|NOFRAME, $0-16 + BR runtime∕internal∕atomic·LoadAcq64(SB) + TEXT runtime∕internal∕atomic·Loaduint(SB), NOSPLIT|NOFRAME, $0-16 BR runtime∕internal∕atomic·Load64(SB) TEXT runtime∕internal∕atomic·Storeuintptr(SB), NOSPLIT, $0-16 BR runtime∕internal∕atomic·Store64(SB) +TEXT runtime∕internal∕atomic·StoreReluintptr(SB), NOSPLIT, $0-16 + BR runtime∕internal∕atomic·StoreRel64(SB) + TEXT runtime∕internal∕atomic·Xadduintptr(SB), NOSPLIT, $0-24 BR runtime∕internal∕atomic·Xadd64(SB) @@ -191,6 +197,13 @@ TEXT runtime∕internal∕atomic·StoreRel(SB), NOSPLIT, $0-12 MOVW R4, 0(R3) RET +TEXT runtime∕internal∕atomic·StoreRel64(SB), NOSPLIT, $0-16 + MOVD ptr+0(FP), R3 + MOVD val+8(FP), R4 + LWSYNC + MOVD R4, 0(R3) + RET + // void runtime∕internal∕atomic·Or8(byte volatile*, byte); TEXT runtime∕internal∕atomic·Or8(SB), NOSPLIT, $0-9 MOVD ptr+0(FP), R3 diff --git a/src/runtime/internal/atomic/atomic_386.go b/src/runtime/internal/atomic/atomic_386.go index 8d002ebfe3..06ce6a5356 100644 --- a/src/runtime/internal/atomic/atomic_386.go +++ b/src/runtime/internal/atomic/atomic_386.go @@ -30,6 +30,12 @@ func LoadAcq(ptr *uint32) uint32 { return *ptr } +//go:nosplit +//go:noinline +func LoadAcquintptr(ptr *uintptr) uintptr { + return *ptr +} + //go:noescape func Xadd64(ptr *uint64, delta int64) uint64 @@ -83,5 +89,8 @@ func Store64(ptr *uint64, val uint64) //go:noescape func StoreRel(ptr *uint32, val uint32) +//go:noescape +func StoreReluintptr(ptr *uintptr, val uintptr) + // NO go:noescape annotation; see atomic_pointer.go. func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) diff --git a/src/runtime/internal/atomic/atomic_amd64.go b/src/runtime/internal/atomic/atomic_amd64.go index 14b8101720..1b71a16d94 100644 --- a/src/runtime/internal/atomic/atomic_amd64.go +++ b/src/runtime/internal/atomic/atomic_amd64.go @@ -35,6 +35,18 @@ func LoadAcq(ptr *uint32) uint32 { return *ptr } +//go:nosplit +//go:noinline +func LoadAcq64(ptr *uint64) uint64 { + return *ptr +} + +//go:nosplit +//go:noinline +func LoadAcquintptr(ptr *uintptr) uintptr { + return *ptr +} + //go:noescape func Xadd(ptr *uint32, delta int32) uint32 @@ -85,6 +97,12 @@ func Store64(ptr *uint64, val uint64) //go:noescape func StoreRel(ptr *uint32, val uint32) +//go:noescape +func StoreRel64(ptr *uint64, val uint64) + +//go:noescape +func StoreReluintptr(ptr *uintptr, val uintptr) + // StorepNoWB performs *ptr = val atomically and without a write // barrier. // diff --git a/src/runtime/internal/atomic/atomic_arm.go b/src/runtime/internal/atomic/atomic_arm.go index 95713afcc1..67d529c1cb 100644 --- a/src/runtime/internal/atomic/atomic_arm.go +++ b/src/runtime/internal/atomic/atomic_arm.go @@ -81,6 +81,9 @@ func Store(addr *uint32, v uint32) //go:noescape func StoreRel(addr *uint32, v uint32) +//go:noescape +func StoreReluintptr(addr *uintptr, v uintptr) + //go:nosplit func goCas64(addr *uint64, old, new uint64) bool { if uintptr(unsafe.Pointer(addr))&7 != 0 { @@ -194,6 +197,9 @@ func Load8(addr *uint8) uint8 //go:noescape func LoadAcq(addr *uint32) uint32 +//go:noescape +func LoadAcquintptr(ptr *uintptr) uintptr + //go:noescape func Cas64(addr *uint64, old, new uint64) bool diff --git a/src/runtime/internal/atomic/atomic_arm64.go b/src/runtime/internal/atomic/atomic_arm64.go index 26ca94d54c..c9b4322fe9 100644 --- a/src/runtime/internal/atomic/atomic_arm64.go +++ b/src/runtime/internal/atomic/atomic_arm64.go @@ -41,6 +41,12 @@ func Loadp(ptr unsafe.Pointer) unsafe.Pointer //go:noescape func LoadAcq(addr *uint32) uint32 +//go:noescape +func LoadAcq64(ptr *uint64) uint64 + +//go:noescape +func LoadAcquintptr(ptr *uintptr) uintptr + //go:noescape func Or8(ptr *uint8, val uint8) @@ -67,3 +73,9 @@ func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) //go:noescape func StoreRel(ptr *uint32, val uint32) + +//go:noescape +func StoreRel64(ptr *uint64, val uint64) + +//go:noescape +func StoreReluintptr(ptr *uintptr, val uintptr) diff --git a/src/runtime/internal/atomic/atomic_arm64.s b/src/runtime/internal/atomic/atomic_arm64.s index a2eb7568d2..36c7698b18 100644 --- a/src/runtime/internal/atomic/atomic_arm64.s +++ b/src/runtime/internal/atomic/atomic_arm64.s @@ -36,12 +36,26 @@ TEXT ·Loadp(SB),NOSPLIT,$0-16 TEXT ·LoadAcq(SB),NOSPLIT,$0-12 B ·Load(SB) +// uint64 runtime∕internal∕atomic·LoadAcquintptr(uint64 volatile* addr) +TEXT ·LoadAcq64(SB),NOSPLIT,$0-16 + B ·Load64(SB) + +// uintptr runtime∕internal∕atomic·LoadAcq64(uintptr volatile* addr) +TEXT ·LoadAcquintptr(SB),NOSPLIT,$0-16 + B ·Load64(SB) + TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-16 B runtime∕internal∕atomic·Store64(SB) TEXT runtime∕internal∕atomic·StoreRel(SB), NOSPLIT, $0-12 B runtime∕internal∕atomic·Store(SB) +TEXT runtime∕internal∕atomic·StoreRel64(SB), NOSPLIT, $0-16 + B runtime∕internal∕atomic·Store64(SB) + +TEXT runtime∕internal∕atomic·StoreReluintptr(SB), NOSPLIT, $0-16 + B runtime∕internal∕atomic·Store64(SB) + TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12 MOVD ptr+0(FP), R0 MOVW val+8(FP), R1 diff --git a/src/runtime/internal/atomic/atomic_mips64x.go b/src/runtime/internal/atomic/atomic_mips64x.go index 1d9977850b..fca2242514 100644 --- a/src/runtime/internal/atomic/atomic_mips64x.go +++ b/src/runtime/internal/atomic/atomic_mips64x.go @@ -41,6 +41,12 @@ func Loadp(ptr unsafe.Pointer) unsafe.Pointer //go:noescape func LoadAcq(ptr *uint32) uint32 +//go:noescape +func LoadAcq64(ptr *uint64) uint64 + +//go:noescape +func LoadAcquintptr(ptr *uintptr) uintptr + //go:noescape func And8(ptr *uint8, val uint8) @@ -69,3 +75,9 @@ func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) //go:noescape func StoreRel(ptr *uint32, val uint32) + +//go:noescape +func StoreRel64(ptr *uint64, val uint64) + +//go:noescape +func StoreReluintptr(ptr *uintptr, val uintptr) diff --git a/src/runtime/internal/atomic/atomic_mips64x.s b/src/runtime/internal/atomic/atomic_mips64x.s index 1ed90937c9..125c0c221c 100644 --- a/src/runtime/internal/atomic/atomic_mips64x.s +++ b/src/runtime/internal/atomic/atomic_mips64x.s @@ -47,3 +47,11 @@ TEXT ·Loadp(SB),NOSPLIT|NOFRAME,$0-16 // uint32 runtime∕internal∕atomic·LoadAcq(uint32 volatile* ptr) TEXT ·LoadAcq(SB),NOSPLIT|NOFRAME,$0-12 JMP atomic·Load(SB) + +// uint64 runtime∕internal∕atomic·LoadAcq64(uint64 volatile* ptr) +TEXT ·LoadAcq64(SB),NOSPLIT|NOFRAME,$0-16 + JMP atomic·Load64(SB) + +// uintptr runtime∕internal∕atomic·LoadAcquintptr(uintptr volatile* ptr) +TEXT ·LoadAcquintptr(SB),NOSPLIT|NOFRAME,$0-16 + JMP atomic·Load64(SB) diff --git a/src/runtime/internal/atomic/atomic_mipsx.go b/src/runtime/internal/atomic/atomic_mipsx.go index b99bfe7dbf..be1e6a038b 100644 --- a/src/runtime/internal/atomic/atomic_mipsx.go +++ b/src/runtime/internal/atomic/atomic_mipsx.go @@ -132,6 +132,9 @@ func Loadp(ptr unsafe.Pointer) unsafe.Pointer //go:noescape func LoadAcq(ptr *uint32) uint32 +//go:noescape +func LoadAcquintptr(ptr *uintptr) uintptr + //go:noescape func And8(ptr *uint8, val uint8) @@ -150,5 +153,8 @@ func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) //go:noescape func StoreRel(ptr *uint32, val uint32) +//go:noescape +func StoreReluintptr(ptr *uintptr, val uintptr) + //go:noescape func CasRel(addr *uint32, old, new uint32) bool diff --git a/src/runtime/internal/atomic/atomic_ppc64x.go b/src/runtime/internal/atomic/atomic_ppc64x.go index a48ecf5ee8..e759bb27a2 100644 --- a/src/runtime/internal/atomic/atomic_ppc64x.go +++ b/src/runtime/internal/atomic/atomic_ppc64x.go @@ -41,6 +41,12 @@ func Loadp(ptr unsafe.Pointer) unsafe.Pointer //go:noescape func LoadAcq(ptr *uint32) uint32 +//go:noescape +func LoadAcq64(ptr *uint64) uint64 + +//go:noescape +func LoadAcquintptr(ptr *uintptr) uintptr + //go:noescape func And8(ptr *uint8, val uint8) @@ -67,5 +73,11 @@ func Store64(ptr *uint64, val uint64) //go:noescape func StoreRel(ptr *uint32, val uint32) +//go:noescape +func StoreRel64(ptr *uint64, val uint64) + +//go:noescape +func StoreReluintptr(ptr *uintptr, val uintptr) + // NO go:noescape annotation; see atomic_pointer.go. func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) diff --git a/src/runtime/internal/atomic/atomic_ppc64x.s b/src/runtime/internal/atomic/atomic_ppc64x.s index c2f696fb34..b79cdbca34 100644 --- a/src/runtime/internal/atomic/atomic_ppc64x.s +++ b/src/runtime/internal/atomic/atomic_ppc64x.s @@ -6,6 +6,15 @@ #include "textflag.h" + +// For more details about how various memory models are +// enforced on POWER, the following paper provides more +// details about how they enforce C/C++ like models. This +// gives context about why the strange looking code +// sequences below work. +// +// http://www.rdrop.com/users/paulmck/scalability/paper/N2745r.2011.03.04a.html + // uint32 runtime∕internal∕atomic·Load(uint32 volatile* ptr) TEXT ·Load(SB),NOSPLIT|NOFRAME,$-8-12 MOVD ptr+0(FP), R3 @@ -56,5 +65,16 @@ TEXT ·LoadAcq(SB),NOSPLIT|NOFRAME,$-8-12 MOVWZ 0(R3), R3 CMPW R3, R3, CR7 BC 4, 30, 1(PC) // bne- cr7, 0x4 + ISYNC MOVW R3, ret+8(FP) RET + +// uint64 runtime∕internal∕atomic·LoadAcq64(uint64 volatile* ptr) +TEXT ·LoadAcq64(SB),NOSPLIT|NOFRAME,$-8-16 + MOVD ptr+0(FP), R3 + MOVD 0(R3), R3 + CMP R3, R3, CR7 + BC 4, 30, 1(PC) // bne- cr7, 0x4 + ISYNC + MOVD R3, ret+8(FP) + RET diff --git a/src/runtime/internal/atomic/atomic_riscv64.go b/src/runtime/internal/atomic/atomic_riscv64.go index d52512369e..617bc1a3eb 100644 --- a/src/runtime/internal/atomic/atomic_riscv64.go +++ b/src/runtime/internal/atomic/atomic_riscv64.go @@ -39,6 +39,12 @@ func Loadp(ptr unsafe.Pointer) unsafe.Pointer //go:noescape func LoadAcq(ptr *uint32) uint32 +//go:noescape +func LoadAcq64(ptr *uint64) uint64 + +//go:noescape +func LoadAcquintptr(ptr *uintptr) uintptr + //go:noescape func Or8(ptr *uint8, val uint8) @@ -65,3 +71,9 @@ func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) //go:noescape func StoreRel(ptr *uint32, val uint32) + +//go:noescape +func StoreRel64(ptr *uint64, val uint64) + +//go:noescape +func StoreReluintptr(ptr *uintptr, val uintptr) diff --git a/src/runtime/internal/atomic/atomic_riscv64.s b/src/runtime/internal/atomic/atomic_riscv64.s index d005325ca3..db139d690a 100644 --- a/src/runtime/internal/atomic/atomic_riscv64.s +++ b/src/runtime/internal/atomic/atomic_riscv64.s @@ -150,6 +150,12 @@ TEXT ·Xaddint64(SB),NOSPLIT,$0-24 TEXT ·LoadAcq(SB),NOSPLIT|NOFRAME,$0-12 JMP ·Load(SB) +TEXT ·LoadAcq64(SB),NOSPLIT|NOFRAME,$0-16 + JMP ·Load64(SB) + +TEXT ·LoadAcquintptr(SB),NOSPLIT|NOFRAME,$0-16 + JMP ·Load64(SB) + // func Loadp(ptr unsafe.Pointer) unsafe.Pointer TEXT ·Loadp(SB),NOSPLIT,$0-16 JMP ·Load64(SB) @@ -161,6 +167,12 @@ TEXT ·StorepNoWB(SB), NOSPLIT, $0-16 TEXT ·StoreRel(SB), NOSPLIT, $0-12 JMP ·Store(SB) +TEXT ·StoreRel64(SB), NOSPLIT, $0-16 + JMP ·Store64(SB) + +TEXT ·StoreReluintptr(SB), NOSPLIT, $0-16 + JMP ·Store64(SB) + // func Xchg(ptr *uint32, new uint32) uint32 TEXT ·Xchg(SB), NOSPLIT, $0-20 MOV ptr+0(FP), A0 diff --git a/src/runtime/internal/atomic/atomic_s390x.go b/src/runtime/internal/atomic/atomic_s390x.go index 4d73b39baf..b649caa39f 100644 --- a/src/runtime/internal/atomic/atomic_s390x.go +++ b/src/runtime/internal/atomic/atomic_s390x.go @@ -41,6 +41,18 @@ func LoadAcq(ptr *uint32) uint32 { return *ptr } +//go:nosplit +//go:noinline +func LoadAcq64(ptr *uint64) uint64 { + return *ptr +} + +//go:nosplit +//go:noinline +func LoadAcquintptr(ptr *uintptr) uintptr { + return *ptr +} + //go:noescape func Store(ptr *uint32, val uint32) @@ -59,6 +71,18 @@ func StoreRel(ptr *uint32, val uint32) { *ptr = val } +//go:nosplit +//go:noinline +func StoreRel64(ptr *uint64, val uint64) { + *ptr = val +} + +//go:nosplit +//go:noinline +func StoreReluintptr(ptr *uintptr, val uintptr) { + *ptr = val +} + //go:noescape func And8(ptr *uint8, val uint8) diff --git a/src/runtime/internal/atomic/atomic_wasm.go b/src/runtime/internal/atomic/atomic_wasm.go index 2c0c3a8174..60a4942884 100644 --- a/src/runtime/internal/atomic/atomic_wasm.go +++ b/src/runtime/internal/atomic/atomic_wasm.go @@ -45,6 +45,18 @@ func LoadAcq(ptr *uint32) uint32 { return *ptr } +//go:nosplit +//go:noinline +func LoadAcq64(ptr *uint64) uint64 { + return *ptr +} + +//go:nosplit +//go:noinline +func LoadAcquintptr(ptr *uintptr) uintptr { + return *ptr +} + //go:nosplit //go:noinline func Load8(ptr *uint8) uint8 { @@ -141,6 +153,18 @@ func StoreRel(ptr *uint32, val uint32) { *ptr = val } +//go:nosplit +//go:noinline +func StoreRel64(ptr *uint64, val uint64) { + *ptr = val +} + +//go:nosplit +//go:noinline +func StoreReluintptr(ptr *uintptr, val uintptr) { + *ptr = val +} + //go:nosplit //go:noinline func Store8(ptr *uint8, val uint8) { diff --git a/src/sync/pool.go b/src/sync/pool.go index ca7afdb12f..137413fdc4 100644 --- a/src/sync/pool.go +++ b/src/sync/pool.go @@ -7,6 +7,7 @@ package sync import ( "internal/race" "runtime" + runtimeatomic "runtime/internal/atomic" "sync/atomic" "unsafe" ) @@ -152,8 +153,8 @@ func (p *Pool) Get() interface{} { func (p *Pool) getSlow(pid int) interface{} { // See the comment in pin regarding ordering of the loads. - size := atomic.LoadUintptr(&p.localSize) // load-acquire - locals := p.local // load-consume + size := runtimeatomic.LoadAcquintptr(&p.localSize) // load-acquire + locals := p.local // load-consume // Try to steal one element from other procs. for i := 0; i < int(size); i++ { l := indexLocal(locals, (pid+i+1)%int(size)) @@ -165,7 +166,7 @@ func (p *Pool) getSlow(pid int) interface{} { // Try the victim cache. We do this after attempting to steal // from all primary caches because we want objects in the // victim cache to age out if at all possible. - size = atomic.LoadUintptr(&p.victimSize) + size = runtimeatomic.Loaduintptr(&p.victimSize) if uintptr(pid) >= size { return nil } @@ -198,8 +199,8 @@ func (p *Pool) pin() (*poolLocal, int) { // Since we've disabled preemption, GC cannot happen in between. // Thus here we must observe local at least as large localSize. // We can observe a newer/larger local, it is fine (we must observe its zero-initialized-ness). - s := atomic.LoadUintptr(&p.localSize) // load-acquire - l := p.local // load-consume + s := runtimeatomic.LoadAcquintptr(&p.localSize) // load-acquire + l := p.local // load-consume if uintptr(pid) < s { return indexLocal(l, pid), pid } @@ -225,8 +226,8 @@ func (p *Pool) pinSlow() (*poolLocal, int) { // If GOMAXPROCS changes between GCs, we re-allocate the array and lose the old one. size := runtime.GOMAXPROCS(0) local := make([]poolLocal, size) - atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-release - atomic.StoreUintptr(&p.localSize, uintptr(size)) // store-release + atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-release + runtimeatomic.StoreReluintptr(&p.localSize, uintptr(size)) // store-release return &local[pid], pid } -- cgit v1.3 From 612a363bef9ae29d190f6daa2a5a1623f78c874b Mon Sep 17 00:00:00 2001 From: hk Date: Wed, 21 Oct 2020 16:18:34 +0000 Subject: cmd/compile/internal/gc: fix comments Change-Id: Id7b0ead39e961a16a85da3e308db10dd4f9b55c3 GitHub-Last-Rev: e640c4a61ade361ac17b7eb95d0ce8913d0b4d6f GitHub-Pull-Request: golang/go#42120 Reviewed-on: https://go-review.googlesource.com/c/go/+/264080 Reviewed-by: Keith Randall Trust: Cuong Manh Le --- src/cmd/compile/internal/gc/dcl.go | 2 +- src/cmd/compile/internal/gc/subr.go | 2 +- src/cmd/compile/internal/gc/syntax.go | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index a362d1a643..b8ca0d2e03 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -283,7 +283,7 @@ func oldname(s *types.Sym) *Node { c.Name.Defn = n // Link into list of active closure variables. - // Popped from list in func closurebody. + // Popped from list in func funcLit. c.Name.Param.Outer = n.Name.Param.Innermost n.Name.Param.Innermost = c diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index f4b0c0fae0..b6e6f3a6da 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -348,7 +348,7 @@ func newname(s *types.Sym) *Node { return n } -// newname returns a new ONAME Node associated with symbol s at position pos. +// newnamel returns a new ONAME Node associated with symbol s at position pos. // The caller is responsible for setting n.Name.Curfn. func newnamel(pos src.XPos, s *types.Sym) *Node { if s == nil { diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 4aa2e230ce..a2ab0fa661 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -460,14 +460,14 @@ type Param struct { // x1 := xN.Defn // x1.Innermost = xN.Outer // - // We leave xN.Innermost set so that we can still get to the original + // We leave x1.Innermost set so that we can still get to the original // variable quickly. Not shown here, but once we're // done parsing a function and no longer need xN.Outer for the - // lexical x reference links as described above, closurebody + // lexical x reference links as described above, funcLit // recomputes xN.Outer as the semantic x reference link tree, // even filling in x in intermediate closures that might not // have mentioned it along the way to inner closures that did. - // See closurebody for details. + // See funcLit for details. // // During the eventual compilation, then, for closure variables we have: // -- cgit v1.3 From 3bac5faa4af2f5c454b2cebaa8be5cde9b8e2add Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Mon, 19 Oct 2020 11:31:10 +0200 Subject: cmd/compile: make gc debug flags collector a struct gc debug flags are currently stored in a 256-long array, that is then addressed using the ASCII numeric value of the flag itself (a quirk inherited from the old C compiler). It is also a little wasteful, since we only define 16 flags, and the other 240 array elements are always empty. This change makes Debug a struct, which also provides static checking that we're not referencing flags that does not exist. Change-Id: I2f0dfef2529325514b3398cf78635543cdf48fe0 Reviewed-on: https://go-review.googlesource.com/c/go/+/263539 Trust: Alberto Donizetti Run-TryBot: Alberto Donizetti TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/alg.go | 8 +-- src/cmd/compile/internal/gc/closure.go | 2 +- src/cmd/compile/internal/gc/esc.go | 16 +++--- src/cmd/compile/internal/gc/escape.go | 32 +++++------ src/cmd/compile/internal/gc/export.go | 10 ++-- src/cmd/compile/internal/gc/go.go | 10 +++- src/cmd/compile/internal/gc/gsubr.go | 2 +- src/cmd/compile/internal/gc/iimport.go | 4 +- src/cmd/compile/internal/gc/inl.go | 38 ++++++------- src/cmd/compile/internal/gc/main.go | 97 ++++++++++++++++++---------------- src/cmd/compile/internal/gc/order.go | 4 +- src/cmd/compile/internal/gc/range.go | 4 +- src/cmd/compile/internal/gc/sinit.go | 2 +- src/cmd/compile/internal/gc/ssa.go | 16 +++--- src/cmd/compile/internal/gc/subr.go | 16 +++--- src/cmd/compile/internal/gc/syntax.go | 4 +- src/cmd/compile/internal/gc/walk.go | 18 +++---- 17 files changed, 149 insertions(+), 134 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index 6302b88f59..2ab69c2c56 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -282,7 +282,7 @@ func genhash(t *types.Type) *obj.LSym { } sym := typesymprefix(".hash", t) - if Debug['r'] != 0 { + if Debug.r != 0 { fmt.Printf("genhash %v %v %v\n", closure, sym, t) } @@ -374,7 +374,7 @@ func genhash(t *types.Type) *obj.LSym { r.List.Append(nh) fn.Nbody.Append(r) - if Debug['r'] != 0 { + if Debug.r != 0 { dumplist("genhash body", fn.Nbody) } @@ -509,7 +509,7 @@ func geneq(t *types.Type) *obj.LSym { return closure } sym := typesymprefix(".eq", t) - if Debug['r'] != 0 { + if Debug.r != 0 { fmt.Printf("geneq %v\n", t) } @@ -732,7 +732,7 @@ func geneq(t *types.Type) *obj.LSym { fn.Nbody.Append(ret) } - if Debug['r'] != 0 { + if Debug.r != 0 { dumplist("geneq body", fn.Nbody) } diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index 5d1012111f..902d2e34a3 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -198,7 +198,7 @@ func capturevars(xfunc *Node) { outer = nod(OADDR, outer, nil) } - if Debug['m'] > 1 { + if Debug.m > 1 { var name *types.Sym if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil { name = v.Name.Curfn.Func.Nname.Sym diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index c11066a62f..6f328ab5ea 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -282,7 +282,7 @@ func addrescapes(n *Node) { // moveToHeap records the parameter or local variable n as moved to the heap. func moveToHeap(n *Node) { - if Debug['r'] != 0 { + if Debug.r != 0 { Dump("MOVE", n) } if compiling_runtime { @@ -359,7 +359,7 @@ func moveToHeap(n *Node) { n.Xoffset = 0 n.Name.Param.Heapaddr = heapaddr n.Esc = EscHeap - if Debug['m'] != 0 { + if Debug.m != 0 { Warnl(n.Pos, "moved to heap: %v", n) } } @@ -389,7 +389,7 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string { // but we are reusing the ability to annotate an individual function // argument and pass those annotations along to importing code. if f.Type.IsUintptr() { - if Debug['m'] != 0 { + if Debug.m != 0 { Warnl(f.Pos, "assuming %v is unsafe uintptr", name()) } return unsafeUintptrTag @@ -404,11 +404,11 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string { // External functions are assumed unsafe, unless // //go:noescape is given before the declaration. if fn.Func.Pragma&Noescape != 0 { - if Debug['m'] != 0 && f.Sym != nil { + if Debug.m != 0 && f.Sym != nil { Warnl(f.Pos, "%v does not escape", name()) } } else { - if Debug['m'] != 0 && f.Sym != nil { + if Debug.m != 0 && f.Sym != nil { Warnl(f.Pos, "leaking param: %v", name()) } esc.AddHeap(0) @@ -419,14 +419,14 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string { if fn.Func.Pragma&UintptrEscapes != 0 { if f.Type.IsUintptr() { - if Debug['m'] != 0 { + if Debug.m != 0 { Warnl(f.Pos, "marking %v as escaping uintptr", name()) } return uintptrEscapesTag } if f.IsDDD() && f.Type.Elem().IsUintptr() { // final argument is ...uintptr. - if Debug['m'] != 0 { + if Debug.m != 0 { Warnl(f.Pos, "marking %v as escaping ...uintptr", name()) } return uintptrEscapesTag @@ -448,7 +448,7 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string { esc := loc.paramEsc esc.Optimize() - if Debug['m'] != 0 && !loc.escapes { + if Debug.m != 0 && !loc.escapes { if esc.Empty() { Warnl(f.Pos, "%v does not escape", name()) } diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index 93965d4fac..618bdf78e2 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -170,7 +170,7 @@ func (e *Escape) initFunc(fn *Node) { Fatalf("unexpected node: %v", fn) } fn.Esc = EscFuncPlanned - if Debug['m'] > 3 { + if Debug.m > 3 { Dump("escAnalyze", fn) } @@ -247,7 +247,7 @@ func (e *Escape) stmt(n *Node) { lineno = lno }() - if Debug['m'] > 2 { + if Debug.m > 2 { fmt.Printf("%v:[%d] %v stmt: %v\n", linestr(lineno), e.loopDepth, funcSym(e.curfn), n) } @@ -275,11 +275,11 @@ func (e *Escape) stmt(n *Node) { case OLABEL: switch asNode(n.Sym.Label) { case &nonlooping: - if Debug['m'] > 2 { + if Debug.m > 2 { fmt.Printf("%v:%v non-looping label\n", linestr(lineno), n) } case &looping: - if Debug['m'] > 2 { + if Debug.m > 2 { fmt.Printf("%v: %v looping label\n", linestr(lineno), n) } e.loopDepth++ @@ -717,7 +717,7 @@ func (e *Escape) addrs(l Nodes) []EscHole { func (e *Escape) assign(dst, src *Node, why string, where *Node) { // Filter out some no-op assignments for escape analysis. ignore := dst != nil && src != nil && isSelfAssign(dst, src) - if ignore && Debug['m'] != 0 { + if ignore && Debug.m != 0 { Warnl(where.Pos, "%v ignoring self-assignment in %S", funcSym(e.curfn), where) } @@ -931,7 +931,7 @@ func (k EscHole) note(where *Node, why string) EscHole { if where == nil || why == "" { Fatalf("note: missing where/why") } - if Debug['m'] >= 2 || logopt.Enabled() { + if Debug.m >= 2 || logopt.Enabled() { k.notes = &EscNote{ next: k.notes, where: where, @@ -1077,9 +1077,9 @@ func (e *Escape) flow(k EscHole, src *EscLocation) { return } if dst.escapes && k.derefs < 0 { // dst = &src - if Debug['m'] >= 2 || logopt.Enabled() { + if Debug.m >= 2 || logopt.Enabled() { pos := linestr(src.n.Pos) - if Debug['m'] >= 2 { + if Debug.m >= 2 { fmt.Printf("%s: %v escapes to heap:\n", pos, src.n) } explanation := e.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{}) @@ -1179,8 +1179,8 @@ func (e *Escape) walkOne(root *EscLocation, walkgen uint32, enqueue func(*EscLoc // that value flow for tagging the function // later. if l.isName(PPARAM) { - if (logopt.Enabled() || Debug['m'] >= 2) && !l.escapes { - if Debug['m'] >= 2 { + if (logopt.Enabled() || Debug.m >= 2) && !l.escapes { + if Debug.m >= 2 { fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", linestr(l.n.Pos), l.n, e.explainLoc(root), base) } explanation := e.explainPath(root, l) @@ -1196,8 +1196,8 @@ func (e *Escape) walkOne(root *EscLocation, walkgen uint32, enqueue func(*EscLoc // outlives it, then l needs to be heap // allocated. if addressOf && !l.escapes { - if logopt.Enabled() || Debug['m'] >= 2 { - if Debug['m'] >= 2 { + if logopt.Enabled() || Debug.m >= 2 { + if Debug.m >= 2 { fmt.Printf("%s: %v escapes to heap:\n", linestr(l.n.Pos), l.n) } explanation := e.explainPath(root, l) @@ -1235,7 +1235,7 @@ func (e *Escape) explainPath(root, src *EscLocation) []*logopt.LoggedOpt { for { // Prevent infinite loop. if visited[src] { - if Debug['m'] >= 2 { + if Debug.m >= 2 { fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos) } break @@ -1263,7 +1263,7 @@ func (e *Escape) explainFlow(pos string, dst, srcloc *EscLocation, derefs int, n if derefs >= 0 { ops = strings.Repeat("*", derefs) } - print := Debug['m'] >= 2 + print := Debug.m >= 2 flow := fmt.Sprintf(" flow: %s = %s%v:", e.explainLoc(dst), ops, e.explainLoc(srcloc)) if print { @@ -1417,7 +1417,7 @@ func (e *Escape) finish(fns []*Node) { if loc.escapes { if n.Op != ONAME { - if Debug['m'] != 0 { + if Debug.m != 0 { Warnl(n.Pos, "%S escapes to heap", n) } if logopt.Enabled() { @@ -1427,7 +1427,7 @@ func (e *Escape) finish(fns []*Node) { n.Esc = EscHeap addrescapes(n) } else { - if Debug['m'] != 0 && n.Op != ONAME { + if Debug.m != 0 && n.Op != ONAME { Warnl(n.Pos, "%S does not escape", n) } n.Esc = EscNone diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 839c2c2c75..c6917e0f81 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -31,7 +31,7 @@ func exportsym(n *Node) { } n.Sym.SetOnExportList(true) - if Debug['E'] != 0 { + if Debug.E != 0 { fmt.Printf("export symbol %v\n", n.Sym) } @@ -150,7 +150,7 @@ func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val n.SetVal(val) - if Debug['E'] != 0 { + if Debug.E != 0 { fmt.Printf("import const %v %L = %v\n", s, t, val) } } @@ -166,7 +166,7 @@ func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) { n.Func = new(Func) t.SetNname(asTypesNode(n)) - if Debug['E'] != 0 { + if Debug.E != 0 { fmt.Printf("import func %v%S\n", s, t) } } @@ -179,7 +179,7 @@ func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) { return } - if Debug['E'] != 0 { + if Debug.E != 0 { fmt.Printf("import var %v %L\n", s, t) } } @@ -192,7 +192,7 @@ func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) { return } - if Debug['E'] != 0 { + if Debug.E != 0 { fmt.Printf("import type %v = %L\n", s, t) } } diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index ee2add3733..da6b6d6e72 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -116,7 +116,15 @@ var decldepth int32 var nolocalimports bool -var Debug [256]int +// gc debug flags +type DebugFlags struct { + P, B, C, E, + K, L, N, S, + W, e, h, j, + l, m, r, w int +} + +var Debug DebugFlags var debugstr string diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index 14c217ff3b..ce5182f203 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -153,7 +153,7 @@ func (pp *Progs) Prog(as obj.As) *obj.Prog { pp.clearp(pp.next) p.Link = pp.next - if !pp.pos.IsKnown() && Debug['K'] != 0 { + if !pp.pos.IsKnown() && Debug.K != 0 { Warn("prog: unknown position (line 0)") } diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index 107e96cc6a..7f2b05f288 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -742,8 +742,8 @@ func (r *importReader) doInline(n *Node) { importlist = append(importlist, n) - if Debug['E'] > 0 && Debug['m'] > 2 { - if Debug['m'] > 3 { + if Debug.E > 0 && Debug.m > 2 { + if Debug.m > 3 { fmt.Printf("inl body for %v %#v: %+v\n", n, n.Type, asNodes(n.Func.Inl.Body)) } else { fmt.Printf("inl body for %v %#v: %v\n", n, n.Type, asNodes(n.Func.Inl.Body)) diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 55a14d378e..a2fb00e132 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -7,7 +7,7 @@ // saves a copy of the body. Then inlcalls walks each function body to // expand calls to inlinable functions. // -// The debug['l'] flag controls the aggressiveness. Note that main() swaps level 0 and 1, +// The Debug.l flag controls the aggressiveness. Note that main() swaps level 0 and 1, // making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and // are not supported. // 0: disabled @@ -21,7 +21,7 @@ // The -d typcheckinl flag enables early typechecking of all imported bodies, // which is useful to flush out bugs. // -// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying +// The Debug.m flag enables diagnostic output. a single -m is useful for verifying // which calls get inlined or not, more is for debugging, and may go away at any point. package gc @@ -85,7 +85,7 @@ func typecheckinl(fn *Node) { return // typecheckinl on local function } - if Debug['m'] > 2 || Debug_export != 0 { + if Debug.m > 2 || Debug_export != 0 { fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, asNodes(fn.Func.Inl.Body)) } @@ -116,10 +116,10 @@ func caninl(fn *Node) { } var reason string // reason, if any, that the function was not inlined - if Debug['m'] > 1 || logopt.Enabled() { + if Debug.m > 1 || logopt.Enabled() { defer func() { if reason != "" { - if Debug['m'] > 1 { + if Debug.m > 1 { fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason) } if logopt.Enabled() { @@ -187,7 +187,7 @@ func caninl(fn *Node) { defer n.Func.SetInlinabilityChecked(true) cc := int32(inlineExtraCallCost) - if Debug['l'] == 4 { + if Debug.l == 4 { cc = 1 // this appears to yield better performance than 0. } @@ -224,9 +224,9 @@ func caninl(fn *Node) { // this is so export can find the body of a method fn.Type.FuncType().Nname = asTypesNode(n) - if Debug['m'] > 1 { + if Debug.m > 1 { fmt.Printf("%v: can inline %#v with cost %d as: %#v { %#v }\n", fn.Line(), n, inlineMaxBudget-visitor.budget, fn.Type, asNodes(n.Func.Inl.Body)) - } else if Debug['m'] != 0 { + } else if Debug.m != 0 { fmt.Printf("%v: can inline %v\n", fn.Line(), n) } if logopt.Enabled() { @@ -425,7 +425,7 @@ func (v *hairyVisitor) visit(n *Node) bool { v.budget-- // When debugging, don't stop early, to get full cost of inlining this function - if v.budget < 0 && Debug['m'] < 2 && !logopt.Enabled() { + if v.budget < 0 && Debug.m < 2 && !logopt.Enabled() { return true } @@ -670,7 +670,7 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node { switch n.Op { case OCALLFUNC: - if Debug['m'] > 3 { + if Debug.m > 3 { fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left) } if isIntrinsicCall(n) { @@ -681,7 +681,7 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node { } case OCALLMETH: - if Debug['m'] > 3 { + if Debug.m > 3 { fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right) } @@ -911,7 +911,7 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { } if inlMap[fn] { - if Debug['m'] > 1 { + if Debug.m > 1 { fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", n.Line(), fn, Curfn.funcname()) } return n @@ -925,12 +925,12 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { } // We have a function node, and it has an inlineable body. - if Debug['m'] > 1 { + if Debug.m > 1 { fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, asNodes(fn.Func.Inl.Body)) - } else if Debug['m'] != 0 { + } else if Debug.m != 0 { fmt.Printf("%v: inlining call to %v\n", n.Line(), fn) } - if Debug['m'] > 2 { + if Debug.m > 2 { fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n) } @@ -1174,7 +1174,7 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { } } - if Debug['m'] > 2 { + if Debug.m > 2 { fmt.Printf("%v: After inlining %+v\n\n", call.Line(), call) } @@ -1185,7 +1185,7 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { // PAUTO's in the calling functions, and link them off of the // PPARAM's, PAUTOS and PPARAMOUTs of the called function. func inlvar(var_ *Node) *Node { - if Debug['m'] > 3 { + if Debug.m > 3 { fmt.Printf("inlvar %+v\n", var_) } @@ -1264,13 +1264,13 @@ func (subst *inlsubst) node(n *Node) *Node { switch n.Op { case ONAME: if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode - if Debug['m'] > 2 { + if Debug.m > 2 { fmt.Printf("substituting name %+v -> %+v\n", n, inlvar) } return inlvar } - if Debug['m'] > 2 { + if Debug.m > 2 { fmt.Printf("not substituting name %+v\n", n) } return n diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 2fffe625cd..949755a0e2 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -211,18 +211,27 @@ func Main(archInit func(*Arch)) { flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime") flag.BoolVar(&compiling_std, "std", false, "compiling standard library") - objabi.Flagcount("%", "debug non-static initializers", &Debug['%']) - objabi.Flagcount("B", "disable bounds checking", &Debug['B']) - objabi.Flagcount("C", "disable printing of columns in error messages", &Debug['C']) // TODO(gri) remove eventually flag.StringVar(&localimport, "D", "", "set relative `path` for local imports") - objabi.Flagcount("E", "debug symbol export", &Debug['E']) + + objabi.Flagcount("%", "debug non-static initializers", &Debug.P) + objabi.Flagcount("B", "disable bounds checking", &Debug.B) + objabi.Flagcount("C", "disable printing of columns in error messages", &Debug.C) + objabi.Flagcount("E", "debug symbol export", &Debug.E) + objabi.Flagcount("K", "debug missing line numbers", &Debug.K) + objabi.Flagcount("L", "show full file names in error messages", &Debug.L) + objabi.Flagcount("N", "disable optimizations", &Debug.N) + objabi.Flagcount("S", "print assembly listing", &Debug.S) + objabi.Flagcount("W", "debug parse tree after type checking", &Debug.W) + objabi.Flagcount("e", "no limit on number of errors reported", &Debug.e) + objabi.Flagcount("h", "halt on error", &Debug.h) + objabi.Flagcount("j", "debug runtime-initialized variables", &Debug.j) + objabi.Flagcount("l", "disable inlining", &Debug.l) + objabi.Flagcount("m", "print optimization decisions", &Debug.m) + objabi.Flagcount("r", "debug generated wrappers", &Debug.r) + objabi.Flagcount("w", "debug type checking", &Debug.w) + objabi.Flagfn1("I", "add `directory` to import search path", addidir) - objabi.Flagcount("K", "debug missing line numbers", &Debug['K']) - objabi.Flagcount("L", "show full file names in error messages", &Debug['L']) - objabi.Flagcount("N", "disable optimizations", &Debug['N']) - objabi.Flagcount("S", "print assembly listing", &Debug['S']) objabi.AddVersionFlag() // -V - objabi.Flagcount("W", "debug parse tree after type checking", &Debug['W']) flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`") flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata") flag.IntVar(&nBackendWorkers, "c", 1, "concurrency during compilation, 1 means no concurrency") @@ -231,17 +240,12 @@ func Main(archInit func(*Arch)) { flag.BoolVar(&flagDWARF, "dwarf", !Wasm, "generate DWARF symbols") flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode") flag.IntVar(&genDwarfInline, "gendwarfinl", 2, "generate DWARF inline info records") - objabi.Flagcount("e", "no limit on number of errors reported", &Debug['e']) - objabi.Flagcount("h", "halt on error", &Debug['h']) objabi.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap) objabi.Flagfn1("importcfg", "read import configuration from `file`", readImportCfg) flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`") - objabi.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) - objabi.Flagcount("l", "disable inlining", &Debug['l']) flag.StringVar(&flag_lang, "lang", "", "release to compile for") flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`") objabi.Flagcount("live", "debug liveness analysis", &debuglive) - objabi.Flagcount("m", "print optimization decisions", &Debug['m']) if sys.MSanSupported(objabi.GOOS, objabi.GOARCH) { flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer") } @@ -249,7 +253,6 @@ func Main(archInit func(*Arch)) { flag.StringVar(&outfile, "o", "", "write output to `file`") flag.StringVar(&myimportpath, "p", "", "set expected package import `path`") flag.BoolVar(&writearchive, "pack", false, "write to file.a instead of file.o") - objabi.Flagcount("r", "debug generated wrappers", &Debug['r']) if sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) { flag.BoolVar(&flag_race, "race", false, "enable race detector") } @@ -259,7 +262,6 @@ func Main(archInit func(*Arch)) { } flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths") flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity") - objabi.Flagcount("w", "debug type checking", &Debug['w']) flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier") var flag_shared bool var flag_dynlink bool @@ -325,9 +327,9 @@ func Main(archInit func(*Arch)) { Ctxt.Flag_shared = flag_dynlink || flag_shared Ctxt.Flag_dynlink = flag_dynlink - Ctxt.Flag_optimize = Debug['N'] == 0 + Ctxt.Flag_optimize = Debug.N == 0 - Ctxt.Debugasm = Debug['S'] + Ctxt.Debugasm = Debug.S Ctxt.Debugvlog = Debug_vlog if flagDWARF { Ctxt.DebugInfo = debuginfo @@ -399,7 +401,7 @@ func Main(archInit func(*Arch)) { instrumenting = true } - if compiling_runtime && Debug['N'] != 0 { + if compiling_runtime && Debug.N != 0 { log.Fatal("cannot disable optimizations while compiling runtime") } if nBackendWorkers < 1 { @@ -504,11 +506,11 @@ func Main(archInit func(*Arch)) { } // enable inlining. for now: - // default: inlining on. (debug['l'] == 1) - // -l: inlining off (debug['l'] == 0) - // -l=2, -l=3: inlining on again, with extra debugging (debug['l'] > 1) - if Debug['l'] <= 1 { - Debug['l'] = 1 - Debug['l'] + // default: inlining on. (Debug.l == 1) + // -l: inlining off (Debug.l == 0) + // -l=2, -l=3: inlining on again, with extra debugging (Debug.l > 1) + if Debug.l <= 1 { + Debug.l = 1 - Debug.l } if jsonLogOpt != "" { // parse version,destination from json logging optimization. @@ -666,7 +668,7 @@ func Main(archInit func(*Arch)) { // Phase 5: Inlining timings.Start("fe", "inlining") if Debug_typecheckinl != 0 { - // Typecheck imported function bodies if debug['l'] > 1, + // Typecheck imported function bodies if Debug.l > 1, // otherwise lazily when used or re-exported. for _, n := range importlist { if n.Func.Inl != nil { @@ -680,7 +682,7 @@ func Main(archInit func(*Arch)) { } } - if Debug['l'] != 0 { + if Debug.l != 0 { // Find functions that can be inlined and clone them before walk expands them. visitBottomUp(xtop, func(list []*Node, recursive bool) { numfns := numNonClosures(list) @@ -691,7 +693,7 @@ func Main(archInit func(*Arch)) { // across more than one function. caninl(n) } else { - if Debug['m'] > 1 { + if Debug.m > 1 { fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname) } } @@ -1408,29 +1410,34 @@ func IsAlias(sym *types.Sym) bool { return sym.Def != nil && asNode(sym.Def).Sym != sym } -// By default, assume any debug flags are incompatible with concurrent compilation. -// A few are safe and potentially in common use for normal compiles, though; mark them as such here. -var concurrentFlagOK = [256]bool{ - 'B': true, // disabled bounds checking - 'C': true, // disable printing of columns in error messages - 'e': true, // no limit on errors; errors all come from non-concurrent code - 'I': true, // add `directory` to import search path - 'N': true, // disable optimizations - 'l': true, // disable inlining - 'w': true, // all printing happens before compilation - 'W': true, // all printing happens before compilation - 'S': true, // printing disassembly happens at the end (but see concurrentBackendAllowed below) +// By default, assume any debug flags are incompatible with concurrent +// compilation. A few are safe and potentially in common use for +// normal compiles, though; return true for those. +func concurrentFlagOk() bool { + // Report whether any debug flag that would prevent concurrent + // compilation is set, by zeroing out the allowed ones and then + // checking if the resulting struct is zero. + d := Debug + d.B = 0 // disable bounds checking + d.C = 0 // disable printing of columns in error messages + d.e = 0 // no limit on errors; errors all come from non-concurrent code + d.N = 0 // disable optimizations + d.l = 0 // disable inlining + d.w = 0 // all printing happens before compilation + d.W = 0 // all printing happens before compilation + d.S = 0 // printing disassembly happens at the end (but see concurrentBackendAllowed below) + + return d == DebugFlags{} } func concurrentBackendAllowed() bool { - for i, x := range &Debug { - if x != 0 && !concurrentFlagOK[i] { - return false - } + if !concurrentFlagOk() { + return false } - // Debug['S'] by itself is ok, because all printing occurs + + // Debug.S by itself is ok, because all printing occurs // while writing the object file, and that is non-concurrent. - // Adding Debug_vlog, however, causes Debug['S'] to also print + // Adding Debug_vlog, however, causes Debug.S to also print // while flushing the plist, which happens concurrently. if Debug_vlog || debugstr != "" || debuglive > 0 { return false diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index e562ab7556..f7fe3ed360 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -50,7 +50,7 @@ type Order struct { // Order rewrites fn.Nbody to apply the ordering constraints // described in the comment at the top of the file. func order(fn *Node) { - if Debug['W'] > 1 { + if Debug.W > 1 { s := fmt.Sprintf("\nbefore order %v", fn.Func.Nname.Sym) dumplist(s, fn.Nbody) } @@ -328,7 +328,7 @@ func orderMakeSliceCopy(s []*Node) { return } - if Debug['N'] != 0 || instrumenting { + if Debug.N != 0 || instrumenting { return } diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go index 6d22964dcd..1b4d765d42 100644 --- a/src/cmd/compile/internal/gc/range.go +++ b/src/cmd/compile/internal/gc/range.go @@ -466,7 +466,7 @@ func walkrange(n *Node) *Node { // // where == for keys of map m is reflexive. func isMapClear(n *Node) bool { - if Debug['N'] != 0 || instrumenting { + if Debug.N != 0 || instrumenting { return false } @@ -533,7 +533,7 @@ func mapClear(m *Node) *Node { // // Parameters are as in walkrange: "for v1, v2 = range a". func arrayClear(n, v1, v2, a *Node) bool { - if Debug['N'] != 0 || instrumenting { + if Debug.N != 0 || instrumenting { return false } diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index fda33534b6..9c4dcd739c 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -39,7 +39,7 @@ func (s *InitSchedule) append(n *Node) { // staticInit adds an initialization statement n to the schedule. func (s *InitSchedule) staticInit(n *Node) { if !s.tryStaticInit(n) { - if Debug['%'] != 0 { + if Debug.P != 0 { Dump("nonstatic", n) } s.append(n) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index e1455d2c3f..65beb84911 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -59,7 +59,7 @@ func initssaconfig() { _ = types.NewPtr(types.Types[TINT64]) // *int64 _ = types.NewPtr(types.Errortype) // *error types.NewPtrCacheEnabled = false - ssaConfig = ssa.NewConfig(thearch.LinkArch.Name, *types_, Ctxt, Debug['N'] == 0) + ssaConfig = ssa.NewConfig(thearch.LinkArch.Name, *types_, Ctxt, Debug.N == 0) ssaConfig.SoftFloat = thearch.SoftFloat ssaConfig.Race = flag_race ssaCaches = make([]ssa.Cache, nBackendWorkers) @@ -357,7 +357,7 @@ func buildssa(fn *Node, worker int) *ssa.Func { s.fwdVars = map[*Node]*ssa.Value{} s.startmem = s.entryNewValue0(ssa.OpInitMem, types.TypeMem) - s.hasOpenDefers = Debug['N'] == 0 && s.hasdefer && !s.curfn.Func.OpenCodedDeferDisallowed() + s.hasOpenDefers = Debug.N == 0 && s.hasdefer && !s.curfn.Func.OpenCodedDeferDisallowed() switch { case s.hasOpenDefers && (Ctxt.Flag_shared || Ctxt.Flag_dynlink) && thearch.LinkArch.Name == "386": // Don't support open-coded defers for 386 ONLY when using shared @@ -741,7 +741,7 @@ func (s *state) pushLine(line src.XPos) { // the frontend may emit node with line number missing, // use the parent line number in this case. line = s.peekPos() - if Debug['K'] != 0 { + if Debug.K != 0 { Warn("buildssa: unknown position (line 0)") } } else { @@ -1214,7 +1214,7 @@ func (s *state) stmt(n *Node) { // Check whether we're writing the result of an append back to the same slice. // If so, we handle it specially to avoid write barriers on the fast // (non-growth) path. - if !samesafeexpr(n.Left, rhs.List.First()) || Debug['N'] != 0 { + if !samesafeexpr(n.Left, rhs.List.First()) || Debug.N != 0 { break } // If the slice can be SSA'd, it'll be on the stack, @@ -4849,7 +4849,7 @@ func (s *state) addr(n *Node) *ssa.Value { // canSSA reports whether n is SSA-able. // n must be an ONAME (or an ODOT sequence with an ONAME base). func (s *state) canSSA(n *Node) bool { - if Debug['N'] != 0 { + if Debug.N != 0 { return false } for n.Op == ODOT || (n.Op == OINDEX && n.Left.Type.IsArray()) { @@ -4960,7 +4960,7 @@ func (s *state) nilCheck(ptr *ssa.Value) { func (s *state) boundsCheck(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bool) *ssa.Value { idx = s.extendIndex(idx, len, kind, bounded) - if bounded || Debug['B'] != 0 { + if bounded || Debug.B != 0 { // If bounded or bounds checking is flag-disabled, then no check necessary, // just return the extended index. // @@ -6310,7 +6310,7 @@ func genssa(f *ssa.Func, pp *Progs) { } // Emit control flow instructions for block var next *ssa.Block - if i < len(f.Blocks)-1 && Debug['N'] == 0 { + if i < len(f.Blocks)-1 && Debug.N == 0 { // If -N, leave next==nil so every block with successors // ends in a JMP (except call blocks - plive doesn't like // select{send,recv} followed by a JMP call). Helps keep @@ -6618,7 +6618,7 @@ func (s *state) extendIndex(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bo } else { lo = s.newValue1(ssa.OpInt64Lo, types.Types[TUINT], idx) } - if bounded || Debug['B'] != 0 { + if bounded || Debug.B != 0 { return lo } bNext := s.f.NewBlock(ssa.BlockPlain) diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index b6e6f3a6da..eccd6f8a74 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -96,7 +96,7 @@ func flusherrors() { } func hcrash() { - if Debug['h'] != 0 { + if Debug.h != 0 { flusherrors() if outfile != "" { os.Remove(outfile) @@ -107,7 +107,7 @@ func hcrash() { } func linestr(pos src.XPos) string { - return Ctxt.OutermostPos(pos).Format(Debug['C'] == 0, Debug['L'] == 1) + return Ctxt.OutermostPos(pos).Format(Debug.C == 0, Debug.L == 1) } // lasterror keeps track of the most recently issued error. @@ -153,7 +153,7 @@ func yyerrorl(pos src.XPos, format string, args ...interface{}) { hcrash() nerrors++ - if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { + if nsavederrors+nerrors >= 10 && Debug.e == 0 { flusherrors() fmt.Printf("%v: too many errors\n", linestr(pos)) errorexit() @@ -175,7 +175,7 @@ func Warn(fmt_ string, args ...interface{}) { func Warnl(line src.XPos, fmt_ string, args ...interface{}) { adderr(line, fmt_, args...) - if Debug['m'] != 0 { + if Debug.m != 0 { flusherrors() } } @@ -222,7 +222,7 @@ func hasUniquePos(n *Node) bool { } if !n.Pos.IsKnown() { - if Debug['K'] != 0 { + if Debug.K != 0 { Warn("setlineno: unknown position (line 0)") } return false @@ -1506,7 +1506,7 @@ func structargs(tl *types.Type, mustname bool) []*Node { // method - M func (t T)(), a TFIELD type struct // newnam - the eventual mangled name of this function func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) { - if false && Debug['r'] != 0 { + if false && Debug.r != 0 { fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam) } @@ -1579,7 +1579,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) { fn.Nbody.Append(call) } - if false && Debug['r'] != 0 { + if false && Debug.r != 0 { dumplist("genwrapper body", fn.Nbody) } @@ -1720,7 +1720,7 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool // the method does not exist for value types. rcvr := tm.Type.Recv().Type if rcvr.IsPtr() && !t0.IsPtr() && !followptr && !isifacemethod(tm.Type) { - if false && Debug['r'] != 0 { + if false && Debug.r != 0 { yyerror("interface pointer mismatch") } diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index a2ab0fa661..e3b4963977 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -247,7 +247,7 @@ func (n *Node) Val() Val { // SetVal sets the Val for the node, which must not have been used with SetOpt. func (n *Node) SetVal(v Val) { if n.HasOpt() { - Debug['h'] = 1 + Debug.h = 1 Dump("have Opt", n) Fatalf("have Opt") } @@ -270,7 +270,7 @@ func (n *Node) SetOpt(x interface{}) { return } if n.HasVal() { - Debug['h'] = 1 + Debug.h = 1 Dump("have Val", n) Fatalf("have Val") } diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 9df288ea65..6ce3eda44b 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -21,7 +21,7 @@ const zeroValSize = 1024 // must match value of runtime/map.go:maxZero func walk(fn *Node) { Curfn = fn - if Debug['W'] != 0 { + if Debug.W != 0 { s := fmt.Sprintf("\nbefore walk %v", Curfn.Func.Nname.Sym) dumplist(s, Curfn.Nbody) } @@ -63,14 +63,14 @@ func walk(fn *Node) { return } walkstmtlist(Curfn.Nbody.Slice()) - if Debug['W'] != 0 { + if Debug.W != 0 { s := fmt.Sprintf("after walk %v", Curfn.Func.Nname.Sym) dumplist(s, Curfn.Nbody) } zeroResults() heapmoves() - if Debug['W'] != 0 && Curfn.Func.Enter.Len() > 0 { + if Debug.W != 0 && Curfn.Func.Enter.Len() > 0 { s := fmt.Sprintf("enter %v", Curfn.Func.Nname.Sym) dumplist(s, Curfn.Func.Enter) } @@ -436,7 +436,7 @@ func walkexpr(n *Node, init *Nodes) *Node { lno := setlineno(n) - if Debug['w'] > 1 { + if Debug.w > 1 { Dump("before walk expr", n) } @@ -1049,7 +1049,7 @@ opswitch: } if t.IsArray() { n.SetBounded(bounded(r, t.NumElem())) - if Debug['m'] != 0 && n.Bounded() && !Isconst(n.Right, CTINT) { + if Debug.m != 0 && n.Bounded() && !Isconst(n.Right, CTINT) { Warn("index bounds check elided") } if smallintconst(n.Right) && !n.Bounded() { @@ -1057,7 +1057,7 @@ opswitch: } } else if Isconst(n.Left, CTSTR) { n.SetBounded(bounded(r, int64(len(n.Left.StringVal())))) - if Debug['m'] != 0 && n.Bounded() && !Isconst(n.Right, CTINT) { + if Debug.m != 0 && n.Bounded() && !Isconst(n.Right, CTINT) { Warn("index bounds check elided") } if smallintconst(n.Right) && !n.Bounded() { @@ -1599,7 +1599,7 @@ opswitch: updateHasCall(n) - if Debug['w'] != 0 && n != nil { + if Debug.w != 0 && n != nil { Dump("after walk expr", n) } @@ -2819,7 +2819,7 @@ func appendslice(n *Node, init *Nodes) *Node { // isAppendOfMake reports whether n is of the form append(x , make([]T, y)...). // isAppendOfMake assumes n has already been typechecked. func isAppendOfMake(n *Node) bool { - if Debug['N'] != 0 || instrumenting { + if Debug.N != 0 || instrumenting { return false } @@ -3976,7 +3976,7 @@ func canMergeLoads() bool { // isRuneCount reports whether n is of the form len([]rune(string)). // These are optimized into a call to runtime.countrunes. func isRuneCount(n *Node) bool { - return Debug['N'] == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES + return Debug.N == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES } func walkCheckPtrAlignment(n *Node, init *Nodes, count *Node) *Node { -- cgit v1.3 From 5d9b66cdc67332be3323e13b3e33713d496d6a56 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Wed, 21 Oct 2020 21:06:44 -0400 Subject: cmd/compile: remove go115makeslicecopy Change-Id: I6fd65fe7c1046c3ba7d7ed0e67282f879c13e9e9 Reviewed-on: https://go-review.googlesource.com/c/go/+/264340 Trust: Cherry Zhang Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/order.go | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index f7fe3ed360..863de5b6c7 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -323,11 +323,6 @@ func (o *Order) stmtList(l Nodes) { // and rewrites it to: // m = OMAKESLICECOPY([]T, x, s); nil func orderMakeSliceCopy(s []*Node) { - const go115makeslicecopy = true - if !go115makeslicecopy { - return - } - if Debug.N != 0 || instrumenting { return } -- cgit v1.3 From 8bde9b320e25b2d6edf96fa5e694046fea0c04c8 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sun, 19 Jul 2020 00:32:02 -0400 Subject: cmd/compile: add //go:embed support This commit contains the compiler support for //go:embed lines. The go command passes to the compiler an "embed config" that maps literal patterns like *.txt to the set of files to embed. The compiler then lays out the content of those files as static data in the form of an embed.Files or string or []byte in the final object file. The test for this code is the end-to-end test hooking up the embed, cmd/compile, and cmd/go changes, in the next CL. For #41191. Change-Id: I916e57f8cc65871dc0044c13d3f90c252a3fe1bf Reviewed-on: https://go-review.googlesource.com/c/go/+/243944 Trust: Russ Cox Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/embed.go | 273 +++++++++++++++++++++++++++++++ src/cmd/compile/internal/gc/main.go | 8 +- src/cmd/compile/internal/gc/noder.go | 145 ++++++++++++++-- src/cmd/compile/internal/gc/obj.go | 132 +++++++++++++-- src/cmd/compile/internal/gc/syntax.go | 86 +++++++++- src/cmd/compile/internal/gc/typecheck.go | 8 +- src/cmd/internal/obj/link.go | 29 +++- src/cmd/internal/obj/objfile.go | 49 +++++- 8 files changed, 682 insertions(+), 48 deletions(-) create mode 100644 src/cmd/compile/internal/gc/embed.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/embed.go b/src/cmd/compile/internal/gc/embed.go new file mode 100644 index 0000000000..103949c1f9 --- /dev/null +++ b/src/cmd/compile/internal/gc/embed.go @@ -0,0 +1,273 @@ +// Copyright 2020 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 gc + +import ( + "cmd/compile/internal/syntax" + "cmd/compile/internal/types" + "cmd/internal/obj" + "encoding/json" + "io/ioutil" + "log" + "path" + "sort" + "strconv" + "strings" +) + +var embedlist []*Node + +var embedCfg struct { + Patterns map[string][]string + Files map[string]string +} + +func readEmbedCfg(file string) { + data, err := ioutil.ReadFile(file) + if err != nil { + log.Fatalf("-embedcfg: %v", err) + } + if err := json.Unmarshal(data, &embedCfg); err != nil { + log.Fatalf("%s: %v", file, err) + } + if embedCfg.Patterns == nil { + log.Fatalf("%s: invalid embedcfg: missing Patterns", file) + } + if embedCfg.Files == nil { + log.Fatalf("%s: invalid embedcfg: missing Files", file) + } +} + +const ( + embedUnknown = iota + embedBytes + embedString + embedFiles +) + +var numLocalEmbed int + +func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) (newExprs []*Node) { + haveEmbed := false + for _, decl := range p.file.DeclList { + imp, ok := decl.(*syntax.ImportDecl) + if !ok { + // imports always come first + break + } + path, _ := strconv.Unquote(imp.Path.Value) + if path == "embed" { + haveEmbed = true + break + } + } + + pos := embeds[0].Pos + if !haveEmbed { + p.yyerrorpos(pos, "invalid go:embed: missing import \"embed\"") + return exprs + } + if embedCfg.Patterns == nil { + p.yyerrorpos(pos, "invalid go:embed: build system did not supply embed configuration") + return exprs + } + if len(names) > 1 { + p.yyerrorpos(pos, "go:embed cannot apply to multiple vars") + return exprs + } + if len(exprs) > 0 { + p.yyerrorpos(pos, "go:embed cannot apply to var with initializer") + return exprs + } + if typ == nil { + // Should not happen, since len(exprs) == 0 now. + p.yyerrorpos(pos, "go:embed cannot apply to var without type") + return exprs + } + + kind := embedKindApprox(typ) + if kind == embedUnknown { + p.yyerrorpos(pos, "go:embed cannot apply to var of type %v", typ) + return exprs + } + + // Build list of files to store. + have := make(map[string]bool) + var list []string + for _, e := range embeds { + for _, pattern := range e.Patterns { + files, ok := embedCfg.Patterns[pattern] + if !ok { + p.yyerrorpos(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern) + } + for _, file := range files { + if embedCfg.Files[file] == "" { + p.yyerrorpos(e.Pos, "invalid go:embed: build system did not map file: %s", file) + continue + } + if !have[file] { + have[file] = true + list = append(list, file) + } + if kind == embedFiles { + for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) { + have[dir] = true + list = append(list, dir+"/") + } + } + } + } + } + sort.Slice(list, func(i, j int) bool { + return embedFileLess(list[i], list[j]) + }) + + if kind == embedString || kind == embedBytes { + if len(list) > 1 { + p.yyerrorpos(pos, "invalid go:embed: multiple files for type %v", typ) + return exprs + } + } + + v := names[0] + if dclcontext != PEXTERN { + numLocalEmbed++ + v = newnamel(v.Pos, lookupN("embed.", numLocalEmbed)) + v.Sym.Def = asTypesNode(v) + v.Name.Param.Ntype = typ + v.SetClass(PEXTERN) + externdcl = append(externdcl, v) + exprs = []*Node{v} + } + + v.Name.Param.SetEmbedFiles(list) + embedlist = append(embedlist, v) + return exprs +} + +// embedKindApprox determines the kind of embedding variable, approximately. +// The match is approximate because we haven't done scope resolution yet and +// can't tell whether "string" and "byte" really mean "string" and "byte". +// The result must be confirmed later, after type checking, using embedKind. +func embedKindApprox(typ *Node) int { + if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) { + return embedFiles + } + // These are not guaranteed to match only string and []byte - + // maybe the local package has redefined one of those words. + // But it's the best we can do now during the noder. + // The stricter check happens later, in initEmbed calling embedKind. + if typ.Sym != nil && typ.Sym.Name == "string" && typ.Sym.Pkg == localpkg { + return embedString + } + if typ.Op == OTARRAY && typ.Left == nil && typ.Right.Sym != nil && typ.Right.Sym.Name == "byte" && typ.Right.Sym.Pkg == localpkg { + return embedBytes + } + return embedUnknown +} + +// embedKind determines the kind of embedding variable. +func embedKind(typ *types.Type) int { + if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) { + return embedFiles + } + if typ == types.Types[TSTRING] { + return embedString + } + if typ.Sym == nil && typ.IsSlice() && typ.Elem() == types.Bytetype { + return embedBytes + } + return embedUnknown +} + +func embedFileNameSplit(name string) (dir, elem string, isDir bool) { + if name[len(name)-1] == '/' { + isDir = true + name = name[:len(name)-1] + } + i := len(name) - 1 + for i >= 0 && name[i] != '/' { + i-- + } + if i < 0 { + return ".", name, isDir + } + return name[:i], name[i+1:], isDir +} + +// embedFileLess implements the sort order for a list of embedded files. +// See the comment inside ../../../../embed/embed.go's Files struct for rationale. +func embedFileLess(x, y string) bool { + xdir, xelem, _ := embedFileNameSplit(x) + ydir, yelem, _ := embedFileNameSplit(y) + return xdir < ydir || xdir == ydir && xelem < yelem +} + +func dumpembeds() { + for _, v := range embedlist { + initEmbed(v) + } +} + +// initEmbed emits the init data for a //go:embed variable, +// which is either a string, a []byte, or an embed.FS. +func initEmbed(v *Node) { + files := v.Name.Param.EmbedFiles() + switch kind := embedKind(v.Type); kind { + case embedUnknown: + yyerrorl(v.Pos, "go:embed cannot apply to var of type %v", v.Type) + + case embedString, embedBytes: + file := files[0] + fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], kind == embedString, nil) + if err != nil { + yyerrorl(v.Pos, "embed %s: %v", file, err) + } + sym := v.Sym.Linksym() + off := 0 + off = dsymptr(sym, off, fsym, 0) // data string + off = duintptr(sym, off, uint64(size)) // len + if kind == embedBytes { + duintptr(sym, off, uint64(size)) // cap for slice + } + + case embedFiles: + slicedata := Ctxt.Lookup(`"".` + v.Sym.Name + `.files`) + off := 0 + // []files pointed at by Files + off = dsymptr(slicedata, off, slicedata, 3*Widthptr) // []file, pointing just past slice + off = duintptr(slicedata, off, uint64(len(files))) + off = duintptr(slicedata, off, uint64(len(files))) + + // embed/embed.go type file is: + // name string + // data string + // hash [16]byte + // Emit one of these per file in the set. + const hashSize = 16 + hash := make([]byte, hashSize) + for _, file := range files { + off = dsymptr(slicedata, off, stringsym(v.Pos, file), 0) // file string + off = duintptr(slicedata, off, uint64(len(file))) + if strings.HasSuffix(file, "/") { + // entry for directory - no data + off = duintptr(slicedata, off, 0) + off = duintptr(slicedata, off, 0) + off += hashSize + } else { + fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], true, hash) + if err != nil { + yyerrorl(v.Pos, "embed %s: %v", file, err) + } + off = dsymptr(slicedata, off, fsym, 0) // data string + off = duintptr(slicedata, off, uint64(size)) + off = int(slicedata.WriteBytes(Ctxt, int64(off), hash)) + } + } + ggloblsym(slicedata, int32(off), obj.RODATA|obj.LOCAL) + sym := v.Sym.Linksym() + dsymptr(sym, 0, slicedata, 0) + } +} diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 949755a0e2..4b401f2aa4 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -34,8 +34,6 @@ import ( "strings" ) -var imported_unsafe bool - var ( buildid string spectre string @@ -240,6 +238,7 @@ func Main(archInit func(*Arch)) { flag.BoolVar(&flagDWARF, "dwarf", !Wasm, "generate DWARF symbols") flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode") flag.IntVar(&genDwarfInline, "gendwarfinl", 2, "generate DWARF inline info records") + objabi.Flagfn1("embedcfg", "read go:embed configuration from `file`", readEmbedCfg) objabi.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap) objabi.Flagfn1("importcfg", "read import configuration from `file`", readImportCfg) flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`") @@ -597,7 +596,7 @@ func Main(archInit func(*Arch)) { timings.Start("fe", "typecheck", "top1") for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) { + if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias()) { xtop[i] = typecheck(n, ctxStmt) } } @@ -609,7 +608,7 @@ func Main(archInit func(*Arch)) { timings.Start("fe", "typecheck", "top2") for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias { + if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias() { xtop[i] = typecheck(n, ctxStmt) } } @@ -1177,7 +1176,6 @@ func importfile(f *Val) *types.Pkg { } if path_ == "unsafe" { - imported_unsafe = true return unsafepkg } diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 85e710086a..67d24ef0bc 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -11,6 +11,7 @@ import ( "runtime" "strconv" "strings" + "unicode" "unicode/utf8" "cmd/compile/internal/syntax" @@ -90,7 +91,11 @@ func (p *noder) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase { } else { // line directive base p0 := b0.Pos() - p1 := src.MakePos(p.makeSrcPosBase(p0.Base()), p0.Line(), p0.Col()) + p0b := p0.Base() + if p0b == b0 { + panic("infinite recursion in makeSrcPosBase") + } + p1 := src.MakePos(p.makeSrcPosBase(p0b), p0.Line(), p0.Col()) b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col()) } p.basemap[b0] = b1 @@ -130,11 +135,13 @@ type noder struct { base *src.PosBase } - file *syntax.File - linknames []linkname - pragcgobuf [][]string - err chan syntax.Error - scope ScopeID + file *syntax.File + linknames []linkname + pragcgobuf [][]string + err chan syntax.Error + scope ScopeID + importedUnsafe bool + importedEmbed bool // scopeVars is a stack tracking the number of variables declared in the // current function at the moment each open scope was opened. @@ -236,7 +243,8 @@ type linkname struct { func (p *noder) node() { types.Block = 1 - imported_unsafe = false + p.importedUnsafe = false + p.importedEmbed = false p.setlineno(p.file.PkgName) mkpackage(p.file.PkgName.Value) @@ -249,7 +257,7 @@ func (p *noder) node() { xtop = append(xtop, p.decls(p.file.DeclList)...) for _, n := range p.linknames { - if !imported_unsafe { + if !p.importedUnsafe { p.yyerrorpos(n.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") continue } @@ -324,7 +332,6 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { val := p.basicLit(imp.Path) ipkg := importfile(&val) - if ipkg == nil { if nerrors == 0 { Fatalf("phase error in import") @@ -332,6 +339,13 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { return } + if ipkg == unsafepkg { + p.importedUnsafe = true + } + if ipkg.Path == "embed" { + p.importedEmbed = true + } + ipkg.Direct = true var my *types.Sym @@ -373,6 +387,20 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node { } if pragma, ok := decl.Pragma.(*Pragma); ok { + if len(pragma.Embeds) > 0 { + if !p.importedEmbed { + // This check can't be done when building the list pragma.Embeds + // because that list is created before the noder starts walking over the file, + // so at that point it hasn't seen the imports. + // We're left to check now, just before applying the //go:embed lines. + for _, e := range pragma.Embeds { + p.yyerrorpos(e.Pos, "//go:embed only allowed in Go files that import \"embed\"") + } + } else { + exprs = varEmbed(p, names, typ, exprs, pragma.Embeds) + } + pragma.Embeds = nil + } p.checkUnused(pragma) } @@ -455,17 +483,17 @@ func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node { param := n.Name.Param param.Ntype = typ - param.Alias = decl.Alias + param.SetAlias(decl.Alias) if pragma, ok := decl.Pragma.(*Pragma); ok { if !decl.Alias { - param.Pragma = pragma.Flag & TypePragmas + param.SetPragma(pragma.Flag & TypePragmas) pragma.Flag &^= TypePragmas } p.checkUnused(pragma) } nod := p.nod(decl, ODCLTYPE, n, nil) - if param.Alias && !langSupported(1, 9, localpkg) { + if param.Alias() && !langSupported(1, 9, localpkg) { yyerrorl(nod.Pos, "type aliases only supported as of -lang=go1.9") } return nod @@ -1493,13 +1521,15 @@ var allowedStdPragmas = map[string]bool{ "go:cgo_import_dynamic": true, "go:cgo_ldflag": true, "go:cgo_dynamic_linker": true, + "go:embed": true, "go:generate": true, } // *Pragma is the value stored in a syntax.Pragma during parsing. type Pragma struct { - Flag PragmaFlag // collected bits - Pos []PragmaPos // position of each individual flag + Flag PragmaFlag // collected bits + Pos []PragmaPos // position of each individual flag + Embeds []PragmaEmbed } type PragmaPos struct { @@ -1507,12 +1537,22 @@ type PragmaPos struct { Pos syntax.Pos } +type PragmaEmbed struct { + Pos syntax.Pos + Patterns []string +} + func (p *noder) checkUnused(pragma *Pragma) { for _, pos := range pragma.Pos { if pos.Flag&pragma.Flag != 0 { p.yyerrorpos(pos.Pos, "misplaced compiler directive") } } + if len(pragma.Embeds) > 0 { + for _, e := range pragma.Embeds { + p.yyerrorpos(e.Pos, "misplaced go:embed directive") + } + } } func (p *noder) checkUnusedDuringParse(pragma *Pragma) { @@ -1521,6 +1561,11 @@ func (p *noder) checkUnusedDuringParse(pragma *Pragma) { p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"}) } } + if len(pragma.Embeds) > 0 { + for _, e := range pragma.Embeds { + p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"}) + } + } } // pragma is called concurrently if files are parsed concurrently. @@ -1565,6 +1610,17 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P } p.linknames = append(p.linknames, linkname{pos, f[1], target}) + case text == "go:embed", strings.HasPrefix(text, "go:embed "): + args, err := parseGoEmbed(text[len("go:embed"):]) + if err != nil { + p.error(syntax.Error{Pos: pos, Msg: err.Error()}) + } + if len(args) == 0 { + p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."}) + break + } + pragma.Embeds = append(pragma.Embeds, PragmaEmbed{pos, args}) + case strings.HasPrefix(text, "go:cgo_import_dynamic "): // This is permitted for general use because Solaris // code relies on it in golang.org/x/sys/unix and others. @@ -1637,3 +1693,64 @@ func mkname(sym *types.Sym) *Node { } return n } + +// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns. +// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. +// go/build/read.go also processes these strings and contains similar logic. +func parseGoEmbed(args string) ([]string, error) { + var list []string + for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) { + var path string + Switch: + switch args[0] { + default: + i := len(args) + for j, c := range args { + if unicode.IsSpace(c) { + i = j + break + } + } + path = args[:i] + args = args[i:] + + case '`': + i := strings.Index(args[1:], "`") + if i < 0 { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + path = args[1 : 1+i] + args = args[1+i+1:] + + case '"': + i := 1 + for ; i < len(args); i++ { + if args[i] == '\\' { + i++ + continue + } + if args[i] == '"' { + q, err := strconv.Unquote(args[:i+1]) + if err != nil { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) + } + path = q + args = args[i+1:] + break Switch + } + } + if i >= len(args) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + + if args != "" { + r, _ := utf8.DecodeRuneInString(args) + if !unicode.IsSpace(r) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + list = append(list, path) + } + return list, nil +} diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index f6557e2d15..226eb45252 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -14,6 +14,8 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" + "os" "sort" "strconv" ) @@ -125,6 +127,7 @@ func dumpdata() { itabsLen := len(itabs) dumpimportstrings() dumpbasictypes() + dumpembeds() // Calls to dumpsignats can generate functions, // like method wrappers and hash and equality routines. @@ -358,28 +361,31 @@ func dbvec(s *obj.LSym, off int, bv bvec) int { return off } +const ( + stringSymPrefix = "go.string." + stringSymPattern = ".gostring.%d.%x" +) + +// stringsym returns a symbol containing the string s. +// The symbol contains the string data, not a string header. func stringsym(pos src.XPos, s string) (data *obj.LSym) { var symname string if len(s) > 100 { // Huge strings are hashed to avoid long names in object files. // Indulge in some paranoia by writing the length of s, too, // as protection against length extension attacks. + // Same pattern is known to fileStringSym below. h := sha256.New() io.WriteString(h, s) - symname = fmt.Sprintf(".gostring.%d.%x", len(s), h.Sum(nil)) + symname = fmt.Sprintf(stringSymPattern, len(s), h.Sum(nil)) } else { // Small strings get named directly by their contents. symname = strconv.Quote(s) } - const prefix = "go.string." - symdataname := prefix + symname - - symdata := Ctxt.Lookup(symdataname) - + symdata := Ctxt.Lookup(stringSymPrefix + symname) if !symdata.OnList() { - // string data - off := dsname(symdata, 0, s, pos, "string") + off := dstringdata(symdata, 0, s, pos, "string") ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) symdata.Set(obj.AttrContentAddressable, true) } @@ -387,26 +393,122 @@ func stringsym(pos src.XPos, s string) (data *obj.LSym) { return symdata } -var slicebytes_gen int +// fileStringSym returns a symbol for the contents and the size of file. +// If readonly is true, the symbol shares storage with any literal string +// or other file with the same content and is placed in a read-only section. +// If readonly is false, the symbol is a read-write copy separate from any other, +// for use as the backing store of a []byte. +// The content hash of file is copied into hash. (If hash is nil, nothing is copied.) +// The returned symbol contains the data itself, not a string header. +func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.LSym, int64, error) { + f, err := os.Open(file) + if err != nil { + return nil, 0, err + } + defer f.Close() + info, err := f.Stat() + if err != nil { + return nil, 0, err + } + if !info.Mode().IsRegular() { + return nil, 0, fmt.Errorf("not a regular file") + } + size := info.Size() + if size <= 1*1024 { + data, err := ioutil.ReadAll(f) + if err != nil { + return nil, 0, err + } + if int64(len(data)) != size { + return nil, 0, fmt.Errorf("file changed between reads") + } + var sym *obj.LSym + if readonly { + sym = stringsym(pos, string(data)) + } else { + sym = slicedata(pos, string(data)).Sym.Linksym() + } + if len(hash) > 0 { + sum := sha256.Sum256(data) + copy(hash, sum[:]) + } + return sym, size, nil + } + if size > 2e9 { + // ggloblsym takes an int32, + // and probably the rest of the toolchain + // can't handle such big symbols either. + // See golang.org/issue/9862. + return nil, 0, fmt.Errorf("file too large") + } -func slicebytes(nam *Node, s string) { - slicebytes_gen++ - symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen) + // File is too big to read and keep in memory. + // Compute hash if needed for read-only content hashing or if the caller wants it. + var sum []byte + if readonly || len(hash) > 0 { + h := sha256.New() + n, err := io.Copy(h, f) + if err != nil { + return nil, 0, err + } + if n != size { + return nil, 0, fmt.Errorf("file changed between reads") + } + sum = h.Sum(nil) + copy(hash, sum) + } + + var symdata *obj.LSym + if readonly { + symname := fmt.Sprintf(stringSymPattern, size, sum) + symdata = Ctxt.Lookup(stringSymPrefix + symname) + if !symdata.OnList() { + info := symdata.NewFileInfo() + info.Name = file + info.Size = size + ggloblsym(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL) + // Note: AttrContentAddressable cannot be set here, + // because the content-addressable-handling code + // does not know about file symbols. + } + } else { + // Emit a zero-length data symbol + // and then fix up length and content to use file. + symdata = slicedata(pos, "").Sym.Linksym() + symdata.Size = size + symdata.Type = objabi.SNOPTRDATA + info := symdata.NewFileInfo() + info.Name = file + info.Size = size + } + + return symdata, size, nil +} + +var slicedataGen int + +func slicedata(pos src.XPos, s string) *Node { + slicedataGen++ + symname := fmt.Sprintf(".gobytes.%d", slicedataGen) sym := localpkg.Lookup(symname) symnode := newname(sym) sym.Def = asTypesNode(symnode) lsym := sym.Linksym() - off := dsname(lsym, 0, s, nam.Pos, "slice") + off := dstringdata(lsym, 0, s, pos, "slice") ggloblsym(lsym, int32(off), obj.NOPTR|obj.LOCAL) + return symnode +} + +func slicebytes(nam *Node, s string) { if nam.Op != ONAME { Fatalf("slicebytes %v", nam) } - slicesym(nam, symnode, int64(len(s))) + slicesym(nam, slicedata(nam.Pos, s), int64(len(s))) } -func dsname(s *obj.LSym, off int, t string, pos src.XPos, what string) int { +func dstringdata(s *obj.LSym, off int, t string, pos src.XPos, what string) int { // Objects that are too large will cause the data section to overflow right away, // causing a cryptic error message by the linker. Check for oversize objects here // and provide a useful error message instead. diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index e3b4963977..83b5db834f 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -480,11 +480,87 @@ type Param struct { Innermost *Node Outer *Node - // OTYPE - // - // TODO: Should Func pragmas also be stored on the Name? - Pragma PragmaFlag - Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE) + // OTYPE & ONAME //go:embed info, + // sharing storage to reduce gc.Param size. + // Extra is nil, or else *Extra is a *paramType or an *embedFileList. + Extra *interface{} +} + +type paramType struct { + flag PragmaFlag + alias bool +} + +type embedFileList []string + +// Pragma returns the PragmaFlag for p, which must be for an OTYPE. +func (p *Param) Pragma() PragmaFlag { + if p.Extra == nil { + return 0 + } + return (*p.Extra).(*paramType).flag +} + +// SetPragma sets the PragmaFlag for p, which must be for an OTYPE. +func (p *Param) SetPragma(flag PragmaFlag) { + if p.Extra == nil { + if flag == 0 { + return + } + p.Extra = new(interface{}) + *p.Extra = ¶mType{flag: flag} + return + } + (*p.Extra).(*paramType).flag = flag +} + +// Alias reports whether p, which must be for an OTYPE, is a type alias. +func (p *Param) Alias() bool { + if p.Extra == nil { + return false + } + t, ok := (*p.Extra).(*paramType) + if !ok { + return false + } + return t.alias +} + +// SetAlias sets whether p, which must be for an OTYPE, is a type alias. +func (p *Param) SetAlias(alias bool) { + if p.Extra == nil { + if !alias { + return + } + p.Extra = new(interface{}) + *p.Extra = ¶mType{alias: alias} + return + } + (*p.Extra).(*paramType).alias = alias +} + +// EmbedFiles returns the list of embedded files for p, +// which must be for an ONAME var. +func (p *Param) EmbedFiles() []string { + if p.Extra == nil { + return nil + } + return *(*p.Extra).(*embedFileList) +} + +// SetEmbedFiles sets the list of embedded files for p, +// which must be for an ONAME var. +func (p *Param) SetEmbedFiles(list []string) { + if p.Extra == nil { + if len(list) == 0 { + return + } + f := embedFileList(list) + p.Extra = new(interface{}) + *p.Extra = &f + return + } + *(*p.Extra).(*embedFileList) = list } // Functions diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index a4b462da1d..ce817db446 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -257,12 +257,12 @@ func typecheck(n *Node, top int) (res *Node) { // are substituted. cycle := cycleFor(n) for _, n1 := range cycle { - if n1.Name != nil && !n1.Name.Param.Alias { + if n1.Name != nil && !n1.Name.Param.Alias() { // Cycle is ok. But if n is an alias type and doesn't // have a type yet, we have a recursive type declaration // with aliases that we can't handle properly yet. // Report an error rather than crashing later. - if n.Name != nil && n.Name.Param.Alias && n.Type == nil { + if n.Name != nil && n.Name.Param.Alias() && n.Type == nil { lineno = n.Pos Fatalf("cannot handle alias type declaration (issue #25838): %v", n) } @@ -3504,7 +3504,7 @@ func setUnderlying(t, underlying *types.Type) { } // Propagate go:notinheap pragma from the Name to the Type. - if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma&NotInHeap != 0 { + if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma()&NotInHeap != 0 { t.SetNotInHeap(true) } @@ -3676,7 +3676,7 @@ func typecheckdef(n *Node) { n.Name.Defn = typecheck(n.Name.Defn, ctxStmt) // fills in n.Type case OTYPE: - if p := n.Name.Param; p.Alias { + if p := n.Name.Param; p.Alias() { // Type alias declaration: Simply use the rhs type - no need // to create a new type. // If we have a syntax error, p.Ntype may be nil. diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index ad4708138f..2037beca72 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -401,7 +401,7 @@ type LSym struct { P []byte R []Reloc - Extra *interface{} // *FuncInfo if present + Extra *interface{} // *FuncInfo or *FileInfo, if present Pkg string PkgIdx int32 @@ -454,6 +454,33 @@ func (s *LSym) Func() *FuncInfo { return f } +// A FileInfo contains extra fields for SDATA symbols backed by files. +// (If LSym.Extra is a *FileInfo, LSym.P == nil.) +type FileInfo struct { + Name string // name of file to read into object file + Size int64 // length of file +} + +// NewFileInfo allocates and returns a FileInfo for LSym. +func (s *LSym) NewFileInfo() *FileInfo { + if s.Extra != nil { + log.Fatalf("invalid use of LSym - NewFileInfo with Extra of type %T", *s.Extra) + } + f := new(FileInfo) + s.Extra = new(interface{}) + *s.Extra = f + return f +} + +// File returns the *FileInfo associated with s, or else nil. +func (s *LSym) File() *FileInfo { + if s.Extra == nil { + return nil + } + f, _ := (*s.Extra).(*FileInfo) + return f +} + type InlMark struct { // When unwinding from an instruction in an inlined body, mark // where we should unwind to. diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index a24a7b878f..bb58b4f0c2 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -16,6 +16,8 @@ import ( "encoding/binary" "fmt" "io" + "log" + "os" "path/filepath" "sort" "strings" @@ -147,14 +149,20 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) { // Data indexes h.Offsets[goobj.BlkDataIdx] = w.Offset() - dataOff := uint32(0) + dataOff := int64(0) for _, list := range lists { for _, s := range list { - w.Uint32(dataOff) - dataOff += uint32(len(s.P)) + w.Uint32(uint32(dataOff)) + dataOff += int64(len(s.P)) + if file := s.File(); file != nil { + dataOff += int64(file.Size) + } } } - w.Uint32(dataOff) + if int64(uint32(dataOff)) != dataOff { + log.Fatalf("data too large") + } + w.Uint32(uint32(dataOff)) // Relocs h.Offsets[goobj.BlkReloc] = w.Offset() @@ -179,6 +187,9 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) { for _, list := range lists { for _, s := range list { w.Bytes(s.P) + if file := s.File(); file != nil { + w.writeFile(ctxt, file) + } } } @@ -218,6 +229,7 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) { type writer struct { *goobj.Writer + filebuf []byte ctxt *Link pkgpath string // the package import path (escaped), "" if unknown pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx @@ -232,6 +244,35 @@ func (w *writer) init() { } } +func (w *writer) writeFile(ctxt *Link, file *FileInfo) { + f, err := os.Open(file.Name) + if err != nil { + ctxt.Diag("%v", err) + return + } + defer f.Close() + if w.filebuf == nil { + w.filebuf = make([]byte, 1024) + } + buf := w.filebuf + written := int64(0) + for { + n, err := f.Read(buf) + w.Bytes(buf[:n]) + written += int64(n) + if err == io.EOF { + break + } + if err != nil { + ctxt.Diag("%v", err) + return + } + } + if written != file.Size { + ctxt.Diag("copy %s: unexpected length %d != %d", file.Name, written, file.Size) + } +} + func (w *writer) StringTable() { w.AddString("") for _, p := range w.ctxt.Imports { -- cgit v1.3 From 44dbeaf35600ae70f3e6296914ea31147d5f010c Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 9 Oct 2020 12:41:50 -0400 Subject: cmd/compile: intrinsify runtime/internal/atomic.{And,Or} on AMD64 These are identical to And8 and Or8, just using ANDL/ORL instead of ANDB/ORB. Change-Id: I99cf90a8b0b5f211fb23325dddd55821875f0c8f Reviewed-on: https://go-review.googlesource.com/c/go/+/263140 Run-TryBot: Michael Pratt TryBot-Result: Go Bot Trust: Michael Pratt Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/amd64/ssa.go | 2 +- src/cmd/compile/internal/gc/ssa.go | 12 +++++++ src/cmd/compile/internal/ssa/gen/AMD64.rules | 6 ++-- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 2 ++ src/cmd/compile/internal/ssa/gen/genericOps.go | 2 ++ src/cmd/compile/internal/ssa/opGen.go | 48 ++++++++++++++++++++++++++ src/cmd/compile/internal/ssa/rewriteAMD64.go | 34 ++++++++++++++++++ 7 files changed, 103 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index 4ac877986c..f30a47b903 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -1210,7 +1210,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p = s.Prog(x86.ASETEQ) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg0() - case ssa.OpAMD64ANDBlock, ssa.OpAMD64ORBlock: + case ssa.OpAMD64ANDBlock, ssa.OpAMD64ANDLlock, ssa.OpAMD64ORBlock, ssa.OpAMD64ORLlock: s.Prog(x86.ALOCK) p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 65beb84911..10d0d5fb56 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3541,12 +3541,24 @@ func init() { return nil }, sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) + addF("runtime/internal/atomic", "And", + func(s *state, n *Node, args []*ssa.Value) *ssa.Value { + s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem()) + return nil + }, + sys.AMD64) // TODO: same arches as And8. addF("runtime/internal/atomic", "Or8", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem()) return nil }, sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) + addF("runtime/internal/atomic", "Or", + func(s *state, n *Node, args []*ssa.Value) *ssa.Value { + s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem()) + return nil + }, + sys.AMD64) // TODO: same arches as Or8. alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...) alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index 8a253035e0..b9b29a489d 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -530,8 +530,10 @@ (AtomicCompareAndSwap64 ptr old new_ mem) => (CMPXCHGQlock ptr old new_ mem) // Atomic memory updates. -(AtomicAnd8 ptr val mem) => (ANDBlock ptr val mem) -(AtomicOr8 ptr val mem) => (ORBlock ptr val mem) +(AtomicAnd8 ptr val mem) => (ANDBlock ptr val mem) +(AtomicAnd32 ptr val mem) => (ANDLlock ptr val mem) +(AtomicOr8 ptr val mem) => (ORBlock ptr val mem) +(AtomicOr32 ptr val mem) => (ORLlock ptr val mem) // Write barrier. (WB ...) => (LoweredWB ...) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index 2df5016d59..de5372670b 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -902,7 +902,9 @@ func init() { // Atomic memory updates. {name: "ANDBlock", argLength: 3, reg: gpstore, asm: "ANDB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) &= arg1 + {name: "ANDLlock", argLength: 3, reg: gpstore, asm: "ANDL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) &= arg1 {name: "ORBlock", argLength: 3, reg: gpstore, asm: "ORB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) |= arg1 + {name: "ORLlock", argLength: 3, reg: gpstore, asm: "ORL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) |= arg1 } var AMD64blocks = []blockData{ diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 12ba9f1fc9..23bd4af2cd 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -565,7 +565,9 @@ var genericOps = []opData{ {name: "AtomicCompareAndSwap64", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true if store happens and new memory. {name: "AtomicCompareAndSwapRel32", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Lock release, reports whether store happens and new memory. {name: "AtomicAnd8", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 &= arg1. arg2=memory. Returns memory. + {name: "AtomicAnd32", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 &= arg1. arg2=memory. Returns memory. {name: "AtomicOr8", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory. + {name: "AtomicOr32", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory. // Atomic operation variants // These variants have the same semantics as above atomic operations. diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index e9c63cdddf..00efc8f38d 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1034,7 +1034,9 @@ const ( OpAMD64CMPXCHGLlock OpAMD64CMPXCHGQlock OpAMD64ANDBlock + OpAMD64ANDLlock OpAMD64ORBlock + OpAMD64ORLlock OpARMADD OpARMADDconst @@ -2854,7 +2856,9 @@ const ( OpAtomicCompareAndSwap64 OpAtomicCompareAndSwapRel32 OpAtomicAnd8 + OpAtomicAnd32 OpAtomicOr8 + OpAtomicOr32 OpAtomicAdd32Variant OpAtomicAdd64Variant OpClobber @@ -13575,6 +13579,22 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ANDLlock", + auxType: auxSymOff, + argLen: 3, + clobberFlags: true, + faultOnNilArg0: true, + hasSideEffects: true, + symEffect: SymRdWr, + asm: x86.AANDL, + reg: regInfo{ + inputs: []inputInfo{ + {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + }, + }, { name: "ORBlock", auxType: auxSymOff, @@ -13591,6 +13611,22 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ORLlock", + auxType: auxSymOff, + argLen: 3, + clobberFlags: true, + faultOnNilArg0: true, + hasSideEffects: true, + symEffect: SymRdWr, + asm: x86.AORL, + reg: regInfo{ + inputs: []inputInfo{ + {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + }, + }, { name: "ADD", @@ -35520,12 +35556,24 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, generic: true, }, + { + name: "AtomicAnd32", + argLen: 3, + hasSideEffects: true, + generic: true, + }, { name: "AtomicOr8", argLen: 3, hasSideEffects: true, generic: true, }, + { + name: "AtomicOr32", + argLen: 3, + hasSideEffects: true, + generic: true, + }, { name: "AtomicAdd32Variant", argLen: 3, diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 32ef26f98d..15bb627450 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -572,6 +572,8 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAtomicAdd32(v) case OpAtomicAdd64: return rewriteValueAMD64_OpAtomicAdd64(v) + case OpAtomicAnd32: + return rewriteValueAMD64_OpAtomicAnd32(v) case OpAtomicAnd8: return rewriteValueAMD64_OpAtomicAnd8(v) case OpAtomicCompareAndSwap32: @@ -590,6 +592,8 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAtomicLoad8(v) case OpAtomicLoadPtr: return rewriteValueAMD64_OpAtomicLoadPtr(v) + case OpAtomicOr32: + return rewriteValueAMD64_OpAtomicOr32(v) case OpAtomicOr8: return rewriteValueAMD64_OpAtomicOr8(v) case OpAtomicStore32: @@ -28476,6 +28480,21 @@ func rewriteValueAMD64_OpAtomicAdd64(v *Value) bool { return true } } +func rewriteValueAMD64_OpAtomicAnd32(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (AtomicAnd32 ptr val mem) + // result: (ANDLlock ptr val mem) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpAMD64ANDLlock) + v.AddArg3(ptr, val, mem) + return true + } +} func rewriteValueAMD64_OpAtomicAnd8(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] @@ -28607,6 +28626,21 @@ func rewriteValueAMD64_OpAtomicLoadPtr(v *Value) bool { return true } } +func rewriteValueAMD64_OpAtomicOr32(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (AtomicOr32 ptr val mem) + // result: (ORLlock ptr val mem) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpAMD64ORLlock) + v.AddArg3(ptr, val, mem) + return true + } +} func rewriteValueAMD64_OpAtomicOr8(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] -- cgit v1.3 From cdb19b4dba58c0e3cabde8b728156dfe273707b3 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 16 Oct 2020 16:34:52 -0400 Subject: cmd/compile: intrinsify runtime/internal/atomic.{And,Or} on ARM64 These are identical to And8 and Or8, just using LDAXRW/STLXRW instead of LDAXRB/STLXRB. Change-Id: I5308832ae165064550bee4bb245809ab952f4cc8 Reviewed-on: https://go-review.googlesource.com/c/go/+/263148 Run-TryBot: Michael Pratt TryBot-Result: Go Bot Trust: Michael Pratt Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/arm64/ssa.go | 18 ++++++++---- src/cmd/compile/internal/gc/ssa.go | 4 +-- src/cmd/compile/internal/ssa/gen/ARM64.rules | 6 ++-- src/cmd/compile/internal/ssa/gen/ARM64Ops.go | 6 ++-- src/cmd/compile/internal/ssa/opGen.go | 38 +++++++++++++++++++++++++ src/cmd/compile/internal/ssa/rewriteARM64.go | 42 ++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 11 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go index 1d6ea6b9d8..5c695ef84c 100644 --- a/src/cmd/compile/internal/arm64/ssa.go +++ b/src/cmd/compile/internal/arm64/ssa.go @@ -688,15 +688,23 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p5.To.Reg = out gc.Patch(p2, p5) case ssa.OpARM64LoweredAtomicAnd8, - ssa.OpARM64LoweredAtomicOr8: - // LDAXRB (Rarg0), Rout + ssa.OpARM64LoweredAtomicAnd32, + ssa.OpARM64LoweredAtomicOr8, + ssa.OpARM64LoweredAtomicOr32: + // LDAXRB/LDAXRW (Rarg0), Rout // AND/OR Rarg1, Rout - // STLXRB Rout, (Rarg0), Rtmp + // STLXRB/STLXRB Rout, (Rarg0), Rtmp // CBNZ Rtmp, -3(PC) + ld := arm64.ALDAXRB + st := arm64.ASTLXRB + if v.Op == ssa.OpARM64LoweredAtomicAnd32 || v.Op == ssa.OpARM64LoweredAtomicOr32 { + ld = arm64.ALDAXRW + st = arm64.ASTLXRW + } r0 := v.Args[0].Reg() r1 := v.Args[1].Reg() out := v.Reg0() - p := s.Prog(arm64.ALDAXRB) + p := s.Prog(ld) p.From.Type = obj.TYPE_MEM p.From.Reg = r0 p.To.Type = obj.TYPE_REG @@ -706,7 +714,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p1.From.Reg = r1 p1.To.Type = obj.TYPE_REG p1.To.Reg = out - p2 := s.Prog(arm64.ASTLXRB) + p2 := s.Prog(st) p2.From.Type = obj.TYPE_REG p2.From.Reg = out p2.To.Type = obj.TYPE_MEM diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 10d0d5fb56..1561fe2106 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3546,7 +3546,7 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64) // TODO: same arches as And8. + sys.AMD64, sys.ARM64) // TODO: same arches as And8. addF("runtime/internal/atomic", "Or8", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem()) @@ -3558,7 +3558,7 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64) // TODO: same arches as Or8. + sys.AMD64, sys.ARM64) // TODO: same arches as Or8. alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...) alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index c4a3532632..f2d2fb6cf6 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -548,8 +548,10 @@ (AtomicCompareAndSwap(32|64) ...) => (LoweredAtomicCas(32|64) ...) // Currently the updated value is not used, but we need a register to temporarily hold it. -(AtomicAnd8 ptr val mem) => (Select1 (LoweredAtomicAnd8 ptr val mem)) -(AtomicOr8 ptr val mem) => (Select1 (LoweredAtomicOr8 ptr val mem)) +(AtomicAnd8 ptr val mem) => (Select1 (LoweredAtomicAnd8 ptr val mem)) +(AtomicAnd32 ptr val mem) => (Select1 (LoweredAtomicAnd32 ptr val mem)) +(AtomicOr8 ptr val mem) => (Select1 (LoweredAtomicOr8 ptr val mem)) +(AtomicOr32 ptr val mem) => (Select1 (LoweredAtomicOr32 ptr val mem)) (AtomicAdd(32|64)Variant ...) => (LoweredAtomicAdd(32|64)Variant ...) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index 9ff53f7e4e..fe9edbf933 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -656,12 +656,14 @@ func init() { // atomic and/or. // *arg0 &= (|=) arg1. arg2=mem. returns . auxint must be zero. - // LDAXRB (Rarg0), Rout + // LDAXR (Rarg0), Rout // AND/OR Rarg1, Rout - // STLXRB Rout, (Rarg0), Rtmp + // STLXR Rout, (Rarg0), Rtmp // CBNZ Rtmp, -3(PC) {name: "LoweredAtomicAnd8", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "AND", typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicAnd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "AND", typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicOr8", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicOr32", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier // It saves all GP registers if necessary, diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 00efc8f38d..f86210e631 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1588,7 +1588,9 @@ const ( OpARM64LoweredAtomicCas64 OpARM64LoweredAtomicCas32 OpARM64LoweredAtomicAnd8 + OpARM64LoweredAtomicAnd32 OpARM64LoweredAtomicOr8 + OpARM64LoweredAtomicOr32 OpARM64LoweredWB OpARM64LoweredPanicBoundsA OpARM64LoweredPanicBoundsB @@ -21096,6 +21098,24 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LoweredAtomicAnd32", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + asm: arm64.AAND, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "LoweredAtomicOr8", argLen: 3, @@ -21114,6 +21134,24 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LoweredAtomicOr32", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + asm: arm64.AORR, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "LoweredWB", auxType: auxSym, diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 6c48812121..327f1674b5 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -424,6 +424,8 @@ func rewriteValueARM64(v *Value) bool { case OpAtomicAdd64Variant: v.Op = OpARM64LoweredAtomicAdd64Variant return true + case OpAtomicAnd32: + return rewriteValueARM64_OpAtomicAnd32(v) case OpAtomicAnd8: return rewriteValueARM64_OpAtomicAnd8(v) case OpAtomicCompareAndSwap32: @@ -450,6 +452,8 @@ func rewriteValueARM64(v *Value) bool { case OpAtomicLoadPtr: v.Op = OpARM64LDAR return true + case OpAtomicOr32: + return rewriteValueARM64_OpAtomicOr32(v) case OpAtomicOr8: return rewriteValueARM64_OpAtomicOr8(v) case OpAtomicStore32: @@ -21340,6 +21344,25 @@ func rewriteValueARM64_OpAddr(v *Value) bool { return true } } +func rewriteValueARM64_OpAtomicAnd32(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (AtomicAnd32 ptr val mem) + // result: (Select1 (LoweredAtomicAnd32 ptr val mem)) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicAnd32, types.NewTuple(typ.UInt32, types.TypeMem)) + v0.AddArg3(ptr, val, mem) + v.AddArg(v0) + return true + } +} func rewriteValueARM64_OpAtomicAnd8(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] @@ -21359,6 +21382,25 @@ func rewriteValueARM64_OpAtomicAnd8(v *Value) bool { return true } } +func rewriteValueARM64_OpAtomicOr32(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (AtomicOr32 ptr val mem) + // result: (Select1 (LoweredAtomicOr32 ptr val mem)) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicOr32, types.NewTuple(typ.UInt32, types.TypeMem)) + v0.AddArg3(ptr, val, mem) + v.AddArg(v0) + return true + } +} func rewriteValueARM64_OpAtomicOr8(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] -- cgit v1.3 From e223c6cf073fe1228c54050a43042b9c5721d4f8 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 16 Oct 2020 17:07:14 -0400 Subject: cmd/compile: intrinsify runtime/internal/atomic.{And,Or} on PPC64 This is a simple case of changing the operand size of the existing 8-bit And/Or. I've also updated a few operand descriptions that were out-of-sync with the implementation. Change-Id: I95ac4445d08f7958768aec9a233698a2d652a39a Reviewed-on: https://go-review.googlesource.com/c/go/+/263150 Run-TryBot: Michael Pratt TryBot-Result: Go Bot Trust: Michael Pratt Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 4 ++-- src/cmd/compile/internal/ppc64/ssa.go | 22 +++++++++++++++++----- src/cmd/compile/internal/ssa/gen/PPC64.rules | 6 ++++-- src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 16 +++++++--------- src/cmd/compile/internal/ssa/opGen.go | 28 ++++++++++++++++++++++++++++ src/cmd/compile/internal/ssa/rewritePPC64.go | 6 ++++++ 6 files changed, 64 insertions(+), 18 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 1561fe2106..2b64b358ed 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3546,7 +3546,7 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64) // TODO: same arches as And8. + sys.AMD64, sys.ARM64, sys.PPC64) // TODO: same arches as And8. addF("runtime/internal/atomic", "Or8", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem()) @@ -3558,7 +3558,7 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64) // TODO: same arches as Or8. + sys.AMD64, sys.ARM64, sys.PPC64) // TODO: same arches as And8. alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...) alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...) diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go index 1ece4d999f..79f18bfebb 100644 --- a/src/cmd/compile/internal/ppc64/ssa.go +++ b/src/cmd/compile/internal/ppc64/ssa.go @@ -166,34 +166,46 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p2.To.Reg = v.Reg1() case ssa.OpPPC64LoweredAtomicAnd8, - ssa.OpPPC64LoweredAtomicOr8: + ssa.OpPPC64LoweredAtomicAnd32, + ssa.OpPPC64LoweredAtomicOr8, + ssa.OpPPC64LoweredAtomicOr32: // LWSYNC - // LBAR (Rarg0), Rtmp + // LBAR/LWAR (Rarg0), Rtmp // AND/OR Rarg1, Rtmp - // STBCCC Rtmp, (Rarg0) + // STBCCC/STWCCC Rtmp, (Rarg0) // BNE -3(PC) + ld := ppc64.ALBAR + st := ppc64.ASTBCCC + if v.Op == ssa.OpPPC64LoweredAtomicAnd32 || v.Op == ssa.OpPPC64LoweredAtomicOr32 { + ld = ppc64.ALWAR + st = ppc64.ASTWCCC + } r0 := v.Args[0].Reg() r1 := v.Args[1].Reg() // LWSYNC - Assuming shared data not write-through-required nor // caching-inhibited. See Appendix B.2.2.2 in the ISA 2.07b. plwsync := s.Prog(ppc64.ALWSYNC) plwsync.To.Type = obj.TYPE_NONE - p := s.Prog(ppc64.ALBAR) + // LBAR or LWAR + p := s.Prog(ld) p.From.Type = obj.TYPE_MEM p.From.Reg = r0 p.To.Type = obj.TYPE_REG p.To.Reg = ppc64.REGTMP + // AND/OR reg1,out p1 := s.Prog(v.Op.Asm()) p1.From.Type = obj.TYPE_REG p1.From.Reg = r1 p1.To.Type = obj.TYPE_REG p1.To.Reg = ppc64.REGTMP - p2 := s.Prog(ppc64.ASTBCCC) + // STBCCC or STWCCC + p2 := s.Prog(st) p2.From.Type = obj.TYPE_REG p2.From.Reg = ppc64.REGTMP p2.To.Type = obj.TYPE_MEM p2.To.Reg = r0 p2.RegTo2 = ppc64.REGTMP + // BNE retry p3 := s.Prog(ppc64.ABNE) p3.To.Type = obj.TYPE_BRANCH gc.Patch(p3, p) diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules index 11b1a318fe..6175b42b89 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64.rules +++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules @@ -980,8 +980,10 @@ (AtomicCompareAndSwap(32|64) ptr old new_ mem) => (LoweredAtomicCas(32|64) [1] ptr old new_ mem) (AtomicCompareAndSwapRel32 ptr old new_ mem) => (LoweredAtomicCas32 [0] ptr old new_ mem) -(AtomicAnd8 ...) => (LoweredAtomicAnd8 ...) -(AtomicOr8 ...) => (LoweredAtomicOr8 ...) +(AtomicAnd8 ...) => (LoweredAtomicAnd8 ...) +(AtomicAnd32 ...) => (LoweredAtomicAnd32 ...) +(AtomicOr8 ...) => (LoweredAtomicOr8 ...) +(AtomicOr32 ...) => (LoweredAtomicOr32 ...) (Slicemask x) => (SRADconst (NEG x) [63]) diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index 5885660597..f4a53262f0 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -602,25 +602,22 @@ func init() { {name: "LoweredAtomicLoadPtr", argLength: 2, reg: gpload, typ: "Int64", aux: "Int64", clobberFlags: true, faultOnNilArg0: true}, // atomic add32, 64 - // SYNC + // LWSYNC // LDAR (Rarg0), Rout // ADD Rarg1, Rout // STDCCC Rout, (Rarg0) // BNE -3(PC) - // ISYNC // return new sum - {name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, {name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, // atomic exchange32, 64 - // SYNC + // LWSYNC // LDAR (Rarg0), Rout // STDCCC Rarg1, (Rarg0) // BNE -2(PC) // ISYNC // return old val - {name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, {name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, @@ -643,15 +640,16 @@ func init() { {name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, aux: "Int64", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, {name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, aux: "Int64", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, - // atomic 8 and/or. + // atomic 8/32 and/or. // *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero. - // LBAR (Rarg0), Rtmp + // LBAR/LWAT (Rarg0), Rtmp // AND/OR Rarg1, Rtmp - // STBCCC Rtmp, (Rarg0), Rtmp + // STBCCC/STWCCC Rtmp, (Rarg0), Rtmp // BNE Rtmp, -3(PC) - {name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true}, + {name: "LoweredAtomicAnd32", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true}, {name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true}, + {name: "LoweredAtomicOr32", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true}, // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier // It preserves R0 through R17 (except special registers R1, R2, R11, R12, R13), g, and its arguments R20 and R21, diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index f86210e631..5afb4abf5c 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -2026,7 +2026,9 @@ const ( OpPPC64LoweredAtomicCas64 OpPPC64LoweredAtomicCas32 OpPPC64LoweredAtomicAnd8 + OpPPC64LoweredAtomicAnd32 OpPPC64LoweredAtomicOr8 + OpPPC64LoweredAtomicOr32 OpPPC64LoweredWB OpPPC64LoweredPanicBoundsA OpPPC64LoweredPanicBoundsB @@ -27022,6 +27024,19 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LoweredAtomicAnd32", + argLen: 3, + faultOnNilArg0: true, + hasSideEffects: true, + asm: ppc64.AAND, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "LoweredAtomicOr8", argLen: 3, @@ -27035,6 +27050,19 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LoweredAtomicOr32", + argLen: 3, + faultOnNilArg0: true, + hasSideEffects: true, + asm: ppc64.AOR, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "LoweredWB", auxType: auxSym, diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go index a820bc0c4e..84938fe27a 100644 --- a/src/cmd/compile/internal/ssa/rewritePPC64.go +++ b/src/cmd/compile/internal/ssa/rewritePPC64.go @@ -59,6 +59,9 @@ func rewriteValuePPC64(v *Value) bool { case OpAtomicAdd64: v.Op = OpPPC64LoweredAtomicAdd64 return true + case OpAtomicAnd32: + v.Op = OpPPC64LoweredAtomicAnd32 + return true case OpAtomicAnd8: v.Op = OpPPC64LoweredAtomicAnd8 return true @@ -86,6 +89,9 @@ func rewriteValuePPC64(v *Value) bool { return rewriteValuePPC64_OpAtomicLoadAcq64(v) case OpAtomicLoadPtr: return rewriteValuePPC64_OpAtomicLoadPtr(v) + case OpAtomicOr32: + v.Op = OpPPC64LoweredAtomicOr32 + return true case OpAtomicOr8: v.Op = OpPPC64LoweredAtomicOr8 return true -- cgit v1.3 From 646531c52aab82a11216f8c79c0ad0e2382a943f Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 16 Oct 2020 17:29:00 -0400 Subject: cmd/compile: intrinsify runtime/internal/atomic.{And,Or} on S390X This is a simplification of LANfloor/LAOfloor since we have a whole word. Change-Id: I791641fb4068cad3f73660ce51699ed4653ae0e6 Reviewed-on: https://go-review.googlesource.com/c/go/+/263151 Run-TryBot: Michael Pratt TryBot-Result: Go Bot Trust: Michael Pratt Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 4 +-- src/cmd/compile/internal/s390x/ssa.go | 8 ++++++ src/cmd/compile/internal/ssa/gen/S390X.rules | 3 +++ src/cmd/compile/internal/ssa/gen/S390XOps.go | 4 ++- src/cmd/compile/internal/ssa/opGen.go | 38 ++++++++++++++++++++++++---- src/cmd/compile/internal/ssa/rewriteS390X.go | 6 +++++ 6 files changed, 55 insertions(+), 8 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 2b64b358ed..e70e5a969b 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3546,7 +3546,7 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.PPC64) // TODO: same arches as And8. + sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X) // TODO: same arches as And8. addF("runtime/internal/atomic", "Or8", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem()) @@ -3558,7 +3558,7 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.PPC64) // TODO: same arches as And8. + sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X) // TODO: same arches as And8. alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...) alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...) diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go index 00d253c95a..e23b31f385 100644 --- a/src/cmd/compile/internal/s390x/ssa.go +++ b/src/cmd/compile/internal/s390x/ssa.go @@ -761,6 +761,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() gc.AddAux(&p.To, v) + case ssa.OpS390XLAN, ssa.OpS390XLAO: + // LA(N|O) Ry, TMP, 0(Rx) + op := s.Prog(v.Op.Asm()) + op.From.Type = obj.TYPE_REG + op.From.Reg = v.Args[1].Reg() + op.Reg = s390x.REGTMP + op.To.Type = obj.TYPE_MEM + op.To.Reg = v.Args[0].Reg() case ssa.OpS390XLANfloor, ssa.OpS390XLAOfloor: r := v.Args[0].Reg() // clobbered, assumed R1 in comments diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules index e564f638d3..2d6f091a4e 100644 --- a/src/cmd/compile/internal/ssa/gen/S390X.rules +++ b/src/cmd/compile/internal/ssa/gen/S390X.rules @@ -198,6 +198,9 @@ (RXSBG {s390x.NewRotateParams(59, 60, 3)} (MOVDconst [3<<3]) ptr)) mem) +(AtomicAnd32 ...) => (LAN ...) +(AtomicOr32 ...) => (LAO ...) + // Lowering extension // Note: we always extend to 64 bits even though some ops don't need that many result bits. (SignExt8to(16|32|64) ...) => (MOVBreg ...) diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go index 417b33cf91..728cfb5508 100644 --- a/src/cmd/compile/internal/ssa/gen/S390XOps.go +++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go @@ -547,8 +547,10 @@ func init() { // Atomic bitwise operations. // Note: 'floor' operations round the pointer down to the nearest word boundary // which reflects how they are used in the runtime. - {name: "LAOfloor", argLength: 3, reg: gpstorelab, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) |= arg1. arg2 = mem. + {name: "LAN", argLength: 3, reg: gpstore, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *arg0 &= arg1. arg2 = mem. {name: "LANfloor", argLength: 3, reg: gpstorelab, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) &= arg1. arg2 = mem. + {name: "LAO", argLength: 3, reg: gpstore, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *arg0 |= arg1. arg2 = mem. + {name: "LAOfloor", argLength: 3, reg: gpstorelab, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) |= arg1. arg2 = mem. // Compare and swap. // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 5afb4abf5c..a4938a4992 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -2374,8 +2374,10 @@ const ( OpS390XLAAG OpS390XAddTupleFirst32 OpS390XAddTupleFirst64 - OpS390XLAOfloor + OpS390XLAN OpS390XLANfloor + OpS390XLAO + OpS390XLAOfloor OpS390XLoweredAtomicCas32 OpS390XLoweredAtomicCas64 OpS390XLoweredAtomicExchange32 @@ -31905,11 +31907,24 @@ var opcodeTable = [...]opInfo{ reg: regInfo{}, }, { - name: "LAOfloor", + name: "LAN", argLen: 3, clobberFlags: true, hasSideEffects: true, - asm: s390x.ALAO, + asm: s390x.ALAN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4295023614}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14 SP SB + {1, 56319}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14 SP + }, + }, + }, + { + name: "LANfloor", + argLen: 3, + clobberFlags: true, + hasSideEffects: true, + asm: s390x.ALAN, reg: regInfo{ inputs: []inputInfo{ {0, 2}, // R1 @@ -31919,11 +31934,24 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "LANfloor", + name: "LAO", argLen: 3, clobberFlags: true, hasSideEffects: true, - asm: s390x.ALAN, + asm: s390x.ALAO, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4295023614}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14 SP SB + {1, 56319}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14 SP + }, + }, + }, + { + name: "LAOfloor", + argLen: 3, + clobberFlags: true, + hasSideEffects: true, + asm: s390x.ALAO, reg: regInfo{ inputs: []inputInfo{ {0, 2}, // R1 diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go index 78a57c2388..35b691c12d 100644 --- a/src/cmd/compile/internal/ssa/rewriteS390X.go +++ b/src/cmd/compile/internal/ssa/rewriteS390X.go @@ -49,6 +49,9 @@ func rewriteValueS390X(v *Value) bool { return rewriteValueS390X_OpAtomicAdd32(v) case OpAtomicAdd64: return rewriteValueS390X_OpAtomicAdd64(v) + case OpAtomicAnd32: + v.Op = OpS390XLAN + return true case OpAtomicAnd8: return rewriteValueS390X_OpAtomicAnd8(v) case OpAtomicCompareAndSwap32: @@ -69,6 +72,9 @@ func rewriteValueS390X(v *Value) bool { return rewriteValueS390X_OpAtomicLoadAcq32(v) case OpAtomicLoadPtr: return rewriteValueS390X_OpAtomicLoadPtr(v) + case OpAtomicOr32: + v.Op = OpS390XLAO + return true case OpAtomicOr8: return rewriteValueS390X_OpAtomicOr8(v) case OpAtomicStore32: -- cgit v1.3 From e5ad73508e5ab5cadfba25e25d6cc3b025865e29 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 16 Oct 2020 16:49:56 -0400 Subject: cmd/compile: intrinsify runtime/internal/atomic.{And,Or} on MIPS This one is trivial, as there are already 32-bit AND and OR ops used to implement the more complex 8-bit versions. Change-Id: Ic48a53ea291d0067ebeab8e96c82e054daf20ae7 Reviewed-on: https://go-review.googlesource.com/c/go/+/263149 Run-TryBot: Michael Pratt TryBot-Result: Go Bot Trust: Michael Pratt Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 4 ++-- src/cmd/compile/internal/ssa/gen/MIPS.rules | 3 +++ src/cmd/compile/internal/ssa/rewriteMIPS.go | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index e70e5a969b..979a092ba1 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3546,7 +3546,7 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X) // TODO: same arches as And8. + sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) addF("runtime/internal/atomic", "Or8", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem()) @@ -3558,7 +3558,7 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X) // TODO: same arches as And8. + sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...) alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...) diff --git a/src/cmd/compile/internal/ssa/gen/MIPS.rules b/src/cmd/compile/internal/ssa/gen/MIPS.rules index 96feaf9234..246b4ebdbc 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS.rules +++ b/src/cmd/compile/internal/ssa/gen/MIPS.rules @@ -383,6 +383,9 @@ (ANDconst [3] (XORconst [3] ptr)))))) mem) +(AtomicAnd32 ...) => (LoweredAtomicAnd ...) +(AtomicOr32 ...) => (LoweredAtomicOr ...) + // checks (NilCheck ...) => (LoweredNilCheck ...) diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS.go b/src/cmd/compile/internal/ssa/rewriteMIPS.go index bdafa9a957..87d1aa378f 100644 --- a/src/cmd/compile/internal/ssa/rewriteMIPS.go +++ b/src/cmd/compile/internal/ssa/rewriteMIPS.go @@ -44,6 +44,9 @@ func rewriteValueMIPS(v *Value) bool { case OpAtomicAdd32: v.Op = OpMIPSLoweredAtomicAdd return true + case OpAtomicAnd32: + v.Op = OpMIPSLoweredAtomicAnd + return true case OpAtomicAnd8: return rewriteValueMIPS_OpAtomicAnd8(v) case OpAtomicCompareAndSwap32: @@ -61,6 +64,9 @@ func rewriteValueMIPS(v *Value) bool { case OpAtomicLoadPtr: v.Op = OpMIPSLoweredAtomicLoad32 return true + case OpAtomicOr32: + v.Op = OpMIPSLoweredAtomicOr + return true case OpAtomicOr8: return rewriteValueMIPS_OpAtomicOr8(v) case OpAtomicStore32: -- cgit v1.3 From c3fe874f25ff55f73e4422bea7aa0b0e0e268f3e Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 17 Aug 2020 16:57:22 -0400 Subject: cmd/compile: avoid generating CSEs; do all aggregates; maintain debug names This adds a pass to detect common selection operations, to avoid generating duplicates. Duplicate offsets are also detected. All aggregate types are now handled; there is some freedom in where expand_calls is run, though it must run before softfloat. Debug-name-maintenance is now incremental both in decompose builtin and in expand_calls; it might be good to push this into all the decompose passes. (this is a smash of 5 CLs that rewrote some of the same code several times to deal with phase-ordering problems, and included an abandoned attempt.) For #40724. Change-Id: I2a0c32f20660bf8b99e2bcecd33545d97d2bd3c6 Reviewed-on: https://go-review.googlesource.com/c/go/+/249458 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/fmtmap_test.go | 1 + src/cmd/compile/internal/gc/ssa.go | 104 ++-- src/cmd/compile/internal/ssa/compile.go | 2 +- src/cmd/compile/internal/ssa/config.go | 1 + src/cmd/compile/internal/ssa/decompose.go | 87 ++- src/cmd/compile/internal/ssa/expand_calls.go | 555 +++++++++++++++---- src/cmd/compile/internal/ssa/export_test.go | 4 + src/cmd/compile/internal/ssa/gen/dec64.rules | 137 +++-- src/cmd/compile/internal/ssa/rewritedec64.go | 796 ++++++++++++++++++--------- 9 files changed, 1163 insertions(+), 524 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/fmtmap_test.go b/src/cmd/compile/fmtmap_test.go index 179c60187f..f8c33ec1f9 100644 --- a/src/cmd/compile/fmtmap_test.go +++ b/src/cmd/compile/fmtmap_test.go @@ -136,6 +136,7 @@ var knownFormats = map[string]string{ "cmd/compile/internal/types.EType %s": "", "cmd/compile/internal/types.EType %v": "", "cmd/internal/obj.ABI %v": "", + "cmd/internal/src.XPos %v": "", "error %v": "", "float64 %.2f": "", "float64 %.3f": "", diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 979a092ba1..f840ef4066 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4740,7 +4740,7 @@ func (s *state) getClosureAndRcvr(fn *Node) (*ssa.Value, *ssa.Value) { s.nilCheck(itab) itabidx := fn.Xoffset + 2*int64(Widthptr) + 8 // offset of fun field in runtime.itab closure := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.UintptrPtr, itabidx, itab) - rcvr := s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i) + rcvr := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, i) return closure, rcvr } @@ -6904,56 +6904,38 @@ func (e *ssafn) Auto(pos src.XPos, t *types.Type) ssa.GCNode { } func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) { - n := name.N.(*Node) ptrType := types.NewPtr(types.Types[TUINT8]) lenType := types.Types[TINT] - if n.Class() == PAUTO && !n.Name.Addrtaken() { - // Split this string up into two separate variables. - p := e.splitSlot(&name, ".ptr", 0, ptrType) - l := e.splitSlot(&name, ".len", ptrType.Size(), lenType) - return p, l - } - // Return the two parts of the larger variable. - return ssa.LocalSlot{N: n, Type: ptrType, Off: name.Off}, ssa.LocalSlot{N: n, Type: lenType, Off: name.Off + int64(Widthptr)} + // Split this string up into two separate variables. + p := e.SplitSlot(&name, ".ptr", 0, ptrType) + l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType) + return p, l } func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) { n := name.N.(*Node) u := types.Types[TUINTPTR] t := types.NewPtr(types.Types[TUINT8]) - if n.Class() == PAUTO && !n.Name.Addrtaken() { - // Split this interface up into two separate variables. - f := ".itab" - if n.Type.IsEmptyInterface() { - f = ".type" - } - c := e.splitSlot(&name, f, 0, u) // see comment in plive.go:onebitwalktype1. - d := e.splitSlot(&name, ".data", u.Size(), t) - return c, d + // Split this interface up into two separate variables. + f := ".itab" + if n.Type.IsEmptyInterface() { + f = ".type" } - // Return the two parts of the larger variable. - return ssa.LocalSlot{N: n, Type: u, Off: name.Off}, ssa.LocalSlot{N: n, Type: t, Off: name.Off + int64(Widthptr)} + c := e.SplitSlot(&name, f, 0, u) // see comment in plive.go:onebitwalktype1. + d := e.SplitSlot(&name, ".data", u.Size(), t) + return c, d } func (e *ssafn) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) { - n := name.N.(*Node) ptrType := types.NewPtr(name.Type.Elem()) lenType := types.Types[TINT] - if n.Class() == PAUTO && !n.Name.Addrtaken() { - // Split this slice up into three separate variables. - p := e.splitSlot(&name, ".ptr", 0, ptrType) - l := e.splitSlot(&name, ".len", ptrType.Size(), lenType) - c := e.splitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType) - return p, l, c - } - // Return the three parts of the larger variable. - return ssa.LocalSlot{N: n, Type: ptrType, Off: name.Off}, - ssa.LocalSlot{N: n, Type: lenType, Off: name.Off + int64(Widthptr)}, - ssa.LocalSlot{N: n, Type: lenType, Off: name.Off + int64(2*Widthptr)} + p := e.SplitSlot(&name, ".ptr", 0, ptrType) + l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType) + c := e.SplitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType) + return p, l, c } func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) { - n := name.N.(*Node) s := name.Type.Size() / 2 var t *types.Type if s == 8 { @@ -6961,53 +6943,35 @@ func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) } else { t = types.Types[TFLOAT32] } - if n.Class() == PAUTO && !n.Name.Addrtaken() { - // Split this complex up into two separate variables. - r := e.splitSlot(&name, ".real", 0, t) - i := e.splitSlot(&name, ".imag", t.Size(), t) - return r, i - } - // Return the two parts of the larger variable. - return ssa.LocalSlot{N: n, Type: t, Off: name.Off}, ssa.LocalSlot{N: n, Type: t, Off: name.Off + s} + r := e.SplitSlot(&name, ".real", 0, t) + i := e.SplitSlot(&name, ".imag", t.Size(), t) + return r, i } func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) { - n := name.N.(*Node) var t *types.Type if name.Type.IsSigned() { t = types.Types[TINT32] } else { t = types.Types[TUINT32] } - if n.Class() == PAUTO && !n.Name.Addrtaken() { - // Split this int64 up into two separate variables. - if thearch.LinkArch.ByteOrder == binary.BigEndian { - return e.splitSlot(&name, ".hi", 0, t), e.splitSlot(&name, ".lo", t.Size(), types.Types[TUINT32]) - } - return e.splitSlot(&name, ".hi", t.Size(), t), e.splitSlot(&name, ".lo", 0, types.Types[TUINT32]) - } - // Return the two parts of the larger variable. if thearch.LinkArch.ByteOrder == binary.BigEndian { - return ssa.LocalSlot{N: n, Type: t, Off: name.Off}, ssa.LocalSlot{N: n, Type: types.Types[TUINT32], Off: name.Off + 4} + return e.SplitSlot(&name, ".hi", 0, t), e.SplitSlot(&name, ".lo", t.Size(), types.Types[TUINT32]) } - return ssa.LocalSlot{N: n, Type: t, Off: name.Off + 4}, ssa.LocalSlot{N: n, Type: types.Types[TUINT32], Off: name.Off} + return e.SplitSlot(&name, ".hi", t.Size(), t), e.SplitSlot(&name, ".lo", 0, types.Types[TUINT32]) } func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot { - n := name.N.(*Node) st := name.Type ft := st.FieldType(i) var offset int64 for f := 0; f < i; f++ { offset += st.FieldType(f).Size() } - if n.Class() == PAUTO && !n.Name.Addrtaken() { - // Note: the _ field may appear several times. But - // have no fear, identically-named but distinct Autos are - // ok, albeit maybe confusing for a debugger. - return e.splitSlot(&name, "."+st.FieldName(i), offset, ft) - } - return ssa.LocalSlot{N: n, Type: ft, Off: name.Off + st.FieldOff(i)} + // Note: the _ field may appear several times. But + // have no fear, identically-named but distinct Autos are + // ok, albeit maybe confusing for a debugger. + return e.SplitSlot(&name, "."+st.FieldName(i), offset, ft) } func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot { @@ -7017,19 +6981,23 @@ func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot { e.Fatalf(n.Pos, "bad array size") } et := at.Elem() - if n.Class() == PAUTO && !n.Name.Addrtaken() { - return e.splitSlot(&name, "[0]", 0, et) - } - return ssa.LocalSlot{N: n, Type: et, Off: name.Off} + return e.SplitSlot(&name, "[0]", 0, et) } func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym { return itabsym(it, offset) } -// splitSlot returns a slot representing the data of parent starting at offset. -func (e *ssafn) splitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot { - s := &types.Sym{Name: parent.N.(*Node).Sym.Name + suffix, Pkg: localpkg} +// SplitSlot returns a slot representing the data of parent starting at offset. +func (e *ssafn) SplitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot { + node := parent.N.(*Node) + + if node.Class() != PAUTO || node.Name.Addrtaken() { + // addressed things and non-autos retain their parents (i.e., cannot truly be split) + return ssa.LocalSlot{N: node, Type: t, Off: parent.Off + offset} + } + + s := &types.Sym{Name: node.Sym.Name + suffix, Pkg: localpkg} n := &Node{ Name: new(Name), diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index 0664c0ba46..bddd271273 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -441,8 +441,8 @@ var passes = [...]pass{ {name: "nilcheckelim", fn: nilcheckelim}, {name: "prove", fn: prove}, {name: "early fuse", fn: fuseEarly}, - {name: "expand calls", fn: expandCalls, required: true}, {name: "decompose builtin", fn: decomposeBuiltIn, required: true}, + {name: "expand calls", fn: expandCalls, required: true}, {name: "softfloat", fn: softfloat, required: true}, {name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules {name: "dead auto elim", fn: elimDeadAutosGeneric}, diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index f1a748309c..cb6f6fe7a1 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -149,6 +149,7 @@ type Frontend interface { SplitStruct(LocalSlot, int) LocalSlot SplitArray(LocalSlot) LocalSlot // array must be length 1 SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo) + SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot // DerefItab dereferences an itab function // entry, given the symbol of the itab and diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index ab27ba85ae..bf7f1e826b 100644 --- a/src/cmd/compile/internal/ssa/decompose.go +++ b/src/cmd/compile/internal/ssa/decompose.go @@ -6,6 +6,7 @@ package ssa import ( "cmd/compile/internal/types" + "sort" ) // decompose converts phi ops on compound builtin types into phi @@ -31,77 +32,79 @@ func decomposeBuiltIn(f *Func) { } // Split up named values into their components. + // accumulate old names for aggregates (that are decomposed) in toDelete for efficient bulk deletion, + // accumulate new LocalSlots in newNames for addition after the iteration. This decomposition is for + // builtin types with leaf components, and thus there is no need to reprocess the newly create LocalSlots. + var toDelete []namedVal var newNames []LocalSlot - for _, name := range f.Names { + for i, name := range f.Names { t := name.Type switch { case t.IsInteger() && t.Size() > f.Config.RegSize: hiName, loName := f.fe.SplitInt64(name) newNames = append(newNames, hiName, loName) - for _, v := range f.NamedValues[name] { + for j, v := range f.NamedValues[name] { if v.Op != OpInt64Make { continue } f.NamedValues[hiName] = append(f.NamedValues[hiName], v.Args[0]) f.NamedValues[loName] = append(f.NamedValues[loName], v.Args[1]) + toDelete = append(toDelete, namedVal{i, j}) } - delete(f.NamedValues, name) case t.IsComplex(): rName, iName := f.fe.SplitComplex(name) newNames = append(newNames, rName, iName) - for _, v := range f.NamedValues[name] { + for j, v := range f.NamedValues[name] { if v.Op != OpComplexMake { continue } f.NamedValues[rName] = append(f.NamedValues[rName], v.Args[0]) f.NamedValues[iName] = append(f.NamedValues[iName], v.Args[1]) - + toDelete = append(toDelete, namedVal{i, j}) } - delete(f.NamedValues, name) case t.IsString(): ptrName, lenName := f.fe.SplitString(name) newNames = append(newNames, ptrName, lenName) - for _, v := range f.NamedValues[name] { + for j, v := range f.NamedValues[name] { if v.Op != OpStringMake { continue } f.NamedValues[ptrName] = append(f.NamedValues[ptrName], v.Args[0]) f.NamedValues[lenName] = append(f.NamedValues[lenName], v.Args[1]) + toDelete = append(toDelete, namedVal{i, j}) } - delete(f.NamedValues, name) case t.IsSlice(): ptrName, lenName, capName := f.fe.SplitSlice(name) newNames = append(newNames, ptrName, lenName, capName) - for _, v := range f.NamedValues[name] { + for j, v := range f.NamedValues[name] { if v.Op != OpSliceMake { continue } f.NamedValues[ptrName] = append(f.NamedValues[ptrName], v.Args[0]) f.NamedValues[lenName] = append(f.NamedValues[lenName], v.Args[1]) f.NamedValues[capName] = append(f.NamedValues[capName], v.Args[2]) + toDelete = append(toDelete, namedVal{i, j}) } - delete(f.NamedValues, name) case t.IsInterface(): typeName, dataName := f.fe.SplitInterface(name) newNames = append(newNames, typeName, dataName) - for _, v := range f.NamedValues[name] { + for j, v := range f.NamedValues[name] { if v.Op != OpIMake { continue } f.NamedValues[typeName] = append(f.NamedValues[typeName], v.Args[0]) f.NamedValues[dataName] = append(f.NamedValues[dataName], v.Args[1]) + toDelete = append(toDelete, namedVal{i, j}) } - delete(f.NamedValues, name) case t.IsFloat(): // floats are never decomposed, even ones bigger than RegSize - newNames = append(newNames, name) case t.Size() > f.Config.RegSize: f.Fatalf("undecomposed named type %s %v", name, t) - default: - newNames = append(newNames, name) } } - f.Names = newNames + + deleteNamedVals(f, toDelete) + f.Names = append(f.Names, newNames...) } func decomposeBuiltInPhi(v *Value) { @@ -263,14 +266,20 @@ func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalS f.Fatalf("array not of size 1") } elemName := f.fe.SplitArray(name) + var keep []*Value for _, v := range f.NamedValues[name] { if v.Op != OpArrayMake1 { + keep = append(keep, v) continue } f.NamedValues[elemName] = append(f.NamedValues[elemName], v.Args[0]) } - // delete the name for the array as a whole - delete(f.NamedValues, name) + if len(keep) == 0 { + // delete the name for the array as a whole + delete(f.NamedValues, name) + } else { + f.NamedValues[name] = keep + } if t.Elem().IsArray() { return decomposeUserArrayInto(f, elemName, slots) @@ -300,17 +309,23 @@ func decomposeUserStructInto(f *Func, name LocalSlot, slots []LocalSlot) []Local } makeOp := StructMakeOp(n) + var keep []*Value // create named values for each struct field for _, v := range f.NamedValues[name] { if v.Op != makeOp { + keep = append(keep, v) continue } for i := 0; i < len(fnames); i++ { f.NamedValues[fnames[i]] = append(f.NamedValues[fnames[i]], v.Args[i]) } } - // remove the name of the struct as a whole - delete(f.NamedValues, name) + if len(keep) == 0 { + // delete the name for the struct as a whole + delete(f.NamedValues, name) + } else { + f.NamedValues[name] = keep + } // now that this f.NamedValues contains values for the struct // fields, recurse into nested structs @@ -400,3 +415,35 @@ func StructMakeOp(nf int) Op { } panic("too many fields in an SSAable struct") } + +type namedVal struct { + locIndex, valIndex int // f.NamedValues[f.Names[locIndex]][valIndex] = key +} + +// deleteNamedVals removes particular values with debugger names from f's naming data structures +func deleteNamedVals(f *Func, toDelete []namedVal) { + // Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalid pending indices. + sort.Slice(toDelete, func(i, j int) bool { + if toDelete[i].locIndex != toDelete[j].locIndex { + return toDelete[i].locIndex > toDelete[j].locIndex + } + return toDelete[i].valIndex > toDelete[j].valIndex + + }) + + // Get rid of obsolete names + for _, d := range toDelete { + loc := f.Names[d.locIndex] + vals := f.NamedValues[loc] + l := len(vals) - 1 + if l > 0 { + vals[d.valIndex] = vals[l] + f.NamedValues[loc] = vals[:l] + } else { + delete(f.NamedValues, loc) + l = len(f.Names) - 1 + f.Names[d.locIndex] = f.Names[l] + f.Names = f.Names[:l] + } + } +} diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index bbd9aeee51..3e3573ff39 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -11,27 +11,44 @@ import ( "sort" ) +type selKey struct { + from *Value + offset int64 + size int64 + typ types.EType +} + +type offsetKey struct { + from *Value + offset int64 + pt *types.Type +} + // expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form // that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into // more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are -// reached (for now, Strings, Slices, Complex, and Interface are not decomposed because they are rewritten in -// a subsequent phase, but that may need to change for a register ABI in case one of those composite values is -// split between registers and memory). -// -// TODO: when it comes time to use registers, might want to include builtin selectors as well, but currently that happens in lower. +// reached. func expandCalls(f *Func) { + // Calls that need lowering have some number of inputs, including a memory input, + // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able. + + // With the current ABI those inputs need to be converted into stores to memory, + // rethreading the call's memory input to the first, and the new call now receiving the last. + + // With the current ABI, the outputs need to be converted to loads, which will all use the call's + // memory output as their input. if !LateCallExpansionEnabledWithin(f) { return } + debug := f.pass.debug > 0 + canSSAType := f.fe.CanSSA regSize := f.Config.RegSize sp, _ := f.spSb() + typ := &f.Config.Types + ptrSize := f.Config.PtrSize - debug := f.pass.debug > 0 - - // For 32-bit, need to deal with decomposition of 64-bit integers - tUint32 := types.Types[types.TUINT32] - tInt32 := types.Types[types.TINT32] + // For 32-bit, need to deal with decomposition of 64-bit integers, which depends on endianness. var hiOffset, lowOffset int64 if f.Config.BigEndian { lowOffset = 4 @@ -39,25 +56,63 @@ func expandCalls(f *Func) { hiOffset = 4 } + namedSelects := make(map[*Value][]namedVal) + // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target // that has no 64-bit integer registers. intPairTypes := func(et types.EType) (tHi, tLo *types.Type) { - tHi = tUint32 + tHi = typ.UInt32 if et == types.TINT64 { - tHi = tInt32 + tHi = typ.Int32 } - tLo = tUint32 + tLo = typ.UInt32 return } // isAlreadyExpandedAggregateType returns whether a type is an SSA-able "aggregate" (multiple register) type - // that was expanded in an earlier phase (small user-defined arrays and structs, lowered in decomposeUser). - // Other aggregate types are expanded in decomposeBuiltin, which comes later. + // that was expanded in an earlier phase (currently, expand_calls is intended to run after decomposeBuiltin, + // so this is all aggregate types -- small struct and array, complex, interface, string, slice, and 64-bit + // integer on 32-bit). isAlreadyExpandedAggregateType := func(t *types.Type) bool { if !canSSAType(t) { return false } - return t.IsStruct() || t.IsArray() || regSize == 4 && t.Size() > 4 && t.IsInteger() + return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice() || + t.Size() > regSize && t.IsInteger() + } + + offsets := make(map[offsetKey]*Value) + + // offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP + // TODO should also optimize offsets from SB? + offsetFrom := func(from *Value, offset int64, pt *types.Type) *Value { + if offset == 0 && from.Type == pt { // this is not actually likely + return from + } + // Simplify, canonicalize + for from.Op == OpOffPtr { + offset += from.AuxInt + from = from.Args[0] + } + if from == sp { + return f.ConstOffPtrSP(pt, offset, sp) + } + key := offsetKey{from, offset, pt} + v := offsets[key] + if v != nil { + return v + } + v = from.Block.NewValue1I(from.Pos.WithNotStmt(), OpOffPtr, pt, offset, from) + offsets[key] = v + return v + } + + splitSlots := func(ls []LocalSlot, sfx string, offset int64, ty *types.Type) []LocalSlot { + var locs []LocalSlot + for i := range ls { + locs = append(locs, f.fe.SplitSlot(&ls[i], sfx, offset, ty)) + } + return locs } // removeTrivialWrapperTypes unwraps layers of @@ -97,11 +152,16 @@ func expandCalls(f *Func) { // end in OpSelectN, it does nothing (this can happen depending on compiler phase ordering). // It emits the code necessary to implement the leaf select operation that leads to the call. // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. - var rewriteSelect func(leaf *Value, selector *Value, offset int64) - rewriteSelect = func(leaf *Value, selector *Value, offset int64) { + var rewriteSelect func(leaf *Value, selector *Value, offset int64) []LocalSlot + rewriteSelect = func(leaf *Value, selector *Value, offset int64) []LocalSlot { + var locs []LocalSlot + leafType := leaf.Type switch selector.Op { case OpSelectN: // TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there. + for _, s := range namedSelects[selector] { + locs = append(locs, f.Names[s.locIndex]) + } call := selector.Args[0] aux := call.Aux.(*AuxCall) which := selector.AuxInt @@ -110,9 +170,13 @@ func expandCalls(f *Func) { leaf.copyOf(call) } else { leafType := removeTrivialWrapperTypes(leaf.Type) - pt := types.NewPtr(leafType) if canSSAType(leafType) { - off := f.ConstOffPtrSP(pt, offset+aux.OffsetOfResult(which), sp) + for leafType.Etype == types.TSTRUCT && leafType.NumFields() == 1 { + // This may not be adequately general -- consider [1]etc but this is caused by immediate IDATA + leafType = leafType.Field(0).Type + } + pt := types.NewPtr(leafType) + off := offsetFrom(sp, offset+aux.OffsetOfResult(which), pt) // Any selection right out of the arg area/registers has to be same Block as call, use call as mem input. if leaf.Block == call.Block { leaf.reset(OpLoad) @@ -123,46 +187,110 @@ func expandCalls(f *Func) { leaf.copyOf(w) } } else { - panic("Should not have non-SSA-able OpSelectN") + f.Fatalf("Should not have non-SSA-able OpSelectN, selector=%s", selector.LongString()) } } case OpStructSelect: w := selector.Args[0] + var ls []LocalSlot if w.Type.Etype != types.TSTRUCT { - fmt.Printf("Bad type for w:\nv=%v\nsel=%v\nw=%v\n,f=%s\n", leaf.LongString(), selector.LongString(), w.LongString(), f.Name) + f.Fatalf("Bad type for w: v=%v; sel=%v; w=%v; ,f=%s\n", leaf.LongString(), selector.LongString(), w.LongString(), f.Name) + // Artifact of immediate interface idata + ls = rewriteSelect(leaf, w, offset) + } else { + ls = rewriteSelect(leaf, w, offset+w.Type.FieldOff(int(selector.AuxInt))) + for _, l := range ls { + locs = append(locs, f.fe.SplitStruct(l, int(selector.AuxInt))) + } } - rewriteSelect(leaf, w, offset+w.Type.FieldOff(int(selector.AuxInt))) + + case OpArraySelect: + w := selector.Args[0] + rewriteSelect(leaf, w, offset+selector.Type.Size()*selector.AuxInt) case OpInt64Hi: w := selector.Args[0] - rewriteSelect(leaf, w, offset+hiOffset) + ls := rewriteSelect(leaf, w, offset+hiOffset) + locs = splitSlots(ls, ".hi", hiOffset, leafType) case OpInt64Lo: w := selector.Args[0] - rewriteSelect(leaf, w, offset+lowOffset) + ls := rewriteSelect(leaf, w, offset+lowOffset) + locs = splitSlots(ls, ".lo", lowOffset, leafType) - case OpArraySelect: + case OpStringPtr: + ls := rewriteSelect(leaf, selector.Args[0], offset) + locs = splitSlots(ls, ".ptr", 0, typ.BytePtr) + //for i := range ls { + // locs = append(locs, f.fe.SplitSlot(&ls[i], ".ptr", 0, typ.BytePtr)) + //} + case OpSlicePtr: w := selector.Args[0] - rewriteSelect(leaf, w, offset+selector.Type.Size()*selector.AuxInt) + ls := rewriteSelect(leaf, w, offset) + locs = splitSlots(ls, ".ptr", 0, types.NewPtr(w.Type.Elem())) + + case OpITab: + w := selector.Args[0] + ls := rewriteSelect(leaf, w, offset) + sfx := ".itab" + if w.Type.IsEmptyInterface() { + sfx = ".type" + } + locs = splitSlots(ls, sfx, 0, typ.Uintptr) + + case OpComplexReal: + ls := rewriteSelect(leaf, selector.Args[0], offset) + locs = splitSlots(ls, ".real", 0, leafType) + + case OpComplexImag: + ls := rewriteSelect(leaf, selector.Args[0], offset+leafType.Width) // result is FloatNN, width of result is offset of imaginary part. + locs = splitSlots(ls, ".imag", leafType.Width, leafType) + + case OpStringLen, OpSliceLen: + ls := rewriteSelect(leaf, selector.Args[0], offset+ptrSize) + locs = splitSlots(ls, ".len", ptrSize, leafType) + + case OpIData: + ls := rewriteSelect(leaf, selector.Args[0], offset+ptrSize) + locs = splitSlots(ls, ".data", ptrSize, leafType) + + case OpSliceCap: + ls := rewriteSelect(leaf, selector.Args[0], offset+2*ptrSize) + locs = splitSlots(ls, ".cap", 2*ptrSize, leafType) + + case OpCopy: // If it's an intermediate result, recurse + locs = rewriteSelect(leaf, selector.Args[0], offset) + for _, s := range namedSelects[selector] { + // this copy may have had its own name, preserve that, too. + locs = append(locs, f.Names[s.locIndex]) + } + default: - // Ignore dead ends; on 32-bit, these can occur running before decompose builtins. + // Ignore dead ends. These can occur if this phase is run before decompose builtin (which is not intended, but allowed). } + + return locs } // storeArg converts stores of SSA-able aggregate arguments (passed to a call) into a series of stores of // smaller types into individual parameter slots. - // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. var storeArg func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value storeArg = func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value { + if debug { + fmt.Printf("\tstoreArg(%s; %s; %v; %d; %s)\n", b, a.LongString(), t, offset, mem.String()) + } + switch a.Op { case OpArrayMake0, OpStructMake0: return mem + case OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4: for i := 0; i < t.NumFields(); i++ { fld := t.Field(i) mem = storeArg(pos, b, a.Args[i], fld.Type, offset+fld.Offset, mem) } return mem + case OpArrayMake1: return storeArg(pos, b, a.Args[0], t.Elem(), offset, mem) @@ -170,55 +298,51 @@ func expandCalls(f *Func) { tHi, tLo := intPairTypes(t.Etype) mem = storeArg(pos, b, a.Args[0], tHi, offset+hiOffset, mem) return storeArg(pos, b, a.Args[1], tLo, offset+lowOffset, mem) + + case OpComplexMake: + tPart := typ.Float32 + wPart := t.Width / 2 + if wPart == 8 { + tPart = typ.Float64 + } + mem = storeArg(pos, b, a.Args[0], tPart, offset, mem) + return storeArg(pos, b, a.Args[1], tPart, offset+wPart, mem) + + case OpIMake: + mem = storeArg(pos, b, a.Args[0], typ.Uintptr, offset, mem) + return storeArg(pos, b, a.Args[1], typ.BytePtr, offset+ptrSize, mem) + + case OpStringMake: + mem = storeArg(pos, b, a.Args[0], typ.BytePtr, offset, mem) + return storeArg(pos, b, a.Args[1], typ.Int, offset+ptrSize, mem) + + case OpSliceMake: + mem = storeArg(pos, b, a.Args[0], typ.BytePtr, offset, mem) + mem = storeArg(pos, b, a.Args[1], typ.Int, offset+ptrSize, mem) + return storeArg(pos, b, a.Args[2], typ.Int, offset+2*ptrSize, mem) } - dst := f.ConstOffPtrSP(types.NewPtr(t), offset, sp) + + dst := offsetFrom(sp, offset, types.NewPtr(t)) x := b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, a, mem) if debug { - fmt.Printf("storeArg(%v) returns %s\n", a, x.LongString()) + fmt.Printf("\t\tstoreArg returns %s\n", x.LongString()) } return x } - // offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP - // TODO should also optimize offsets from SB? - offsetFrom := func(dst *Value, offset int64, t *types.Type) *Value { - pt := types.NewPtr(t) - if offset == 0 && dst.Type == pt { // this is not actually likely - return dst - } - if dst.Op != OpOffPtr { - return dst.Block.NewValue1I(dst.Pos.WithNotStmt(), OpOffPtr, pt, offset, dst) - } - // Simplify OpOffPtr - from := dst.Args[0] - offset += dst.AuxInt - if from == sp { - return f.ConstOffPtrSP(pt, offset, sp) - } - return dst.Block.NewValue1I(dst.Pos.WithNotStmt(), OpOffPtr, pt, offset, from) - } - // splitStore converts a store of an SSA-able aggregate into a series of smaller stores, emitting // appropriate Struct/Array Select operations (which will soon go dead) to obtain the parts. - var splitStore func(dst, src, mem, v *Value, t *types.Type, offset int64, firstStorePos src.XPos) *Value - splitStore = func(dst, src, mem, v *Value, t *types.Type, offset int64, firstStorePos src.XPos) *Value { - // TODO might be worth commoning up duplicate selectors, but since they go dead, maybe no point. + // This has to handle aggregate types that have already been lowered by an earlier phase. + var splitStore func(dest, source, mem, v *Value, t *types.Type, offset int64, firstStorePos src.XPos) *Value + splitStore = func(dest, source, mem, v *Value, t *types.Type, offset int64, firstStorePos src.XPos) *Value { + if debug { + fmt.Printf("\tsplitStore(%s; %s; %s; %s; %v; %d; %v)\n", dest.LongString(), source.LongString(), mem.String(), v.LongString(), t, offset, firstStorePos) + } pos := v.Pos.WithNotStmt() switch t.Etype { - case types.TINT64, types.TUINT64: - if t.Width == regSize { - break - } - tHi, tLo := intPairTypes(t.Etype) - sel := src.Block.NewValue1(pos, OpInt64Hi, tHi, src) - mem = splitStore(dst, sel, mem, v, tHi, offset+hiOffset, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() - sel = src.Block.NewValue1(pos, OpInt64Lo, tLo, src) - return splitStore(dst, sel, mem, v, tLo, offset+lowOffset, firstStorePos) - case types.TARRAY: elt := t.Elem() - if src.Op == OpIData && t.NumElem() == 1 && t.Width == regSize && elt.Width == regSize { + if t.NumElem() == 1 && t.Width == regSize && elt.Width == regSize { t = removeTrivialWrapperTypes(t) if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY { f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it") @@ -226,13 +350,14 @@ func expandCalls(f *Func) { break // handle the leaf type. } for i := int64(0); i < t.NumElem(); i++ { - sel := src.Block.NewValue1I(pos, OpArraySelect, elt, i, src) - mem = splitStore(dst, sel, mem, v, elt, offset+i*elt.Width, firstStorePos) + sel := source.Block.NewValue1I(pos, OpArraySelect, elt, i, source) + mem = splitStore(dest, sel, mem, v, elt, offset+i*elt.Width, firstStorePos) firstStorePos = firstStorePos.WithNotStmt() } return mem + case types.TSTRUCT: - if src.Op == OpIData && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize { + if t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize { // This peculiar test deals with accesses to immediate interface data. // It works okay because everything is the same size. // Example code that triggers this can be found in go/constant/value.go, function ToComplex @@ -240,26 +365,87 @@ func expandCalls(f *Func) { // v121 (+882) = StaticLECall {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1 // This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)" // Guard against "struct{struct{*foo}}" + // Other rewriting phases create minor glitches when they transform IData, for instance the + // interface-typed Arg "x" of ToFloat in go/constant/value.go + // v6 (858) = Arg {x} (x[Value], x[Value]) + // is rewritten by decomposeArgs into + // v141 (858) = Arg {x} + // v139 (858) = Arg <*uint8> {x} [8] + // because of a type case clause on line 862 of go/constant/value.go + // case intVal: + // return itof(x) + // v139 is later stored as an intVal == struct{val *big.Int} which naively requires the fields of + // of a *uint8, which does not succeed. t = removeTrivialWrapperTypes(t) - if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY { - f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it") - } - break // handle the leaf type. + + // it could be a leaf type, but the "leaf" could be complex64 (for example) + return splitStore(dest, source, mem, v, t, offset, firstStorePos) } + for i := 0; i < t.NumFields(); i++ { fld := t.Field(i) - sel := src.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), src) - mem = splitStore(dst, sel, mem, v, fld.Type, offset+fld.Offset, firstStorePos) + sel := source.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), source) + mem = splitStore(dest, sel, mem, v, fld.Type, offset+fld.Offset, firstStorePos) firstStorePos = firstStorePos.WithNotStmt() } return mem + + case types.TINT64, types.TUINT64: + if t.Width == regSize { + break + } + tHi, tLo := intPairTypes(t.Etype) + sel := source.Block.NewValue1(pos, OpInt64Hi, tHi, source) + mem = splitStore(dest, sel, mem, v, tHi, offset+hiOffset, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + sel = source.Block.NewValue1(pos, OpInt64Lo, tLo, source) + return splitStore(dest, sel, mem, v, tLo, offset+lowOffset, firstStorePos) + + case types.TINTER: + sel := source.Block.NewValue1(pos, OpITab, typ.BytePtr, source) + mem = splitStore(dest, sel, mem, v, typ.BytePtr, offset, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + sel = source.Block.NewValue1(pos, OpIData, typ.BytePtr, source) + return splitStore(dest, sel, mem, v, typ.BytePtr, offset+ptrSize, firstStorePos) + + case types.TSTRING: + sel := source.Block.NewValue1(pos, OpStringPtr, typ.BytePtr, source) + mem = splitStore(dest, sel, mem, v, typ.BytePtr, offset, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + sel = source.Block.NewValue1(pos, OpStringLen, typ.Int, source) + return splitStore(dest, sel, mem, v, typ.Int, offset+ptrSize, firstStorePos) + + case types.TSLICE: + et := types.NewPtr(t.Elem()) + sel := source.Block.NewValue1(pos, OpSlicePtr, et, source) + mem = splitStore(dest, sel, mem, v, et, offset, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + sel = source.Block.NewValue1(pos, OpSliceLen, typ.Int, source) + mem = splitStore(dest, sel, mem, v, typ.Int, offset+ptrSize, firstStorePos) + sel = source.Block.NewValue1(pos, OpSliceCap, typ.Int, source) + return splitStore(dest, sel, mem, v, typ.Int, offset+2*ptrSize, firstStorePos) + + case types.TCOMPLEX64: + sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float32, source) + mem = splitStore(dest, sel, mem, v, typ.Float32, offset, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float32, source) + return splitStore(dest, sel, mem, v, typ.Float32, offset+4, firstStorePos) + + case types.TCOMPLEX128: + sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float64, source) + mem = splitStore(dest, sel, mem, v, typ.Float64, offset, firstStorePos) + firstStorePos = firstStorePos.WithNotStmt() + sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float64, source) + return splitStore(dest, sel, mem, v, typ.Float64, offset+8, firstStorePos) } // Default, including for aggregates whose single element exactly fills their container // TODO this will be a problem for cast interfaces containing floats when we move to registers. - x := v.Block.NewValue3A(firstStorePos, OpStore, types.TypeMem, t, offsetFrom(dst, offset, t), src, mem) + x := v.Block.NewValue3A(firstStorePos, OpStore, types.TypeMem, t, offsetFrom(dest, offset, types.NewPtr(t)), source, mem) if debug { - fmt.Printf("splitStore(%v, %v, %v, %v) returns %s\n", dst, src, mem, v, x.LongString()) + fmt.Printf("\t\tsplitStore returns %s\n", x.LongString()) } + return x } @@ -286,21 +472,24 @@ func expandCalls(f *Func) { } // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move // TODO this will be more complicated with registers in the picture. - src := a.Args[0] - dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(auxI), sp) + source := a.Args[0] + dst := f.ConstOffPtrSP(source.Type, aux.OffsetOfArg(auxI), sp) if a.Uses == 1 && a.Block == v.Block { a.reset(OpMove) a.Pos = pos a.Type = types.TypeMem a.Aux = aux.TypeOfArg(auxI) a.AuxInt = aux.SizeOfArg(auxI) - a.SetArgs3(dst, src, mem) + a.SetArgs3(dst, source, mem) mem = a } else { - mem = v.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, src, mem) + mem = v.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, source, mem) mem.AuxInt = aux.SizeOfArg(auxI) } } else { + if debug { + fmt.Printf("storeArg %s, %v, %d\n", a.LongString(), aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI)) + } mem = storeArg(pos, v.Block, a, aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI), mem) } } @@ -308,6 +497,8 @@ func expandCalls(f *Func) { return mem } + // TODO if too slow, whole program iteration can be replaced w/ slices of appropriate values, accumulated in first loop here. + // Step 0: rewrite the calls to convert incoming args to stores. for _, b := range f.Blocks { for _, v := range b.Values { @@ -328,15 +519,40 @@ func expandCalls(f *Func) { } } + for i, name := range f.Names { + t := name.Type + if isAlreadyExpandedAggregateType(t) { + for j, v := range f.NamedValues[name] { + if v.Op == OpSelectN { + ns := namedSelects[v] + namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j}) + } + } + } + } + // Step 1: any stores of aggregates remaining are believed to be sourced from call results. // Decompose those stores into a series of smaller stores, adding selection ops as necessary. for _, b := range f.Blocks { for _, v := range b.Values { if v.Op == OpStore { t := v.Aux.(*types.Type) - if isAlreadyExpandedAggregateType(t) { - dst, src, mem := v.Args[0], v.Args[1], v.Args[2] - mem = splitStore(dst, src, mem, v, t, 0, v.Pos) + iAEATt := isAlreadyExpandedAggregateType(t) + if !iAEATt { + // guarding against store immediate struct into interface data field -- store type is *uint8 + // TODO can this happen recursively? + tSrc := v.Args[1].Type + iAEATt = isAlreadyExpandedAggregateType(tSrc) + if iAEATt { + t = tSrc + } + } + if iAEATt { + if debug { + fmt.Printf("Splitting store %s\n", v.LongString()) + } + dst, source, mem := v.Args[0], v.Args[1], v.Args[2] + mem = splitStore(dst, source, mem, v, t, 0, v.Pos) v.copyOf(mem) } } @@ -345,23 +561,32 @@ func expandCalls(f *Func) { val2Preds := make(map[*Value]int32) // Used to accumulate dependency graph of selection operations for topological ordering. - // Step 2: accumulate selection operations for rewrite in topological order. + // Step 2: transform or accumulate selection operations for rewrite in topological order. + // + // Aggregate types that have already (in earlier phases) been transformed must be lowered comprehensively to finish + // the transformation (user-defined structs and arrays, slices, strings, interfaces, complex, 64-bit on 32-bit architectures), + // // Any select-for-addressing applied to call results can be transformed directly. - // TODO this is overkill; with the transformation of aggregate references into series of leaf references, it is only necessary to remember and recurse on the leaves. for _, b := range f.Blocks { for _, v := range b.Values { // Accumulate chains of selectors for processing in topological order switch v.Op { - case OpStructSelect, OpArraySelect, OpInt64Hi, OpInt64Lo: + case OpStructSelect, OpArraySelect, + OpIData, OpITab, + OpStringPtr, OpStringLen, + OpSlicePtr, OpSliceLen, OpSliceCap, + OpComplexReal, OpComplexImag, + OpInt64Hi, OpInt64Lo: w := v.Args[0] switch w.Op { - case OpStructSelect, OpArraySelect, OpInt64Hi, OpInt64Lo, OpSelectN: + case OpStructSelect, OpArraySelect, OpSelectN: val2Preds[w] += 1 if debug { fmt.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w]) } } fallthrough + case OpSelectN: if _, ok := val2Preds[v]; !ok { val2Preds[v] = 0 @@ -369,53 +594,153 @@ func expandCalls(f *Func) { fmt.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) } } + case OpSelectNAddr: // Do these directly, there are no chains of selectors. call := v.Args[0] which := v.AuxInt aux := call.Aux.(*AuxCall) pt := v.Type - off := f.ConstOffPtrSP(pt, aux.OffsetOfResult(which), sp) + off := offsetFrom(sp, aux.OffsetOfResult(which), pt) v.copyOf(off) } } } - // Compilation must be deterministic - var ordered []*Value - less := func(i, j int) bool { return ordered[i].ID < ordered[j].ID } + // Step 3: Compute topological order of selectors, + // then process it in reverse to eliminate duplicates, + // then forwards to rewrite selectors. + // + // All chains of selectors end up in same block as the call. + sdom := f.Sdom() + + // Compilation must be deterministic, so sort after extracting first zeroes from map. + // Sorting allows dominators-last order within each batch, + // so that the backwards scan for duplicates will most often find copies from dominating blocks (it is best-effort). + var toProcess []*Value + less := func(i, j int) bool { + vi, vj := toProcess[i], toProcess[j] + bi, bj := vi.Block, vj.Block + if bi == bj { + return vi.ID < vj.ID + } + return sdom.domorder(bi) > sdom.domorder(bj) // reverse the order to put dominators last. + } - // Step 3: Rewrite in topological order. All chains of selectors end up in same block as the call. + // Accumulate order in allOrdered + var allOrdered []*Value + for v, n := range val2Preds { + if n == 0 { + allOrdered = append(allOrdered, v) + } + } + last := 0 // allOrdered[0:last] has been top-sorted and processed for len(val2Preds) > 0 { - ordered = ordered[:0] - for v, n := range val2Preds { - if n == 0 { - ordered = append(ordered, v) + toProcess = allOrdered[last:] + last = len(allOrdered) + sort.SliceStable(toProcess, less) + for _, v := range toProcess { + w := v.Args[0] + delete(val2Preds, v) + n, ok := val2Preds[w] + if !ok { + continue } + if n == 1 { + allOrdered = append(allOrdered, w) + delete(val2Preds, w) + continue + } + val2Preds[w] = n - 1 } - sort.Slice(ordered, less) - for _, v := range ordered { - for { - w := v.Args[0] - if debug { - fmt.Printf("About to rewrite %s, args[0]=%s\n", v.LongString(), w.LongString()) - } - delete(val2Preds, v) - rewriteSelect(v, v, 0) - v = w - n, ok := val2Preds[v] - if !ok { - break - } - if n != 1 { - val2Preds[v] = n - 1 - break - } - // Loop on new v; val2Preds[v] == 1 will be deleted in that iteration, no need to store zero. + } + + common := make(map[selKey]*Value) + // Rewrite duplicate selectors as copies where possible. + for i := len(allOrdered) - 1; i >= 0; i-- { + v := allOrdered[i] + w := v.Args[0] + for w.Op == OpCopy { + w = w.Args[0] + } + typ := v.Type + if typ.IsMemory() { + continue // handled elsewhere, not an indexable result + } + size := typ.Width + offset := int64(0) + switch v.Op { + case OpStructSelect: + if w.Type.Etype == types.TSTRUCT { + offset = w.Type.FieldOff(int(v.AuxInt)) + } else { // Immediate interface data artifact, offset is zero. + f.Fatalf("Expand calls interface data problem, func %s, v=%s, w=%s\n", f.Name, v.LongString(), w.LongString()) } + case OpArraySelect: + offset = size * v.AuxInt + case OpSelectN: + offset = w.Aux.(*AuxCall).OffsetOfResult(v.AuxInt) + case OpInt64Hi: + offset = hiOffset + case OpInt64Lo: + offset = lowOffset + case OpStringLen, OpSliceLen, OpIData: + offset = ptrSize + case OpSliceCap: + offset = 2 * ptrSize + case OpComplexImag: + offset = size + } + sk := selKey{from: w, size: size, offset: offset, typ: typ.Etype} + dupe := common[sk] + if dupe == nil { + common[sk] = v + } else if sdom.IsAncestorEq(dupe.Block, v.Block) { + v.copyOf(dupe) + } else { + // Because values are processed in dominator order, the old common[s] will never dominate after a miss is seen. + // Installing the new value might match some future values. + common[sk] = v } } + // Indices of entries in f.Names that need to be deleted. + var toDelete []namedVal + + // Rewrite selectors. + for i, v := range allOrdered { + if debug { + b := v.Block + fmt.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses) + } + if v.Uses == 0 { + v.reset(OpInvalid) + continue + } + if v.Op == OpCopy { + continue + } + locs := rewriteSelect(v, v, 0) + // Install new names. + if v.Type.IsMemory() { + continue + } + // Leaf types may have debug locations + if !isAlreadyExpandedAggregateType(v.Type) { + for _, l := range locs { + f.NamedValues[l] = append(f.NamedValues[l], v) + } + f.Names = append(f.Names, locs...) + continue + } + // Not-leaf types that had debug locations need to lose them. + if ns, ok := namedSelects[v]; ok { + toDelete = append(toDelete, ns...) + } + } + + deleteNamedVals(f, toDelete) + // Step 4: rewrite the calls themselves, correcting the type for _, b := range f.Blocks { for _, v := range b.Values { diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index 51665c60e2..b4c3e5cfdf 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -125,6 +125,10 @@ func (d DummyFrontend) SplitStruct(s LocalSlot, i int) LocalSlot { func (d DummyFrontend) SplitArray(s LocalSlot) LocalSlot { return LocalSlot{N: s.N, Type: s.Type.Elem(), Off: s.Off} } + +func (d DummyFrontend) SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot { + return LocalSlot{N: parent.N, Type: t, Off: offset} +} func (DummyFrontend) Line(_ src.XPos) string { return "unknown.go:0" } diff --git a/src/cmd/compile/internal/ssa/gen/dec64.rules b/src/cmd/compile/internal/ssa/gen/dec64.rules index 4f9e863f90..07607960fa 100644 --- a/src/cmd/compile/internal/ssa/gen/dec64.rules +++ b/src/cmd/compile/internal/ssa/gen/dec64.rules @@ -9,7 +9,6 @@ (Int64Hi (Int64Make hi _)) => hi (Int64Lo (Int64Make _ lo)) => lo - (Load ptr mem) && is64BitInt(t) && !config.BigEndian && t.IsSigned() => (Int64Make (Load (OffPtr [4] ptr) mem) @@ -143,6 +142,10 @@ (Trunc64to32 (Int64Make _ lo)) => lo (Trunc64to16 (Int64Make _ lo)) => (Trunc32to16 lo) (Trunc64to8 (Int64Make _ lo)) => (Trunc32to8 lo) +// Most general +(Trunc64to32 x) => (Int64Lo x) +(Trunc64to16 x) => (Trunc32to16 (Int64Lo x)) +(Trunc64to8 x) => (Trunc32to8 (Int64Lo x)) (Lsh32x64 _ (Int64Make (Const32 [c]) _)) && c != 0 => (Const32 [0]) (Rsh32x64 x (Int64Make (Const32 [c]) _)) && c != 0 => (Signmask x) @@ -175,156 +178,174 @@ // turn x64 non-constant shifts to x32 shifts // if high 32-bit of the shift is nonzero, make a huge shift (Lsh64x64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Lsh64x32 x (Or32 (Zeromask hi) lo)) + (Lsh64x32 x (Or32 (Zeromask hi) lo)) (Rsh64x64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Rsh64x32 x (Or32 (Zeromask hi) lo)) + (Rsh64x32 x (Or32 (Zeromask hi) lo)) (Rsh64Ux64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Rsh64Ux32 x (Or32 (Zeromask hi) lo)) + (Rsh64Ux32 x (Or32 (Zeromask hi) lo)) (Lsh32x64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Lsh32x32 x (Or32 (Zeromask hi) lo)) + (Lsh32x32 x (Or32 (Zeromask hi) lo)) (Rsh32x64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Rsh32x32 x (Or32 (Zeromask hi) lo)) + (Rsh32x32 x (Or32 (Zeromask hi) lo)) (Rsh32Ux64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Rsh32Ux32 x (Or32 (Zeromask hi) lo)) + (Rsh32Ux32 x (Or32 (Zeromask hi) lo)) (Lsh16x64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Lsh16x32 x (Or32 (Zeromask hi) lo)) + (Lsh16x32 x (Or32 (Zeromask hi) lo)) (Rsh16x64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Rsh16x32 x (Or32 (Zeromask hi) lo)) + (Rsh16x32 x (Or32 (Zeromask hi) lo)) (Rsh16Ux64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Rsh16Ux32 x (Or32 (Zeromask hi) lo)) + (Rsh16Ux32 x (Or32 (Zeromask hi) lo)) (Lsh8x64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Lsh8x32 x (Or32 (Zeromask hi) lo)) + (Lsh8x32 x (Or32 (Zeromask hi) lo)) (Rsh8x64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Rsh8x32 x (Or32 (Zeromask hi) lo)) + (Rsh8x32 x (Or32 (Zeromask hi) lo)) (Rsh8Ux64 x (Int64Make hi lo)) && hi.Op != OpConst32 => - (Rsh8Ux32 x (Or32 (Zeromask hi) lo)) + (Rsh8Ux32 x (Or32 (Zeromask hi) lo)) + +// Most general +(Lsh64x64 x y) => (Lsh64x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Rsh64x64 x y) => (Rsh64x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Rsh64Ux64 x y) => (Rsh64Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Lsh32x64 x y) => (Lsh32x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Rsh32x64 x y) => (Rsh32x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Rsh32Ux64 x y) => (Rsh32Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Lsh16x64 x y) => (Lsh16x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Rsh16x64 x y) => (Rsh16x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Rsh16Ux64 x y) => (Rsh16Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Lsh8x64 x y) => (Lsh8x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Rsh8x64 x y) => (Rsh8x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(Rsh8Ux64 x y) => (Rsh8Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + +// Clean up constants a little +(Or32 (Zeromask (Const32 [c])) y) && c == 0 => y +(Or32 (Zeromask (Const32 [c])) y) && c != 0 => (Const32 [-1]) // 64x left shift // result.hi = hi<>(32-s) | lo<<(s-32) // >> is unsigned, large shifts result 0 // result.lo = lo< +(Lsh64x32 x s) => (Int64Make (Or32 (Or32 - (Lsh32x32 hi s) + (Lsh32x32 (Int64Hi x) s) (Rsh32Ux32 - lo + (Int64Lo x) (Sub32 (Const32 [32]) s))) (Lsh32x32 - lo + (Int64Lo x) (Sub32 s (Const32 [32])))) - (Lsh32x32 lo s)) -(Lsh64x16 (Int64Make hi lo) s) => + (Lsh32x32 (Int64Lo x) s)) +(Lsh64x16 x s) => (Int64Make (Or32 (Or32 - (Lsh32x16 hi s) + (Lsh32x16 (Int64Hi x) s) (Rsh32Ux16 - lo + (Int64Lo x) (Sub16 (Const16 [32]) s))) (Lsh32x16 - lo + (Int64Lo x) (Sub16 s (Const16 [32])))) - (Lsh32x16 lo s)) -(Lsh64x8 (Int64Make hi lo) s) => + (Lsh32x16 (Int64Lo x) s)) +(Lsh64x8 x s) => (Int64Make (Or32 (Or32 - (Lsh32x8 hi s) + (Lsh32x8 (Int64Hi x) s) (Rsh32Ux8 - lo + (Int64Lo x) (Sub8 (Const8 [32]) s))) (Lsh32x8 - lo + (Int64Lo x) (Sub8 s (Const8 [32])))) - (Lsh32x8 lo s)) + (Lsh32x8 (Int64Lo x) s)) // 64x unsigned right shift // result.hi = hi>>s // result.lo = lo>>s | hi<<(32-s) | hi>>(s-32) // >> is unsigned, large shifts result 0 -(Rsh64Ux32 (Int64Make hi lo) s) => +(Rsh64Ux32 x s) => (Int64Make - (Rsh32Ux32 hi s) + (Rsh32Ux32 (Int64Hi x) s) (Or32 (Or32 - (Rsh32Ux32 lo s) + (Rsh32Ux32 (Int64Lo x) s) (Lsh32x32 - hi + (Int64Hi x) (Sub32 (Const32 [32]) s))) (Rsh32Ux32 - hi + (Int64Hi x) (Sub32 s (Const32 [32]))))) -(Rsh64Ux16 (Int64Make hi lo) s) => +(Rsh64Ux16 x s) => (Int64Make - (Rsh32Ux16 hi s) + (Rsh32Ux16 (Int64Hi x) s) (Or32 (Or32 - (Rsh32Ux16 lo s) + (Rsh32Ux16 (Int64Lo x) s) (Lsh32x16 - hi + (Int64Hi x) (Sub16 (Const16 [32]) s))) (Rsh32Ux16 - hi + (Int64Hi x) (Sub16 s (Const16 [32]))))) -(Rsh64Ux8 (Int64Make hi lo) s) => +(Rsh64Ux8 x s) => (Int64Make - (Rsh32Ux8 hi s) + (Rsh32Ux8 (Int64Hi x) s) (Or32 (Or32 - (Rsh32Ux8 lo s) + (Rsh32Ux8 (Int64Lo x) s) (Lsh32x8 - hi + (Int64Hi x) (Sub8 (Const8 [32]) s))) (Rsh32Ux8 - hi + (Int64Hi x) (Sub8 s (Const8 [32]))))) // 64x signed right shift // result.hi = hi>>s // result.lo = lo>>s | hi<<(32-s) | (hi>>(s-32))&zeromask(s>>5) // hi>>(s-32) is signed, large shifts result 0/-1 -(Rsh64x32 (Int64Make hi lo) s) => +(Rsh64x32 x s) => (Int64Make - (Rsh32x32 hi s) + (Rsh32x32 (Int64Hi x) s) (Or32 (Or32 - (Rsh32Ux32 lo s) + (Rsh32Ux32 (Int64Lo x) s) (Lsh32x32 - hi + (Int64Hi x) (Sub32 (Const32 [32]) s))) (And32 (Rsh32x32 - hi + (Int64Hi x) (Sub32 s (Const32 [32]))) (Zeromask (Rsh32Ux32 s (Const32 [5])))))) -(Rsh64x16 (Int64Make hi lo) s) => +(Rsh64x16 x s) => (Int64Make - (Rsh32x16 hi s) + (Rsh32x16 (Int64Hi x) s) (Or32 (Or32 - (Rsh32Ux16 lo s) + (Rsh32Ux16 (Int64Lo x) s) (Lsh32x16 - hi + (Int64Hi x) (Sub16 (Const16 [32]) s))) (And32 (Rsh32x16 - hi + (Int64Hi x) (Sub16 s (Const16 [32]))) (Zeromask (ZeroExt16to32 (Rsh16Ux32 s (Const32 [5]))))))) -(Rsh64x8 (Int64Make hi lo) s) => +(Rsh64x8 x s) => (Int64Make - (Rsh32x8 hi s) + (Rsh32x8 (Int64Hi x) s) (Or32 (Or32 - (Rsh32Ux8 lo s) + (Rsh32Ux8 (Int64Lo x) s) (Lsh32x8 - hi + (Int64Hi x) (Sub8 (Const8 [32]) s))) (And32 (Rsh32x8 - hi + (Int64Hi x) (Sub8 s (Const8 [32]))) (Zeromask (ZeroExt8to32 diff --git a/src/cmd/compile/internal/ssa/rewritedec64.go b/src/cmd/compile/internal/ssa/rewritedec64.go index 86fbc9901a..8b9753414f 100644 --- a/src/cmd/compile/internal/ssa/rewritedec64.go +++ b/src/cmd/compile/internal/ssa/rewritedec64.go @@ -62,6 +62,8 @@ func rewriteValuedec64(v *Value) bool { return rewriteValuedec64_OpNeg64(v) case OpNeq64: return rewriteValuedec64_OpNeq64(v) + case OpOr32: + return rewriteValuedec64_OpOr32(v) case OpOr64: return rewriteValuedec64_OpOr64(v) case OpRsh16Ux64: @@ -728,7 +730,23 @@ func rewriteValuedec64_OpLsh16x64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Lsh16x64 x y) + // result: (Lsh16x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpLsh16x32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpLsh32x64(v *Value) bool { v_1 := v.Args[1] @@ -793,83 +811,97 @@ func rewriteValuedec64_OpLsh32x64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Lsh32x64 x y) + // result: (Lsh32x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpLsh32x32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpLsh64x16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Lsh64x16 (Int64Make hi lo) s) - // result: (Int64Make (Or32 (Or32 (Lsh32x16 hi s) (Rsh32Ux16 lo (Sub16 (Const16 [32]) s))) (Lsh32x16 lo (Sub16 s (Const16 [32])))) (Lsh32x16 lo s)) + // match: (Lsh64x16 x s) + // result: (Int64Make (Or32 (Or32 (Lsh32x16 (Int64Hi x) s) (Rsh32Ux16 (Int64Lo x) (Sub16 (Const16 [32]) s))) (Lsh32x16 (Int64Lo x) (Sub16 s (Const16 [32])))) (Lsh32x16 (Int64Lo x) s)) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) v2 := b.NewValue0(v.Pos, OpLsh32x16, typ.UInt32) - v2.AddArg2(hi, s) - v3 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) - v4 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) - v5 := b.NewValue0(v.Pos, OpConst16, typ.UInt16) - v5.AuxInt = int16ToAuxInt(32) - v4.AddArg2(v5, s) - v3.AddArg2(lo, v4) - v1.AddArg2(v2, v3) - v6 := b.NewValue0(v.Pos, OpLsh32x16, typ.UInt32) - v7 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) - v7.AddArg2(s, v5) - v6.AddArg2(lo, v7) - v0.AddArg2(v1, v6) + v3 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v3.AddArg(x) + v2.AddArg2(v3, s) + v4 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v6 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) + v7 := b.NewValue0(v.Pos, OpConst16, typ.UInt16) + v7.AuxInt = int16ToAuxInt(32) + v6.AddArg2(v7, s) + v4.AddArg2(v5, v6) + v1.AddArg2(v2, v4) v8 := b.NewValue0(v.Pos, OpLsh32x16, typ.UInt32) - v8.AddArg2(lo, s) - v.AddArg2(v0, v8) + v9 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) + v9.AddArg2(s, v7) + v8.AddArg2(v5, v9) + v0.AddArg2(v1, v8) + v10 := b.NewValue0(v.Pos, OpLsh32x16, typ.UInt32) + v10.AddArg2(v5, s) + v.AddArg2(v0, v10) return true } - return false } func rewriteValuedec64_OpLsh64x32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Lsh64x32 (Int64Make hi lo) s) - // result: (Int64Make (Or32 (Or32 (Lsh32x32 hi s) (Rsh32Ux32 lo (Sub32 (Const32 [32]) s))) (Lsh32x32 lo (Sub32 s (Const32 [32])))) (Lsh32x32 lo s)) + // match: (Lsh64x32 x s) + // result: (Int64Make (Or32 (Or32 (Lsh32x32 (Int64Hi x) s) (Rsh32Ux32 (Int64Lo x) (Sub32 (Const32 [32]) s))) (Lsh32x32 (Int64Lo x) (Sub32 s (Const32 [32])))) (Lsh32x32 (Int64Lo x) s)) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) v2 := b.NewValue0(v.Pos, OpLsh32x32, typ.UInt32) - v2.AddArg2(hi, s) - v3 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) - v4 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) - v5 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) - v5.AuxInt = int32ToAuxInt(32) - v4.AddArg2(v5, s) - v3.AddArg2(lo, v4) - v1.AddArg2(v2, v3) - v6 := b.NewValue0(v.Pos, OpLsh32x32, typ.UInt32) - v7 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) - v7.AddArg2(s, v5) - v6.AddArg2(lo, v7) - v0.AddArg2(v1, v6) + v3 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v3.AddArg(x) + v2.AddArg2(v3, s) + v4 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v6 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) + v7 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) + v7.AuxInt = int32ToAuxInt(32) + v6.AddArg2(v7, s) + v4.AddArg2(v5, v6) + v1.AddArg2(v2, v4) v8 := b.NewValue0(v.Pos, OpLsh32x32, typ.UInt32) - v8.AddArg2(lo, s) - v.AddArg2(v0, v8) + v9 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) + v9.AddArg2(s, v7) + v8.AddArg2(v5, v9) + v0.AddArg2(v1, v8) + v10 := b.NewValue0(v.Pos, OpLsh32x32, typ.UInt32) + v10.AddArg2(v5, s) + v.AddArg2(v0, v10) return true } - return false } func rewriteValuedec64_OpLsh64x64(v *Value) bool { v_1 := v.Args[1] @@ -934,45 +966,60 @@ func rewriteValuedec64_OpLsh64x64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Lsh64x64 x y) + // result: (Lsh64x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpLsh64x32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpLsh64x8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Lsh64x8 (Int64Make hi lo) s) - // result: (Int64Make (Or32 (Or32 (Lsh32x8 hi s) (Rsh32Ux8 lo (Sub8 (Const8 [32]) s))) (Lsh32x8 lo (Sub8 s (Const8 [32])))) (Lsh32x8 lo s)) + // match: (Lsh64x8 x s) + // result: (Int64Make (Or32 (Or32 (Lsh32x8 (Int64Hi x) s) (Rsh32Ux8 (Int64Lo x) (Sub8 (Const8 [32]) s))) (Lsh32x8 (Int64Lo x) (Sub8 s (Const8 [32])))) (Lsh32x8 (Int64Lo x) s)) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) v2 := b.NewValue0(v.Pos, OpLsh32x8, typ.UInt32) - v2.AddArg2(hi, s) - v3 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) - v4 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) - v5 := b.NewValue0(v.Pos, OpConst8, typ.UInt8) - v5.AuxInt = int8ToAuxInt(32) - v4.AddArg2(v5, s) - v3.AddArg2(lo, v4) - v1.AddArg2(v2, v3) - v6 := b.NewValue0(v.Pos, OpLsh32x8, typ.UInt32) - v7 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) - v7.AddArg2(s, v5) - v6.AddArg2(lo, v7) - v0.AddArg2(v1, v6) + v3 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v3.AddArg(x) + v2.AddArg2(v3, s) + v4 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v6 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) + v7 := b.NewValue0(v.Pos, OpConst8, typ.UInt8) + v7.AuxInt = int8ToAuxInt(32) + v6.AddArg2(v7, s) + v4.AddArg2(v5, v6) + v1.AddArg2(v2, v4) v8 := b.NewValue0(v.Pos, OpLsh32x8, typ.UInt32) - v8.AddArg2(lo, s) - v.AddArg2(v0, v8) + v9 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) + v9.AddArg2(s, v7) + v8.AddArg2(v5, v9) + v0.AddArg2(v1, v8) + v10 := b.NewValue0(v.Pos, OpLsh32x8, typ.UInt32) + v10.AddArg2(v5, s) + v.AddArg2(v0, v10) return true } - return false } func rewriteValuedec64_OpLsh8x64(v *Value) bool { v_1 := v.Args[1] @@ -1037,7 +1084,23 @@ func rewriteValuedec64_OpLsh8x64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Lsh8x64 x y) + // result: (Lsh8x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpLsh8x32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpMul64(v *Value) bool { v_1 := v.Args[1] @@ -1118,6 +1181,64 @@ func rewriteValuedec64_OpNeq64(v *Value) bool { return true } } +func rewriteValuedec64_OpOr32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Or32 (Zeromask (Const32 [c])) y) + // cond: c == 0 + // result: y + for { + if v.Type != typ.UInt32 { + break + } + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpZeromask { + continue + } + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_0_0.AuxInt) + y := v_1 + if !(c == 0) { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Or32 (Zeromask (Const32 [c])) y) + // cond: c != 0 + // result: (Const32 [-1]) + for { + if v.Type != typ.UInt32 { + break + } + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpZeromask { + continue + } + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_0_0.AuxInt) + if !(c != 0) { + continue + } + v.reset(OpConst32) + v.Type = typ.UInt32 + v.AuxInt = int32ToAuxInt(-1) + return true + } + break + } + return false +} func rewriteValuedec64_OpOr64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -1208,7 +1329,23 @@ func rewriteValuedec64_OpRsh16Ux64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Rsh16Ux64 x y) + // result: (Rsh16Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpRsh16Ux32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpRsh16x64(v *Value) bool { v_1 := v.Args[1] @@ -1276,7 +1413,23 @@ func rewriteValuedec64_OpRsh16x64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Rsh16x64 x y) + // result: (Rsh16x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpRsh16x32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpRsh32Ux64(v *Value) bool { v_1 := v.Args[1] @@ -1341,7 +1494,23 @@ func rewriteValuedec64_OpRsh32Ux64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Rsh32Ux64 x y) + // result: (Rsh32Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpRsh32Ux32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpRsh32x64(v *Value) bool { v_1 := v.Args[1] @@ -1407,83 +1576,97 @@ func rewriteValuedec64_OpRsh32x64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Rsh32x64 x y) + // result: (Rsh32x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpRsh32x32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpRsh64Ux16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Rsh64Ux16 (Int64Make hi lo) s) - // result: (Int64Make (Rsh32Ux16 hi s) (Or32 (Or32 (Rsh32Ux16 lo s) (Lsh32x16 hi (Sub16 (Const16 [32]) s))) (Rsh32Ux16 hi (Sub16 s (Const16 [32]))))) + // match: (Rsh64Ux16 x s) + // result: (Int64Make (Rsh32Ux16 (Int64Hi x) s) (Or32 (Or32 (Rsh32Ux16 (Int64Lo x) s) (Lsh32x16 (Int64Hi x) (Sub16 (Const16 [32]) s))) (Rsh32Ux16 (Int64Hi x) (Sub16 s (Const16 [32]))))) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) - v0.AddArg2(hi, s) - v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v1.AddArg(x) + v0.AddArg2(v1, s) v2 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) - v3 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) - v3.AddArg2(lo, s) - v4 := b.NewValue0(v.Pos, OpLsh32x16, typ.UInt32) - v5 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) - v6 := b.NewValue0(v.Pos, OpConst16, typ.UInt16) - v6.AuxInt = int16ToAuxInt(32) - v5.AddArg2(v6, s) - v4.AddArg2(hi, v5) - v2.AddArg2(v3, v4) - v7 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) - v8 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) - v8.AddArg2(s, v6) - v7.AddArg2(hi, v8) - v1.AddArg2(v2, v7) - v.AddArg2(v0, v1) + v3 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v4 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v4.AddArg2(v5, s) + v6 := b.NewValue0(v.Pos, OpLsh32x16, typ.UInt32) + v7 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) + v8 := b.NewValue0(v.Pos, OpConst16, typ.UInt16) + v8.AuxInt = int16ToAuxInt(32) + v7.AddArg2(v8, s) + v6.AddArg2(v1, v7) + v3.AddArg2(v4, v6) + v9 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) + v10 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) + v10.AddArg2(s, v8) + v9.AddArg2(v1, v10) + v2.AddArg2(v3, v9) + v.AddArg2(v0, v2) return true } - return false } func rewriteValuedec64_OpRsh64Ux32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Rsh64Ux32 (Int64Make hi lo) s) - // result: (Int64Make (Rsh32Ux32 hi s) (Or32 (Or32 (Rsh32Ux32 lo s) (Lsh32x32 hi (Sub32 (Const32 [32]) s))) (Rsh32Ux32 hi (Sub32 s (Const32 [32]))))) + // match: (Rsh64Ux32 x s) + // result: (Int64Make (Rsh32Ux32 (Int64Hi x) s) (Or32 (Or32 (Rsh32Ux32 (Int64Lo x) s) (Lsh32x32 (Int64Hi x) (Sub32 (Const32 [32]) s))) (Rsh32Ux32 (Int64Hi x) (Sub32 s (Const32 [32]))))) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) - v0.AddArg2(hi, s) - v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v1.AddArg(x) + v0.AddArg2(v1, s) v2 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) - v3 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) - v3.AddArg2(lo, s) - v4 := b.NewValue0(v.Pos, OpLsh32x32, typ.UInt32) - v5 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) - v6 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) - v6.AuxInt = int32ToAuxInt(32) - v5.AddArg2(v6, s) - v4.AddArg2(hi, v5) - v2.AddArg2(v3, v4) - v7 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) - v8 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) - v8.AddArg2(s, v6) - v7.AddArg2(hi, v8) - v1.AddArg2(v2, v7) - v.AddArg2(v0, v1) + v3 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v4 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v4.AddArg2(v5, s) + v6 := b.NewValue0(v.Pos, OpLsh32x32, typ.UInt32) + v7 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) + v8 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) + v8.AuxInt = int32ToAuxInt(32) + v7.AddArg2(v8, s) + v6.AddArg2(v1, v7) + v3.AddArg2(v4, v6) + v9 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) + v10 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) + v10.AddArg2(s, v8) + v9.AddArg2(v1, v10) + v2.AddArg2(v3, v9) + v.AddArg2(v0, v2) return true } - return false } func rewriteValuedec64_OpRsh64Ux64(v *Value) bool { v_1 := v.Args[1] @@ -1548,139 +1731,152 @@ func rewriteValuedec64_OpRsh64Ux64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Rsh64Ux64 x y) + // result: (Rsh64Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpRsh64Ux32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpRsh64Ux8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Rsh64Ux8 (Int64Make hi lo) s) - // result: (Int64Make (Rsh32Ux8 hi s) (Or32 (Or32 (Rsh32Ux8 lo s) (Lsh32x8 hi (Sub8 (Const8 [32]) s))) (Rsh32Ux8 hi (Sub8 s (Const8 [32]))))) + // match: (Rsh64Ux8 x s) + // result: (Int64Make (Rsh32Ux8 (Int64Hi x) s) (Or32 (Or32 (Rsh32Ux8 (Int64Lo x) s) (Lsh32x8 (Int64Hi x) (Sub8 (Const8 [32]) s))) (Rsh32Ux8 (Int64Hi x) (Sub8 s (Const8 [32]))))) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) - v0.AddArg2(hi, s) - v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v1.AddArg(x) + v0.AddArg2(v1, s) v2 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) - v3 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) - v3.AddArg2(lo, s) - v4 := b.NewValue0(v.Pos, OpLsh32x8, typ.UInt32) - v5 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) - v6 := b.NewValue0(v.Pos, OpConst8, typ.UInt8) - v6.AuxInt = int8ToAuxInt(32) - v5.AddArg2(v6, s) - v4.AddArg2(hi, v5) - v2.AddArg2(v3, v4) - v7 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) - v8 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) - v8.AddArg2(s, v6) - v7.AddArg2(hi, v8) - v1.AddArg2(v2, v7) - v.AddArg2(v0, v1) + v3 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v4 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v4.AddArg2(v5, s) + v6 := b.NewValue0(v.Pos, OpLsh32x8, typ.UInt32) + v7 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) + v8 := b.NewValue0(v.Pos, OpConst8, typ.UInt8) + v8.AuxInt = int8ToAuxInt(32) + v7.AddArg2(v8, s) + v6.AddArg2(v1, v7) + v3.AddArg2(v4, v6) + v9 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) + v10 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) + v10.AddArg2(s, v8) + v9.AddArg2(v1, v10) + v2.AddArg2(v3, v9) + v.AddArg2(v0, v2) return true } - return false } func rewriteValuedec64_OpRsh64x16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Rsh64x16 (Int64Make hi lo) s) - // result: (Int64Make (Rsh32x16 hi s) (Or32 (Or32 (Rsh32Ux16 lo s) (Lsh32x16 hi (Sub16 (Const16 [32]) s))) (And32 (Rsh32x16 hi (Sub16 s (Const16 [32]))) (Zeromask (ZeroExt16to32 (Rsh16Ux32 s (Const32 [5]))))))) + // match: (Rsh64x16 x s) + // result: (Int64Make (Rsh32x16 (Int64Hi x) s) (Or32 (Or32 (Rsh32Ux16 (Int64Lo x) s) (Lsh32x16 (Int64Hi x) (Sub16 (Const16 [32]) s))) (And32 (Rsh32x16 (Int64Hi x) (Sub16 s (Const16 [32]))) (Zeromask (ZeroExt16to32 (Rsh16Ux32 s (Const32 [5]))))))) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpRsh32x16, typ.UInt32) - v0.AddArg2(hi, s) - v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v1.AddArg(x) + v0.AddArg2(v1, s) v2 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) - v3 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) - v3.AddArg2(lo, s) - v4 := b.NewValue0(v.Pos, OpLsh32x16, typ.UInt32) - v5 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) - v6 := b.NewValue0(v.Pos, OpConst16, typ.UInt16) - v6.AuxInt = int16ToAuxInt(32) - v5.AddArg2(v6, s) - v4.AddArg2(hi, v5) - v2.AddArg2(v3, v4) - v7 := b.NewValue0(v.Pos, OpAnd32, typ.UInt32) - v8 := b.NewValue0(v.Pos, OpRsh32x16, typ.UInt32) - v9 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) - v9.AddArg2(s, v6) - v8.AddArg2(hi, v9) - v10 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) - v11 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32) - v12 := b.NewValue0(v.Pos, OpRsh16Ux32, typ.UInt16) - v13 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) - v13.AuxInt = int32ToAuxInt(5) - v12.AddArg2(s, v13) - v11.AddArg(v12) - v10.AddArg(v11) - v7.AddArg2(v8, v10) - v1.AddArg2(v2, v7) - v.AddArg2(v0, v1) + v3 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v4 := b.NewValue0(v.Pos, OpRsh32Ux16, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v4.AddArg2(v5, s) + v6 := b.NewValue0(v.Pos, OpLsh32x16, typ.UInt32) + v7 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) + v8 := b.NewValue0(v.Pos, OpConst16, typ.UInt16) + v8.AuxInt = int16ToAuxInt(32) + v7.AddArg2(v8, s) + v6.AddArg2(v1, v7) + v3.AddArg2(v4, v6) + v9 := b.NewValue0(v.Pos, OpAnd32, typ.UInt32) + v10 := b.NewValue0(v.Pos, OpRsh32x16, typ.UInt32) + v11 := b.NewValue0(v.Pos, OpSub16, typ.UInt16) + v11.AddArg2(s, v8) + v10.AddArg2(v1, v11) + v12 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v13 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32) + v14 := b.NewValue0(v.Pos, OpRsh16Ux32, typ.UInt16) + v15 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) + v15.AuxInt = int32ToAuxInt(5) + v14.AddArg2(s, v15) + v13.AddArg(v14) + v12.AddArg(v13) + v9.AddArg2(v10, v12) + v2.AddArg2(v3, v9) + v.AddArg2(v0, v2) return true } - return false } func rewriteValuedec64_OpRsh64x32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Rsh64x32 (Int64Make hi lo) s) - // result: (Int64Make (Rsh32x32 hi s) (Or32 (Or32 (Rsh32Ux32 lo s) (Lsh32x32 hi (Sub32 (Const32 [32]) s))) (And32 (Rsh32x32 hi (Sub32 s (Const32 [32]))) (Zeromask (Rsh32Ux32 s (Const32 [5])))))) + // match: (Rsh64x32 x s) + // result: (Int64Make (Rsh32x32 (Int64Hi x) s) (Or32 (Or32 (Rsh32Ux32 (Int64Lo x) s) (Lsh32x32 (Int64Hi x) (Sub32 (Const32 [32]) s))) (And32 (Rsh32x32 (Int64Hi x) (Sub32 s (Const32 [32]))) (Zeromask (Rsh32Ux32 s (Const32 [5])))))) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpRsh32x32, typ.UInt32) - v0.AddArg2(hi, s) - v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v1.AddArg(x) + v0.AddArg2(v1, s) v2 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) - v3 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) - v3.AddArg2(lo, s) - v4 := b.NewValue0(v.Pos, OpLsh32x32, typ.UInt32) - v5 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) - v6 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) - v6.AuxInt = int32ToAuxInt(32) - v5.AddArg2(v6, s) - v4.AddArg2(hi, v5) - v2.AddArg2(v3, v4) - v7 := b.NewValue0(v.Pos, OpAnd32, typ.UInt32) - v8 := b.NewValue0(v.Pos, OpRsh32x32, typ.UInt32) - v9 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) - v9.AddArg2(s, v6) - v8.AddArg2(hi, v9) - v10 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) - v11 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) - v12 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) - v12.AuxInt = int32ToAuxInt(5) - v11.AddArg2(s, v12) - v10.AddArg(v11) - v7.AddArg2(v8, v10) - v1.AddArg2(v2, v7) - v.AddArg2(v0, v1) + v3 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v4 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v4.AddArg2(v5, s) + v6 := b.NewValue0(v.Pos, OpLsh32x32, typ.UInt32) + v7 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) + v8 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) + v8.AuxInt = int32ToAuxInt(32) + v7.AddArg2(v8, s) + v6.AddArg2(v1, v7) + v3.AddArg2(v4, v6) + v9 := b.NewValue0(v.Pos, OpAnd32, typ.UInt32) + v10 := b.NewValue0(v.Pos, OpRsh32x32, typ.UInt32) + v11 := b.NewValue0(v.Pos, OpSub32, typ.UInt32) + v11.AddArg2(s, v8) + v10.AddArg2(v1, v11) + v12 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v13 := b.NewValue0(v.Pos, OpRsh32Ux32, typ.UInt32) + v14 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) + v14.AuxInt = int32ToAuxInt(5) + v13.AddArg2(s, v14) + v12.AddArg(v13) + v9.AddArg2(v10, v12) + v2.AddArg2(v3, v9) + v.AddArg2(v0, v2) return true } - return false } func rewriteValuedec64_OpRsh64x64(v *Value) bool { v_1 := v.Args[1] @@ -1750,55 +1946,70 @@ func rewriteValuedec64_OpRsh64x64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Rsh64x64 x y) + // result: (Rsh64x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpRsh64x32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpRsh64x8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (Rsh64x8 (Int64Make hi lo) s) - // result: (Int64Make (Rsh32x8 hi s) (Or32 (Or32 (Rsh32Ux8 lo s) (Lsh32x8 hi (Sub8 (Const8 [32]) s))) (And32 (Rsh32x8 hi (Sub8 s (Const8 [32]))) (Zeromask (ZeroExt8to32 (Rsh8Ux32 s (Const32 [5]))))))) + // match: (Rsh64x8 x s) + // result: (Int64Make (Rsh32x8 (Int64Hi x) s) (Or32 (Or32 (Rsh32Ux8 (Int64Lo x) s) (Lsh32x8 (Int64Hi x) (Sub8 (Const8 [32]) s))) (And32 (Rsh32x8 (Int64Hi x) (Sub8 s (Const8 [32]))) (Zeromask (ZeroExt8to32 (Rsh8Ux32 s (Const32 [5]))))))) for { - if v_0.Op != OpInt64Make { - break - } - lo := v_0.Args[1] - hi := v_0.Args[0] + x := v_0 s := v_1 v.reset(OpInt64Make) v0 := b.NewValue0(v.Pos, OpRsh32x8, typ.UInt32) - v0.AddArg2(hi, s) - v1 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v1.AddArg(x) + v0.AddArg2(v1, s) v2 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) - v3 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) - v3.AddArg2(lo, s) - v4 := b.NewValue0(v.Pos, OpLsh32x8, typ.UInt32) - v5 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) - v6 := b.NewValue0(v.Pos, OpConst8, typ.UInt8) - v6.AuxInt = int8ToAuxInt(32) - v5.AddArg2(v6, s) - v4.AddArg2(hi, v5) - v2.AddArg2(v3, v4) - v7 := b.NewValue0(v.Pos, OpAnd32, typ.UInt32) - v8 := b.NewValue0(v.Pos, OpRsh32x8, typ.UInt32) - v9 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) - v9.AddArg2(s, v6) - v8.AddArg2(hi, v9) - v10 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) - v11 := b.NewValue0(v.Pos, OpZeroExt8to32, typ.UInt32) - v12 := b.NewValue0(v.Pos, OpRsh8Ux32, typ.UInt8) - v13 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) - v13.AuxInt = int32ToAuxInt(5) - v12.AddArg2(s, v13) - v11.AddArg(v12) - v10.AddArg(v11) - v7.AddArg2(v8, v10) - v1.AddArg2(v2, v7) - v.AddArg2(v0, v1) + v3 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v4 := b.NewValue0(v.Pos, OpRsh32Ux8, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v5.AddArg(x) + v4.AddArg2(v5, s) + v6 := b.NewValue0(v.Pos, OpLsh32x8, typ.UInt32) + v7 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) + v8 := b.NewValue0(v.Pos, OpConst8, typ.UInt8) + v8.AuxInt = int8ToAuxInt(32) + v7.AddArg2(v8, s) + v6.AddArg2(v1, v7) + v3.AddArg2(v4, v6) + v9 := b.NewValue0(v.Pos, OpAnd32, typ.UInt32) + v10 := b.NewValue0(v.Pos, OpRsh32x8, typ.UInt32) + v11 := b.NewValue0(v.Pos, OpSub8, typ.UInt8) + v11.AddArg2(s, v8) + v10.AddArg2(v1, v11) + v12 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v13 := b.NewValue0(v.Pos, OpZeroExt8to32, typ.UInt32) + v14 := b.NewValue0(v.Pos, OpRsh8Ux32, typ.UInt8) + v15 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) + v15.AuxInt = int32ToAuxInt(5) + v14.AddArg2(s, v15) + v13.AddArg(v14) + v12.AddArg(v13) + v9.AddArg2(v10, v12) + v2.AddArg2(v3, v9) + v.AddArg2(v0, v2) return true } - return false } func rewriteValuedec64_OpRsh8Ux64(v *Value) bool { v_1 := v.Args[1] @@ -1863,7 +2074,23 @@ func rewriteValuedec64_OpRsh8Ux64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Rsh8Ux64 x y) + // result: (Rsh8Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpRsh8Ux32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpRsh8x64(v *Value) bool { v_1 := v.Args[1] @@ -1931,7 +2158,23 @@ func rewriteValuedec64_OpRsh8x64(v *Value) bool { v.AddArg2(x, v0) return true } - return false + // match: (Rsh8x64 x y) + // result: (Rsh8x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) + for { + x := v_0 + y := v_1 + v.reset(OpRsh8x32) + v0 := b.NewValue0(v.Pos, OpOr32, typ.UInt32) + v1 := b.NewValue0(v.Pos, OpZeromask, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpInt64Hi, typ.UInt32) + v2.AddArg(y) + v1.AddArg(v2) + v3 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v3.AddArg(y) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } } func rewriteValuedec64_OpSignExt16to64(v *Value) bool { v_0 := v.Args[0] @@ -2071,6 +2314,8 @@ func rewriteValuedec64_OpSub64(v *Value) bool { } func rewriteValuedec64_OpTrunc64to16(v *Value) bool { v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types // match: (Trunc64to16 (Int64Make _ lo)) // result: (Trunc32to16 lo) for { @@ -2082,7 +2327,16 @@ func rewriteValuedec64_OpTrunc64to16(v *Value) bool { v.AddArg(lo) return true } - return false + // match: (Trunc64to16 x) + // result: (Trunc32to16 (Int64Lo x)) + for { + x := v_0 + v.reset(OpTrunc32to16) + v0 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v0.AddArg(x) + v.AddArg(v0) + return true + } } func rewriteValuedec64_OpTrunc64to32(v *Value) bool { v_0 := v.Args[0] @@ -2096,10 +2350,19 @@ func rewriteValuedec64_OpTrunc64to32(v *Value) bool { v.copyOf(lo) return true } - return false + // match: (Trunc64to32 x) + // result: (Int64Lo x) + for { + x := v_0 + v.reset(OpInt64Lo) + v.AddArg(x) + return true + } } func rewriteValuedec64_OpTrunc64to8(v *Value) bool { v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types // match: (Trunc64to8 (Int64Make _ lo)) // result: (Trunc32to8 lo) for { @@ -2111,7 +2374,16 @@ func rewriteValuedec64_OpTrunc64to8(v *Value) bool { v.AddArg(lo) return true } - return false + // match: (Trunc64to8 x) + // result: (Trunc32to8 (Int64Lo x)) + for { + x := v_0 + v.reset(OpTrunc32to8) + v0 := b.NewValue0(v.Pos, OpInt64Lo, typ.UInt32) + v0.AddArg(x) + v.AddArg(v0) + return true + } } func rewriteValuedec64_OpXor64(v *Value) bool { v_1 := v.Args[1] -- cgit v1.3 From c305e49e96deafe54a8e43010ea76fead6da0a98 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 26 Oct 2020 14:32:13 -0400 Subject: cmd/go,cmd/compile,sync: remove special import case in cmd/go CL 253748 introduced a special case in cmd/go to allow sync to import runtime/internal/atomic. Besides introducing unnecessary complexity into cmd/go, this breaks other packages (like gopls) that understand how imports work, but don't understand this special case. Fix this by using the more standard linkname-based approach to pull the necessary functions from runtime/internal/atomic into sync. Since these are compiler intrinsics, we also have to tell the compiler that the linknamed symbols are intrinsics to get this optimization in sync. Fixes #42196. Change-Id: I1f91498c255c91583950886a89c3c9adc39a32f0 Reviewed-on: https://go-review.googlesource.com/c/go/+/265124 Trust: Austin Clements Run-TryBot: Austin Clements Reviewed-by: Bryan C. Mills Reviewed-by: Keith Randall Reviewed-by: Paul Murphy TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/ssa.go | 4 ++++ src/cmd/go/internal/load/pkg.go | 5 ----- src/sync/pool.go | 25 +++++++++++++++++-------- 3 files changed, 21 insertions(+), 13 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index f840ef4066..a1b5a03687 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3569,13 +3569,17 @@ func init() { alias("runtime/internal/atomic", "LoadAcq", "runtime/internal/atomic", "Load", lwatomics...) alias("runtime/internal/atomic", "LoadAcq64", "runtime/internal/atomic", "Load64", lwatomics...) alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...) + alias("sync", "runtime_LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...) // linknamed alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...) + alias("sync", "runtime_LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...) // linknamed alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store", p4...) alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store64", p8...) alias("runtime/internal/atomic", "StoreRel", "runtime/internal/atomic", "Store", lwatomics...) alias("runtime/internal/atomic", "StoreRel64", "runtime/internal/atomic", "Store64", lwatomics...) alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...) + alias("sync", "runtime_StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...) // linknamed alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...) + alias("sync", "runtime_StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...) // linknamed alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg", p4...) alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg64", p8...) alias("runtime/internal/atomic", "Xadduintptr", "runtime/internal/atomic", "Xadd", p4...) diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index fcd7728c7b..615b5ef769 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -1338,11 +1338,6 @@ func disallowInternal(srcDir string, importer *Package, importerPath string, p * return p } - // Allow sync package to access lightweight atomic functions limited to the runtime. - if p.Standard && strings.HasPrefix(importerPath, "sync") && p.ImportPath == "runtime/internal/atomic" { - return p - } - // Internal is present. // Map import path back to directory corresponding to parent of internal. if i > 0 { diff --git a/src/sync/pool.go b/src/sync/pool.go index 137413fdc4..1ae70127ac 100644 --- a/src/sync/pool.go +++ b/src/sync/pool.go @@ -7,7 +7,6 @@ package sync import ( "internal/race" "runtime" - runtimeatomic "runtime/internal/atomic" "sync/atomic" "unsafe" ) @@ -153,8 +152,8 @@ func (p *Pool) Get() interface{} { func (p *Pool) getSlow(pid int) interface{} { // See the comment in pin regarding ordering of the loads. - size := runtimeatomic.LoadAcquintptr(&p.localSize) // load-acquire - locals := p.local // load-consume + size := runtime_LoadAcquintptr(&p.localSize) // load-acquire + locals := p.local // load-consume // Try to steal one element from other procs. for i := 0; i < int(size); i++ { l := indexLocal(locals, (pid+i+1)%int(size)) @@ -166,7 +165,7 @@ func (p *Pool) getSlow(pid int) interface{} { // Try the victim cache. We do this after attempting to steal // from all primary caches because we want objects in the // victim cache to age out if at all possible. - size = runtimeatomic.Loaduintptr(&p.victimSize) + size = atomic.LoadUintptr(&p.victimSize) if uintptr(pid) >= size { return nil } @@ -199,8 +198,8 @@ func (p *Pool) pin() (*poolLocal, int) { // Since we've disabled preemption, GC cannot happen in between. // Thus here we must observe local at least as large localSize. // We can observe a newer/larger local, it is fine (we must observe its zero-initialized-ness). - s := runtimeatomic.LoadAcquintptr(&p.localSize) // load-acquire - l := p.local // load-consume + s := runtime_LoadAcquintptr(&p.localSize) // load-acquire + l := p.local // load-consume if uintptr(pid) < s { return indexLocal(l, pid), pid } @@ -226,8 +225,8 @@ func (p *Pool) pinSlow() (*poolLocal, int) { // If GOMAXPROCS changes between GCs, we re-allocate the array and lose the old one. size := runtime.GOMAXPROCS(0) local := make([]poolLocal, size) - atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-release - runtimeatomic.StoreReluintptr(&p.localSize, uintptr(size)) // store-release + atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-release + runtime_StoreReluintptr(&p.localSize, uintptr(size)) // store-release return &local[pid], pid } @@ -283,3 +282,13 @@ func indexLocal(l unsafe.Pointer, i int) *poolLocal { func runtime_registerPoolCleanup(cleanup func()) func runtime_procPin() int func runtime_procUnpin() + +// The below are implemented in runtime/internal/atomic and the +// compiler also knows to intrinsify the symbol we linkname into this +// package. + +//go:linkname runtime_LoadAcquintptr runtime/internal/atomic.LoadAcquintptr +func runtime_LoadAcquintptr(ptr *uintptr) uintptr + +//go:linkname runtime_StoreReluintptr runtime/internal/atomic.StoreReluintptr +func runtime_StoreReluintptr(ptr *uintptr, val uintptr) uintptr -- cgit v1.3 From a19cf510af8182751fefc16ce962f91fe17c1e1b Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 24 Oct 2020 10:40:09 +0700 Subject: cmd/compile: defer lowering OANDNOT until SSA Currently, "x &^ y" gets rewriten into "x & ^y" during walk. It adds unnecessary complexity to other parts, which must aware about this. Instead, we can just implement "&^" in the conversion to SSA, so "&^" can be handled like other binary operators. However, this CL does not pass toolstash-check. It seems that implements "&^" in the conversion to SSA causes registers allocation change. With the parent: obj: 00212 (.../src/runtime/complex.go:47) MOVQ X0, AX obj: 00213 (.../src/runtime/complex.go:47) BTRQ $63, AX obj: 00214 (.../src/runtime/complex.go:47) MOVQ "".n(SP), CX obj: 00215 (.../src/runtime/complex.go:47) MOVQ $-9223372036854775808, DX obj: 00216 (.../src/runtime/complex.go:47) ANDQ DX, CX obj: 00217 (.../src/runtime/complex.go:47) ORQ AX, CX With this CL: obj: 00212 (.../src/runtime/complex.go:47) MOVQ X0, AX obj: 00213 (.../src/runtime/complex.go:47) BTRQ $63, AX obj: 00214 (.../src/runtime/complex.go:47) MOVQ $-9223372036854775808, CX obj: 00215 (.../src/runtime/complex.go:47) MOVQ "".n(SP), DX obj: 00216 (.../src/runtime/complex.go:47) ANDQ CX, DX obj: 00217 (.../src/runtime/complex.go:47) ORQ AX, DX Change-Id: I80acf8496a91be4804fb7ef3df04c19baae2754c Reviewed-on: https://go-review.googlesource.com/c/go/+/264660 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/ssa.go | 5 +++++ src/cmd/compile/internal/gc/syntax.go | 2 +- src/cmd/compile/internal/gc/walk.go | 30 ++++++++++++------------------ test/bounds.go | 5 +++++ 4 files changed, 23 insertions(+), 19 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index a1b5a03687..4769c2c7d9 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -2472,6 +2472,11 @@ func (s *state) expr(n *Node) *ssa.Value { a := s.expr(n.Left) b := s.expr(n.Right) return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b) + case OANDNOT: + a := s.expr(n.Left) + b := s.expr(n.Right) + b = s.newValue1(s.ssaOp(OBITNOT, b.Type), b.Type, b) + return s.newValue2(s.ssaOp(OAND, n.Type), a.Type, a, b) case OLSH, ORSH: a := s.expr(n.Left) b := s.expr(n.Right) diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 83b5db834f..58de9b5e3f 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -142,7 +142,7 @@ const ( _, _ // second nodeInitorder bit _, nodeHasBreak _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only - _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND + _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP _, nodeIsDDD // is the argument variadic _, nodeDiag // already printed error about this _, nodeColas // OAS resulting from := diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 6ce3eda44b..927f6c4b1e 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -474,7 +474,7 @@ opswitch: ODEREF, OSPTR, OITAB, OIDATA, OADDR: n.Left = walkexpr(n.Left, init) - case OEFACE, OAND, OSUB, OMUL, OADD, OOR, OXOR, OLSH, ORSH: + case OEFACE, OAND, OANDNOT, OSUB, OMUL, OADD, OOR, OXOR, OLSH, ORSH: n.Left = walkexpr(n.Left, init) n.Right = walkexpr(n.Right, init) @@ -965,14 +965,6 @@ opswitch: fn := basicnames[param] + "to" + basicnames[result] n = conv(mkcall(fn, types.Types[result], init, conv(n.Left, types.Types[param])), n.Type) - case OANDNOT: - n.Left = walkexpr(n.Left, init) - n.Op = OAND - n.SetImplicit(true) // for walkCheckPtrArithmetic - n.Right = nod(OBITNOT, n.Right, nil) - n.Right = typecheck(n.Right, ctxExpr) - n.Right = walkexpr(n.Right, init) - case ODIV, OMOD: n.Left = walkexpr(n.Left, init) n.Right = walkexpr(n.Right, init) @@ -3609,14 +3601,20 @@ func bounded(n *Node, max int64) bool { } switch n.Op { - case OAND: + case OAND, OANDNOT: v := int64(-1) - if smallintconst(n.Left) { + switch { + case smallintconst(n.Left): v = n.Left.Int64Val() - } else if smallintconst(n.Right) { + case smallintconst(n.Right): v = n.Right.Int64Val() + if n.Op == OANDNOT { + v = ^v + if !sign { + v &= 1< Date: Tue, 2 Jun 2020 23:34:06 +1000 Subject: cmd/compile,cmd/internal/sys: enable additional build modes on linux/riscv64 Enable c-archive, c-shared, shared and pie build modes for linux/riscv64. Change-Id: I15a8a51b84dbbb82a5b6592aec84a7f09f0cc37f Reviewed-on: https://go-review.googlesource.com/c/go/+/263457 Trust: Joel Sing Reviewed-by: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/main.go | 2 +- src/cmd/dist/test.go | 8 ++++---- src/cmd/go/go_test.go | 2 +- src/cmd/internal/sys/supported.go | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 4b401f2aa4..0b65e8a0b4 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -130,7 +130,7 @@ func hidePanic() { // supportsDynlink reports whether or not the code generator for the given // architecture supports the -shared and -dynlink flags. func supportsDynlink(arch *sys.Arch) bool { - return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X) + return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) } // timing data for compiler phases diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index dbe55a7c38..622d0cee83 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -983,7 +983,7 @@ func (t *tester) supportedBuildmode(mode string) bool { switch pair { case "aix-ppc64", "darwin-amd64", "darwin-arm64", "ios-arm64", - "linux-amd64", "linux-386", "linux-ppc64le", "linux-s390x", + "linux-amd64", "linux-386", "linux-ppc64le", "linux-riscv64", "linux-s390x", "freebsd-amd64", "windows-amd64", "windows-386": return true @@ -991,7 +991,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "c-shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", "darwin-amd64", "darwin-arm64", "freebsd-amd64", "android-arm", "android-arm64", "android-386", @@ -1001,7 +1001,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x": + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x": return true } return false @@ -1020,7 +1020,7 @@ func (t *tester) supportedBuildmode(mode string) bool { case "pie": switch pair { case "aix/ppc64", - "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", + "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", "android-amd64", "android-arm", "android-arm64", "android-386": return true case "darwin-amd64", "darwin-arm64": diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index e9c26d161a..d1bd516a5d 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -2070,7 +2070,7 @@ func TestBuildmodePIE(t *testing.T) { platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "windows/386", "windows/amd64", "windows/arm": diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index ccc5b2245b..1d813bbb47 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -67,7 +67,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "c-shared": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x", + case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", @@ -84,7 +84,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "pie": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", @@ -97,7 +97,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "shared": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x": + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x": return true } return false -- cgit v1.3 From e3bb53a7683eb9c3d04c09f28abb4cf9aa89a7c1 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 27 Oct 2020 17:08:57 +0700 Subject: cmd/compile: remove isLiteral It has duplicated logic with "n.isGoConst". Passes toolstash-check. Change-Id: I5bf871ef81c7188ca09dae29c7ff55b3a254d972 Reviewed-on: https://go-review.googlesource.com/c/go/+/265437 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/sinit.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index 9c4dcd739c..212fcc022d 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -375,11 +375,6 @@ func readonlystaticname(t *types.Type) *Node { return n } -func isLiteral(n *Node) bool { - // Treat nils as zeros rather than literals. - return n.Op == OLITERAL && n.Val().Ctype() != CTNIL -} - func (n *Node) isSimpleName() bool { return n.Op == ONAME && n.Class() != PAUTOHEAP && n.Class() != PEXTERN } @@ -404,7 +399,7 @@ const ( func getdyn(n *Node, top bool) initGenType { switch n.Op { default: - if isLiteral(n) { + if n.isGoConst() { return initConst } return initDynamic @@ -559,7 +554,7 @@ func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) continue } - islit := isLiteral(value) + islit := value.isGoConst() if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) { continue } @@ -732,7 +727,7 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) { continue } - if vstat != nil && isLiteral(value) { // already set by copy from static value + if vstat != nil && value.isGoConst() { // already set by copy from static value continue } -- cgit v1.3 From 933721b8c7f981229974e2603850c2e9a7ffc5a1 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 22 Oct 2020 13:11:16 -0700 Subject: cmd/compile: fix storeType to handle pointers to go:notinheap types storeType splits compound stores up into a scalar parts and a pointer parts. The scalar part happens unconditionally, and the pointer part happens under the guard of a write barrier check. Types which are declared as pointers, but are represented as scalars because they might have "bad" values, were not handled correctly here. They ended up not getting stored in either set. Fixes #42032 Change-Id: I46f6600075c0c370e640b807066247237f93c7ac Reviewed-on: https://go-review.googlesource.com/c/go/+/264300 Trust: Keith Randall Reviewed-by: Ian Lance Taylor --- src/cmd/compile/internal/gc/ssa.go | 8 +++++++- test/fixedbugs/issue42032.go | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/fixedbugs/issue42032.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 4769c2c7d9..4488337924 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -5222,7 +5222,10 @@ func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip ski case t.IsBoolean() || t.IsInteger() || t.IsFloat() || t.IsComplex(): s.store(t, left, right) case t.IsPtrShaped(): - // no scalar fields. + if t.IsPtr() && t.Elem().NotInHeap() { + s.store(t, left, right) // see issue 42032 + } + // otherwise, no scalar fields. case t.IsString(): if skip&skipLen != 0 { return @@ -5266,6 +5269,9 @@ func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip ski func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) { switch { case t.IsPtrShaped(): + if t.IsPtr() && t.Elem().NotInHeap() { + break // see issue 42032 + } s.store(t, left, right) case t.IsString(): ptr := s.newValue1(ssa.OpStringPtr, s.f.Config.Types.BytePtr, right) diff --git a/test/fixedbugs/issue42032.go b/test/fixedbugs/issue42032.go new file mode 100644 index 0000000000..c456b1db02 --- /dev/null +++ b/test/fixedbugs/issue42032.go @@ -0,0 +1,27 @@ +// run + +// Copyright 2020 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 main + +//go:notinheap +type NIH struct { +} + +type T struct { + x *NIH + p *int +} + +var y NIH +var z int + +func main() { + a := []T{{&y, &z}} + a = append(a, T{&y, &z}) + if a[1].x == nil { + panic("pointer not written") + } +} -- cgit v1.3 From 009d71409821a6ac4f1b32aaae2c856c20a29f92 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 22 Oct 2020 16:37:19 -0700 Subject: cmd/compile, runtime: store pointers to go:notinheap types indirectly pointers to go:notinheap types should be treated as scalars. That means they shouldn't be stored directly in interfaces, or directly in reflect.Value.ptr. Also be sure to use uintpr to compare such pointers in reflect.DeepEqual. Fixes #42076 Change-Id: I53735f6d434e9c3108d4940bd1bae14c61ef2a74 Reviewed-on: https://go-review.googlesource.com/c/go/+/264480 Trust: Keith Randall Reviewed-by: Ian Lance Taylor --- src/cmd/compile/internal/gc/subr.go | 6 ++-- src/cmd/compile/internal/gc/typecheck.go | 2 +- src/reflect/deepequal.go | 12 +++++++- src/reflect/value.go | 12 +++++++- src/runtime/netpoll.go | 47 ++++++++++++++++++++++---------- test/fixedbugs/issue42076.go | 21 ++++++++++++++ 6 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 test/fixedbugs/issue42076.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index eccd6f8a74..defefd76b3 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1854,8 +1854,10 @@ func isdirectiface(t *types.Type) bool { } switch t.Etype { - case TPTR, - TCHAN, + case TPTR: + // Pointers to notinheap types must be stored indirectly. See issue 42076. + return !t.Elem().NotInHeap() + case TCHAN, TMAP, TFUNC, TUNSAFEPTR: diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index ce817db446..8ebeaf1330 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2516,7 +2516,7 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field { n.Left = nod(OADDR, n.Left, nil) n.Left.SetImplicit(true) n.Left = typecheck(n.Left, ctxType|ctxExpr) - } else if tt.IsPtr() && !rcvr.IsPtr() && types.Identical(tt.Elem(), rcvr) { + } else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) { n.Left = nod(ODEREF, n.Left, nil) n.Left.SetImplicit(true) n.Left = typecheck(n.Left, ctxType|ctxExpr) diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go index be66464129..d951d8d999 100644 --- a/src/reflect/deepequal.go +++ b/src/reflect/deepequal.go @@ -35,7 +35,17 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { // and it's safe and valid to get Value's internal pointer. hard := func(v1, v2 Value) bool { switch v1.Kind() { - case Map, Slice, Ptr, Interface: + case Ptr: + if v1.typ.ptrdata == 0 { + // go:notinheap pointers can't be cyclic. + // At least, all of our current uses of go:notinheap have + // that property. The runtime ones aren't cyclic (and we don't use + // DeepEqual on them anyway), and the cgo-generated ones are + // all empty structs. + return false + } + fallthrough + case Map, Slice, Interface: // Nil pointers cannot be cyclic. Avoid putting them in the visited map. return !v1.IsNil() && !v2.IsNil() } diff --git a/src/reflect/value.go b/src/reflect/value.go index bb6371b867..24eab6a2c6 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -90,6 +90,7 @@ func (f flag) ro() flag { // pointer returns the underlying pointer represented by v. // v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer +// if v.Kind() == Ptr, the base type must not be go:notinheap. func (v Value) pointer() unsafe.Pointer { if v.typ.size != ptrSize || !v.typ.pointers() { panic("can't call pointer on a non-pointer Value") @@ -1453,7 +1454,16 @@ func (v Value) Pointer() uintptr { // TODO: deprecate k := v.kind() switch k { - case Chan, Map, Ptr, UnsafePointer: + case Ptr: + if v.typ.ptrdata == 0 { + // Handle pointers to go:notinheap types directly, + // so we never materialize such pointers as an + // unsafe.Pointer. (Such pointers are always indirect.) + // See issue 42076. + return *(*uintptr)(v.ptr) + } + fallthrough + case Chan, Map, UnsafePointer: return uintptr(v.pointer()) case Func: if v.flag&flagMethod != 0 { diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index 34ea82a7fa..77eb3aa4c6 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -79,16 +79,17 @@ type pollDesc struct { lock mutex // protects the following fields fd uintptr closing bool - everr bool // marks event scanning error happened - user uint32 // user settable cookie - rseq uintptr // protects from stale read timers - rg uintptr // pdReady, pdWait, G waiting for read or nil - rt timer // read deadline timer (set if rt.f != nil) - rd int64 // read deadline - wseq uintptr // protects from stale write timers - wg uintptr // pdReady, pdWait, G waiting for write or nil - wt timer // write deadline timer - wd int64 // write deadline + everr bool // marks event scanning error happened + user uint32 // user settable cookie + rseq uintptr // protects from stale read timers + rg uintptr // pdReady, pdWait, G waiting for read or nil + rt timer // read deadline timer (set if rt.f != nil) + rd int64 // read deadline + wseq uintptr // protects from stale write timers + wg uintptr // pdReady, pdWait, G waiting for write or nil + wt timer // write deadline timer + wd int64 // write deadline + self *pollDesc // storage for indirect interface. See (*pollDesc).makeArg. } type pollCache struct { @@ -157,6 +158,7 @@ func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { pd.wseq++ pd.wg = 0 pd.wd = 0 + pd.self = pd unlock(&pd.lock) var errno int32 @@ -271,14 +273,14 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { // Copy current seq into the timer arg. // Timer func will check the seq against current descriptor seq, // if they differ the descriptor was reused or timers were reset. - pd.rt.arg = pd + pd.rt.arg = pd.makeArg() pd.rt.seq = pd.rseq resettimer(&pd.rt, pd.rd) } } else if pd.rd != rd0 || combo != combo0 { pd.rseq++ // invalidate current timers if pd.rd > 0 { - modtimer(&pd.rt, pd.rd, 0, rtf, pd, pd.rseq) + modtimer(&pd.rt, pd.rd, 0, rtf, pd.makeArg(), pd.rseq) } else { deltimer(&pd.rt) pd.rt.f = nil @@ -287,14 +289,14 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { if pd.wt.f == nil { if pd.wd > 0 && !combo { pd.wt.f = netpollWriteDeadline - pd.wt.arg = pd + pd.wt.arg = pd.makeArg() pd.wt.seq = pd.wseq resettimer(&pd.wt, pd.wd) } } else if pd.wd != wd0 || combo != combo0 { pd.wseq++ // invalidate current timers if pd.wd > 0 && !combo { - modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd, pd.wseq) + modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd.makeArg(), pd.wseq) } else { deltimer(&pd.wt) pd.wt.f = nil @@ -547,3 +549,20 @@ func (c *pollCache) alloc() *pollDesc { unlock(&c.lock) return pd } + +// makeArg converts pd to an interface{}. +// makeArg does not do any allocation. Normally, such +// a conversion requires an allocation because pointers to +// go:notinheap types (which pollDesc is) must be stored +// in interfaces indirectly. See issue 42076. +func (pd *pollDesc) makeArg() (i interface{}) { + x := (*eface)(unsafe.Pointer(&i)) + x._type = pdType + x.data = unsafe.Pointer(&pd.self) + return +} + +var ( + pdEface interface{} = (*pollDesc)(nil) + pdType *_type = efaceOf(&pdEface)._type +) diff --git a/test/fixedbugs/issue42076.go b/test/fixedbugs/issue42076.go new file mode 100644 index 0000000000..3e954813c9 --- /dev/null +++ b/test/fixedbugs/issue42076.go @@ -0,0 +1,21 @@ +// run + +// Copyright 2020 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 main + +import "reflect" + +//go:notinheap +type NIH struct { +} + +var x, y NIH + +func main() { + if reflect.DeepEqual(&x, &y) != true { + panic("should report true") + } +} -- cgit v1.3 From 091257def92b0280b07bde9536b7cdf5f3b02aec Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 27 Oct 2020 14:15:00 -0700 Subject: cmd/compile: print pointers to go:notinheap types without converting to unsafe.Pointer Pretty minor concern, but after auditing the compiler/runtime for conversions from pointers to go:notinheap types to unsafe.Pointer, this is the only remaining one I found. Update #42076 Change-Id: I81d5b893c9ada2fc19a51c2559262f2e9ff71c35 Reviewed-on: https://go-review.googlesource.com/c/go/+/265757 Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/builtin.go | 431 +++++++++++++------------ src/cmd/compile/internal/gc/builtin/runtime.go | 1 + src/cmd/compile/internal/gc/walk.go | 12 +- src/runtime/print.go | 3 + 4 files changed, 231 insertions(+), 216 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go index da7b107bfe..fd95b657b2 100644 --- a/src/cmd/compile/internal/gc/builtin.go +++ b/src/cmd/compile/internal/gc/builtin.go @@ -44,6 +44,7 @@ var runtimeDecls = [...]struct { {"printcomplex", funcTag, 27}, {"printstring", funcTag, 29}, {"printpointer", funcTag, 30}, + {"printuintptr", funcTag, 31}, {"printiface", funcTag, 30}, {"printeface", funcTag, 30}, {"printslice", funcTag, 30}, @@ -51,134 +52,134 @@ var runtimeDecls = [...]struct { {"printsp", funcTag, 9}, {"printlock", funcTag, 9}, {"printunlock", funcTag, 9}, - {"concatstring2", funcTag, 33}, - {"concatstring3", funcTag, 34}, - {"concatstring4", funcTag, 35}, - {"concatstring5", funcTag, 36}, - {"concatstrings", funcTag, 38}, - {"cmpstring", funcTag, 39}, - {"intstring", funcTag, 42}, - {"slicebytetostring", funcTag, 43}, - {"slicebytetostringtmp", funcTag, 44}, - {"slicerunetostring", funcTag, 47}, - {"stringtoslicebyte", funcTag, 49}, - {"stringtoslicerune", funcTag, 52}, - {"slicecopy", funcTag, 53}, - {"decoderune", funcTag, 54}, - {"countrunes", funcTag, 55}, - {"convI2I", funcTag, 56}, - {"convT16", funcTag, 57}, - {"convT32", funcTag, 57}, - {"convT64", funcTag, 57}, - {"convTstring", funcTag, 57}, - {"convTslice", funcTag, 57}, - {"convT2E", funcTag, 58}, - {"convT2Enoptr", funcTag, 58}, - {"convT2I", funcTag, 58}, - {"convT2Inoptr", funcTag, 58}, - {"assertE2I", funcTag, 56}, - {"assertE2I2", funcTag, 59}, - {"assertI2I", funcTag, 56}, - {"assertI2I2", funcTag, 59}, - {"panicdottypeE", funcTag, 60}, - {"panicdottypeI", funcTag, 60}, - {"panicnildottype", funcTag, 61}, - {"ifaceeq", funcTag, 63}, - {"efaceeq", funcTag, 63}, - {"fastrand", funcTag, 65}, - {"makemap64", funcTag, 67}, - {"makemap", funcTag, 68}, - {"makemap_small", funcTag, 69}, - {"mapaccess1", funcTag, 70}, - {"mapaccess1_fast32", funcTag, 71}, - {"mapaccess1_fast64", funcTag, 71}, - {"mapaccess1_faststr", funcTag, 71}, - {"mapaccess1_fat", funcTag, 72}, - {"mapaccess2", funcTag, 73}, - {"mapaccess2_fast32", funcTag, 74}, - {"mapaccess2_fast64", funcTag, 74}, - {"mapaccess2_faststr", funcTag, 74}, - {"mapaccess2_fat", funcTag, 75}, - {"mapassign", funcTag, 70}, - {"mapassign_fast32", funcTag, 71}, - {"mapassign_fast32ptr", funcTag, 71}, - {"mapassign_fast64", funcTag, 71}, - {"mapassign_fast64ptr", funcTag, 71}, - {"mapassign_faststr", funcTag, 71}, - {"mapiterinit", funcTag, 76}, - {"mapdelete", funcTag, 76}, - {"mapdelete_fast32", funcTag, 77}, - {"mapdelete_fast64", funcTag, 77}, - {"mapdelete_faststr", funcTag, 77}, - {"mapiternext", funcTag, 78}, - {"mapclear", funcTag, 79}, - {"makechan64", funcTag, 81}, - {"makechan", funcTag, 82}, - {"chanrecv1", funcTag, 84}, - {"chanrecv2", funcTag, 85}, - {"chansend1", funcTag, 87}, + {"concatstring2", funcTag, 34}, + {"concatstring3", funcTag, 35}, + {"concatstring4", funcTag, 36}, + {"concatstring5", funcTag, 37}, + {"concatstrings", funcTag, 39}, + {"cmpstring", funcTag, 40}, + {"intstring", funcTag, 43}, + {"slicebytetostring", funcTag, 44}, + {"slicebytetostringtmp", funcTag, 45}, + {"slicerunetostring", funcTag, 48}, + {"stringtoslicebyte", funcTag, 50}, + {"stringtoslicerune", funcTag, 53}, + {"slicecopy", funcTag, 54}, + {"decoderune", funcTag, 55}, + {"countrunes", funcTag, 56}, + {"convI2I", funcTag, 57}, + {"convT16", funcTag, 58}, + {"convT32", funcTag, 58}, + {"convT64", funcTag, 58}, + {"convTstring", funcTag, 58}, + {"convTslice", funcTag, 58}, + {"convT2E", funcTag, 59}, + {"convT2Enoptr", funcTag, 59}, + {"convT2I", funcTag, 59}, + {"convT2Inoptr", funcTag, 59}, + {"assertE2I", funcTag, 57}, + {"assertE2I2", funcTag, 60}, + {"assertI2I", funcTag, 57}, + {"assertI2I2", funcTag, 60}, + {"panicdottypeE", funcTag, 61}, + {"panicdottypeI", funcTag, 61}, + {"panicnildottype", funcTag, 62}, + {"ifaceeq", funcTag, 64}, + {"efaceeq", funcTag, 64}, + {"fastrand", funcTag, 66}, + {"makemap64", funcTag, 68}, + {"makemap", funcTag, 69}, + {"makemap_small", funcTag, 70}, + {"mapaccess1", funcTag, 71}, + {"mapaccess1_fast32", funcTag, 72}, + {"mapaccess1_fast64", funcTag, 72}, + {"mapaccess1_faststr", funcTag, 72}, + {"mapaccess1_fat", funcTag, 73}, + {"mapaccess2", funcTag, 74}, + {"mapaccess2_fast32", funcTag, 75}, + {"mapaccess2_fast64", funcTag, 75}, + {"mapaccess2_faststr", funcTag, 75}, + {"mapaccess2_fat", funcTag, 76}, + {"mapassign", funcTag, 71}, + {"mapassign_fast32", funcTag, 72}, + {"mapassign_fast32ptr", funcTag, 72}, + {"mapassign_fast64", funcTag, 72}, + {"mapassign_fast64ptr", funcTag, 72}, + {"mapassign_faststr", funcTag, 72}, + {"mapiterinit", funcTag, 77}, + {"mapdelete", funcTag, 77}, + {"mapdelete_fast32", funcTag, 78}, + {"mapdelete_fast64", funcTag, 78}, + {"mapdelete_faststr", funcTag, 78}, + {"mapiternext", funcTag, 79}, + {"mapclear", funcTag, 80}, + {"makechan64", funcTag, 82}, + {"makechan", funcTag, 83}, + {"chanrecv1", funcTag, 85}, + {"chanrecv2", funcTag, 86}, + {"chansend1", funcTag, 88}, {"closechan", funcTag, 30}, - {"writeBarrier", varTag, 89}, - {"typedmemmove", funcTag, 90}, - {"typedmemclr", funcTag, 91}, - {"typedslicecopy", funcTag, 92}, - {"selectnbsend", funcTag, 93}, - {"selectnbrecv", funcTag, 94}, - {"selectnbrecv2", funcTag, 96}, - {"selectsetpc", funcTag, 97}, - {"selectgo", funcTag, 98}, + {"writeBarrier", varTag, 90}, + {"typedmemmove", funcTag, 91}, + {"typedmemclr", funcTag, 92}, + {"typedslicecopy", funcTag, 93}, + {"selectnbsend", funcTag, 94}, + {"selectnbrecv", funcTag, 95}, + {"selectnbrecv2", funcTag, 97}, + {"selectsetpc", funcTag, 98}, + {"selectgo", funcTag, 99}, {"block", funcTag, 9}, - {"makeslice", funcTag, 99}, - {"makeslice64", funcTag, 100}, - {"makeslicecopy", funcTag, 101}, - {"growslice", funcTag, 103}, - {"memmove", funcTag, 104}, - {"memclrNoHeapPointers", funcTag, 105}, - {"memclrHasPointers", funcTag, 105}, - {"memequal", funcTag, 106}, - {"memequal0", funcTag, 107}, - {"memequal8", funcTag, 107}, - {"memequal16", funcTag, 107}, - {"memequal32", funcTag, 107}, - {"memequal64", funcTag, 107}, - {"memequal128", funcTag, 107}, - {"f32equal", funcTag, 108}, - {"f64equal", funcTag, 108}, - {"c64equal", funcTag, 108}, - {"c128equal", funcTag, 108}, - {"strequal", funcTag, 108}, - {"interequal", funcTag, 108}, - {"nilinterequal", funcTag, 108}, - {"memhash", funcTag, 109}, - {"memhash0", funcTag, 110}, - {"memhash8", funcTag, 110}, - {"memhash16", funcTag, 110}, - {"memhash32", funcTag, 110}, - {"memhash64", funcTag, 110}, - {"memhash128", funcTag, 110}, - {"f32hash", funcTag, 110}, - {"f64hash", funcTag, 110}, - {"c64hash", funcTag, 110}, - {"c128hash", funcTag, 110}, - {"strhash", funcTag, 110}, - {"interhash", funcTag, 110}, - {"nilinterhash", funcTag, 110}, - {"int64div", funcTag, 111}, - {"uint64div", funcTag, 112}, - {"int64mod", funcTag, 111}, - {"uint64mod", funcTag, 112}, - {"float64toint64", funcTag, 113}, - {"float64touint64", funcTag, 114}, - {"float64touint32", funcTag, 115}, - {"int64tofloat64", funcTag, 116}, - {"uint64tofloat64", funcTag, 117}, - {"uint32tofloat64", funcTag, 118}, - {"complex128div", funcTag, 119}, - {"racefuncenter", funcTag, 120}, + {"makeslice", funcTag, 100}, + {"makeslice64", funcTag, 101}, + {"makeslicecopy", funcTag, 102}, + {"growslice", funcTag, 104}, + {"memmove", funcTag, 105}, + {"memclrNoHeapPointers", funcTag, 106}, + {"memclrHasPointers", funcTag, 106}, + {"memequal", funcTag, 107}, + {"memequal0", funcTag, 108}, + {"memequal8", funcTag, 108}, + {"memequal16", funcTag, 108}, + {"memequal32", funcTag, 108}, + {"memequal64", funcTag, 108}, + {"memequal128", funcTag, 108}, + {"f32equal", funcTag, 109}, + {"f64equal", funcTag, 109}, + {"c64equal", funcTag, 109}, + {"c128equal", funcTag, 109}, + {"strequal", funcTag, 109}, + {"interequal", funcTag, 109}, + {"nilinterequal", funcTag, 109}, + {"memhash", funcTag, 110}, + {"memhash0", funcTag, 111}, + {"memhash8", funcTag, 111}, + {"memhash16", funcTag, 111}, + {"memhash32", funcTag, 111}, + {"memhash64", funcTag, 111}, + {"memhash128", funcTag, 111}, + {"f32hash", funcTag, 111}, + {"f64hash", funcTag, 111}, + {"c64hash", funcTag, 111}, + {"c128hash", funcTag, 111}, + {"strhash", funcTag, 111}, + {"interhash", funcTag, 111}, + {"nilinterhash", funcTag, 111}, + {"int64div", funcTag, 112}, + {"uint64div", funcTag, 113}, + {"int64mod", funcTag, 112}, + {"uint64mod", funcTag, 113}, + {"float64toint64", funcTag, 114}, + {"float64touint64", funcTag, 115}, + {"float64touint32", funcTag, 116}, + {"int64tofloat64", funcTag, 117}, + {"uint64tofloat64", funcTag, 118}, + {"uint32tofloat64", funcTag, 119}, + {"complex128div", funcTag, 120}, + {"racefuncenter", funcTag, 31}, {"racefuncenterfp", funcTag, 9}, {"racefuncexit", funcTag, 9}, - {"raceread", funcTag, 120}, - {"racewrite", funcTag, 120}, + {"raceread", funcTag, 31}, + {"racewrite", funcTag, 31}, {"racereadrange", funcTag, 121}, {"racewriterange", funcTag, 121}, {"msanread", funcTag, 121}, @@ -233,96 +234,96 @@ func runtimeTypes() []*types.Type { typs[28] = types.Types[TSTRING] typs[29] = functype(nil, []*Node{anonfield(typs[28])}, nil) typs[30] = functype(nil, []*Node{anonfield(typs[2])}, nil) - typs[31] = types.NewArray(typs[0], 32) - typs[32] = types.NewPtr(typs[31]) - typs[33] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])}) - typs[34] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])}) - typs[35] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])}) - typs[36] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])}) - typs[37] = types.NewSlice(typs[28]) - typs[38] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[37])}, []*Node{anonfield(typs[28])}) - typs[39] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[15])}) - typs[40] = types.NewArray(typs[0], 4) - typs[41] = types.NewPtr(typs[40]) - typs[42] = functype(nil, []*Node{anonfield(typs[41]), anonfield(typs[22])}, []*Node{anonfield(typs[28])}) - typs[43] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])}) - typs[44] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])}) - typs[45] = types.Runetype - typs[46] = types.NewSlice(typs[45]) - typs[47] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[46])}, []*Node{anonfield(typs[28])}) - typs[48] = types.NewSlice(typs[0]) - typs[49] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28])}, []*Node{anonfield(typs[48])}) - typs[50] = types.NewArray(typs[45], 32) - typs[51] = types.NewPtr(typs[50]) - typs[52] = functype(nil, []*Node{anonfield(typs[51]), anonfield(typs[28])}, []*Node{anonfield(typs[46])}) - typs[53] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])}) - typs[54] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[45]), anonfield(typs[15])}) - typs[55] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])}) - typs[56] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])}) - typs[57] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])}) - typs[58] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) - typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])}) - typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) - typs[61] = functype(nil, []*Node{anonfield(typs[1])}, nil) - typs[62] = types.NewPtr(typs[5]) - typs[63] = functype(nil, []*Node{anonfield(typs[62]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) - typs[64] = types.Types[TUINT32] - typs[65] = functype(nil, nil, []*Node{anonfield(typs[64])}) - typs[66] = types.NewMap(typs[2], typs[2]) - typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[66])}) - typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[66])}) - typs[69] = functype(nil, nil, []*Node{anonfield(typs[66])}) - typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) - typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) - typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) - typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) - typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, nil) - typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, nil) - typs[78] = functype(nil, []*Node{anonfield(typs[3])}, nil) - typs[79] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66])}, nil) - typs[80] = types.NewChan(typs[2], types.Cboth) - typs[81] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[80])}) - typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[80])}) - typs[83] = types.NewChan(typs[2], types.Crecv) - typs[84] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, nil) - typs[85] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[86] = types.NewChan(typs[2], types.Csend) - typs[87] = functype(nil, []*Node{anonfield(typs[86]), anonfield(typs[3])}, nil) - typs[88] = types.NewArray(typs[0], 3) - typs[89] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[88]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])}) - typs[90] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) - typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) - typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) - typs[93] = functype(nil, []*Node{anonfield(typs[86]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[94] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[83])}, []*Node{anonfield(typs[6])}) - typs[95] = types.NewPtr(typs[6]) - typs[96] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[95]), anonfield(typs[83])}, []*Node{anonfield(typs[6])}) - typs[97] = functype(nil, []*Node{anonfield(typs[62])}, nil) - typs[98] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])}) - typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])}) - typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])}) - typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])}) - typs[102] = types.NewSlice(typs[2]) - typs[103] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[102]), anonfield(typs[15])}, []*Node{anonfield(typs[102])}) - typs[104] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, nil) - typs[105] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, nil) - typs[106] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, []*Node{anonfield(typs[6])}) - typs[107] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) - typs[108] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) - typs[109] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) - typs[110] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) - typs[111] = functype(nil, []*Node{anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[22])}) - typs[112] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])}) - typs[113] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])}) - typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])}) - typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[64])}) - typs[116] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])}) - typs[117] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])}) - typs[118] = functype(nil, []*Node{anonfield(typs[64])}, []*Node{anonfield(typs[20])}) - typs[119] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])}) - typs[120] = functype(nil, []*Node{anonfield(typs[5])}, nil) + typs[31] = functype(nil, []*Node{anonfield(typs[5])}, nil) + typs[32] = types.NewArray(typs[0], 32) + typs[33] = types.NewPtr(typs[32]) + typs[34] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])}) + typs[35] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])}) + typs[36] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])}) + typs[37] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])}) + typs[38] = types.NewSlice(typs[28]) + typs[39] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[38])}, []*Node{anonfield(typs[28])}) + typs[40] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[15])}) + typs[41] = types.NewArray(typs[0], 4) + typs[42] = types.NewPtr(typs[41]) + typs[43] = functype(nil, []*Node{anonfield(typs[42]), anonfield(typs[22])}, []*Node{anonfield(typs[28])}) + typs[44] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])}) + typs[45] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])}) + typs[46] = types.Runetype + typs[47] = types.NewSlice(typs[46]) + typs[48] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[47])}, []*Node{anonfield(typs[28])}) + typs[49] = types.NewSlice(typs[0]) + typs[50] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28])}, []*Node{anonfield(typs[49])}) + typs[51] = types.NewArray(typs[46], 32) + typs[52] = types.NewPtr(typs[51]) + typs[53] = functype(nil, []*Node{anonfield(typs[52]), anonfield(typs[28])}, []*Node{anonfield(typs[47])}) + typs[54] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])}) + typs[55] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[46]), anonfield(typs[15])}) + typs[56] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])}) + typs[57] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])}) + typs[58] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])}) + typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) + typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])}) + typs[61] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) + typs[62] = functype(nil, []*Node{anonfield(typs[1])}, nil) + typs[63] = types.NewPtr(typs[5]) + typs[64] = functype(nil, []*Node{anonfield(typs[63]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) + typs[65] = types.Types[TUINT32] + typs[66] = functype(nil, nil, []*Node{anonfield(typs[65])}) + typs[67] = types.NewMap(typs[2], typs[2]) + typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[67])}) + typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[67])}) + typs[70] = functype(nil, nil, []*Node{anonfield(typs[67])}) + typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) + typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) + typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) + typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) + typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, nil) + typs[78] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, nil) + typs[79] = functype(nil, []*Node{anonfield(typs[3])}, nil) + typs[80] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67])}, nil) + typs[81] = types.NewChan(typs[2], types.Cboth) + typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[81])}) + typs[83] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[81])}) + typs[84] = types.NewChan(typs[2], types.Crecv) + typs[85] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, nil) + typs[86] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[87] = types.NewChan(typs[2], types.Csend) + typs[88] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, nil) + typs[89] = types.NewArray(typs[0], 3) + typs[90] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[89]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])}) + typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) + typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) + typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) + typs[94] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[95] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[84])}, []*Node{anonfield(typs[6])}) + typs[96] = types.NewPtr(typs[6]) + typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[96]), anonfield(typs[84])}, []*Node{anonfield(typs[6])}) + typs[98] = functype(nil, []*Node{anonfield(typs[63])}, nil) + typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])}) + typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])}) + typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])}) + typs[102] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])}) + typs[103] = types.NewSlice(typs[2]) + typs[104] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[103]), anonfield(typs[15])}, []*Node{anonfield(typs[103])}) + typs[105] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, nil) + typs[106] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, nil) + typs[107] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, []*Node{anonfield(typs[6])}) + typs[108] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) + typs[109] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) + typs[110] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) + typs[111] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, []*Node{anonfield(typs[5])}) + typs[112] = functype(nil, []*Node{anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[22])}) + typs[113] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])}) + typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])}) + typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])}) + typs[116] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[65])}) + typs[117] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])}) + typs[118] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])}) + typs[119] = functype(nil, []*Node{anonfield(typs[65])}, []*Node{anonfield(typs[20])}) + typs[120] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])}) typs[121] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil) typs[122] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil) typs[123] = types.NewSlice(typs[7]) @@ -331,7 +332,7 @@ func runtimeTypes() []*types.Type { typs[126] = functype(nil, []*Node{anonfield(typs[125]), anonfield(typs[125])}, nil) typs[127] = types.Types[TUINT16] typs[128] = functype(nil, []*Node{anonfield(typs[127]), anonfield(typs[127])}, nil) - typs[129] = functype(nil, []*Node{anonfield(typs[64]), anonfield(typs[64])}, nil) + typs[129] = functype(nil, []*Node{anonfield(typs[65]), anonfield(typs[65])}, nil) typs[130] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil) return typs[:] } diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go index 02d6c7b7f5..aac2de38c6 100644 --- a/src/cmd/compile/internal/gc/builtin/runtime.go +++ b/src/cmd/compile/internal/gc/builtin/runtime.go @@ -54,6 +54,7 @@ func printuint(uint64) func printcomplex(complex128) func printstring(string) func printpointer(any) +func printuintptr(uintptr) func printiface(any) func printeface(any) func printslice(any) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 927f6c4b1e..b453e9f1d9 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -1957,7 +1957,17 @@ func walkprint(nn *Node, init *Nodes) *Node { on = syslook("printiface") } on = substArgTypes(on, n.Type) // any-1 - case TPTR, TCHAN, TMAP, TFUNC, TUNSAFEPTR: + case TPTR: + if n.Type.Elem().NotInHeap() { + on = syslook("printuintptr") + n = nod(OCONV, n, nil) + n.Type = types.Types[TUNSAFEPTR] + n = nod(OCONV, n, nil) + n.Type = types.Types[TUINTPTR] + break + } + fallthrough + case TCHAN, TMAP, TFUNC, TUNSAFEPTR: on = syslook("printpointer") on = substArgTypes(on, n.Type) // any-1 case TSLICE: diff --git a/src/runtime/print.go b/src/runtime/print.go index e605eb34cb..64055a34cc 100644 --- a/src/runtime/print.go +++ b/src/runtime/print.go @@ -237,6 +237,9 @@ func printhex(v uint64) { func printpointer(p unsafe.Pointer) { printhex(uint64(uintptr(p))) } +func printuintptr(p uintptr) { + printhex(uint64(p)) +} func printstring(s string) { gwrite(bytes(s)) -- cgit v1.3 From 2414e1f17b2f82be10110513b17cdfa80c9937c6 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 26 Oct 2020 19:06:39 -0400 Subject: cmd/compile: make sure field offset is aligned for structure splitting Always use the aligned form -- the old code sometimes could at least nominally use a misaligned field in an SSA-able struct, even if not actually. Fixes #42181. Change-Id: Ibdce0985f9349da70921a37423054b85ee4200d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/265277 Reviewed-by: Than McIntosh Reviewed-by: Cherry Zhang Trust: David Chase --- src/cmd/compile/internal/gc/ssa.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 4488337924..fb9d3e811a 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -6978,15 +6978,10 @@ func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) { func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot { st := name.Type - ft := st.FieldType(i) - var offset int64 - for f := 0; f < i; f++ { - offset += st.FieldType(f).Size() - } // Note: the _ field may appear several times. But // have no fear, identically-named but distinct Autos are // ok, albeit maybe confusing for a debugger. - return e.SplitSlot(&name, "."+st.FieldName(i), offset, ft) + return e.SplitSlot(&name, "."+st.FieldName(i), st.FieldOff(i), st.FieldType(i)) } func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot { -- cgit v1.3 From c95bd2e6a99ab06efadb265bf42bbaf8d964904f Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 16 Sep 2020 10:13:37 -0700 Subject: cmd/compile: optimize generated struct/array equality code Use a standard "not-equal" label that we can jump to when we detect that the arguments are not equal. This prevents the recombination that was noticed in #39428. Fixes #39428 Change-Id: Ib7c6b3539f4f6046043fd7148f940fcdcab70427 Reviewed-on: https://go-review.googlesource.com/c/go/+/255317 Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/gc/alg.go | 144 +++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 45 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index 2ab69c2c56..2f7fa27bb9 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -529,6 +529,10 @@ func geneq(t *types.Type) *obj.LSym { fn := dclfunc(sym, tfn) np := asNode(tfn.Type.Params().Field(0).Nname) nq := asNode(tfn.Type.Params().Field(1).Nname) + nr := asNode(tfn.Type.Results().Field(0).Nname) + + // Label to jump to if an equality test fails. + neq := autolabel(".neq") // We reach here only for types that have equality but // cannot be handled by the standard algorithms, @@ -555,13 +559,13 @@ func geneq(t *types.Type) *obj.LSym { // for i := 0; i < nelem; i++ { // if eq(p[i], q[i]) { // } else { - // return + // goto neq // } // } // // TODO(josharian): consider doing some loop unrolling // for larger nelem as well, processing a few elements at a time in a loop. - checkAll := func(unroll int64, eq func(pi, qi *Node) *Node) { + checkAll := func(unroll int64, last bool, eq func(pi, qi *Node) *Node) { // checkIdx generates a node to check for equality at index i. checkIdx := func(i *Node) *Node { // pi := p[i] @@ -576,37 +580,38 @@ func geneq(t *types.Type) *obj.LSym { } if nelem <= unroll { + if last { + // Do last comparison in a different manner. + nelem-- + } // Generate a series of checks. - var cond *Node for i := int64(0); i < nelem; i++ { - c := nodintconst(i) - check := checkIdx(c) - if cond == nil { - cond = check - continue - } - cond = nod(OANDAND, cond, check) + // if check {} else { goto neq } + nif := nod(OIF, checkIdx(nodintconst(i)), nil) + nif.Rlist.Append(nodSym(OGOTO, nil, neq)) + fn.Nbody.Append(nif) + } + if last { + fn.Nbody.Append(nod(OAS, nr, checkIdx(nodintconst(nelem)))) + } + } else { + // Generate a for loop. + // for i := 0; i < nelem; i++ + i := temp(types.Types[TINT]) + init := nod(OAS, i, nodintconst(0)) + cond := nod(OLT, i, nodintconst(nelem)) + post := nod(OAS, i, nod(OADD, i, nodintconst(1))) + loop := nod(OFOR, cond, post) + loop.Ninit.Append(init) + // if eq(pi, qi) {} else { goto neq } + nif := nod(OIF, checkIdx(i), nil) + nif.Rlist.Append(nodSym(OGOTO, nil, neq)) + loop.Nbody.Append(nif) + fn.Nbody.Append(loop) + if last { + fn.Nbody.Append(nod(OAS, nr, nodbool(true))) } - nif := nod(OIF, cond, nil) - nif.Rlist.Append(nod(ORETURN, nil, nil)) - fn.Nbody.Append(nif) - return } - - // Generate a for loop. - // for i := 0; i < nelem; i++ - i := temp(types.Types[TINT]) - init := nod(OAS, i, nodintconst(0)) - cond := nod(OLT, i, nodintconst(nelem)) - post := nod(OAS, i, nod(OADD, i, nodintconst(1))) - loop := nod(OFOR, cond, post) - loop.Ninit.Append(init) - // if eq(pi, qi) {} else { return } - check := checkIdx(i) - nif := nod(OIF, check, nil) - nif.Rlist.Append(nod(ORETURN, nil, nil)) - loop.Nbody.Append(nif) - fn.Nbody.Append(loop) } switch t.Elem().Etype { @@ -614,32 +619,28 @@ func geneq(t *types.Type) *obj.LSym { // Do two loops. First, check that all the lengths match (cheap). // Second, check that all the contents match (expensive). // TODO: when the array size is small, unroll the length match checks. - checkAll(3, func(pi, qi *Node) *Node { + checkAll(3, false, func(pi, qi *Node) *Node { // Compare lengths. eqlen, _ := eqstring(pi, qi) return eqlen }) - checkAll(1, func(pi, qi *Node) *Node { + checkAll(1, true, func(pi, qi *Node) *Node { // Compare contents. _, eqmem := eqstring(pi, qi) return eqmem }) case TFLOAT32, TFLOAT64: - checkAll(2, func(pi, qi *Node) *Node { + checkAll(2, true, func(pi, qi *Node) *Node { // p[i] == q[i] return nod(OEQ, pi, qi) }) // TODO: pick apart structs, do them piecemeal too default: - checkAll(1, func(pi, qi *Node) *Node { + checkAll(1, true, func(pi, qi *Node) *Node { // p[i] == q[i] return nod(OEQ, pi, qi) }) } - // return true - ret := nod(ORETURN, nil, nil) - ret.List.Append(nodbool(true)) - fn.Nbody.Append(ret) case TSTRUCT: // Build a list of conditions to satisfy. @@ -717,20 +718,40 @@ func geneq(t *types.Type) *obj.LSym { flatConds = append(flatConds, c...) } - var cond *Node if len(flatConds) == 0 { - cond = nodbool(true) + fn.Nbody.Append(nod(OAS, nr, nodbool(true))) } else { - cond = flatConds[0] - for _, c := range flatConds[1:] { - cond = nod(OANDAND, cond, c) + for _, c := range flatConds[:len(flatConds)-1] { + // if cond {} else { goto neq } + n := nod(OIF, c, nil) + n.Rlist.Append(nodSym(OGOTO, nil, neq)) + fn.Nbody.Append(n) } + fn.Nbody.Append(nod(OAS, nr, flatConds[len(flatConds)-1])) } + } - ret := nod(ORETURN, nil, nil) - ret.List.Append(cond) - fn.Nbody.Append(ret) + // ret: + // return + ret := autolabel(".ret") + fn.Nbody.Append(nodSym(OLABEL, nil, ret)) + fn.Nbody.Append(nod(ORETURN, nil, nil)) + + // neq: + // r = false + // return (or goto ret) + fn.Nbody.Append(nodSym(OLABEL, nil, neq)) + fn.Nbody.Append(nod(OAS, nr, nodbool(false))) + if EqCanPanic(t) || hasCall(fn) { + // Epilogue is large, so share it with the equal case. + fn.Nbody.Append(nodSym(OGOTO, nil, ret)) + } else { + // Epilogue is small, so don't bother sharing. + fn.Nbody.Append(nod(ORETURN, nil, nil)) } + // TODO(khr): the epilogue size detection condition above isn't perfect. + // We should really do a generic CL that shares epilogues across + // the board. See #24936. if Debug.r != 0 { dumplist("geneq body", fn.Nbody) @@ -762,6 +783,39 @@ func geneq(t *types.Type) *obj.LSym { return closure } +func hasCall(n *Node) bool { + if n.Op == OCALL || n.Op == OCALLFUNC { + return true + } + if n.Left != nil && hasCall(n.Left) { + return true + } + if n.Right != nil && hasCall(n.Right) { + return true + } + for _, x := range n.Ninit.Slice() { + if hasCall(x) { + return true + } + } + for _, x := range n.Nbody.Slice() { + if hasCall(x) { + return true + } + } + for _, x := range n.List.Slice() { + if hasCall(x) { + return true + } + } + for _, x := range n.Rlist.Slice() { + if hasCall(x) { + return true + } + } + return false +} + // eqfield returns the node // p.field == q.field func eqfield(p *Node, q *Node, field *types.Sym) *Node { -- cgit v1.3 From 642329fdd55aabafc67b3a7c50902e29125621ab Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 22 Oct 2020 00:25:17 +0700 Subject: Revert "cmd/compile: split exported/non-exported methods for interface type" This reverts commit 8f26b57f9afc238bdecb9b7030bc2f4364093885. Reason for revert: break a bunch of code, include standard library. Fixes #42123 Change-Id: Ife90ecbafd2cb395623d1db555fbfc9c1b0098e0 Reviewed-on: https://go-review.googlesource.com/c/go/+/264026 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Russ Cox --- doc/go1.16.html | 9 ------ src/cmd/compile/internal/gc/reflect.go | 3 +- src/internal/reflectlite/type.go | 35 +++++++--------------- src/internal/reflectlite/value.go | 4 +-- src/reflect/all_test.go | 18 ++++------- src/reflect/type.go | 55 ++++++++++++++-------------------- src/reflect/value.go | 21 ++++++------- src/runtime/alg.go | 2 +- src/runtime/iface.go | 12 ++++---- src/runtime/mfinal.go | 4 +-- src/runtime/type.go | 26 ++++------------ 11 files changed, 64 insertions(+), 125 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/doc/go1.16.html b/doc/go1.16.html index ba2f80f95e..3592d0b663 100644 --- a/doc/go1.16.html +++ b/doc/go1.16.html @@ -264,15 +264,6 @@ Do not send CLs removing the interior tags from such phrases. On Linux kernel version 4.1 and above, the maximum is now 4294967295.

-

reflect

- -

- For interface types and values, Method, - MethodByName, and - NumMethod now - operate on the interface's exported method set, rather than its full method set. -

-

text/template/parse

diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 229fcfeaee..21429af782 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -1275,9 +1275,8 @@ func dtypesym(t *types.Type) *obj.LSym { } ot = dgopkgpath(lsym, ot, tpkg) - xcount := sort.Search(n, func(i int) bool { return !types.IsExported(m[i].name.Name) }) ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t)) - ot = duintptr(lsym, ot, uint64(xcount)) + ot = duintptr(lsym, ot, uint64(n)) ot = duintptr(lsym, ot, uint64(n)) dataAdd := imethodSize() * n ot = dextratype(lsym, ot, t, dataAdd) diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go index 37cf03594f..15ba30da36 100644 --- a/src/internal/reflectlite/type.go +++ b/src/internal/reflectlite/type.go @@ -234,13 +234,10 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { rtype - pkgPath name // import path - expMethods []imethod // sorted by name, see runtime/type.go:interfacetype to see how it is encoded. + pkgPath name // import path + methods []imethod // sorted by hash } -func (t *interfaceType) methods() []imethod { return t.expMethods[:cap(t.expMethods)] } -func (t *interfaceType) isEmpty() bool { return cap(t.expMethods) == 0 } - // mapType represents a map type. type mapType struct { rtype @@ -698,7 +695,7 @@ func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { } // NumMethod returns the number of interface methods in the type's method set. -func (t *interfaceType) NumMethod() int { return len(t.expMethods) } +func (t *interfaceType) NumMethod() int { return len(t.methods) } // TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. @@ -735,10 +732,9 @@ func implements(T, V *rtype) bool { return false } t := (*interfaceType)(unsafe.Pointer(T)) - if t.isEmpty() { + if len(t.methods) == 0 { return true } - tmethods := t.methods() // The same algorithm applies in both cases, but the // method tables for an interface type and a concrete type @@ -755,11 +751,10 @@ func implements(T, V *rtype) bool { if V.Kind() == Interface { v := (*interfaceType)(unsafe.Pointer(V)) i := 0 - vmethods := v.methods() - for j := 0; j < len(vmethods); j++ { - tm := &tmethods[i] + for j := 0; j < len(v.methods); j++ { + tm := &t.methods[i] tmName := t.nameOff(tm.name) - vm := &vmethods[j] + vm := &v.methods[j] vmName := V.nameOff(vm.name) if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if !tmName.isExported() { @@ -775,7 +770,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(tmethods) { + if i++; i >= len(t.methods) { return true } } @@ -790,7 +785,7 @@ func implements(T, V *rtype) bool { i := 0 vmethods := v.methods() for j := 0; j < int(v.mcount); j++ { - tm := &tmethods[i] + tm := &t.methods[i] tmName := t.nameOff(tm.name) vm := vmethods[j] vmName := V.nameOff(vm.name) @@ -808,7 +803,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(tmethods) { + if i++; i >= len(t.methods) { return true } } @@ -902,7 +897,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { case Interface: t := (*interfaceType)(unsafe.Pointer(T)) v := (*interfaceType)(unsafe.Pointer(V)) - if t.isEmpty() && v.isEmpty() { + if len(t.methods) == 0 && len(v.methods) == 0 { return true } // Might have the same methods but still @@ -967,11 +962,3 @@ func toType(t *rtype) Type { func ifaceIndir(t *rtype) bool { return t.kind&kindDirectIface == 0 } - -func isEmptyIface(t *rtype) bool { - if t.Kind() != Interface { - return false - } - tt := (*interfaceType)(unsafe.Pointer(t)) - return tt.isEmpty() -} diff --git a/src/internal/reflectlite/value.go b/src/internal/reflectlite/value.go index fb0ec77b58..0365eeeabf 100644 --- a/src/internal/reflectlite/value.go +++ b/src/internal/reflectlite/value.go @@ -228,7 +228,7 @@ func (v Value) Elem() Value { switch k { case Interface: var eface interface{} - if isEmptyIface(v.typ) { + if v.typ.NumMethod() == 0 { eface = *(*interface{})(v.ptr) } else { eface = (interface{})(*(*interface { @@ -433,7 +433,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value return Value{dst, nil, flag(Interface)} } x := valueInterface(v) - if isEmptyIface(dst) { + if dst.NumMethod() == 0 { *(*interface{})(target) = x } else { ifaceE2I(dst, x, target) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index be15362aae..a12712d254 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -2995,14 +2995,6 @@ func TestUnexportedMethods(t *testing.T) { if got := typ.NumMethod(); got != 0 { t.Errorf("NumMethod=%d, want 0 satisfied methods", got) } - - var i unexpI - if got := TypeOf(&i).Elem().NumMethod(); got != 0 { - t.Errorf("NumMethod=%d, want 0 satisfied methods", got) - } - if got := ValueOf(&i).Elem().NumMethod(); got != 0 { - t.Errorf("NumMethod=%d, want 0 satisfied methods", got) - } } type InnerInt struct { @@ -3656,21 +3648,21 @@ func TestCallPanic(t *testing.T) { v := ValueOf(T{i, i, i, i, T2{i, i}, i, i, T2{i, i}}) badCall(func() { call(v.Field(0).Method(0)) }) // .t0.W badCall(func() { call(v.Field(0).Elem().Method(0)) }) // .t0.W - badMethod(func() { call(v.Field(0).Method(1)) }) // .t0.w + badCall(func() { call(v.Field(0).Method(1)) }) // .t0.w badMethod(func() { call(v.Field(0).Elem().Method(2)) }) // .t0.w ok(func() { call(v.Field(1).Method(0)) }) // .T1.Y ok(func() { call(v.Field(1).Elem().Method(0)) }) // .T1.Y - badMethod(func() { call(v.Field(1).Method(1)) }) // .T1.y + badCall(func() { call(v.Field(1).Method(1)) }) // .T1.y badMethod(func() { call(v.Field(1).Elem().Method(2)) }) // .T1.y ok(func() { call(v.Field(2).Method(0)) }) // .NamedT0.W ok(func() { call(v.Field(2).Elem().Method(0)) }) // .NamedT0.W - badMethod(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w + badCall(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w badMethod(func() { call(v.Field(2).Elem().Method(2)) }) // .NamedT0.w ok(func() { call(v.Field(3).Method(0)) }) // .NamedT1.Y ok(func() { call(v.Field(3).Elem().Method(0)) }) // .NamedT1.Y - badMethod(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y + badCall(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y badMethod(func() { call(v.Field(3).Elem().Method(3)) }) // .NamedT1.y ok(func() { call(v.Field(4).Field(0).Method(0)) }) // .NamedT2.T1.Y @@ -3680,7 +3672,7 @@ func TestCallPanic(t *testing.T) { badCall(func() { call(v.Field(5).Method(0)) }) // .namedT0.W badCall(func() { call(v.Field(5).Elem().Method(0)) }) // .namedT0.W - badMethod(func() { call(v.Field(5).Method(1)) }) // .namedT0.w + badCall(func() { call(v.Field(5).Method(1)) }) // .namedT0.w badMethod(func() { call(v.Field(5).Elem().Method(2)) }) // .namedT0.w badCall(func() { call(v.Field(6).Method(0)) }) // .namedT1.Y diff --git a/src/reflect/type.go b/src/reflect/type.go index 0b34ca0c94..a3a616701b 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -386,14 +386,10 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { rtype - pkgPath name // import path - expMethods []imethod // sorted by name, see runtime/type.go:interfacetype to see how it is encoded. + pkgPath name // import path + methods []imethod // sorted by hash } -// methods returns t's full method set, both exported and non-exported. -func (t *interfaceType) methods() []imethod { return t.expMethods[:cap(t.expMethods)] } -func (t *interfaceType) isEmpty() bool { return cap(t.expMethods) == 0 } - // mapType represents a map type. type mapType struct { rtype @@ -1053,22 +1049,25 @@ func (d ChanDir) String() string { // Method returns the i'th method in the type's method set. func (t *interfaceType) Method(i int) (m Method) { - if i < 0 || i >= len(t.expMethods) { - panic("reflect: Method index out of range") + if i < 0 || i >= len(t.methods) { + return } - p := &t.expMethods[i] + p := &t.methods[i] pname := t.nameOff(p.name) m.Name = pname.name() if !pname.isExported() { - panic("reflect: unexported method: " + pname.name()) + m.PkgPath = pname.pkgPath() + if m.PkgPath == "" { + m.PkgPath = t.pkgPath.name() + } } m.Type = toType(t.typeOff(p.typ)) m.Index = i return } -// NumMethod returns the number of exported interface methods in the type's method set. -func (t *interfaceType) NumMethod() int { return len(t.expMethods) } +// NumMethod returns the number of interface methods in the type's method set. +func (t *interfaceType) NumMethod() int { return len(t.methods) } // MethodByName method with the given name in the type's method set. func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { @@ -1076,8 +1075,8 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { return } var p *imethod - for i := range t.expMethods { - p = &t.expMethods[i] + for i := range t.methods { + p = &t.methods[i] if t.nameOff(p.name).name() == name { return t.Method(i), true } @@ -1486,10 +1485,9 @@ func implements(T, V *rtype) bool { return false } t := (*interfaceType)(unsafe.Pointer(T)) - if t.isEmpty() { + if len(t.methods) == 0 { return true } - tmethods := t.methods() // The same algorithm applies in both cases, but the // method tables for an interface type and a concrete type @@ -1506,11 +1504,10 @@ func implements(T, V *rtype) bool { if V.Kind() == Interface { v := (*interfaceType)(unsafe.Pointer(V)) i := 0 - vmethods := v.methods() - for j := 0; j < len(vmethods); j++ { - tm := &tmethods[i] + for j := 0; j < len(v.methods); j++ { + tm := &t.methods[i] tmName := t.nameOff(tm.name) - vm := &vmethods[j] + vm := &v.methods[j] vmName := V.nameOff(vm.name) if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if !tmName.isExported() { @@ -1526,7 +1523,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(tmethods) { + if i++; i >= len(t.methods) { return true } } @@ -1541,7 +1538,7 @@ func implements(T, V *rtype) bool { i := 0 vmethods := v.methods() for j := 0; j < int(v.mcount); j++ { - tm := &tmethods[i] + tm := &t.methods[i] tmName := t.nameOff(tm.name) vm := vmethods[j] vmName := V.nameOff(vm.name) @@ -1559,7 +1556,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(tmethods) { + if i++; i >= len(t.methods) { return true } } @@ -1661,7 +1658,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { case Interface: t := (*interfaceType)(unsafe.Pointer(T)) v := (*interfaceType)(unsafe.Pointer(V)) - if t.isEmpty() && v.isEmpty() { + if len(t.methods) == 0 && len(v.methods) == 0 { return true } // Might have the same methods but still @@ -2445,7 +2442,7 @@ func StructOf(fields []StructField) Type { switch f.typ.Kind() { case Interface: ift := (*interfaceType)(unsafe.Pointer(ft)) - for im, m := range ift.methods() { + for im, m := range ift.methods { if ift.nameOff(m.name).pkgPath() != "" { // TODO(sbinet). Issue 15924. panic("reflect: embedded interface with unexported method(s) not implemented") @@ -3152,11 +3149,3 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) { } } } - -func isEmptyIface(rt *rtype) bool { - if rt.Kind() != Interface { - return false - } - tt := (*interfaceType)(unsafe.Pointer(rt)) - return len(tt.methods()) == 0 -} diff --git a/src/reflect/value.go b/src/reflect/value.go index 24eab6a2c6..bf926a7453 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -636,11 +636,10 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *rtype, t *fu i := methodIndex if v.typ.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(v.typ)) - ttmethods := tt.methods() - if uint(i) >= uint(len(ttmethods)) { + if uint(i) >= uint(len(tt.methods)) { panic("reflect: internal error: invalid method index") } - m := &ttmethods[i] + m := &tt.methods[i] if !tt.nameOff(m.name).isExported() { panic("reflect: " + op + " of unexported method") } @@ -814,7 +813,7 @@ func (v Value) Elem() Value { switch k { case Interface: var eface interface{} - if isEmptyIface(v.typ) { + if v.typ.NumMethod() == 0 { eface = *(*interface{})(v.ptr) } else { eface = (interface{})(*(*interface { @@ -1035,7 +1034,7 @@ func valueInterface(v Value, safe bool) interface{} { // Special case: return the element inside the interface. // Empty interface has one layout, all interfaces with // methods have a second layout. - if isEmptyIface(v.typ) { + if v.NumMethod() == 0 { return *(*interface{})(v.ptr) } return *(*interface { @@ -1919,11 +1918,10 @@ func (v Value) Type() Type { if v.typ.Kind() == Interface { // Method on interface. tt := (*interfaceType)(unsafe.Pointer(v.typ)) - ttmethods := tt.methods() - if uint(i) >= uint(len(ttmethods)) { + if uint(i) >= uint(len(tt.methods)) { panic("reflect: internal error: invalid method index") } - m := &ttmethods[i] + m := &tt.methods[i] return v.typ.typeOff(m.typ) } // Method on concrete type. @@ -2441,7 +2439,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value return Value{dst, nil, flag(Interface)} } x := valueInterface(v, false) - if isEmptyIface(dst) { + if dst.NumMethod() == 0 { *(*interface{})(target) = x } else { ifaceE2I(dst, x, target) @@ -2730,11 +2728,10 @@ func cvtDirect(v Value, typ Type) Value { func cvtT2I(v Value, typ Type) Value { target := unsafe_New(typ.common()) x := valueInterface(v, false) - rt := typ.(*rtype) - if isEmptyIface(rt) { + if typ.NumMethod() == 0 { *(*interface{})(target) = x } else { - ifaceE2I(rt, x, target) + ifaceE2I(typ.(*rtype), x, target) } return Value{typ.common(), target, v.flag.ro() | flagIndir | flag(Interface)} } diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 2ec3fc3658..1b3bf1180d 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -166,7 +166,7 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr { return strhash(p, h) case kindInterface: i := (*interfacetype)(unsafe.Pointer(t)) - if i.isEmpty() { + if len(i.mhdr) == 0 { return nilinterhash(p, h) } return interhash(p, h) diff --git a/src/runtime/iface.go b/src/runtime/iface.go index f8b7d429a3..0504b89363 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -31,17 +31,16 @@ func itabHashFunc(inter *interfacetype, typ *_type) uintptr { } func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { - if inter.isEmpty() { + if len(inter.mhdr) == 0 { throw("internal error - misuse of itab") } - imethods := inter.methods() // easy case if typ.tflag&tflagUncommon == 0 { if canfail { return nil } - name := inter.typ.nameOff(imethods[0].name) + name := inter.typ.nameOff(inter.mhdr[0].name) panic(&TypeAssertionError{nil, typ, &inter.typ, name.name()}) } @@ -64,7 +63,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { } // Entry doesn't exist yet. Make a new entry & add it. - m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(imethods)-1)*sys.PtrSize, 0, &memstats.other_sys)) + m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*sys.PtrSize, 0, &memstats.other_sys)) m.inter = inter m._type = typ // The hash is used in type switches. However, compiler statically generates itab's @@ -198,8 +197,7 @@ func (m *itab) init() string { // and interface names are unique, // so can iterate over both in lock step; // the loop is O(ni+nt) not O(ni*nt). - imethods := inter.methods() - ni := len(imethods) + ni := len(inter.mhdr) nt := int(x.mcount) xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt] j := 0 @@ -207,7 +205,7 @@ func (m *itab) init() string { var fun0 unsafe.Pointer imethods: for k := 0; k < ni; k++ { - i := &imethods[k] + i := &inter.mhdr[k] itype := inter.typ.typeOff(i.ityp) name := inter.typ.nameOff(i.name) iname := name.name() diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index 6ec5133be0..f4dbd77252 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -210,7 +210,7 @@ func runfinq() { // set up with empty interface (*eface)(frame)._type = &f.ot.typ (*eface)(frame).data = f.arg - if !ityp.isEmpty() { + if len(ityp.mhdr) != 0 { // convert to interface with methods // this conversion is guaranteed to succeed - we checked in SetFinalizer *(*iface)(frame) = assertE2I(ityp, *(*eface)(frame)) @@ -394,7 +394,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { } case fint.kind&kindMask == kindInterface: ityp := (*interfacetype)(unsafe.Pointer(fint)) - if ityp.isEmpty() { + if len(ityp.mhdr) == 0 { // ok - satisfies empty interface goto okarg } diff --git a/src/runtime/type.go b/src/runtime/type.go index 36492619e1..81455f3532 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -366,19 +366,7 @@ type imethod struct { type interfacetype struct { typ _type pkgpath name - // expMethods contains all interface methods. - // - // - len(expMethods) returns number of exported methods. - // - cap(expMethods) returns all interface methods, including both exported/non-exported methods. - expMethods []imethod -} - -func (it *interfacetype) methods() []imethod { - return it.expMethods[:cap(it.expMethods)] -} - -func (it *interfacetype) isEmpty() bool { - return cap(it.expMethods) == 0 + mhdr []imethod } type maptype struct { @@ -676,15 +664,13 @@ func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool { if it.pkgpath.name() != iv.pkgpath.name() { return false } - itmethods := it.methods() - ivmethods := iv.methods() - if len(itmethods) != len(ivmethods) { + if len(it.mhdr) != len(iv.mhdr) { return false } - for i := range itmethods { - tm := &itmethods[i] - vm := &ivmethods[i] - // Note the expMethods array can be relocated from + for i := range it.mhdr { + tm := &it.mhdr[i] + vm := &iv.mhdr[i] + // Note the mhdr array can be relocated from // another module. See #17724. tname := resolveNameOff(unsafe.Pointer(tm), tm.name) vname := resolveNameOff(unsafe.Pointer(vm), vm.name) -- cgit v1.3 From c1afbf69c71bc624a4766a48ef637a5f726dfe4e Mon Sep 17 00:00:00 2001 From: Rémy Oudompheng Date: Sun, 25 Oct 2020 11:52:29 +0100 Subject: cmd/compile: use magic multiply for unsigned values less than 1<<16 on 32-bit architectures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is done by decomposing the number to be divided in 32-bit components and using the 32-bit magic multiply. For the lowering to be effective the constant must fit in 16 bits. On ARM the expression n / 5 compiles to 25 instructions. Benchmark for GOARCH=arm (Cortex-A53) name old time/op new time/op delta DivconstU64/3-6 1.19µs ± 0% 0.03µs ± 1% -97.40% (p=0.000 n=9+9) DivconstU64/5-6 1.18µs ± 1% 0.03µs ± 1% -97.38% (p=0.000 n=10+8) DivconstU64/37-6 1.13µs ± 1% 0.04µs ± 1% -96.51% (p=0.000 n=10+8) DivconstU64/1234567-6 852ns ± 0% 901ns ± 1% +5.73% (p=0.000 n=8+9) Benchmark for GOARCH=386 (Haswell) name old time/op new time/op delta DivconstU64/3-4 18.0ns ± 2% 5.6ns ± 1% -69.06% (p=0.000 n=10+10) DivconstU64/5-4 17.8ns ± 1% 5.5ns ± 1% -68.87% (p=0.000 n=9+10) DivconstU64/37-4 17.8ns ± 1% 7.3ns ± 0% -58.90% (p=0.000 n=10+10) DivconstU64/1234567-4 17.5ns ± 1% 16.0ns ± 0% -8.55% (p=0.000 n=10+9) Change-Id: I38a19b4d59093ec021ef2e5241364a3dad4eae73 Reviewed-on: https://go-review.googlesource.com/c/go/+/264683 Run-TryBot: Emmanuel Odeke TryBot-Result: Go Bot Reviewed-by: Keith Randall Trust: Emmanuel Odeke --- src/cmd/compile/internal/gc/walk.go | 5 +- src/cmd/compile/internal/ssa/gen/generic.rules | 40 +++++++++++++ src/cmd/compile/internal/ssa/rewritegeneric.go | 60 +++++++++++++++++++ src/cmd/compile/internal/test/divconst_test.go | 81 +++++++++++++++++++++++++- 4 files changed, 182 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index b453e9f1d9..82898c8167 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -989,7 +989,7 @@ opswitch: // runtime calls late in SSA processing. if Widthreg < 8 && (et == TINT64 || et == TUINT64) { if n.Right.Op == OLITERAL { - // Leave div/mod by constant powers of 2. + // Leave div/mod by constant powers of 2 or small 16-bit constants. // The SSA backend will handle those. switch et { case TINT64: @@ -1002,6 +1002,9 @@ opswitch: } case TUINT64: c := uint64(n.Right.Int64Val()) + if c < 1<<16 { + break opswitch + } if c != 0 && c&(c-1) == 0 { break opswitch } diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index 4351ef5bdd..de0ef9349d 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -1040,6 +1040,46 @@ (ZeroExt32to64 x))) (Const64 [32+umagic32(c).s-1]))) +// For unsigned 64-bit divides on 32-bit machines, +// if the constant fits in 16 bits (so that the last term +// fits in 32 bits), convert to three 32-bit divides by a constant. +// +// If 1<<32 = Q * c + R +// and x = hi << 32 + lo +// +// Then x = (hi/c*c + hi%c) << 32 + lo +// = hi/c*c<<32 + hi%c<<32 + lo +// = hi/c*c<<32 + (hi%c)*(Q*c+R) + lo/c*c + lo%c +// = hi/c*c<<32 + (hi%c)*Q*c + lo/c*c + (hi%c*R+lo%c) +// and x / c = (hi/c)<<32 + (hi%c)*Q + lo/c + (hi%c*R+lo%c)/c +(Div64u x (Const64 [c])) && c > 0 && c <= 0xFFFF && umagicOK32(int32(c)) && config.RegSize == 4 && config.useHmul => + (Add64 + (Add64 + (Add64 + (Lsh64x64 + (ZeroExt32to64 + (Div32u + (Trunc64to32 (Rsh64Ux64 x (Const64 [32]))) + (Const32 [int32(c)]))) + (Const64 [32])) + (ZeroExt32to64 (Div32u (Trunc64to32 x) (Const32 [int32(c)])))) + (Mul64 + (ZeroExt32to64 + (Mod32u + (Trunc64to32 (Rsh64Ux64 x (Const64 [32]))) + (Const32 [int32(c)]))) + (Const64 [int64((1<<32)/c)]))) + (ZeroExt32to64 + (Div32u + (Add32 + (Mod32u (Trunc64to32 x) (Const32 [int32(c)])) + (Mul32 + (Mod32u + (Trunc64to32 (Rsh64Ux64 x (Const64 [32]))) + (Const32 [int32(c)])) + (Const32 [int32((1<<32)%c)]))) + (Const32 [int32(c)])))) + // For 64-bit divides on 64-bit machines // (64-bit divides on 32-bit machines are lowered to a runtime call by the walk pass.) (Div64u x (Const64 [c])) && umagicOK64(c) && config.RegSize == 8 && umagic64(c).m&1 == 0 && config.useHmul => diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 11f4cc7c58..0d25c76abf 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -5208,6 +5208,66 @@ func rewriteValuegeneric_OpDiv64u(v *Value) bool { return true } // match: (Div64u x (Const64 [c])) + // cond: c > 0 && c <= 0xFFFF && umagicOK32(int32(c)) && config.RegSize == 4 + // result: (Add64 (Add64 (Add64 (Lsh64x64 (ZeroExt32to64 (Div32u (Trunc64to32 (Rsh64Ux64 x (Const64 [32]))) (Const32 [int32(c)]))) (Const64 [32])) (ZeroExt32to64 (Div32u (Trunc64to32 x) (Const32 [int32(c)])))) (Mul64 (ZeroExt32to64 (Mod32u (Trunc64to32 (Rsh64Ux64 x (Const64 [32]))) (Const32 [int32(c)]))) (Const64 [int64((1<<32)/c)]))) (ZeroExt32to64 (Div32u (Add32 (Mod32u (Trunc64to32 x) (Const32 [int32(c)])) (Mul32 (Mod32u (Trunc64to32 (Rsh64Ux64 x (Const64 [32]))) (Const32 [int32(c)])) (Const32 [int32((1<<32)%c)]))) (Const32 [int32(c)])))) + for { + x := v_0 + if v_1.Op != OpConst64 { + break + } + c := auxIntToInt64(v_1.AuxInt) + if !(c > 0 && c <= 0xFFFF && umagicOK32(int32(c)) && config.RegSize == 4) { + break + } + v.reset(OpAdd64) + v0 := b.NewValue0(v.Pos, OpAdd64, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpAdd64, typ.UInt64) + v2 := b.NewValue0(v.Pos, OpLsh64x64, typ.UInt64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4 := b.NewValue0(v.Pos, OpDiv32u, typ.UInt32) + v5 := b.NewValue0(v.Pos, OpTrunc64to32, typ.UInt32) + v6 := b.NewValue0(v.Pos, OpRsh64Ux64, typ.UInt64) + v7 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) + v7.AuxInt = int64ToAuxInt(32) + v6.AddArg2(x, v7) + v5.AddArg(v6) + v8 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) + v8.AuxInt = int32ToAuxInt(int32(c)) + v4.AddArg2(v5, v8) + v3.AddArg(v4) + v2.AddArg2(v3, v7) + v9 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v10 := b.NewValue0(v.Pos, OpDiv32u, typ.UInt32) + v11 := b.NewValue0(v.Pos, OpTrunc64to32, typ.UInt32) + v11.AddArg(x) + v10.AddArg2(v11, v8) + v9.AddArg(v10) + v1.AddArg2(v2, v9) + v12 := b.NewValue0(v.Pos, OpMul64, typ.UInt64) + v13 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v14 := b.NewValue0(v.Pos, OpMod32u, typ.UInt32) + v14.AddArg2(v5, v8) + v13.AddArg(v14) + v15 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) + v15.AuxInt = int64ToAuxInt(int64((1 << 32) / c)) + v12.AddArg2(v13, v15) + v0.AddArg2(v1, v12) + v16 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v17 := b.NewValue0(v.Pos, OpDiv32u, typ.UInt32) + v18 := b.NewValue0(v.Pos, OpAdd32, typ.UInt32) + v19 := b.NewValue0(v.Pos, OpMod32u, typ.UInt32) + v19.AddArg2(v11, v8) + v20 := b.NewValue0(v.Pos, OpMul32, typ.UInt32) + v21 := b.NewValue0(v.Pos, OpConst32, typ.UInt32) + v21.AuxInt = int32ToAuxInt(int32((1 << 32) % c)) + v20.AddArg2(v14, v21) + v18.AddArg2(v19, v20) + v17.AddArg2(v18, v8) + v16.AddArg(v17) + v.AddArg2(v0, v16) + return true + } + // match: (Div64u x (Const64 [c])) // cond: umagicOK64(c) && config.RegSize == 8 && umagic64(c).m&1 == 0 && config.useHmul // result: (Rsh64Ux64 (Hmul64u (Const64 [int64(1<<63+umagic64(c).m/2)]) x) (Const64 [umagic64(c).s-1])) for { diff --git a/src/cmd/compile/internal/test/divconst_test.go b/src/cmd/compile/internal/test/divconst_test.go index b03550058f..9358a60374 100644 --- a/src/cmd/compile/internal/test/divconst_test.go +++ b/src/cmd/compile/internal/test/divconst_test.go @@ -44,10 +44,85 @@ func BenchmarkDivisibleWDivconstI64(b *testing.B) { var u64res uint64 +func TestDivmodConstU64(t *testing.T) { + // Test division by c. Function f must be func(n) { return n/c, n%c } + testdiv := func(c uint64, f func(uint64) (uint64, uint64)) func(*testing.T) { + return func(t *testing.T) { + x := uint64(12345) + for i := 0; i < 10000; i++ { + x += x << 2 + q, r := f(x) + if r < 0 || r >= c || q*c+r != x { + t.Errorf("divmod(%d, %d) returned incorrect (%d, %d)", x, c, q, r) + } + } + max := uint64(1<<64-1) / c * c + xs := []uint64{0, 1, c - 1, c, c + 1, 2*c - 1, 2 * c, 2*c + 1, + c*c - 1, c * c, c*c + 1, max - 1, max, max + 1, 1<<64 - 1} + for _, x := range xs { + q, r := f(x) + if r < 0 || r >= c || q*c+r != x { + t.Errorf("divmod(%d, %d) returned incorrect (%d, %d)", x, c, q, r) + } + } + } + } + t.Run("2", testdiv(2, func(n uint64) (uint64, uint64) { return n / 2, n % 2 })) + t.Run("3", testdiv(3, func(n uint64) (uint64, uint64) { return n / 3, n % 3 })) + t.Run("4", testdiv(4, func(n uint64) (uint64, uint64) { return n / 4, n % 4 })) + t.Run("5", testdiv(5, func(n uint64) (uint64, uint64) { return n / 5, n % 5 })) + t.Run("6", testdiv(6, func(n uint64) (uint64, uint64) { return n / 6, n % 6 })) + t.Run("7", testdiv(7, func(n uint64) (uint64, uint64) { return n / 7, n % 7 })) + t.Run("8", testdiv(8, func(n uint64) (uint64, uint64) { return n / 8, n % 8 })) + t.Run("9", testdiv(9, func(n uint64) (uint64, uint64) { return n / 9, n % 9 })) + t.Run("10", testdiv(10, func(n uint64) (uint64, uint64) { return n / 10, n % 10 })) + t.Run("11", testdiv(11, func(n uint64) (uint64, uint64) { return n / 11, n % 11 })) + t.Run("12", testdiv(12, func(n uint64) (uint64, uint64) { return n / 12, n % 12 })) + t.Run("13", testdiv(13, func(n uint64) (uint64, uint64) { return n / 13, n % 13 })) + t.Run("14", testdiv(14, func(n uint64) (uint64, uint64) { return n / 14, n % 14 })) + t.Run("15", testdiv(15, func(n uint64) (uint64, uint64) { return n / 15, n % 15 })) + t.Run("16", testdiv(16, func(n uint64) (uint64, uint64) { return n / 16, n % 16 })) + t.Run("17", testdiv(17, func(n uint64) (uint64, uint64) { return n / 17, n % 17 })) + t.Run("255", testdiv(255, func(n uint64) (uint64, uint64) { return n / 255, n % 255 })) + t.Run("256", testdiv(256, func(n uint64) (uint64, uint64) { return n / 256, n % 256 })) + t.Run("257", testdiv(257, func(n uint64) (uint64, uint64) { return n / 257, n % 257 })) + t.Run("65535", testdiv(65535, func(n uint64) (uint64, uint64) { return n / 65535, n % 65535 })) + t.Run("65536", testdiv(65536, func(n uint64) (uint64, uint64) { return n / 65536, n % 65536 })) + t.Run("65537", testdiv(65537, func(n uint64) (uint64, uint64) { return n / 65537, n % 65537 })) + t.Run("1<<32-1", testdiv(1<<32-1, func(n uint64) (uint64, uint64) { return n / (1<<32 - 1), n % (1<<32 - 1) })) + t.Run("1<<32+1", testdiv(1<<32+1, func(n uint64) (uint64, uint64) { return n / (1<<32 + 1), n % (1<<32 + 1) })) + t.Run("1<<64-1", testdiv(1<<64-1, func(n uint64) (uint64, uint64) { return n / (1<<64 - 1), n % (1<<64 - 1) })) +} + func BenchmarkDivconstU64(b *testing.B) { - for i := 0; i < b.N; i++ { - u64res = uint64(i) / 7 - } + b.Run("3", func(b *testing.B) { + x := uint64(123456789123456789) + for i := 0; i < b.N; i++ { + x += x << 4 + u64res = uint64(x) / 3 + } + }) + b.Run("5", func(b *testing.B) { + x := uint64(123456789123456789) + for i := 0; i < b.N; i++ { + x += x << 4 + u64res = uint64(x) / 5 + } + }) + b.Run("37", func(b *testing.B) { + x := uint64(123456789123456789) + for i := 0; i < b.N; i++ { + x += x << 4 + u64res = uint64(x) / 37 + } + }) + b.Run("1234567", func(b *testing.B) { + x := uint64(123456789123456789) + for i := 0; i < b.N; i++ { + x += x << 4 + u64res = uint64(x) / 1234567 + } + }) } func BenchmarkModconstU64(b *testing.B) { -- cgit v1.3 From 15f01d6ae9853fd51ee8842d9af93d04ce25458c Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 13 Oct 2020 19:24:04 -0400 Subject: cmd/compile: delay expansion of OpArg until expand_calls As it says, delay expanpsion of OpArg to the expand_calls phase, to enable (eventually) interprocedural SSA optimizations, and (sooner) change to a register ABI. Includes a round of cleanup to function names and comments, largely to match the expanded scope of the functions. This CL removes the per-function dependence on GOSSAHASH, but the go116lateCallExpansion kill switch remains (and was tested locally to ensure it worked). Two functions in expand_calls.go that performed overlapping things were combined into a single function that is called twice. Fixes #42236. For #40724. Change-Id: Icbb78947eaa39f17f2c1210d5c2caef20abd6571 Reviewed-on: https://go-review.googlesource.com/c/go/+/262117 Trust: David Chase Run-TryBot: David Chase Reviewed-by: Cherry Zhang --- src/cmd/compile/fmtmap_test.go | 1 - src/cmd/compile/internal/gc/ssa.go | 12 +- src/cmd/compile/internal/ssa/compile.go | 2 +- src/cmd/compile/internal/ssa/config.go | 4 +- src/cmd/compile/internal/ssa/expand_calls.go | 406 ++++++++++++++++++++------- src/cmd/compile/internal/ssa/gen/dec64.rules | 9 +- src/cmd/compile/internal/ssa/rewritedec64.go | 16 +- src/cmd/compile/internal/ssa/stackalloc.go | 3 + 8 files changed, 329 insertions(+), 124 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/fmtmap_test.go b/src/cmd/compile/fmtmap_test.go index f8c33ec1f9..179c60187f 100644 --- a/src/cmd/compile/fmtmap_test.go +++ b/src/cmd/compile/fmtmap_test.go @@ -136,7 +136,6 @@ var knownFormats = map[string]string{ "cmd/compile/internal/types.EType %s": "", "cmd/compile/internal/types.EType %v": "", "cmd/internal/obj.ABI %v": "", - "cmd/internal/src.XPos %v": "", "error %v": "", "float64 %.2f": "", "float64 %.3f": "", diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index fb9d3e811a..45d628cc5e 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -409,11 +409,17 @@ func buildssa(fn *Node, worker int) *ssa.Func { // Generate addresses of local declarations s.decladdrs = map[*Node]*ssa.Value{} + var args []ssa.Param + var results []ssa.Param for _, n := range fn.Func.Dcl { switch n.Class() { - case PPARAM, PPARAMOUT: + case PPARAM: + s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type), n, s.sp, s.startmem) + args = append(args, ssa.Param{Type: n.Type, Offset: int32(n.Xoffset)}) + case PPARAMOUT: s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type), n, s.sp, s.startmem) - if n.Class() == PPARAMOUT && s.canSSA(n) { + results = append(results, ssa.Param{Type: n.Type, Offset: int32(n.Xoffset)}) + if s.canSSA(n) { // Save ssa-able PPARAMOUT variables so we can // store them back to the stack at the end of // the function. @@ -4909,7 +4915,7 @@ func (s *state) canSSA(n *Node) bool { if n.Class() == PPARAM && n.Sym != nil && n.Sym.Name == ".this" { // wrappers generated by genwrapper need to update // the .this pointer in place. - // TODO: treat as a PPARMOUT? + // TODO: treat as a PPARAMOUT? return false } return canSSAType(n.Type) diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index bddd271273..9ddc53060c 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -429,7 +429,7 @@ var passes = [...]pass{ {name: "early copyelim", fn: copyelim}, {name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt {name: "short circuit", fn: shortcircuit}, - {name: "decompose args", fn: decomposeArgs, required: true}, + {name: "decompose args", fn: decomposeArgs, required: !go116lateCallExpansion, disabled: go116lateCallExpansion}, // handled by late call lowering {name: "decompose user", fn: decomposeUser, required: true}, {name: "pre-opt deadcode", fn: deadcode}, {name: "opt", fn: opt, required: true}, // NB: some generic rules know the name of the opt pass. TODO: split required rules and optimizing rules diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index cb6f6fe7a1..0fe0337ddf 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -199,9 +199,9 @@ const ( const go116lateCallExpansion = true // LateCallExpansionEnabledWithin returns true if late call expansion should be tested -// within compilation of a function/method triggered by GOSSAHASH (defaults to "yes"). +// within compilation of a function/method. func LateCallExpansionEnabledWithin(f *Func) bool { - return go116lateCallExpansion && f.DebugTest // Currently set up for GOSSAHASH bug searches + return go116lateCallExpansion } // NewConfig returns a new configuration object for the given architecture. diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 3e3573ff39..fbde19d94c 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -15,7 +15,7 @@ type selKey struct { from *Value offset int64 size int64 - typ types.EType + typ *types.Type } type offsetKey struct { @@ -27,7 +27,8 @@ type offsetKey struct { // expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form // that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into // more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are -// reached. +// reached. On the callee side, OpArg nodes are not decomposed until this phase is run. +// TODO results should not be lowered until this phase. func expandCalls(f *Func) { // Calls that need lowering have some number of inputs, including a memory input, // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able. @@ -42,6 +43,10 @@ func expandCalls(f *Func) { } debug := f.pass.debug > 0 + if debug { + fmt.Printf("\nexpandsCalls(%s)\n", f.Name) + } + canSSAType := f.fe.CanSSA regSize := f.Config.RegSize sp, _ := f.spSb() @@ -58,6 +63,10 @@ func expandCalls(f *Func) { namedSelects := make(map[*Value][]namedVal) + sdom := f.Sdom() + + common := make(map[selKey]*Value) + // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target // that has no 64-bit integer registers. intPairTypes := func(et types.EType) (tHi, tLo *types.Type) { @@ -107,6 +116,7 @@ func expandCalls(f *Func) { return v } + // splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates. splitSlots := func(ls []LocalSlot, sfx string, offset int64, ty *types.Type) []LocalSlot { var locs []LocalSlot for i := range ls { @@ -147,21 +157,103 @@ func expandCalls(f *Func) { // With the current ABI, the outputs need to be converted to loads, which will all use the call's // memory output as their input. - // rewriteSelect recursively walks leaf selector to a root (OpSelectN) through - // a chain of Struct/Array Select operations. If the chain of selectors does not - // end in OpSelectN, it does nothing (this can happen depending on compiler phase ordering). - // It emits the code necessary to implement the leaf select operation that leads to the call. + // rewriteSelect recursively walks from leaf selector to a root (OpSelectN, OpLoad, OpArg) + // through a chain of Struct/Array/builtin Select operations. If the chain of selectors does not + // end in an expected root, it does nothing (this can happen depending on compiler phase ordering). + // The "leaf" provides the type, the root supplies the container, and the leaf-to-root path + // accumulates the offset. + // It emits the code necessary to implement the leaf select operation that leads to the root. + // // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. var rewriteSelect func(leaf *Value, selector *Value, offset int64) []LocalSlot rewriteSelect = func(leaf *Value, selector *Value, offset int64) []LocalSlot { + if debug { + fmt.Printf("rewriteSelect(%s, %s, %d)\n", leaf.LongString(), selector.LongString(), offset) + } var locs []LocalSlot leafType := leaf.Type + if len(selector.Args) > 0 { + w := selector.Args[0] + if w.Op == OpCopy { + for w.Op == OpCopy { + w = w.Args[0] + } + selector.SetArg(0, w) + } + } switch selector.Op { - case OpSelectN: - // TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there. + case OpArg: + if !isAlreadyExpandedAggregateType(selector.Type) { + if leafType == selector.Type { // OpIData leads us here, sometimes. + leaf.copyOf(selector) + } else { + f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString()) + } + if debug { + fmt.Printf("\tOpArg, break\n") + } + break + } + if leaf.Op == OpIData { + leafType = removeTrivialWrapperTypes(leaf.Type) + } + aux := selector.Aux + auxInt := selector.AuxInt + offset + if leaf.Block == selector.Block { + leaf.reset(OpArg) + leaf.Aux = aux + leaf.AuxInt = auxInt + leaf.Type = leafType + } else { + w := selector.Block.NewValue0IA(leaf.Pos, OpArg, leafType, auxInt, aux) + leaf.copyOf(w) + if debug { + fmt.Printf("\tnew %s\n", w.LongString()) + } + } for _, s := range namedSelects[selector] { locs = append(locs, f.Names[s.locIndex]) } + + case OpLoad: // We end up here because of IData of immediate structures. + // Failure case: + // (note the failure case is very rare; w/o this case, make.bash and run.bash both pass, as well as + // the hard cases of building {syscall,math,math/cmplx,math/bits,go/constant} on ppc64le and mips-softfloat). + // + // GOSSAFUNC='(*dumper).dump' go build -gcflags=-l -tags=math_big_pure_go cmd/compile/internal/gc + // cmd/compile/internal/gc/dump.go:136:14: internal compiler error: '(*dumper).dump': not lowered: v827, StructSelect PTR PTR + // b2: ← b1 + // v20 (+142) = StaticLECall {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v8 v1 + // v21 (142) = SelectN [1] v20 + // v22 (142) = SelectN [0] v20 + // b15: ← b8 + // v71 (+143) = IData v22 (v[Nodes]) + // v73 (+146) = StaticLECall <[]*Node,mem> {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v71 v21 + // + // translates (w/o the "case OpLoad:" above) to: + // + // b2: ← b1 + // v20 (+142) = StaticCall {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v715 + // v23 (142) = Load <*uintptr> v19 v20 + // v823 (142) = IsNonNil v23 + // v67 (+143) = Load <*[]*Node> v880 v20 + // b15: ← b8 + // v827 (146) = StructSelect <*[]*Node> [0] v67 + // v846 (146) = Store {*[]*Node} v769 v827 v20 + // v73 (+146) = StaticCall {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v846 + // i.e., the struct select is generated and remains in because it is not applied to an actual structure. + // The OpLoad was created to load the single field of the IData + // This case removes that StructSelect. + if leafType != selector.Type { + f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString()) + } + leaf.copyOf(selector) + for _, s := range namedSelects[selector] { + locs = append(locs, f.Names[s.locIndex]) + } + + case OpSelectN: + // TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there. call := selector.Args[0] aux := call.Aux.(*AuxCall) which := selector.AuxInt @@ -171,10 +263,6 @@ func expandCalls(f *Func) { } else { leafType := removeTrivialWrapperTypes(leaf.Type) if canSSAType(leafType) { - for leafType.Etype == types.TSTRUCT && leafType.NumFields() == 1 { - // This may not be adequately general -- consider [1]etc but this is caused by immediate IDATA - leafType = leafType.Field(0).Type - } pt := types.NewPtr(leafType) off := offsetFrom(sp, offset+aux.OffsetOfResult(which), pt) // Any selection right out of the arg area/registers has to be same Block as call, use call as mem input. @@ -185,22 +273,29 @@ func expandCalls(f *Func) { } else { w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call) leaf.copyOf(w) + if debug { + fmt.Printf("\tnew %s\n", w.LongString()) + } + } + for _, s := range namedSelects[selector] { + locs = append(locs, f.Names[s.locIndex]) } } else { f.Fatalf("Should not have non-SSA-able OpSelectN, selector=%s", selector.LongString()) } } + case OpStructSelect: w := selector.Args[0] var ls []LocalSlot - if w.Type.Etype != types.TSTRUCT { - f.Fatalf("Bad type for w: v=%v; sel=%v; w=%v; ,f=%s\n", leaf.LongString(), selector.LongString(), w.LongString(), f.Name) - // Artifact of immediate interface idata + if w.Type.Etype != types.TSTRUCT { // IData artifact ls = rewriteSelect(leaf, w, offset) } else { ls = rewriteSelect(leaf, w, offset+w.Type.FieldOff(int(selector.AuxInt))) - for _, l := range ls { - locs = append(locs, f.fe.SplitStruct(l, int(selector.AuxInt))) + if w.Op != OpIData { + for _, l := range ls { + locs = append(locs, f.fe.SplitStruct(l, int(selector.AuxInt))) + } } } @@ -221,9 +316,7 @@ func expandCalls(f *Func) { case OpStringPtr: ls := rewriteSelect(leaf, selector.Args[0], offset) locs = splitSlots(ls, ".ptr", 0, typ.BytePtr) - //for i := range ls { - // locs = append(locs, f.fe.SplitSlot(&ls[i], ".ptr", 0, typ.BytePtr)) - //} + case OpSlicePtr: w := selector.Args[0] ls := rewriteSelect(leaf, w, offset) @@ -272,32 +365,130 @@ func expandCalls(f *Func) { return locs } - // storeArg converts stores of SSA-able aggregate arguments (passed to a call) into a series of stores of - // smaller types into individual parameter slots. - var storeArg func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value - storeArg = func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value { + // storeArgOrLoad converts stores of SSA-able aggregate arguments (passed to a call) into a series of primitive-typed + // stores of non-aggregate types. It recursively walks up a chain of selectors until it reaches a Load or an Arg. + // If it does not reach a Load or an Arg, nothing happens; this allows a little freedom in phase ordering. + var storeArgOrLoad func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64) *Value + + // decomposeArgOrLoad is a helper for storeArgOrLoad. + // It decomposes a Load or an Arg into smaller parts, parameterized by the decomposeOne and decomposeTwo functions + // passed to it, and returns the new mem. If the type does not match one of the expected aggregate types, it returns nil instead. + decomposeArgOrLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64, + decomposeOne func(pos src.XPos, b *Block, base, source, mem *Value, t1 *types.Type, offArg, offStore int64) *Value, + decomposeTwo func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value) *Value { + u := source.Type + switch u.Etype { + case types.TARRAY: + elem := u.Elem() + for i := int64(0); i < u.NumElem(); i++ { + elemOff := i * elem.Size() + mem = decomposeOne(pos, b, base, source, mem, elem, source.AuxInt+elemOff, offset+elemOff) + pos = pos.WithNotStmt() + } + return mem + case types.TSTRUCT: + for i := 0; i < u.NumFields(); i++ { + fld := u.Field(i) + mem = decomposeOne(pos, b, base, source, mem, fld.Type, source.AuxInt+fld.Offset, offset+fld.Offset) + pos = pos.WithNotStmt() + } + return mem + case types.TINT64, types.TUINT64: + if t.Width == regSize { + break + } + tHi, tLo := intPairTypes(t.Etype) + mem = decomposeOne(pos, b, base, source, mem, tHi, source.AuxInt+hiOffset, offset+hiOffset) + pos = pos.WithNotStmt() + return decomposeOne(pos, b, base, source, mem, tLo, source.AuxInt+lowOffset, offset+lowOffset) + case types.TINTER: + return decomposeTwo(pos, b, base, source, mem, typ.Uintptr, typ.BytePtr, source.AuxInt, offset) + case types.TSTRING: + return decomposeTwo(pos, b, base, source, mem, typ.BytePtr, typ.Int, source.AuxInt, offset) + case types.TCOMPLEX64: + return decomposeTwo(pos, b, base, source, mem, typ.Float32, typ.Float32, source.AuxInt, offset) + case types.TCOMPLEX128: + return decomposeTwo(pos, b, base, source, mem, typ.Float64, typ.Float64, source.AuxInt, offset) + case types.TSLICE: + mem = decomposeTwo(pos, b, base, source, mem, typ.BytePtr, typ.Int, source.AuxInt, offset) + return decomposeOne(pos, b, base, source, mem, typ.Int, source.AuxInt+2*ptrSize, offset+2*ptrSize) + } + return nil + } + + // storeOneArg creates a decomposed (one step) arg that is then stored. + // pos and b locate the store instruction, base is the base of the store target, source is the "base" of the value input, + // mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases. + storeOneArg := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64) *Value { + w := common[selKey{source, offArg, t.Width, t}] + if w == nil { + w = source.Block.NewValue0IA(source.Pos, OpArg, t, offArg, source.Aux) + common[selKey{source, offArg, t.Width, t}] = w + } + return storeArgOrLoad(pos, b, base, w, mem, t, offStore) + } + + // storeOneLoad creates a decomposed (one step) load that is then stored. + storeOneLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64) *Value { + from := offsetFrom(source.Args[0], offArg, types.NewPtr(t)) + w := source.Block.NewValue2(source.Pos, OpLoad, t, from, mem) + return storeArgOrLoad(pos, b, base, w, mem, t, offStore) + } + + storeTwoArg := func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value { + mem = storeOneArg(pos, b, base, source, mem, t1, offArg, offStore) + pos = pos.WithNotStmt() + t1Size := t1.Size() + return storeOneArg(pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size) + } + + storeTwoLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value { + mem = storeOneLoad(pos, b, base, source, mem, t1, offArg, offStore) + pos = pos.WithNotStmt() + t1Size := t1.Size() + return storeOneLoad(pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size) + } + + storeArgOrLoad = func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64) *Value { if debug { - fmt.Printf("\tstoreArg(%s; %s; %v; %d; %s)\n", b, a.LongString(), t, offset, mem.String()) + fmt.Printf("\tstoreArgOrLoad(%s; %s; %s; %s; %d)\n", base.LongString(), source.LongString(), mem.String(), t.String(), offset) } - switch a.Op { + switch source.Op { + case OpCopy: + return storeArgOrLoad(pos, b, base, source.Args[0], mem, t, offset) + + case OpLoad: + ret := decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneLoad, storeTwoLoad) + if ret != nil { + return ret + } + + case OpArg: + ret := decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneArg, storeTwoArg) + if ret != nil { + return ret + } + case OpArrayMake0, OpStructMake0: return mem case OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4: for i := 0; i < t.NumFields(); i++ { fld := t.Field(i) - mem = storeArg(pos, b, a.Args[i], fld.Type, offset+fld.Offset, mem) + mem = storeArgOrLoad(pos, b, base, source.Args[i], mem, fld.Type, offset+fld.Offset) + pos = pos.WithNotStmt() } return mem case OpArrayMake1: - return storeArg(pos, b, a.Args[0], t.Elem(), offset, mem) + return storeArgOrLoad(pos, b, base, source.Args[0], mem, t.Elem(), offset) case OpInt64Make: tHi, tLo := intPairTypes(t.Etype) - mem = storeArg(pos, b, a.Args[0], tHi, offset+hiOffset, mem) - return storeArg(pos, b, a.Args[1], tLo, offset+lowOffset, mem) + mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, tHi, offset+hiOffset) + pos = pos.WithNotStmt() + return storeArgOrLoad(pos, b, base, source.Args[1], mem, tLo, offset+lowOffset) case OpComplexMake: tPart := typ.Float32 @@ -305,59 +496,45 @@ func expandCalls(f *Func) { if wPart == 8 { tPart = typ.Float64 } - mem = storeArg(pos, b, a.Args[0], tPart, offset, mem) - return storeArg(pos, b, a.Args[1], tPart, offset+wPart, mem) + mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, tPart, offset) + pos = pos.WithNotStmt() + return storeArgOrLoad(pos, b, base, source.Args[1], mem, tPart, offset+wPart) case OpIMake: - mem = storeArg(pos, b, a.Args[0], typ.Uintptr, offset, mem) - return storeArg(pos, b, a.Args[1], typ.BytePtr, offset+ptrSize, mem) + mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.Uintptr, offset) + pos = pos.WithNotStmt() + return storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.BytePtr, offset+ptrSize) case OpStringMake: - mem = storeArg(pos, b, a.Args[0], typ.BytePtr, offset, mem) - return storeArg(pos, b, a.Args[1], typ.Int, offset+ptrSize, mem) + mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.BytePtr, offset) + pos = pos.WithNotStmt() + return storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.Int, offset+ptrSize) case OpSliceMake: - mem = storeArg(pos, b, a.Args[0], typ.BytePtr, offset, mem) - mem = storeArg(pos, b, a.Args[1], typ.Int, offset+ptrSize, mem) - return storeArg(pos, b, a.Args[2], typ.Int, offset+2*ptrSize, mem) + mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.BytePtr, offset) + pos = pos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.Int, offset+ptrSize) + return storeArgOrLoad(pos, b, base, source.Args[2], mem, typ.Int, offset+2*ptrSize) } - dst := offsetFrom(sp, offset, types.NewPtr(t)) - x := b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, a, mem) - if debug { - fmt.Printf("\t\tstoreArg returns %s\n", x.LongString()) - } - return x - } - - // splitStore converts a store of an SSA-able aggregate into a series of smaller stores, emitting - // appropriate Struct/Array Select operations (which will soon go dead) to obtain the parts. - // This has to handle aggregate types that have already been lowered by an earlier phase. - var splitStore func(dest, source, mem, v *Value, t *types.Type, offset int64, firstStorePos src.XPos) *Value - splitStore = func(dest, source, mem, v *Value, t *types.Type, offset int64, firstStorePos src.XPos) *Value { - if debug { - fmt.Printf("\tsplitStore(%s; %s; %s; %s; %v; %d; %v)\n", dest.LongString(), source.LongString(), mem.String(), v.LongString(), t, offset, firstStorePos) - } - pos := v.Pos.WithNotStmt() + // For nodes that cannot be taken apart -- OpSelectN, other structure selectors. switch t.Etype { case types.TARRAY: elt := t.Elem() - if t.NumElem() == 1 && t.Width == regSize && elt.Width == regSize { + if source.Type != t && t.NumElem() == 1 && elt.Width == t.Width && t.Width == regSize { t = removeTrivialWrapperTypes(t) - if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY { - f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it") - } - break // handle the leaf type. + // it could be a leaf type, but the "leaf" could be complex64 (for example) + return storeArgOrLoad(pos, b, base, source, mem, t, offset) } for i := int64(0); i < t.NumElem(); i++ { sel := source.Block.NewValue1I(pos, OpArraySelect, elt, i, source) - mem = splitStore(dest, sel, mem, v, elt, offset+i*elt.Width, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, sel, mem, elt, offset+i*elt.Width) + pos = pos.WithNotStmt() } return mem case types.TSTRUCT: - if t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize { + if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize { // This peculiar test deals with accesses to immediate interface data. // It works okay because everything is the same size. // Example code that triggers this can be found in go/constant/value.go, function ToComplex @@ -377,16 +554,15 @@ func expandCalls(f *Func) { // v139 is later stored as an intVal == struct{val *big.Int} which naively requires the fields of // of a *uint8, which does not succeed. t = removeTrivialWrapperTypes(t) - // it could be a leaf type, but the "leaf" could be complex64 (for example) - return splitStore(dest, source, mem, v, t, offset, firstStorePos) + return storeArgOrLoad(pos, b, base, source, mem, t, offset) } for i := 0; i < t.NumFields(); i++ { fld := t.Field(i) sel := source.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), source) - mem = splitStore(dest, sel, mem, v, fld.Type, offset+fld.Offset, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, sel, mem, fld.Type, offset+fld.Offset) + pos = pos.WithNotStmt() } return mem @@ -396,56 +572,55 @@ func expandCalls(f *Func) { } tHi, tLo := intPairTypes(t.Etype) sel := source.Block.NewValue1(pos, OpInt64Hi, tHi, source) - mem = splitStore(dest, sel, mem, v, tHi, offset+hiOffset, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, sel, mem, tHi, offset+hiOffset) + pos = pos.WithNotStmt() sel = source.Block.NewValue1(pos, OpInt64Lo, tLo, source) - return splitStore(dest, sel, mem, v, tLo, offset+lowOffset, firstStorePos) + return storeArgOrLoad(pos, b, base, sel, mem, tLo, offset+lowOffset) case types.TINTER: sel := source.Block.NewValue1(pos, OpITab, typ.BytePtr, source) - mem = splitStore(dest, sel, mem, v, typ.BytePtr, offset, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset) + pos = pos.WithNotStmt() sel = source.Block.NewValue1(pos, OpIData, typ.BytePtr, source) - return splitStore(dest, sel, mem, v, typ.BytePtr, offset+ptrSize, firstStorePos) + return storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset+ptrSize) case types.TSTRING: sel := source.Block.NewValue1(pos, OpStringPtr, typ.BytePtr, source) - mem = splitStore(dest, sel, mem, v, typ.BytePtr, offset, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset) + pos = pos.WithNotStmt() sel = source.Block.NewValue1(pos, OpStringLen, typ.Int, source) - return splitStore(dest, sel, mem, v, typ.Int, offset+ptrSize, firstStorePos) + return storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+ptrSize) case types.TSLICE: et := types.NewPtr(t.Elem()) sel := source.Block.NewValue1(pos, OpSlicePtr, et, source) - mem = splitStore(dest, sel, mem, v, et, offset, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, sel, mem, et, offset) + pos = pos.WithNotStmt() sel = source.Block.NewValue1(pos, OpSliceLen, typ.Int, source) - mem = splitStore(dest, sel, mem, v, typ.Int, offset+ptrSize, firstStorePos) + mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+ptrSize) sel = source.Block.NewValue1(pos, OpSliceCap, typ.Int, source) - return splitStore(dest, sel, mem, v, typ.Int, offset+2*ptrSize, firstStorePos) + return storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+2*ptrSize) case types.TCOMPLEX64: sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float32, source) - mem = splitStore(dest, sel, mem, v, typ.Float32, offset, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Float32, offset) + pos = pos.WithNotStmt() sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float32, source) - return splitStore(dest, sel, mem, v, typ.Float32, offset+4, firstStorePos) + return storeArgOrLoad(pos, b, base, sel, mem, typ.Float32, offset+4) case types.TCOMPLEX128: sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float64, source) - mem = splitStore(dest, sel, mem, v, typ.Float64, offset, firstStorePos) - firstStorePos = firstStorePos.WithNotStmt() + mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Float64, offset) + pos = pos.WithNotStmt() sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float64, source) - return splitStore(dest, sel, mem, v, typ.Float64, offset+8, firstStorePos) + return storeArgOrLoad(pos, b, base, sel, mem, typ.Float64, offset+8) } - // Default, including for aggregates whose single element exactly fills their container - // TODO this will be a problem for cast interfaces containing floats when we move to registers. - x := v.Block.NewValue3A(firstStorePos, OpStore, types.TypeMem, t, offsetFrom(dest, offset, types.NewPtr(t)), source, mem) + + dst := offsetFrom(base, offset, types.NewPtr(t)) + x := b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem) if debug { - fmt.Printf("\t\tsplitStore returns %s\n", x.LongString()) + fmt.Printf("\t\tstoreArg returns %s\n", x.LongString()) } - return x } @@ -490,7 +665,7 @@ func expandCalls(f *Func) { if debug { fmt.Printf("storeArg %s, %v, %d\n", a.LongString(), aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI)) } - mem = storeArg(pos, v.Block, a, aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI), mem) + mem = storeArgOrLoad(pos, v.Block, sp, a, mem, aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI)) } } v.resetArgs() @@ -523,7 +698,7 @@ func expandCalls(f *Func) { t := name.Type if isAlreadyExpandedAggregateType(t) { for j, v := range f.NamedValues[name] { - if v.Op == OpSelectN { + if v.Op == OpSelectN || v.Op == OpArg && isAlreadyExpandedAggregateType(v.Type) { ns := namedSelects[v] namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j}) } @@ -531,17 +706,19 @@ func expandCalls(f *Func) { } } - // Step 1: any stores of aggregates remaining are believed to be sourced from call results. + // Step 1: any stores of aggregates remaining are believed to be sourced from call results or args. // Decompose those stores into a series of smaller stores, adding selection ops as necessary. for _, b := range f.Blocks { for _, v := range b.Values { if v.Op == OpStore { t := v.Aux.(*types.Type) + source := v.Args[1] + tSrc := source.Type iAEATt := isAlreadyExpandedAggregateType(t) + if !iAEATt { // guarding against store immediate struct into interface data field -- store type is *uint8 // TODO can this happen recursively? - tSrc := v.Args[1].Type iAEATt = isAlreadyExpandedAggregateType(tSrc) if iAEATt { t = tSrc @@ -551,8 +728,8 @@ func expandCalls(f *Func) { if debug { fmt.Printf("Splitting store %s\n", v.LongString()) } - dst, source, mem := v.Args[0], v.Args[1], v.Args[2] - mem = splitStore(dst, source, mem, v, t, 0, v.Pos) + dst, mem := v.Args[0], v.Args[2] + mem = storeArgOrLoad(v.Pos, b, dst, source, mem, t, 0) v.copyOf(mem) } } @@ -579,7 +756,7 @@ func expandCalls(f *Func) { OpInt64Hi, OpInt64Lo: w := v.Args[0] switch w.Op { - case OpStructSelect, OpArraySelect, OpSelectN: + case OpStructSelect, OpArraySelect, OpSelectN, OpArg: val2Preds[w] += 1 if debug { fmt.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w]) @@ -595,6 +772,17 @@ func expandCalls(f *Func) { } } + case OpArg: + if !isAlreadyExpandedAggregateType(v.Type) { + continue + } + if _, ok := val2Preds[v]; !ok { + val2Preds[v] = 0 + if debug { + fmt.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) + } + } + case OpSelectNAddr: // Do these directly, there are no chains of selectors. call := v.Args[0] @@ -612,7 +800,6 @@ func expandCalls(f *Func) { // then forwards to rewrite selectors. // // All chains of selectors end up in same block as the call. - sdom := f.Sdom() // Compilation must be deterministic, so sort after extracting first zeroes from map. // Sorting allows dominators-last order within each batch, @@ -640,8 +827,11 @@ func expandCalls(f *Func) { last = len(allOrdered) sort.SliceStable(toProcess, less) for _, v := range toProcess { - w := v.Args[0] delete(val2Preds, v) + if v.Op == OpArg { + continue // no Args[0], hence done. + } + w := v.Args[0] n, ok := val2Preds[w] if !ok { continue @@ -655,13 +845,19 @@ func expandCalls(f *Func) { } } - common := make(map[selKey]*Value) + common = make(map[selKey]*Value) // Rewrite duplicate selectors as copies where possible. for i := len(allOrdered) - 1; i >= 0; i-- { v := allOrdered[i] + if v.Op == OpArg { + continue + } w := v.Args[0] - for w.Op == OpCopy { - w = w.Args[0] + if w.Op == OpCopy { + for w.Op == OpCopy { + w = w.Args[0] + } + v.SetArg(0, w) } typ := v.Type if typ.IsMemory() { @@ -691,7 +887,7 @@ func expandCalls(f *Func) { case OpComplexImag: offset = size } - sk := selKey{from: w, size: size, offset: offset, typ: typ.Etype} + sk := selKey{from: w, size: size, offset: offset, typ: typ} dupe := common[sk] if dupe == nil { common[sk] = v diff --git a/src/cmd/compile/internal/ssa/gen/dec64.rules b/src/cmd/compile/internal/ssa/gen/dec64.rules index 07607960fa..9297ed8d2e 100644 --- a/src/cmd/compile/internal/ssa/gen/dec64.rules +++ b/src/cmd/compile/internal/ssa/gen/dec64.rules @@ -41,20 +41,21 @@ lo (Store {hi.Type} dst hi mem)) -(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() => +// These are not enabled during decomposeBuiltin if late call expansion, but they are always enabled for softFloat +(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") => (Int64Make (Arg {n} [off+4]) (Arg {n} [off])) -(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() => +(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") => (Int64Make (Arg {n} [off+4]) (Arg {n} [off])) -(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() => +(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") => (Int64Make (Arg {n} [off]) (Arg {n} [off+4])) -(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() => +(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") => (Int64Make (Arg {n} [off]) (Arg {n} [off+4])) diff --git a/src/cmd/compile/internal/ssa/rewritedec64.go b/src/cmd/compile/internal/ssa/rewritedec64.go index 8b9753414f..c49bc8043e 100644 --- a/src/cmd/compile/internal/ssa/rewritedec64.go +++ b/src/cmd/compile/internal/ssa/rewritedec64.go @@ -184,12 +184,12 @@ func rewriteValuedec64_OpArg(v *Value) bool { config := b.Func.Config typ := &b.Func.Config.Types // match: (Arg {n} [off]) - // cond: is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() + // cond: is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") // result: (Int64Make (Arg {n} [off+4]) (Arg {n} [off])) for { off := auxIntToInt32(v.AuxInt) n := auxToSym(v.Aux) - if !(is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned()) { + if !(is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")) { break } v.reset(OpInt64Make) @@ -203,12 +203,12 @@ func rewriteValuedec64_OpArg(v *Value) bool { return true } // match: (Arg {n} [off]) - // cond: is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() + // cond: is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") // result: (Int64Make (Arg {n} [off+4]) (Arg {n} [off])) for { off := auxIntToInt32(v.AuxInt) n := auxToSym(v.Aux) - if !(is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned()) { + if !(is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")) { break } v.reset(OpInt64Make) @@ -222,12 +222,12 @@ func rewriteValuedec64_OpArg(v *Value) bool { return true } // match: (Arg {n} [off]) - // cond: is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() + // cond: is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") // result: (Int64Make (Arg {n} [off]) (Arg {n} [off+4])) for { off := auxIntToInt32(v.AuxInt) n := auxToSym(v.Aux) - if !(is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned()) { + if !(is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")) { break } v.reset(OpInt64Make) @@ -241,12 +241,12 @@ func rewriteValuedec64_OpArg(v *Value) bool { return true } // match: (Arg {n} [off]) - // cond: is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() + // cond: is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") // result: (Int64Make (Arg {n} [off]) (Arg {n} [off+4])) for { off := auxIntToInt32(v.AuxInt) n := auxToSym(v.Aux) - if !(is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned()) { + if !(is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")) { break } v.reset(OpInt64Make) diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 7612585136..406a3c3ea5 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -153,6 +153,9 @@ func (s *stackAllocState) stackalloc() { if v.Op != OpArg { continue } + if v.Aux == nil { + f.Fatalf("%s has nil Aux\n", v.LongString()) + } loc := LocalSlot{N: v.Aux.(GCNode), Type: v.Type, Off: v.AuxInt} if f.pass.debug > stackDebug { fmt.Printf("stackalloc %s to %s\n", v, loc) -- cgit v1.3 From 50af50d136551e2009b2b52e829570536271cdaa Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Wed, 14 Oct 2020 08:36:11 -0400 Subject: reflect,runtime: use internal ABI for selected ASM routines Change the definitions of selected runtime assembly routines from ABI0 (the default) to ABIInternal. The ABIInternal def is intended to indicate that these functions don't follow the existing Go runtime ABI. In addition, convert the assembly reference to runtime.main (from runtime.mainPC) to ABIInternal. Finally, for functions such as "runtime.duffzero" that are called directly from generated code, make sure that the compiler looks up the correct ABI version. This is intended to support the register abi work, however these changes should not have any issues even when GOEXPERIMENT=regabi is not in effect. Updates #27539, #40724. Change-Id: I9846f8dcaccc95718cf2e61a18b7e924a0677e4c Reviewed-on: https://go-review.googlesource.com/c/go/+/262319 Run-TryBot: Than McIntosh TryBot-Result: Go Bot Trust: Than McIntosh Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 86 ++++++++++++++++---------------- src/cmd/internal/obj/wasm/wasmobj.go | 6 +-- src/reflect/asm_amd64.s | 12 +++-- src/runtime/asm_amd64.s | 96 ++++++++++++++++++++++-------------- src/runtime/asm_wasm.s | 4 +- src/runtime/duff_amd64.s | 4 +- src/runtime/mkpreempt.go | 3 +- src/runtime/preempt_386.s | 3 +- src/runtime/preempt_amd64.s | 3 +- src/runtime/preempt_arm.s | 3 +- src/runtime/preempt_arm64.s | 3 +- src/runtime/preempt_mips64x.s | 3 +- src/runtime/preempt_mipsx.s | 3 +- src/runtime/preempt_ppc64x.s | 3 +- src/runtime/preempt_riscv64.s | 3 +- src/runtime/preempt_s390x.s | 3 +- src/runtime/preempt_wasm.s | 3 +- src/runtime/race_amd64.s | 12 +++-- src/runtime/sys_linux_amd64.s | 13 +++-- 19 files changed, 154 insertions(+), 112 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 45d628cc5e..7388e4e3e8 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -72,9 +72,9 @@ func initssaconfig() { deferproc = sysfunc("deferproc") deferprocStack = sysfunc("deferprocStack") Deferreturn = sysfunc("deferreturn") - Duffcopy = sysvar("duffcopy") // asm func with special ABI - Duffzero = sysvar("duffzero") // asm func with special ABI - gcWriteBarrier = sysvar("gcWriteBarrier") // asm func with special ABI + Duffcopy = sysfunc("duffcopy") + Duffzero = sysfunc("duffzero") + gcWriteBarrier = sysfunc("gcWriteBarrier") goschedguarded = sysfunc("goschedguarded") growslice = sysfunc("growslice") msanread = sysfunc("msanread") @@ -105,51 +105,51 @@ func initssaconfig() { // asm funcs with special ABI if thearch.LinkArch.Name == "amd64" { GCWriteBarrierReg = map[int16]*obj.LSym{ - x86.REG_AX: sysvar("gcWriteBarrier"), - x86.REG_CX: sysvar("gcWriteBarrierCX"), - x86.REG_DX: sysvar("gcWriteBarrierDX"), - x86.REG_BX: sysvar("gcWriteBarrierBX"), - x86.REG_BP: sysvar("gcWriteBarrierBP"), - x86.REG_SI: sysvar("gcWriteBarrierSI"), - x86.REG_R8: sysvar("gcWriteBarrierR8"), - x86.REG_R9: sysvar("gcWriteBarrierR9"), + x86.REG_AX: sysfunc("gcWriteBarrier"), + x86.REG_CX: sysfunc("gcWriteBarrierCX"), + x86.REG_DX: sysfunc("gcWriteBarrierDX"), + x86.REG_BX: sysfunc("gcWriteBarrierBX"), + x86.REG_BP: sysfunc("gcWriteBarrierBP"), + x86.REG_SI: sysfunc("gcWriteBarrierSI"), + x86.REG_R8: sysfunc("gcWriteBarrierR8"), + x86.REG_R9: sysfunc("gcWriteBarrierR9"), } } if thearch.LinkArch.Family == sys.Wasm { - BoundsCheckFunc[ssa.BoundsIndex] = sysvar("goPanicIndex") - BoundsCheckFunc[ssa.BoundsIndexU] = sysvar("goPanicIndexU") - BoundsCheckFunc[ssa.BoundsSliceAlen] = sysvar("goPanicSliceAlen") - BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysvar("goPanicSliceAlenU") - BoundsCheckFunc[ssa.BoundsSliceAcap] = sysvar("goPanicSliceAcap") - BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysvar("goPanicSliceAcapU") - BoundsCheckFunc[ssa.BoundsSliceB] = sysvar("goPanicSliceB") - BoundsCheckFunc[ssa.BoundsSliceBU] = sysvar("goPanicSliceBU") - BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysvar("goPanicSlice3Alen") - BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("goPanicSlice3AlenU") - BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysvar("goPanicSlice3Acap") - BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("goPanicSlice3AcapU") - BoundsCheckFunc[ssa.BoundsSlice3B] = sysvar("goPanicSlice3B") - BoundsCheckFunc[ssa.BoundsSlice3BU] = sysvar("goPanicSlice3BU") - BoundsCheckFunc[ssa.BoundsSlice3C] = sysvar("goPanicSlice3C") - BoundsCheckFunc[ssa.BoundsSlice3CU] = sysvar("goPanicSlice3CU") + BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("goPanicIndex") + BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("goPanicIndexU") + BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("goPanicSliceAlen") + BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("goPanicSliceAlenU") + BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("goPanicSliceAcap") + BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("goPanicSliceAcapU") + BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("goPanicSliceB") + BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("goPanicSliceBU") + BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("goPanicSlice3Alen") + BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("goPanicSlice3AlenU") + BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("goPanicSlice3Acap") + BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("goPanicSlice3AcapU") + BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("goPanicSlice3B") + BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("goPanicSlice3BU") + BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("goPanicSlice3C") + BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("goPanicSlice3CU") } else { - BoundsCheckFunc[ssa.BoundsIndex] = sysvar("panicIndex") - BoundsCheckFunc[ssa.BoundsIndexU] = sysvar("panicIndexU") - BoundsCheckFunc[ssa.BoundsSliceAlen] = sysvar("panicSliceAlen") - BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysvar("panicSliceAlenU") - BoundsCheckFunc[ssa.BoundsSliceAcap] = sysvar("panicSliceAcap") - BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysvar("panicSliceAcapU") - BoundsCheckFunc[ssa.BoundsSliceB] = sysvar("panicSliceB") - BoundsCheckFunc[ssa.BoundsSliceBU] = sysvar("panicSliceBU") - BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysvar("panicSlice3Alen") - BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("panicSlice3AlenU") - BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysvar("panicSlice3Acap") - BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("panicSlice3AcapU") - BoundsCheckFunc[ssa.BoundsSlice3B] = sysvar("panicSlice3B") - BoundsCheckFunc[ssa.BoundsSlice3BU] = sysvar("panicSlice3BU") - BoundsCheckFunc[ssa.BoundsSlice3C] = sysvar("panicSlice3C") - BoundsCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicSlice3CU") + BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("panicIndex") + BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("panicIndexU") + BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("panicSliceAlen") + BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("panicSliceAlenU") + BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("panicSliceAcap") + BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("panicSliceAcapU") + BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("panicSliceB") + BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("panicSliceBU") + BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("panicSlice3Alen") + BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("panicSlice3AlenU") + BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("panicSlice3Acap") + BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("panicSlice3AcapU") + BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("panicSlice3B") + BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("panicSlice3BU") + BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("panicSlice3C") + BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("panicSlice3CU") } if thearch.LinkArch.PtrSize == 4 { ExtendCheckFunc[ssa.BoundsIndex] = sysvar("panicExtendIndex") diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index f7f66a1255..2e9890d86c 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -129,7 +129,6 @@ var ( morestackNoCtxt *obj.LSym gcWriteBarrier *obj.LSym sigpanic *obj.LSym - sigpanic0 *obj.LSym deferreturn *obj.LSym jmpdefer *obj.LSym ) @@ -142,9 +141,8 @@ const ( func instinit(ctxt *obj.Link) { morestack = ctxt.Lookup("runtime.morestack") morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt") - gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier") + gcWriteBarrier = ctxt.LookupABI("runtime.gcWriteBarrier", obj.ABIInternal) sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal) - sigpanic0 = ctxt.LookupABI("runtime.sigpanic", 0) // sigpanic called from assembly, which has ABI0 deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal) // jmpdefer is defined in assembly as ABI0, but what we're // looking for is the *call* to jmpdefer from the Go function @@ -493,7 +491,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } // return value of call is on the top of the stack, indicating whether to unwind the WebAssembly stack - if call.As == ACALLNORESUME && call.To.Sym != sigpanic && call.To.Sym != sigpanic0 { // sigpanic unwinds the stack, but it never resumes + if call.As == ACALLNORESUME && call.To.Sym != sigpanic { // sigpanic unwinds the stack, but it never resumes // trying to unwind WebAssembly stack but call has no resume point, terminate with error p = appendp(p, AIf) p = appendp(p, obj.AUNDEF) diff --git a/src/reflect/asm_amd64.s b/src/reflect/asm_amd64.s index fb28ab87f1..5c8e56558c 100644 --- a/src/reflect/asm_amd64.s +++ b/src/reflect/asm_amd64.s @@ -9,7 +9,9 @@ // See the comment on the declaration of makeFuncStub in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 +// makeFuncStub must be ABIInternal because it is placed directly +// in function values. +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 NO_LOCAL_POINTERS MOVQ DX, 0(SP) LEAQ argframe+0(FP), CX @@ -17,14 +19,16 @@ TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 MOVB $0, 24(SP) LEAQ 24(SP), AX MOVQ AX, 16(SP) - CALL ·callReflect(SB) + CALL ·callReflect(SB) RET // methodValueCall is the code half of the function returned by makeMethodValue. // See the comment on the declaration of methodValueCall in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 +// methodValueCall must be ABIInternal because it is placed directly +// in function values. +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 NO_LOCAL_POINTERS MOVQ DX, 0(SP) LEAQ argframe+0(FP), CX @@ -32,5 +36,5 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 MOVB $0, 24(SP) LEAQ 24(SP), AX MOVQ AX, 16(SP) - CALL ·callMethod(SB) + CALL ·callMethod(SB) RET diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 19a3bb2d7d..196252e1dd 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -84,7 +84,9 @@ GLOBL _rt0_amd64_lib_argc<>(SB),NOPTR, $8 DATA _rt0_amd64_lib_argv<>(SB)/8, $0 GLOBL _rt0_amd64_lib_argv<>(SB),NOPTR, $8 -TEXT runtime·rt0_go(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stack-based Go ABI (and +// in addition there are no calls to this entry point from Go code). +TEXT runtime·rt0_go(SB),NOSPLIT,$0 // copy arguments forward on an even stack MOVQ DI, AX // argc MOVQ SI, BX // argv @@ -229,10 +231,13 @@ ok: // Prevent dead-code elimination of debugCallV1, which is // intended to be called by debuggers. - MOVQ $runtime·debugCallV1(SB), AX + MOVQ $runtime·debugCallV1(SB), AX RET -DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +// mainPC is a function value for runtime.main, to be passed to newproc. +// The reference to runtime.main is made via ABIInternal, since the +// actual function (not the ABI0 wrapper) is needed by newproc. +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 TEXT runtime·breakpoint(SB),NOSPLIT,$0-0 @@ -468,7 +473,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0 JMP AX // Note: can't just "JMP NAME(SB)" - bad inlining results. -TEXT ·reflectcall(SB), NOSPLIT, $0-32 +TEXT ·reflectcall(SB), NOSPLIT, $0-32 MOVLQZX argsize+24(FP), CX DISPATCH(runtime·call16, 16) DISPATCH(runtime·call32, 32) @@ -1354,8 +1359,11 @@ TEXT _cgo_topofstack(SB),NOSPLIT,$0 RET // The top-most function running on a goroutine -// returns to goexit+PCQuantum. -TEXT runtime·goexit(SB),NOSPLIT,$0-0 +// returns to goexit+PCQuantum. Defined as ABIInternal +// so as to make it identifiable to traceback (this +// function it used as a sentinel; traceback wants to +// see the func PC, not a wrapper PC). +TEXT runtime·goexit(SB),NOSPLIT,$0-0 BYTE $0x90 // NOP CALL runtime·goexit1(SB) // does not return // traceback from goexit1 must hit code range of goexit @@ -1377,7 +1385,8 @@ TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0 // - AX is the value being written at DI // It clobbers FLAGS. It does not clobber any general-purpose registers, // but may clobber others (e.g., SSE registers). -TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$120 +// Defined as ABIInternal since it does not use the stack-based Go ABI. +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$120 // Save the registers clobbered by the fast path. This is slightly // faster than having the caller spill these. MOVQ R14, 104(SP) @@ -1461,51 +1470,58 @@ flush: JMP ret // gcWriteBarrierCX is gcWriteBarrier, but with args in DI and CX. -TEXT runtime·gcWriteBarrierCX(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierCX(SB),NOSPLIT,$0 XCHGQ CX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ CX, AX RET // gcWriteBarrierDX is gcWriteBarrier, but with args in DI and DX. -TEXT runtime·gcWriteBarrierDX(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierDX(SB),NOSPLIT,$0 XCHGQ DX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ DX, AX RET // gcWriteBarrierBX is gcWriteBarrier, but with args in DI and BX. -TEXT runtime·gcWriteBarrierBX(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierBX(SB),NOSPLIT,$0 XCHGQ BX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ BX, AX RET // gcWriteBarrierBP is gcWriteBarrier, but with args in DI and BP. -TEXT runtime·gcWriteBarrierBP(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierBP(SB),NOSPLIT,$0 XCHGQ BP, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ BP, AX RET // gcWriteBarrierSI is gcWriteBarrier, but with args in DI and SI. -TEXT runtime·gcWriteBarrierSI(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierSI(SB),NOSPLIT,$0 XCHGQ SI, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ SI, AX RET // gcWriteBarrierR8 is gcWriteBarrier, but with args in DI and R8. -TEXT runtime·gcWriteBarrierR8(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierR8(SB),NOSPLIT,$0 XCHGQ R8, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ R8, AX RET // gcWriteBarrierR9 is gcWriteBarrier, but with args in DI and R9. -TEXT runtime·gcWriteBarrierR9(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierR9(SB),NOSPLIT,$0 XCHGQ R9, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ R9, AX RET @@ -1544,7 +1560,10 @@ GLOBL debugCallFrameTooLarge<>(SB), RODATA, $20 // Size duplicated below // obey escape analysis requirements. Specifically, it must not pass // a stack pointer to an escaping argument. debugCallV1 cannot check // this invariant. -TEXT runtime·debugCallV1(SB),NOSPLIT,$152-0 +// +// This is ABIInternal because Go code injects its PC directly into new +// goroutine stacks. +TEXT runtime·debugCallV1(SB),NOSPLIT,$152-0 // Save all registers that may contain pointers so they can be // conservatively scanned. // @@ -1705,67 +1724,68 @@ TEXT runtime·debugCallPanicked(SB),NOSPLIT,$16-16 // in the caller's stack frame. These stubs write the args into that stack space and // then tail call to the corresponding runtime handler. // The tail call makes these stubs disappear in backtraces. -TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 +// Defined as ABIInternal since they do not use the stack-based Go ABI. +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicIndex(SB) -TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicIndexU(SB) -TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAlen(SB) -TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAlenU(SB) -TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAcap(SB) -TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAcapU(SB) -TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSliceB(SB) -TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSliceBU(SB) -TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3Alen(SB) -TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3AlenU(SB) -TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3Acap(SB) -TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3AcapU(SB) -TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSlice3B(SB) -TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSlice3BU(SB) -TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSlice3C(SB) -TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSlice3CU(SB) diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s index 67e81adf0b..fcb780f1dc 100644 --- a/src/runtime/asm_wasm.s +++ b/src/runtime/asm_wasm.s @@ -196,7 +196,7 @@ TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16 Get CTXT I64Eqz If - CALLNORESUME runtime·sigpanic(SB) + CALLNORESUME runtime·sigpanic(SB) End // caller sp after CALL @@ -300,7 +300,7 @@ TEXT ·reflectcall(SB), NOSPLIT, $0-32 I64Load fn+8(FP) I64Eqz If - CALLNORESUME runtime·sigpanic(SB) + CALLNORESUME runtime·sigpanic(SB) End MOVW argsize+24(FP), R0 diff --git a/src/runtime/duff_amd64.s b/src/runtime/duff_amd64.s index 44dc75d297..2ff5bf6dbc 100644 --- a/src/runtime/duff_amd64.s +++ b/src/runtime/duff_amd64.s @@ -4,7 +4,7 @@ #include "textflag.h" -TEXT runtime·duffzero(SB), NOSPLIT, $0-0 +TEXT runtime·duffzero(SB), NOSPLIT, $0-0 MOVUPS X0,(DI) MOVUPS X0,16(DI) MOVUPS X0,32(DI) @@ -103,7 +103,7 @@ TEXT runtime·duffzero(SB), NOSPLIT, $0-0 RET -TEXT runtime·duffcopy(SB), NOSPLIT, $0-0 +TEXT runtime·duffcopy(SB), NOSPLIT, $0-0 MOVUPS (SI), X0 ADDQ $16, SI MOVUPS X0, (DI) diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index 286f81489a..1d614dd003 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -126,7 +126,8 @@ func header(arch string) { } fmt.Fprintf(out, "#include \"go_asm.h\"\n") fmt.Fprintf(out, "#include \"textflag.h\"\n\n") - fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n") + fmt.Fprintf(out, "// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally.\n") + fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n") } func p(f string, args ...interface{}) { diff --git a/src/runtime/preempt_386.s b/src/runtime/preempt_386.s index c3a5fa1f36..a803b24dc6 100644 --- a/src/runtime/preempt_386.s +++ b/src/runtime/preempt_386.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHFL ADJSP $156 NOP SP diff --git a/src/runtime/preempt_amd64.s b/src/runtime/preempt_amd64.s index 4765e9f448..92c664d79a 100644 --- a/src/runtime/preempt_amd64.s +++ b/src/runtime/preempt_amd64.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHQ BP MOVQ SP, BP // Save flags before clobbering them diff --git a/src/runtime/preempt_arm.s b/src/runtime/preempt_arm.s index 8f243c0dcd..bbc9fbb1ea 100644 --- a/src/runtime/preempt_arm.s +++ b/src/runtime/preempt_arm.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW.W R14, -188(R13) MOVW R0, 4(R13) MOVW R1, 8(R13) diff --git a/src/runtime/preempt_arm64.s b/src/runtime/preempt_arm64.s index 36ee13282c..2b70a28479 100644 --- a/src/runtime/preempt_arm64.s +++ b/src/runtime/preempt_arm64.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD R30, -496(RSP) SUB $496, RSP #ifdef GOOS_linux diff --git a/src/runtime/preempt_mips64x.s b/src/runtime/preempt_mips64x.s index 1e123e8077..0d0c157c36 100644 --- a/src/runtime/preempt_mips64x.s +++ b/src/runtime/preempt_mips64x.s @@ -5,7 +5,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVV R31, -488(R29) SUBV $488, R29 MOVV R1, 8(R29) diff --git a/src/runtime/preempt_mipsx.s b/src/runtime/preempt_mipsx.s index afac33e0a0..86d3a918d3 100644 --- a/src/runtime/preempt_mipsx.s +++ b/src/runtime/preempt_mipsx.s @@ -5,7 +5,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW R31, -244(R29) SUB $244, R29 MOVW R1, 4(R29) diff --git a/src/runtime/preempt_ppc64x.s b/src/runtime/preempt_ppc64x.s index b2d7e30ec7..90634386db 100644 --- a/src/runtime/preempt_ppc64x.s +++ b/src/runtime/preempt_ppc64x.s @@ -5,7 +5,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD R31, -488(R1) MOVD LR, R31 MOVDU R31, -520(R1) diff --git a/src/runtime/preempt_riscv64.s b/src/runtime/preempt_riscv64.s index eb68dcba2b..d4f9cc277f 100644 --- a/src/runtime/preempt_riscv64.s +++ b/src/runtime/preempt_riscv64.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOV X1, -472(X2) ADD $-472, X2 MOV X3, 8(X2) diff --git a/src/runtime/preempt_s390x.s b/src/runtime/preempt_s390x.s index ca9e47cde1..c6f11571df 100644 --- a/src/runtime/preempt_s390x.s +++ b/src/runtime/preempt_s390x.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 IPM R10 MOVD R14, -248(R15) ADD $-248, R15 diff --git a/src/runtime/preempt_wasm.s b/src/runtime/preempt_wasm.s index 0cf57d3d22..da90e8aa6d 100644 --- a/src/runtime/preempt_wasm.s +++ b/src/runtime/preempt_wasm.s @@ -3,6 +3,7 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 // No async preemption on wasm UNDEF diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s index 758d543203..4a86b3371a 100644 --- a/src/runtime/race_amd64.s +++ b/src/runtime/race_amd64.s @@ -41,7 +41,9 @@ // func runtime·raceread(addr uintptr) // Called from instrumented code. -TEXT runtime·raceread(SB), NOSPLIT, $0-8 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would render runtime.getcallerpc ineffective. +TEXT runtime·raceread(SB), NOSPLIT, $0-8 MOVQ addr+0(FP), RARG1 MOVQ (SP), RARG2 // void __tsan_read(ThreadState *thr, void *addr, void *pc); @@ -65,7 +67,9 @@ TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 // func runtime·racewrite(addr uintptr) // Called from instrumented code. -TEXT runtime·racewrite(SB), NOSPLIT, $0-8 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would render runtime.getcallerpc ineffective. +TEXT runtime·racewrite(SB), NOSPLIT, $0-8 MOVQ addr+0(FP), RARG1 MOVQ (SP), RARG2 // void __tsan_write(ThreadState *thr, void *addr, void *pc); @@ -114,7 +118,9 @@ TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24 // func runtime·racewriterange(addr, size uintptr) // Called from instrumented code. -TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would render runtime.getcallerpc ineffective. +TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG1 MOVQ size+8(FP), RARG2 MOVQ (SP), RARG3 diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index 681cd20274..37cb8dad03 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -380,7 +380,8 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 POPQ BP RET -TEXT runtime·sigtramp(SB),NOSPLIT,$72 +// Defined as ABIInternal since it does not use the stack-based Go ABI. +TEXT runtime·sigtramp(SB),NOSPLIT,$72 // Save callee-saved C registers, since the caller may be a C signal handler. MOVQ BX, bx-8(SP) MOVQ BP, bp-16(SP) // save in case GOEXPERIMENT=noframepointer is set @@ -407,7 +408,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$72 // Used instead of sigtramp in programs that use cgo. // Arguments from kernel are in DI, SI, DX. -TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stack-based Go ABI. +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // If no traceback function, do usual sigtramp. MOVQ runtime·cgoTraceback(SB), AX TESTQ AX, AX @@ -450,12 +452,12 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // The first three arguments, and the fifth, are already in registers. // Set the two remaining arguments now. MOVQ runtime·cgoTraceback(SB), CX - MOVQ $runtime·sigtramp(SB), R9 + MOVQ $runtime·sigtramp(SB), R9 MOVQ _cgo_callers(SB), AX JMP AX sigtramp: - JMP runtime·sigtramp(SB) + JMP runtime·sigtramp(SB) sigtrampnog: // Signal arrived on a non-Go thread. If this is SIGPROF, get a @@ -486,7 +488,8 @@ sigtrampnog: // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/x86_64/sigaction.c // The code that cares about the precise instructions used is: // https://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/i386/linux-unwind.h?revision=219188&view=markup -TEXT runtime·sigreturn(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stack-based Go ABI. +TEXT runtime·sigreturn(SB),NOSPLIT,$0 MOVQ $SYS_rt_sigreturn, AX SYSCALL INT $3 // not reached -- cgit v1.3 From ddc7e1d16f58c73a2587bba130a4a49ffac8b0d1 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Thu, 29 Oct 2020 15:37:35 +0000 Subject: Revert "reflect,runtime: use internal ABI for selected ASM routines" This reverts commit 50af50d136551e2009b2b52e829570536271cdaa. Reason for revert: Causes failures in the runtime package test on Darwin, apparently. Change-Id: I006bc1b3443fa7207e92fb4a93e3fb438d4d3de3 Reviewed-on: https://go-review.googlesource.com/c/go/+/266257 Trust: Than McIntosh Run-TryBot: Than McIntosh TryBot-Result: Go Bot Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/ssa.go | 86 ++++++++++++++++---------------- src/cmd/internal/obj/wasm/wasmobj.go | 6 ++- src/reflect/asm_amd64.s | 12 ++--- src/runtime/asm_amd64.s | 96 ++++++++++++++---------------------- src/runtime/asm_wasm.s | 4 +- src/runtime/duff_amd64.s | 4 +- src/runtime/mkpreempt.go | 3 +- src/runtime/preempt_386.s | 3 +- src/runtime/preempt_amd64.s | 3 +- src/runtime/preempt_arm.s | 3 +- src/runtime/preempt_arm64.s | 3 +- src/runtime/preempt_mips64x.s | 3 +- src/runtime/preempt_mipsx.s | 3 +- src/runtime/preempt_ppc64x.s | 3 +- src/runtime/preempt_riscv64.s | 3 +- src/runtime/preempt_s390x.s | 3 +- src/runtime/preempt_wasm.s | 3 +- src/runtime/race_amd64.s | 12 ++--- src/runtime/sys_linux_amd64.s | 13 ++--- 19 files changed, 112 insertions(+), 154 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 7388e4e3e8..45d628cc5e 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -72,9 +72,9 @@ func initssaconfig() { deferproc = sysfunc("deferproc") deferprocStack = sysfunc("deferprocStack") Deferreturn = sysfunc("deferreturn") - Duffcopy = sysfunc("duffcopy") - Duffzero = sysfunc("duffzero") - gcWriteBarrier = sysfunc("gcWriteBarrier") + Duffcopy = sysvar("duffcopy") // asm func with special ABI + Duffzero = sysvar("duffzero") // asm func with special ABI + gcWriteBarrier = sysvar("gcWriteBarrier") // asm func with special ABI goschedguarded = sysfunc("goschedguarded") growslice = sysfunc("growslice") msanread = sysfunc("msanread") @@ -105,51 +105,51 @@ func initssaconfig() { // asm funcs with special ABI if thearch.LinkArch.Name == "amd64" { GCWriteBarrierReg = map[int16]*obj.LSym{ - x86.REG_AX: sysfunc("gcWriteBarrier"), - x86.REG_CX: sysfunc("gcWriteBarrierCX"), - x86.REG_DX: sysfunc("gcWriteBarrierDX"), - x86.REG_BX: sysfunc("gcWriteBarrierBX"), - x86.REG_BP: sysfunc("gcWriteBarrierBP"), - x86.REG_SI: sysfunc("gcWriteBarrierSI"), - x86.REG_R8: sysfunc("gcWriteBarrierR8"), - x86.REG_R9: sysfunc("gcWriteBarrierR9"), + x86.REG_AX: sysvar("gcWriteBarrier"), + x86.REG_CX: sysvar("gcWriteBarrierCX"), + x86.REG_DX: sysvar("gcWriteBarrierDX"), + x86.REG_BX: sysvar("gcWriteBarrierBX"), + x86.REG_BP: sysvar("gcWriteBarrierBP"), + x86.REG_SI: sysvar("gcWriteBarrierSI"), + x86.REG_R8: sysvar("gcWriteBarrierR8"), + x86.REG_R9: sysvar("gcWriteBarrierR9"), } } if thearch.LinkArch.Family == sys.Wasm { - BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("goPanicIndex") - BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("goPanicIndexU") - BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("goPanicSliceAlen") - BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("goPanicSliceAlenU") - BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("goPanicSliceAcap") - BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("goPanicSliceAcapU") - BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("goPanicSliceB") - BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("goPanicSliceBU") - BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("goPanicSlice3Alen") - BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("goPanicSlice3AlenU") - BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("goPanicSlice3Acap") - BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("goPanicSlice3AcapU") - BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("goPanicSlice3B") - BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("goPanicSlice3BU") - BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("goPanicSlice3C") - BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("goPanicSlice3CU") + BoundsCheckFunc[ssa.BoundsIndex] = sysvar("goPanicIndex") + BoundsCheckFunc[ssa.BoundsIndexU] = sysvar("goPanicIndexU") + BoundsCheckFunc[ssa.BoundsSliceAlen] = sysvar("goPanicSliceAlen") + BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysvar("goPanicSliceAlenU") + BoundsCheckFunc[ssa.BoundsSliceAcap] = sysvar("goPanicSliceAcap") + BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysvar("goPanicSliceAcapU") + BoundsCheckFunc[ssa.BoundsSliceB] = sysvar("goPanicSliceB") + BoundsCheckFunc[ssa.BoundsSliceBU] = sysvar("goPanicSliceBU") + BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysvar("goPanicSlice3Alen") + BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("goPanicSlice3AlenU") + BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysvar("goPanicSlice3Acap") + BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("goPanicSlice3AcapU") + BoundsCheckFunc[ssa.BoundsSlice3B] = sysvar("goPanicSlice3B") + BoundsCheckFunc[ssa.BoundsSlice3BU] = sysvar("goPanicSlice3BU") + BoundsCheckFunc[ssa.BoundsSlice3C] = sysvar("goPanicSlice3C") + BoundsCheckFunc[ssa.BoundsSlice3CU] = sysvar("goPanicSlice3CU") } else { - BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("panicIndex") - BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("panicIndexU") - BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("panicSliceAlen") - BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("panicSliceAlenU") - BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("panicSliceAcap") - BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("panicSliceAcapU") - BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("panicSliceB") - BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("panicSliceBU") - BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("panicSlice3Alen") - BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("panicSlice3AlenU") - BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("panicSlice3Acap") - BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("panicSlice3AcapU") - BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("panicSlice3B") - BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("panicSlice3BU") - BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("panicSlice3C") - BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("panicSlice3CU") + BoundsCheckFunc[ssa.BoundsIndex] = sysvar("panicIndex") + BoundsCheckFunc[ssa.BoundsIndexU] = sysvar("panicIndexU") + BoundsCheckFunc[ssa.BoundsSliceAlen] = sysvar("panicSliceAlen") + BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysvar("panicSliceAlenU") + BoundsCheckFunc[ssa.BoundsSliceAcap] = sysvar("panicSliceAcap") + BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysvar("panicSliceAcapU") + BoundsCheckFunc[ssa.BoundsSliceB] = sysvar("panicSliceB") + BoundsCheckFunc[ssa.BoundsSliceBU] = sysvar("panicSliceBU") + BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysvar("panicSlice3Alen") + BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("panicSlice3AlenU") + BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysvar("panicSlice3Acap") + BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("panicSlice3AcapU") + BoundsCheckFunc[ssa.BoundsSlice3B] = sysvar("panicSlice3B") + BoundsCheckFunc[ssa.BoundsSlice3BU] = sysvar("panicSlice3BU") + BoundsCheckFunc[ssa.BoundsSlice3C] = sysvar("panicSlice3C") + BoundsCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicSlice3CU") } if thearch.LinkArch.PtrSize == 4 { ExtendCheckFunc[ssa.BoundsIndex] = sysvar("panicExtendIndex") diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index 2e9890d86c..f7f66a1255 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -129,6 +129,7 @@ var ( morestackNoCtxt *obj.LSym gcWriteBarrier *obj.LSym sigpanic *obj.LSym + sigpanic0 *obj.LSym deferreturn *obj.LSym jmpdefer *obj.LSym ) @@ -141,8 +142,9 @@ const ( func instinit(ctxt *obj.Link) { morestack = ctxt.Lookup("runtime.morestack") morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt") - gcWriteBarrier = ctxt.LookupABI("runtime.gcWriteBarrier", obj.ABIInternal) + gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier") sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal) + sigpanic0 = ctxt.LookupABI("runtime.sigpanic", 0) // sigpanic called from assembly, which has ABI0 deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal) // jmpdefer is defined in assembly as ABI0, but what we're // looking for is the *call* to jmpdefer from the Go function @@ -491,7 +493,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } // return value of call is on the top of the stack, indicating whether to unwind the WebAssembly stack - if call.As == ACALLNORESUME && call.To.Sym != sigpanic { // sigpanic unwinds the stack, but it never resumes + if call.As == ACALLNORESUME && call.To.Sym != sigpanic && call.To.Sym != sigpanic0 { // sigpanic unwinds the stack, but it never resumes // trying to unwind WebAssembly stack but call has no resume point, terminate with error p = appendp(p, AIf) p = appendp(p, obj.AUNDEF) diff --git a/src/reflect/asm_amd64.s b/src/reflect/asm_amd64.s index 5c8e56558c..fb28ab87f1 100644 --- a/src/reflect/asm_amd64.s +++ b/src/reflect/asm_amd64.s @@ -9,9 +9,7 @@ // See the comment on the declaration of makeFuncStub in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -// makeFuncStub must be ABIInternal because it is placed directly -// in function values. -TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 NO_LOCAL_POINTERS MOVQ DX, 0(SP) LEAQ argframe+0(FP), CX @@ -19,16 +17,14 @@ TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 MOVB $0, 24(SP) LEAQ 24(SP), AX MOVQ AX, 16(SP) - CALL ·callReflect(SB) + CALL ·callReflect(SB) RET // methodValueCall is the code half of the function returned by makeMethodValue. // See the comment on the declaration of methodValueCall in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -// methodValueCall must be ABIInternal because it is placed directly -// in function values. -TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 NO_LOCAL_POINTERS MOVQ DX, 0(SP) LEAQ argframe+0(FP), CX @@ -36,5 +32,5 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 MOVB $0, 24(SP) LEAQ 24(SP), AX MOVQ AX, 16(SP) - CALL ·callMethod(SB) + CALL ·callMethod(SB) RET diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 196252e1dd..19a3bb2d7d 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -84,9 +84,7 @@ GLOBL _rt0_amd64_lib_argc<>(SB),NOPTR, $8 DATA _rt0_amd64_lib_argv<>(SB)/8, $0 GLOBL _rt0_amd64_lib_argv<>(SB),NOPTR, $8 -// Defined as ABIInternal since it does not use the stack-based Go ABI (and -// in addition there are no calls to this entry point from Go code). -TEXT runtime·rt0_go(SB),NOSPLIT,$0 +TEXT runtime·rt0_go(SB),NOSPLIT,$0 // copy arguments forward on an even stack MOVQ DI, AX // argc MOVQ SI, BX // argv @@ -231,13 +229,10 @@ ok: // Prevent dead-code elimination of debugCallV1, which is // intended to be called by debuggers. - MOVQ $runtime·debugCallV1(SB), AX + MOVQ $runtime·debugCallV1(SB), AX RET -// mainPC is a function value for runtime.main, to be passed to newproc. -// The reference to runtime.main is made via ABIInternal, since the -// actual function (not the ABI0 wrapper) is needed by newproc. -DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 TEXT runtime·breakpoint(SB),NOSPLIT,$0-0 @@ -473,7 +468,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0 JMP AX // Note: can't just "JMP NAME(SB)" - bad inlining results. -TEXT ·reflectcall(SB), NOSPLIT, $0-32 +TEXT ·reflectcall(SB), NOSPLIT, $0-32 MOVLQZX argsize+24(FP), CX DISPATCH(runtime·call16, 16) DISPATCH(runtime·call32, 32) @@ -1359,11 +1354,8 @@ TEXT _cgo_topofstack(SB),NOSPLIT,$0 RET // The top-most function running on a goroutine -// returns to goexit+PCQuantum. Defined as ABIInternal -// so as to make it identifiable to traceback (this -// function it used as a sentinel; traceback wants to -// see the func PC, not a wrapper PC). -TEXT runtime·goexit(SB),NOSPLIT,$0-0 +// returns to goexit+PCQuantum. +TEXT runtime·goexit(SB),NOSPLIT,$0-0 BYTE $0x90 // NOP CALL runtime·goexit1(SB) // does not return // traceback from goexit1 must hit code range of goexit @@ -1385,8 +1377,7 @@ TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0 // - AX is the value being written at DI // It clobbers FLAGS. It does not clobber any general-purpose registers, // but may clobber others (e.g., SSE registers). -// Defined as ABIInternal since it does not use the stack-based Go ABI. -TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$120 +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$120 // Save the registers clobbered by the fast path. This is slightly // faster than having the caller spill these. MOVQ R14, 104(SP) @@ -1470,58 +1461,51 @@ flush: JMP ret // gcWriteBarrierCX is gcWriteBarrier, but with args in DI and CX. -// Defined as ABIInternal since it does not use the stable Go ABI. -TEXT runtime·gcWriteBarrierCX(SB),NOSPLIT,$0 +TEXT runtime·gcWriteBarrierCX(SB),NOSPLIT,$0 XCHGQ CX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ CX, AX RET // gcWriteBarrierDX is gcWriteBarrier, but with args in DI and DX. -// Defined as ABIInternal since it does not use the stable Go ABI. -TEXT runtime·gcWriteBarrierDX(SB),NOSPLIT,$0 +TEXT runtime·gcWriteBarrierDX(SB),NOSPLIT,$0 XCHGQ DX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ DX, AX RET // gcWriteBarrierBX is gcWriteBarrier, but with args in DI and BX. -// Defined as ABIInternal since it does not use the stable Go ABI. -TEXT runtime·gcWriteBarrierBX(SB),NOSPLIT,$0 +TEXT runtime·gcWriteBarrierBX(SB),NOSPLIT,$0 XCHGQ BX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ BX, AX RET // gcWriteBarrierBP is gcWriteBarrier, but with args in DI and BP. -// Defined as ABIInternal since it does not use the stable Go ABI. -TEXT runtime·gcWriteBarrierBP(SB),NOSPLIT,$0 +TEXT runtime·gcWriteBarrierBP(SB),NOSPLIT,$0 XCHGQ BP, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ BP, AX RET // gcWriteBarrierSI is gcWriteBarrier, but with args in DI and SI. -// Defined as ABIInternal since it does not use the stable Go ABI. -TEXT runtime·gcWriteBarrierSI(SB),NOSPLIT,$0 +TEXT runtime·gcWriteBarrierSI(SB),NOSPLIT,$0 XCHGQ SI, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ SI, AX RET // gcWriteBarrierR8 is gcWriteBarrier, but with args in DI and R8. -// Defined as ABIInternal since it does not use the stable Go ABI. -TEXT runtime·gcWriteBarrierR8(SB),NOSPLIT,$0 +TEXT runtime·gcWriteBarrierR8(SB),NOSPLIT,$0 XCHGQ R8, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ R8, AX RET // gcWriteBarrierR9 is gcWriteBarrier, but with args in DI and R9. -// Defined as ABIInternal since it does not use the stable Go ABI. -TEXT runtime·gcWriteBarrierR9(SB),NOSPLIT,$0 +TEXT runtime·gcWriteBarrierR9(SB),NOSPLIT,$0 XCHGQ R9, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ R9, AX RET @@ -1560,10 +1544,7 @@ GLOBL debugCallFrameTooLarge<>(SB), RODATA, $20 // Size duplicated below // obey escape analysis requirements. Specifically, it must not pass // a stack pointer to an escaping argument. debugCallV1 cannot check // this invariant. -// -// This is ABIInternal because Go code injects its PC directly into new -// goroutine stacks. -TEXT runtime·debugCallV1(SB),NOSPLIT,$152-0 +TEXT runtime·debugCallV1(SB),NOSPLIT,$152-0 // Save all registers that may contain pointers so they can be // conservatively scanned. // @@ -1724,68 +1705,67 @@ TEXT runtime·debugCallPanicked(SB),NOSPLIT,$16-16 // in the caller's stack frame. These stubs write the args into that stack space and // then tail call to the corresponding runtime handler. // The tail call makes these stubs disappear in backtraces. -// Defined as ABIInternal since they do not use the stack-based Go ABI. -TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicIndex(SB) -TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicIndexU(SB) -TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAlen(SB) -TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAlenU(SB) -TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAcap(SB) -TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAcapU(SB) -TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSliceB(SB) -TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSliceBU(SB) -TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3Alen(SB) -TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3AlenU(SB) -TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3Acap(SB) -TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3AcapU(SB) -TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSlice3B(SB) -TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSlice3BU(SB) -TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSlice3C(SB) -TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSlice3CU(SB) diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s index fcb780f1dc..67e81adf0b 100644 --- a/src/runtime/asm_wasm.s +++ b/src/runtime/asm_wasm.s @@ -196,7 +196,7 @@ TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16 Get CTXT I64Eqz If - CALLNORESUME runtime·sigpanic(SB) + CALLNORESUME runtime·sigpanic(SB) End // caller sp after CALL @@ -300,7 +300,7 @@ TEXT ·reflectcall(SB), NOSPLIT, $0-32 I64Load fn+8(FP) I64Eqz If - CALLNORESUME runtime·sigpanic(SB) + CALLNORESUME runtime·sigpanic(SB) End MOVW argsize+24(FP), R0 diff --git a/src/runtime/duff_amd64.s b/src/runtime/duff_amd64.s index 2ff5bf6dbc..44dc75d297 100644 --- a/src/runtime/duff_amd64.s +++ b/src/runtime/duff_amd64.s @@ -4,7 +4,7 @@ #include "textflag.h" -TEXT runtime·duffzero(SB), NOSPLIT, $0-0 +TEXT runtime·duffzero(SB), NOSPLIT, $0-0 MOVUPS X0,(DI) MOVUPS X0,16(DI) MOVUPS X0,32(DI) @@ -103,7 +103,7 @@ TEXT runtime·duffzero(SB), NOSPLIT, $0-0 RET -TEXT runtime·duffcopy(SB), NOSPLIT, $0-0 +TEXT runtime·duffcopy(SB), NOSPLIT, $0-0 MOVUPS (SI), X0 ADDQ $16, SI MOVUPS X0, (DI) diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index 1d614dd003..286f81489a 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -126,8 +126,7 @@ func header(arch string) { } fmt.Fprintf(out, "#include \"go_asm.h\"\n") fmt.Fprintf(out, "#include \"textflag.h\"\n\n") - fmt.Fprintf(out, "// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally.\n") - fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n") + fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n") } func p(f string, args ...interface{}) { diff --git a/src/runtime/preempt_386.s b/src/runtime/preempt_386.s index a803b24dc6..c3a5fa1f36 100644 --- a/src/runtime/preempt_386.s +++ b/src/runtime/preempt_386.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHFL ADJSP $156 NOP SP diff --git a/src/runtime/preempt_amd64.s b/src/runtime/preempt_amd64.s index 92c664d79a..4765e9f448 100644 --- a/src/runtime/preempt_amd64.s +++ b/src/runtime/preempt_amd64.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHQ BP MOVQ SP, BP // Save flags before clobbering them diff --git a/src/runtime/preempt_arm.s b/src/runtime/preempt_arm.s index bbc9fbb1ea..8f243c0dcd 100644 --- a/src/runtime/preempt_arm.s +++ b/src/runtime/preempt_arm.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW.W R14, -188(R13) MOVW R0, 4(R13) MOVW R1, 8(R13) diff --git a/src/runtime/preempt_arm64.s b/src/runtime/preempt_arm64.s index 2b70a28479..36ee13282c 100644 --- a/src/runtime/preempt_arm64.s +++ b/src/runtime/preempt_arm64.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD R30, -496(RSP) SUB $496, RSP #ifdef GOOS_linux diff --git a/src/runtime/preempt_mips64x.s b/src/runtime/preempt_mips64x.s index 0d0c157c36..1e123e8077 100644 --- a/src/runtime/preempt_mips64x.s +++ b/src/runtime/preempt_mips64x.s @@ -5,8 +5,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVV R31, -488(R29) SUBV $488, R29 MOVV R1, 8(R29) diff --git a/src/runtime/preempt_mipsx.s b/src/runtime/preempt_mipsx.s index 86d3a918d3..afac33e0a0 100644 --- a/src/runtime/preempt_mipsx.s +++ b/src/runtime/preempt_mipsx.s @@ -5,8 +5,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW R31, -244(R29) SUB $244, R29 MOVW R1, 4(R29) diff --git a/src/runtime/preempt_ppc64x.s b/src/runtime/preempt_ppc64x.s index 90634386db..b2d7e30ec7 100644 --- a/src/runtime/preempt_ppc64x.s +++ b/src/runtime/preempt_ppc64x.s @@ -5,8 +5,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD R31, -488(R1) MOVD LR, R31 MOVDU R31, -520(R1) diff --git a/src/runtime/preempt_riscv64.s b/src/runtime/preempt_riscv64.s index d4f9cc277f..eb68dcba2b 100644 --- a/src/runtime/preempt_riscv64.s +++ b/src/runtime/preempt_riscv64.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOV X1, -472(X2) ADD $-472, X2 MOV X3, 8(X2) diff --git a/src/runtime/preempt_s390x.s b/src/runtime/preempt_s390x.s index c6f11571df..ca9e47cde1 100644 --- a/src/runtime/preempt_s390x.s +++ b/src/runtime/preempt_s390x.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 IPM R10 MOVD R14, -248(R15) ADD $-248, R15 diff --git a/src/runtime/preempt_wasm.s b/src/runtime/preempt_wasm.s index da90e8aa6d..0cf57d3d22 100644 --- a/src/runtime/preempt_wasm.s +++ b/src/runtime/preempt_wasm.s @@ -3,7 +3,6 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 // No async preemption on wasm UNDEF diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s index 4a86b3371a..758d543203 100644 --- a/src/runtime/race_amd64.s +++ b/src/runtime/race_amd64.s @@ -41,9 +41,7 @@ // func runtime·raceread(addr uintptr) // Called from instrumented code. -// Defined as ABIInternal so as to avoid introducing a wrapper, -// which would render runtime.getcallerpc ineffective. -TEXT runtime·raceread(SB), NOSPLIT, $0-8 +TEXT runtime·raceread(SB), NOSPLIT, $0-8 MOVQ addr+0(FP), RARG1 MOVQ (SP), RARG2 // void __tsan_read(ThreadState *thr, void *addr, void *pc); @@ -67,9 +65,7 @@ TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 // func runtime·racewrite(addr uintptr) // Called from instrumented code. -// Defined as ABIInternal so as to avoid introducing a wrapper, -// which would render runtime.getcallerpc ineffective. -TEXT runtime·racewrite(SB), NOSPLIT, $0-8 +TEXT runtime·racewrite(SB), NOSPLIT, $0-8 MOVQ addr+0(FP), RARG1 MOVQ (SP), RARG2 // void __tsan_write(ThreadState *thr, void *addr, void *pc); @@ -118,9 +114,7 @@ TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24 // func runtime·racewriterange(addr, size uintptr) // Called from instrumented code. -// Defined as ABIInternal so as to avoid introducing a wrapper, -// which would render runtime.getcallerpc ineffective. -TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 +TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG1 MOVQ size+8(FP), RARG2 MOVQ (SP), RARG3 diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index 37cb8dad03..681cd20274 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -380,8 +380,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 POPQ BP RET -// Defined as ABIInternal since it does not use the stack-based Go ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$72 +TEXT runtime·sigtramp(SB),NOSPLIT,$72 // Save callee-saved C registers, since the caller may be a C signal handler. MOVQ BX, bx-8(SP) MOVQ BP, bp-16(SP) // save in case GOEXPERIMENT=noframepointer is set @@ -408,8 +407,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$72 // Used instead of sigtramp in programs that use cgo. // Arguments from kernel are in DI, SI, DX. -// Defined as ABIInternal since it does not use the stack-based Go ABI. -TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // If no traceback function, do usual sigtramp. MOVQ runtime·cgoTraceback(SB), AX TESTQ AX, AX @@ -452,12 +450,12 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // The first three arguments, and the fifth, are already in registers. // Set the two remaining arguments now. MOVQ runtime·cgoTraceback(SB), CX - MOVQ $runtime·sigtramp(SB), R9 + MOVQ $runtime·sigtramp(SB), R9 MOVQ _cgo_callers(SB), AX JMP AX sigtramp: - JMP runtime·sigtramp(SB) + JMP runtime·sigtramp(SB) sigtrampnog: // Signal arrived on a non-Go thread. If this is SIGPROF, get a @@ -488,8 +486,7 @@ sigtrampnog: // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/x86_64/sigaction.c // The code that cares about the precise instructions used is: // https://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/i386/linux-unwind.h?revision=219188&view=markup -// Defined as ABIInternal since it does not use the stack-based Go ABI. -TEXT runtime·sigreturn(SB),NOSPLIT,$0 +TEXT runtime·sigreturn(SB),NOSPLIT,$0 MOVQ $SYS_rt_sigreturn, AX SYSCALL INT $3 // not reached -- cgit v1.3 From 4fb429138881e3fe171e4c2e958ed0da26ddfd9c Mon Sep 17 00:00:00 2001 From: Branden J Brown Date: Sun, 25 Oct 2020 14:26:19 -0400 Subject: cmd/compile: inline functions evaluated in go and defer statements The inlining pass previously bailed upon encountering a go or defer statement, so it would not inline functions e.g. used to provide arguments to the deferred function. This change preserves the behavior of not inlining the deferred function itself, but it allows the inlining walk to proceed into its arguments. Fixes #42194 Change-Id: I4e82029d8dcbe69019cc83ae63a4b29af45ec777 Reviewed-on: https://go-review.googlesource.com/c/go/+/264997 Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le Reviewed-by: Matthew Dempsky Trust: Cuong Manh Le --- src/cmd/compile/internal/gc/inl.go | 2 -- test/inline.go | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index a2fb00e132..137675aa20 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -574,13 +574,11 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node { } switch n.Op { - // inhibit inlining of their argument case ODEFER, OGO: switch n.Left.Op { case OCALLFUNC, OCALLMETH: n.Left.SetNoInline(true) } - return n // TODO do them here (or earlier), // so escape analysis can avoid more heapmoves. diff --git a/test/inline.go b/test/inline.go index 9b75bc5065..470414f883 100644 --- a/test/inline.go +++ b/test/inline.go @@ -246,3 +246,20 @@ func ii() { // ERROR "can inline ii" f := getMeth(t1) // ERROR "inlining call to getMeth" "t1.meth does not escape" _ = f(3) } + +// Issue #42194 - make sure that functions evaluated in +// go and defer statements can be inlined. +func gd1(int) { + defer gd1(gd2()) // ERROR "inlining call to gd2" + defer gd3()() // ERROR "inlining call to gd3" + go gd1(gd2()) // ERROR "inlining call to gd2" + go gd3()() // ERROR "inlining call to gd3" +} + +func gd2() int { // ERROR "can inline gd2" + return 1 +} + +func gd3() func() { // ERROR "can inline gd3" + return ii +} -- cgit v1.3 From 2e6f50020cacd68f24e413d24d4c3e92e0c9e69f Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Fri, 30 Oct 2020 04:32:18 +1100 Subject: Revert "cmd/compile,cmd/internal/sys: enable additional build modes on linux/riscv64" This reverts CL 263457. It turns out that this still missed changes to cmd/link/internal/ld/config.go and some of these build modes also fail once cgo is enabled. Disable again for now. Change-Id: Iaf40d44e1551afd5b040d357f04af134f55a64a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/266317 Reviewed-by: Cherry Zhang Trust: Joel Sing --- src/cmd/compile/internal/gc/main.go | 2 +- src/cmd/dist/test.go | 8 ++++---- src/cmd/go/go_test.go | 2 +- src/cmd/internal/sys/supported.go | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 0b65e8a0b4..4b401f2aa4 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -130,7 +130,7 @@ func hidePanic() { // supportsDynlink reports whether or not the code generator for the given // architecture supports the -shared and -dynlink flags. func supportsDynlink(arch *sys.Arch) bool { - return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) + return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X) } // timing data for compiler phases diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index aeffc2659f..7c454dd38d 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -984,7 +984,7 @@ func (t *tester) supportedBuildmode(mode string) bool { switch pair { case "aix-ppc64", "darwin-amd64", "darwin-arm64", "ios-arm64", - "linux-amd64", "linux-386", "linux-ppc64le", "linux-riscv64", "linux-s390x", + "linux-amd64", "linux-386", "linux-ppc64le", "linux-s390x", "freebsd-amd64", "windows-amd64", "windows-386": return true @@ -992,7 +992,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "c-shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", "darwin-amd64", "darwin-arm64", "freebsd-amd64", "android-arm", "android-arm64", "android-386", @@ -1002,7 +1002,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x": + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x": return true } return false @@ -1021,7 +1021,7 @@ func (t *tester) supportedBuildmode(mode string) bool { case "pie": switch pair { case "aix/ppc64", - "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", + "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", "android-amd64", "android-arm", "android-arm64", "android-386": return true case "darwin-amd64", "darwin-arm64": diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index c7ca73b5b5..1fb1325519 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -2021,7 +2021,7 @@ func TestBuildmodePIE(t *testing.T) { platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "windows/386", "windows/amd64", "windows/arm": diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index afc81381fd..3c750774ed 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -67,7 +67,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "c-shared": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", + case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", @@ -84,7 +84,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "pie": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", @@ -97,7 +97,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "shared": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x": + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x": return true } return false -- cgit v1.3 From f2c0c2b90200b470c39a2db821b7c707604fe083 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Sat, 24 Oct 2020 02:08:06 -0700 Subject: cmd/compile: improve inlining and static analysis When inlining a function call "f()", if "f" contains exactly 1 "return" statement and doesn't name its result parameters, it's inlined to declare+initialize the result value using the AST representation that's compatible with staticValue. Also, extend staticValue to skip over OCONVNOP nodes (often introduced by inlining), and fix various bits of code related to handling method expressions. Updates #33160. Change-Id: If8652e319f0a5700cf9d40a7a62e369a2a359229 Reviewed-on: https://go-review.googlesource.com/c/go/+/266199 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: David Chase --- src/cmd/compile/internal/gc/inl.go | 104 +++++++++++++++++++++++++++++-------- src/cmd/compile/internal/gc/scc.go | 15 +++++- 2 files changed, 94 insertions(+), 25 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 137675aa20..098c0c99d5 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -257,21 +257,39 @@ func inlFlood(n *Node) { typecheckinl(n) + // Recursively identify all referenced functions for + // reexport. We want to include even non-called functions, + // because after inlining they might be callable. inspectList(asNodes(n.Func.Inl.Body), func(n *Node) bool { switch n.Op { case ONAME: - // Mark any referenced global variables or - // functions for reexport. Skip methods, - // because they're reexported alongside their - // receiver type. - if n.Class() == PEXTERN || n.Class() == PFUNC && !n.isMethodExpression() { + switch n.Class() { + case PFUNC: + if n.isMethodExpression() { + inlFlood(asNode(n.Type.Nname())) + } else { + inlFlood(n) + exportsym(n) + } + case PEXTERN: exportsym(n) } - case OCALLFUNC, OCALLMETH: - // Recursively flood any functions called by - // this one. - inlFlood(asNode(n.Left.Type.Nname())) + case ODOTMETH: + fn := asNode(n.Type.Nname()) + inlFlood(fn) + + case OCALLPART: + // Okay, because we don't yet inline indirect + // calls to method values. + case OCLOSURE: + // If the closure is inlinable, we'll need to + // flood it too. But today we don't support + // inlining functions that contain closures. + // + // When we do, we'll probably want: + // inlFlood(n.Func.Closure.Func.Nname) + Fatalf("unexpected closure in inlinable function") } return true }) @@ -706,7 +724,14 @@ func inlCallee(fn *Node) *Node { switch { case fn.Op == ONAME && fn.Class() == PFUNC: if fn.isMethodExpression() { - return asNode(fn.Sym.Def) + n := asNode(fn.Type.Nname()) + // Check that receiver type matches fn.Left. + // TODO(mdempsky): Handle implicit dereference + // of pointer receiver argument? + if n == nil || !types.Identical(n.Type.Recv().Type, fn.Left.Type) { + return nil + } + return n } return fn case fn.Op == OCLOSURE: @@ -719,6 +744,11 @@ func inlCallee(fn *Node) *Node { func staticValue(n *Node) *Node { for { + if n.Op == OCONVNOP { + n = n.Left + continue + } + n1 := staticValue1(n) if n1 == nil { return n @@ -1009,15 +1039,28 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { } } + nreturns := 0 + inspectList(asNodes(fn.Func.Inl.Body), func(n *Node) bool { + if n != nil && n.Op == ORETURN { + nreturns++ + } + return true + }) + + // We can delay declaring+initializing result parameters if: + // (1) there's only one "return" statement in the inlined + // function, and (2) the result parameters aren't named. + delayretvars := nreturns == 1 + // temporaries for return values. var retvars []*Node for i, t := range fn.Type.Results().Fields().Slice() { var m *Node - mpos := t.Pos if n := asNode(t.Nname); n != nil && !n.isBlank() { m = inlvar(n) m = typecheck(m, ctxExpr) inlvars[n] = m + delayretvars = false // found a named result parameter } else { // anonymous return values, synthesize names for use in assignment that replaces return m = retvar(t, i) @@ -1029,12 +1072,11 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { // were not part of the original callee. if !strings.HasPrefix(m.Sym.Name, "~R") { m.Name.SetInlFormal(true) - m.Pos = mpos + m.Pos = t.Pos inlfvars = append(inlfvars, m) } } - ninit.Append(nod(ODCL, m, nil)) retvars = append(retvars, m) } @@ -1095,11 +1137,14 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { ninit.Append(vas) } - // Zero the return parameters. - for _, n := range retvars { - ras := nod(OAS, n, nil) - ras = typecheck(ras, ctxStmt) - ninit.Append(ras) + if !delayretvars { + // Zero the return parameters. + for _, n := range retvars { + ninit.Append(nod(ODCL, n, nil)) + ras := nod(OAS, n, nil) + ras = typecheck(ras, ctxStmt) + ninit.Append(ras) + } } retlabel := autolabel(".i") @@ -1130,11 +1175,12 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { } subst := inlsubst{ - retlabel: retlabel, - retvars: retvars, - inlvars: inlvars, - bases: make(map[*src.PosBase]*src.PosBase), - newInlIndex: newIndex, + retlabel: retlabel, + retvars: retvars, + delayretvars: delayretvars, + inlvars: inlvars, + bases: make(map[*src.PosBase]*src.PosBase), + newInlIndex: newIndex, } body := subst.list(asNodes(fn.Func.Inl.Body)) @@ -1230,6 +1276,10 @@ type inlsubst struct { // Temporary result variables. retvars []*Node + // Whether result variables should be initialized at the + // "return" statement. + delayretvars bool + inlvars map[*Node]*Node // bases maps from original PosBase to PosBase with an extra @@ -1298,6 +1348,14 @@ func (subst *inlsubst) node(n *Node) *Node { as.List.Append(n) } as.Rlist.Set(subst.list(n.List)) + + if subst.delayretvars { + for _, n := range as.List.Slice() { + as.Ninit.Append(nod(ODCL, n, nil)) + n.Name.Defn = as + } + } + as = typecheck(as, ctxStmt) m.Ninit.Append(as) } diff --git a/src/cmd/compile/internal/gc/scc.go b/src/cmd/compile/internal/gc/scc.go index 60e0a9b8b5..5c7935aa87 100644 --- a/src/cmd/compile/internal/gc/scc.go +++ b/src/cmd/compile/internal/gc/scc.go @@ -75,8 +75,19 @@ func (v *bottomUpVisitor) visit(n *Node) uint32 { inspectList(n.Nbody, func(n *Node) bool { switch n.Op { - case OCALLFUNC, OCALLMETH: - fn := asNode(n.Left.Type.Nname()) + case ONAME: + if n.Class() == PFUNC { + if n.isMethodExpression() { + n = asNode(n.Type.Nname()) + } + if n != nil && n.Name.Defn != nil { + if m := v.visit(n.Name.Defn); m < min { + min = m + } + } + } + case ODOTMETH: + fn := asNode(n.Type.Nname()) if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil { if m := v.visit(fn.Name.Defn); m < min { min = m -- cgit v1.3 From 5cc43c51c9929ce089ce2fc17a0f5631d21cd27d Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 28 Oct 2020 18:49:10 -0700 Subject: cmd/compile: early devirtualization of interface method calls After inlining, add a pass that looks for interface calls where we can statically determine the interface value's concrete type. If such a case is found, insert an explicit type assertion to the concrete type so that escape analysis can see it. Fixes #33160. Change-Id: I36932c691693f0069e34384086d63133e249b06b Reviewed-on: https://go-review.googlesource.com/c/go/+/264837 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: David Chase --- src/cmd/compile/internal/gc/inl.go | 53 +++++++++++++++++++++++++++++++++++++ src/cmd/compile/internal/gc/main.go | 7 +++++ test/escape_iface.go | 19 ++++++------- 3 files changed, 68 insertions(+), 11 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 098c0c99d5..c35691bfd2 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -1418,3 +1418,56 @@ func pruneUnusedAutos(ll []*Node, vis *hairyVisitor) []*Node { } return s } + +// devirtualize replaces interface method calls within fn with direct +// concrete-type method calls where applicable. +func devirtualize(fn *Node) { + Curfn = fn + inspectList(fn.Nbody, func(n *Node) bool { + if n.Op == OCALLINTER { + devirtualizeCall(n) + } + return true + }) +} + +func devirtualizeCall(call *Node) { + recv := staticValue(call.Left.Left) + if recv.Op != OCONVIFACE { + return + } + + typ := recv.Left.Type + if typ.IsInterface() { + return + } + + if Debug.m != 0 { + Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ) + } + + x := nodl(call.Left.Pos, ODOTTYPE, call.Left.Left, nil) + x.Type = typ + x = nodlSym(call.Left.Pos, OXDOT, x, call.Left.Sym) + x = typecheck(x, ctxExpr|ctxCallee) + if x.Op != ODOTMETH { + Fatalf("devirtualization failed: %v", x) + } + call.Op = OCALLMETH + call.Left = x + + // Duplicated logic from typecheck for function call return + // value types. + // + // Receiver parameter size may have changed; need to update + // call.Type to get correct stack offsets for result + // parameters. + checkwidth(x.Type) + switch ft := x.Type; ft.NumResults() { + case 0: + case 1: + call.Type = ft.Results().Field(0).Type + default: + call.Type = ft.Results() + } +} diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 4b401f2aa4..8b94c7f71b 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -701,6 +701,13 @@ func Main(archInit func(*Arch)) { }) } + for _, n := range xtop { + if n.Op == ODCLFUNC { + devirtualize(n) + } + } + Curfn = nil + // Phase 6: Escape analysis. // Required for moving heap allocations onto stack, // which in turn is required by the closure implementation, diff --git a/test/escape_iface.go b/test/escape_iface.go index 7b0914cadb..5a232fdbd4 100644 --- a/test/escape_iface.go +++ b/test/escape_iface.go @@ -58,11 +58,10 @@ func efaceEscape0() { sink = v1 } { - i := 0 // ERROR "moved to heap: i" + i := 0 v := M0{&i} - // BAD: v does not escape to heap here var x M = v - x.M() + x.M() // ERROR "devirtualizing x.M" } { i := 0 // ERROR "moved to heap: i" @@ -115,11 +114,10 @@ func efaceEscape1() { sink = v1 // ERROR "v1 escapes to heap" } { - i := 0 // ERROR "moved to heap: i" + i := 0 v := M1{&i, 0} - // BAD: v does not escape to heap here - var x M = v // ERROR "v escapes to heap" - x.M() + var x M = v // ERROR "v does not escape" + x.M() // ERROR "devirtualizing x.M" } { i := 0 // ERROR "moved to heap: i" @@ -189,11 +187,10 @@ func efaceEscape2() { _ = ok } { - i := 0 // ERROR "moved to heap: i" - v := &M2{&i} // ERROR "&M2{...} escapes to heap" - // BAD: v does not escape to heap here + i := 0 + v := &M2{&i} // ERROR "&M2{...} does not escape" var x M = v - x.M() + x.M() // ERROR "devirtualizing x.M" } { i := 0 // ERROR "moved to heap: i" -- cgit v1.3 From aa4f48b751dbbfcb82e69d7622e7a9e9b8e48ee0 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 29 Oct 2020 12:47:15 -0700 Subject: cmd/compile: gracefully fail when devirtualization fails We should still be able to devirtualize here, but I need to understand the AST better. While I'm doing that, at least switch to a graceful failure case (i.e., skip the optimization and print a warning message) to fix the x/text builders. Updates #42279. Change-Id: Ie2b0b701fccf590d0cabfead703fc2fa999072cf Reviewed-on: https://go-review.googlesource.com/c/go/+/266359 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: David Chase TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/inl.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index c35691bfd2..6c69867789 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -1442,16 +1442,20 @@ func devirtualizeCall(call *Node) { return } - if Debug.m != 0 { - Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ) - } - x := nodl(call.Left.Pos, ODOTTYPE, call.Left.Left, nil) x.Type = typ x = nodlSym(call.Left.Pos, OXDOT, x, call.Left.Sym) x = typecheck(x, ctxExpr|ctxCallee) if x.Op != ODOTMETH { - Fatalf("devirtualization failed: %v", x) + // TODO(mdempsky): Figure out how to avoid this and + // turn back into a Fatalf. + if Debug.m != 0 { + Warnl(call.Pos, "failed to devirtualize %v", x) + } + return + } + if Debug.m != 0 { + Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ) } call.Op = OCALLMETH call.Left = x -- cgit v1.3 From e62adb1c0b8e7ca49eefc70389fbb9f739d6e32c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 29 Oct 2020 13:30:54 -0700 Subject: cmd/compile: fix devirtualization of promoted interface methods A method selector expression can pick out a method or promoted method (represented by ODOTMETH), but it can also pick out an interface method from an embedded interface-typed field (represented by ODOTINTER). In the case that we're picking out an interface method, we're not able to fully devirtualize the method call. However, we're still able to improve escape analysis somewhat. E.g., the included test case demonstrates that we can optimize "i.M()" to "i.(T).I.M()", which means the T literal can be stack allocated instead of heap allocated. Fixes #42279. Change-Id: Ifa21d19011e2f008d84f9624b7055b4676b6d188 Reviewed-on: https://go-review.googlesource.com/c/go/+/266300 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: David Chase --- src/cmd/compile/internal/gc/inl.go | 26 +++++++++++++++++--------- test/escape_iface.go | 8 ++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 6c69867789..5b58908299 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -1446,19 +1446,27 @@ func devirtualizeCall(call *Node) { x.Type = typ x = nodlSym(call.Left.Pos, OXDOT, x, call.Left.Sym) x = typecheck(x, ctxExpr|ctxCallee) - if x.Op != ODOTMETH { - // TODO(mdempsky): Figure out how to avoid this and - // turn back into a Fatalf. + switch x.Op { + case ODOTMETH: if Debug.m != 0 { - Warnl(call.Pos, "failed to devirtualize %v", x) + Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ) + } + call.Op = OCALLMETH + call.Left = x + case ODOTINTER: + // Promoted method from embedded interface-typed field (#42279). + if Debug.m != 0 { + Warnl(call.Pos, "partially devirtualizing %v to %v", call.Left, typ) + } + call.Op = OCALLINTER + call.Left = x + default: + // TODO(mdempsky): Turn back into Fatalf after more testing. + if Debug.m != 0 { + Warnl(call.Pos, "failed to devirtualize %v (%v)", x, x.Op) } return } - if Debug.m != 0 { - Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ) - } - call.Op = OCALLMETH - call.Left = x // Duplicated logic from typecheck for function call return // value types. diff --git a/test/escape_iface.go b/test/escape_iface.go index 5a232fdbd4..dba08e3cb3 100644 --- a/test/escape_iface.go +++ b/test/escape_iface.go @@ -255,3 +255,11 @@ func dotTypeEscape2() { // #13805, #15796 sink, *(&ok) = y.(*int) } } + +func issue42279() { + type I interface{ M() } + type T struct{ I } + + var i I = T{} // ERROR "T\{\} does not escape" + i.M() // ERROR "partially devirtualizing i.M to T" +} -- cgit v1.3 From a313eec3869c609c0da2402a4cac2d32113d599c Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Wed, 14 Oct 2020 08:36:11 -0400 Subject: reflect,runtime: use internal ABI for selected ASM routines, attempt 2 [This is a roll-forward of CL 262319, with a fix for some Darwin test failures]. Change the definitions of selected runtime assembly routines from ABI0 (the default) to ABIInternal. The ABIInternal def is intended to indicate that these functions don't follow the existing Go runtime ABI. In addition, convert the assembly reference to runtime.main (from runtime.mainPC) to ABIInternal. Finally, for functions such as "runtime.duffzero" that are called directly from generated code, make sure that the compiler looks up the correct ABI version. This is intended to support the register abi work, however these changes should not have any issues even when GOEXPERIMENT=regabi is not in effect. Updates #27539, #40724. Change-Id: Idf507f1c06176073563845239e1a54dad51a9ea9 Reviewed-on: https://go-review.googlesource.com/c/go/+/266638 Trust: Than McIntosh Run-TryBot: Than McIntosh Reviewed-by: Cherry Zhang TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/ssa.go | 86 ++++++++++++++++---------------- src/cmd/internal/obj/wasm/wasmobj.go | 6 +-- src/cmd/internal/obj/x86/obj6.go | 4 +- src/reflect/asm_amd64.s | 12 +++-- src/runtime/asm_amd64.s | 96 ++++++++++++++++++++++-------------- src/runtime/asm_wasm.s | 4 +- src/runtime/duff_amd64.s | 4 +- src/runtime/mkpreempt.go | 3 +- src/runtime/preempt_386.s | 3 +- src/runtime/preempt_amd64.s | 3 +- src/runtime/preempt_arm.s | 3 +- src/runtime/preempt_arm64.s | 3 +- src/runtime/preempt_mips64x.s | 3 +- src/runtime/preempt_mipsx.s | 3 +- src/runtime/preempt_ppc64x.s | 3 +- src/runtime/preempt_riscv64.s | 3 +- src/runtime/preempt_s390x.s | 3 +- src/runtime/preempt_wasm.s | 3 +- src/runtime/race_amd64.s | 12 +++-- src/runtime/sys_linux_amd64.s | 13 +++-- 20 files changed, 156 insertions(+), 114 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 45d628cc5e..7388e4e3e8 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -72,9 +72,9 @@ func initssaconfig() { deferproc = sysfunc("deferproc") deferprocStack = sysfunc("deferprocStack") Deferreturn = sysfunc("deferreturn") - Duffcopy = sysvar("duffcopy") // asm func with special ABI - Duffzero = sysvar("duffzero") // asm func with special ABI - gcWriteBarrier = sysvar("gcWriteBarrier") // asm func with special ABI + Duffcopy = sysfunc("duffcopy") + Duffzero = sysfunc("duffzero") + gcWriteBarrier = sysfunc("gcWriteBarrier") goschedguarded = sysfunc("goschedguarded") growslice = sysfunc("growslice") msanread = sysfunc("msanread") @@ -105,51 +105,51 @@ func initssaconfig() { // asm funcs with special ABI if thearch.LinkArch.Name == "amd64" { GCWriteBarrierReg = map[int16]*obj.LSym{ - x86.REG_AX: sysvar("gcWriteBarrier"), - x86.REG_CX: sysvar("gcWriteBarrierCX"), - x86.REG_DX: sysvar("gcWriteBarrierDX"), - x86.REG_BX: sysvar("gcWriteBarrierBX"), - x86.REG_BP: sysvar("gcWriteBarrierBP"), - x86.REG_SI: sysvar("gcWriteBarrierSI"), - x86.REG_R8: sysvar("gcWriteBarrierR8"), - x86.REG_R9: sysvar("gcWriteBarrierR9"), + x86.REG_AX: sysfunc("gcWriteBarrier"), + x86.REG_CX: sysfunc("gcWriteBarrierCX"), + x86.REG_DX: sysfunc("gcWriteBarrierDX"), + x86.REG_BX: sysfunc("gcWriteBarrierBX"), + x86.REG_BP: sysfunc("gcWriteBarrierBP"), + x86.REG_SI: sysfunc("gcWriteBarrierSI"), + x86.REG_R8: sysfunc("gcWriteBarrierR8"), + x86.REG_R9: sysfunc("gcWriteBarrierR9"), } } if thearch.LinkArch.Family == sys.Wasm { - BoundsCheckFunc[ssa.BoundsIndex] = sysvar("goPanicIndex") - BoundsCheckFunc[ssa.BoundsIndexU] = sysvar("goPanicIndexU") - BoundsCheckFunc[ssa.BoundsSliceAlen] = sysvar("goPanicSliceAlen") - BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysvar("goPanicSliceAlenU") - BoundsCheckFunc[ssa.BoundsSliceAcap] = sysvar("goPanicSliceAcap") - BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysvar("goPanicSliceAcapU") - BoundsCheckFunc[ssa.BoundsSliceB] = sysvar("goPanicSliceB") - BoundsCheckFunc[ssa.BoundsSliceBU] = sysvar("goPanicSliceBU") - BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysvar("goPanicSlice3Alen") - BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("goPanicSlice3AlenU") - BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysvar("goPanicSlice3Acap") - BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("goPanicSlice3AcapU") - BoundsCheckFunc[ssa.BoundsSlice3B] = sysvar("goPanicSlice3B") - BoundsCheckFunc[ssa.BoundsSlice3BU] = sysvar("goPanicSlice3BU") - BoundsCheckFunc[ssa.BoundsSlice3C] = sysvar("goPanicSlice3C") - BoundsCheckFunc[ssa.BoundsSlice3CU] = sysvar("goPanicSlice3CU") + BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("goPanicIndex") + BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("goPanicIndexU") + BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("goPanicSliceAlen") + BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("goPanicSliceAlenU") + BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("goPanicSliceAcap") + BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("goPanicSliceAcapU") + BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("goPanicSliceB") + BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("goPanicSliceBU") + BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("goPanicSlice3Alen") + BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("goPanicSlice3AlenU") + BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("goPanicSlice3Acap") + BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("goPanicSlice3AcapU") + BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("goPanicSlice3B") + BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("goPanicSlice3BU") + BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("goPanicSlice3C") + BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("goPanicSlice3CU") } else { - BoundsCheckFunc[ssa.BoundsIndex] = sysvar("panicIndex") - BoundsCheckFunc[ssa.BoundsIndexU] = sysvar("panicIndexU") - BoundsCheckFunc[ssa.BoundsSliceAlen] = sysvar("panicSliceAlen") - BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysvar("panicSliceAlenU") - BoundsCheckFunc[ssa.BoundsSliceAcap] = sysvar("panicSliceAcap") - BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysvar("panicSliceAcapU") - BoundsCheckFunc[ssa.BoundsSliceB] = sysvar("panicSliceB") - BoundsCheckFunc[ssa.BoundsSliceBU] = sysvar("panicSliceBU") - BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysvar("panicSlice3Alen") - BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("panicSlice3AlenU") - BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysvar("panicSlice3Acap") - BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("panicSlice3AcapU") - BoundsCheckFunc[ssa.BoundsSlice3B] = sysvar("panicSlice3B") - BoundsCheckFunc[ssa.BoundsSlice3BU] = sysvar("panicSlice3BU") - BoundsCheckFunc[ssa.BoundsSlice3C] = sysvar("panicSlice3C") - BoundsCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicSlice3CU") + BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("panicIndex") + BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("panicIndexU") + BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("panicSliceAlen") + BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("panicSliceAlenU") + BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("panicSliceAcap") + BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("panicSliceAcapU") + BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("panicSliceB") + BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("panicSliceBU") + BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("panicSlice3Alen") + BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("panicSlice3AlenU") + BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("panicSlice3Acap") + BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("panicSlice3AcapU") + BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("panicSlice3B") + BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("panicSlice3BU") + BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("panicSlice3C") + BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("panicSlice3CU") } if thearch.LinkArch.PtrSize == 4 { ExtendCheckFunc[ssa.BoundsIndex] = sysvar("panicExtendIndex") diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index f7f66a1255..2e9890d86c 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -129,7 +129,6 @@ var ( morestackNoCtxt *obj.LSym gcWriteBarrier *obj.LSym sigpanic *obj.LSym - sigpanic0 *obj.LSym deferreturn *obj.LSym jmpdefer *obj.LSym ) @@ -142,9 +141,8 @@ const ( func instinit(ctxt *obj.Link) { morestack = ctxt.Lookup("runtime.morestack") morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt") - gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier") + gcWriteBarrier = ctxt.LookupABI("runtime.gcWriteBarrier", obj.ABIInternal) sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal) - sigpanic0 = ctxt.LookupABI("runtime.sigpanic", 0) // sigpanic called from assembly, which has ABI0 deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal) // jmpdefer is defined in assembly as ABI0, but what we're // looking for is the *call* to jmpdefer from the Go function @@ -493,7 +491,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } // return value of call is on the top of the stack, indicating whether to unwind the WebAssembly stack - if call.As == ACALLNORESUME && call.To.Sym != sigpanic && call.To.Sym != sigpanic0 { // sigpanic unwinds the stack, but it never resumes + if call.As == ACALLNORESUME && call.To.Sym != sigpanic { // sigpanic unwinds the stack, but it never resumes // trying to unwind WebAssembly stack but call has no resume point, terminate with error p = appendp(p, AIf) p = appendp(p, obj.AUNDEF) diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go index e11fa13f65..184fb4308b 100644 --- a/src/cmd/internal/obj/x86/obj6.go +++ b/src/cmd/internal/obj/x86/obj6.go @@ -324,9 +324,9 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { // flags and duffzero on 386 does not otherwise do so). var sym *obj.LSym if p.As == obj.ADUFFZERO { - sym = ctxt.Lookup("runtime.duffzero") + sym = ctxt.LookupABI("runtime.duffzero", obj.ABIInternal) } else { - sym = ctxt.Lookup("runtime.duffcopy") + sym = ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal) } offset := p.To.Offset p.As = mov diff --git a/src/reflect/asm_amd64.s b/src/reflect/asm_amd64.s index fb28ab87f1..5c8e56558c 100644 --- a/src/reflect/asm_amd64.s +++ b/src/reflect/asm_amd64.s @@ -9,7 +9,9 @@ // See the comment on the declaration of makeFuncStub in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 +// makeFuncStub must be ABIInternal because it is placed directly +// in function values. +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 NO_LOCAL_POINTERS MOVQ DX, 0(SP) LEAQ argframe+0(FP), CX @@ -17,14 +19,16 @@ TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$32 MOVB $0, 24(SP) LEAQ 24(SP), AX MOVQ AX, 16(SP) - CALL ·callReflect(SB) + CALL ·callReflect(SB) RET // methodValueCall is the code half of the function returned by makeMethodValue. // See the comment on the declaration of methodValueCall in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 +// methodValueCall must be ABIInternal because it is placed directly +// in function values. +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 NO_LOCAL_POINTERS MOVQ DX, 0(SP) LEAQ argframe+0(FP), CX @@ -32,5 +36,5 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$32 MOVB $0, 24(SP) LEAQ 24(SP), AX MOVQ AX, 16(SP) - CALL ·callMethod(SB) + CALL ·callMethod(SB) RET diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 19a3bb2d7d..196252e1dd 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -84,7 +84,9 @@ GLOBL _rt0_amd64_lib_argc<>(SB),NOPTR, $8 DATA _rt0_amd64_lib_argv<>(SB)/8, $0 GLOBL _rt0_amd64_lib_argv<>(SB),NOPTR, $8 -TEXT runtime·rt0_go(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stack-based Go ABI (and +// in addition there are no calls to this entry point from Go code). +TEXT runtime·rt0_go(SB),NOSPLIT,$0 // copy arguments forward on an even stack MOVQ DI, AX // argc MOVQ SI, BX // argv @@ -229,10 +231,13 @@ ok: // Prevent dead-code elimination of debugCallV1, which is // intended to be called by debuggers. - MOVQ $runtime·debugCallV1(SB), AX + MOVQ $runtime·debugCallV1(SB), AX RET -DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +// mainPC is a function value for runtime.main, to be passed to newproc. +// The reference to runtime.main is made via ABIInternal, since the +// actual function (not the ABI0 wrapper) is needed by newproc. +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 TEXT runtime·breakpoint(SB),NOSPLIT,$0-0 @@ -468,7 +473,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0 JMP AX // Note: can't just "JMP NAME(SB)" - bad inlining results. -TEXT ·reflectcall(SB), NOSPLIT, $0-32 +TEXT ·reflectcall(SB), NOSPLIT, $0-32 MOVLQZX argsize+24(FP), CX DISPATCH(runtime·call16, 16) DISPATCH(runtime·call32, 32) @@ -1354,8 +1359,11 @@ TEXT _cgo_topofstack(SB),NOSPLIT,$0 RET // The top-most function running on a goroutine -// returns to goexit+PCQuantum. -TEXT runtime·goexit(SB),NOSPLIT,$0-0 +// returns to goexit+PCQuantum. Defined as ABIInternal +// so as to make it identifiable to traceback (this +// function it used as a sentinel; traceback wants to +// see the func PC, not a wrapper PC). +TEXT runtime·goexit(SB),NOSPLIT,$0-0 BYTE $0x90 // NOP CALL runtime·goexit1(SB) // does not return // traceback from goexit1 must hit code range of goexit @@ -1377,7 +1385,8 @@ TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0 // - AX is the value being written at DI // It clobbers FLAGS. It does not clobber any general-purpose registers, // but may clobber others (e.g., SSE registers). -TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$120 +// Defined as ABIInternal since it does not use the stack-based Go ABI. +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$120 // Save the registers clobbered by the fast path. This is slightly // faster than having the caller spill these. MOVQ R14, 104(SP) @@ -1461,51 +1470,58 @@ flush: JMP ret // gcWriteBarrierCX is gcWriteBarrier, but with args in DI and CX. -TEXT runtime·gcWriteBarrierCX(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierCX(SB),NOSPLIT,$0 XCHGQ CX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ CX, AX RET // gcWriteBarrierDX is gcWriteBarrier, but with args in DI and DX. -TEXT runtime·gcWriteBarrierDX(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierDX(SB),NOSPLIT,$0 XCHGQ DX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ DX, AX RET // gcWriteBarrierBX is gcWriteBarrier, but with args in DI and BX. -TEXT runtime·gcWriteBarrierBX(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierBX(SB),NOSPLIT,$0 XCHGQ BX, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ BX, AX RET // gcWriteBarrierBP is gcWriteBarrier, but with args in DI and BP. -TEXT runtime·gcWriteBarrierBP(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierBP(SB),NOSPLIT,$0 XCHGQ BP, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ BP, AX RET // gcWriteBarrierSI is gcWriteBarrier, but with args in DI and SI. -TEXT runtime·gcWriteBarrierSI(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierSI(SB),NOSPLIT,$0 XCHGQ SI, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ SI, AX RET // gcWriteBarrierR8 is gcWriteBarrier, but with args in DI and R8. -TEXT runtime·gcWriteBarrierR8(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierR8(SB),NOSPLIT,$0 XCHGQ R8, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ R8, AX RET // gcWriteBarrierR9 is gcWriteBarrier, but with args in DI and R9. -TEXT runtime·gcWriteBarrierR9(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stable Go ABI. +TEXT runtime·gcWriteBarrierR9(SB),NOSPLIT,$0 XCHGQ R9, AX - CALL runtime·gcWriteBarrier(SB) + CALL runtime·gcWriteBarrier(SB) XCHGQ R9, AX RET @@ -1544,7 +1560,10 @@ GLOBL debugCallFrameTooLarge<>(SB), RODATA, $20 // Size duplicated below // obey escape analysis requirements. Specifically, it must not pass // a stack pointer to an escaping argument. debugCallV1 cannot check // this invariant. -TEXT runtime·debugCallV1(SB),NOSPLIT,$152-0 +// +// This is ABIInternal because Go code injects its PC directly into new +// goroutine stacks. +TEXT runtime·debugCallV1(SB),NOSPLIT,$152-0 // Save all registers that may contain pointers so they can be // conservatively scanned. // @@ -1705,67 +1724,68 @@ TEXT runtime·debugCallPanicked(SB),NOSPLIT,$16-16 // in the caller's stack frame. These stubs write the args into that stack space and // then tail call to the corresponding runtime handler. // The tail call makes these stubs disappear in backtraces. -TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 +// Defined as ABIInternal since they do not use the stack-based Go ABI. +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicIndex(SB) -TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicIndexU(SB) -TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAlen(SB) -TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAlenU(SB) -TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAcap(SB) -TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSliceAcapU(SB) -TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSliceB(SB) -TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSliceBU(SB) -TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3Alen(SB) -TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3AlenU(SB) -TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3Acap(SB) -TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 MOVQ DX, x+0(FP) MOVQ BX, y+8(FP) JMP runtime·goPanicSlice3AcapU(SB) -TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSlice3B(SB) -TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 MOVQ CX, x+0(FP) MOVQ DX, y+8(FP) JMP runtime·goPanicSlice3BU(SB) -TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSlice3C(SB) -TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 MOVQ AX, x+0(FP) MOVQ CX, y+8(FP) JMP runtime·goPanicSlice3CU(SB) diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s index 67e81adf0b..fcb780f1dc 100644 --- a/src/runtime/asm_wasm.s +++ b/src/runtime/asm_wasm.s @@ -196,7 +196,7 @@ TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16 Get CTXT I64Eqz If - CALLNORESUME runtime·sigpanic(SB) + CALLNORESUME runtime·sigpanic(SB) End // caller sp after CALL @@ -300,7 +300,7 @@ TEXT ·reflectcall(SB), NOSPLIT, $0-32 I64Load fn+8(FP) I64Eqz If - CALLNORESUME runtime·sigpanic(SB) + CALLNORESUME runtime·sigpanic(SB) End MOVW argsize+24(FP), R0 diff --git a/src/runtime/duff_amd64.s b/src/runtime/duff_amd64.s index 44dc75d297..2ff5bf6dbc 100644 --- a/src/runtime/duff_amd64.s +++ b/src/runtime/duff_amd64.s @@ -4,7 +4,7 @@ #include "textflag.h" -TEXT runtime·duffzero(SB), NOSPLIT, $0-0 +TEXT runtime·duffzero(SB), NOSPLIT, $0-0 MOVUPS X0,(DI) MOVUPS X0,16(DI) MOVUPS X0,32(DI) @@ -103,7 +103,7 @@ TEXT runtime·duffzero(SB), NOSPLIT, $0-0 RET -TEXT runtime·duffcopy(SB), NOSPLIT, $0-0 +TEXT runtime·duffcopy(SB), NOSPLIT, $0-0 MOVUPS (SI), X0 ADDQ $16, SI MOVUPS X0, (DI) diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index 286f81489a..1d614dd003 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -126,7 +126,8 @@ func header(arch string) { } fmt.Fprintf(out, "#include \"go_asm.h\"\n") fmt.Fprintf(out, "#include \"textflag.h\"\n\n") - fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n") + fmt.Fprintf(out, "// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally.\n") + fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n") } func p(f string, args ...interface{}) { diff --git a/src/runtime/preempt_386.s b/src/runtime/preempt_386.s index c3a5fa1f36..a803b24dc6 100644 --- a/src/runtime/preempt_386.s +++ b/src/runtime/preempt_386.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHFL ADJSP $156 NOP SP diff --git a/src/runtime/preempt_amd64.s b/src/runtime/preempt_amd64.s index 4765e9f448..92c664d79a 100644 --- a/src/runtime/preempt_amd64.s +++ b/src/runtime/preempt_amd64.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHQ BP MOVQ SP, BP // Save flags before clobbering them diff --git a/src/runtime/preempt_arm.s b/src/runtime/preempt_arm.s index 8f243c0dcd..bbc9fbb1ea 100644 --- a/src/runtime/preempt_arm.s +++ b/src/runtime/preempt_arm.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW.W R14, -188(R13) MOVW R0, 4(R13) MOVW R1, 8(R13) diff --git a/src/runtime/preempt_arm64.s b/src/runtime/preempt_arm64.s index 36ee13282c..2b70a28479 100644 --- a/src/runtime/preempt_arm64.s +++ b/src/runtime/preempt_arm64.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD R30, -496(RSP) SUB $496, RSP #ifdef GOOS_linux diff --git a/src/runtime/preempt_mips64x.s b/src/runtime/preempt_mips64x.s index 1e123e8077..0d0c157c36 100644 --- a/src/runtime/preempt_mips64x.s +++ b/src/runtime/preempt_mips64x.s @@ -5,7 +5,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVV R31, -488(R29) SUBV $488, R29 MOVV R1, 8(R29) diff --git a/src/runtime/preempt_mipsx.s b/src/runtime/preempt_mipsx.s index afac33e0a0..86d3a918d3 100644 --- a/src/runtime/preempt_mipsx.s +++ b/src/runtime/preempt_mipsx.s @@ -5,7 +5,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW R31, -244(R29) SUB $244, R29 MOVW R1, 4(R29) diff --git a/src/runtime/preempt_ppc64x.s b/src/runtime/preempt_ppc64x.s index b2d7e30ec7..90634386db 100644 --- a/src/runtime/preempt_ppc64x.s +++ b/src/runtime/preempt_ppc64x.s @@ -5,7 +5,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD R31, -488(R1) MOVD LR, R31 MOVDU R31, -520(R1) diff --git a/src/runtime/preempt_riscv64.s b/src/runtime/preempt_riscv64.s index eb68dcba2b..d4f9cc277f 100644 --- a/src/runtime/preempt_riscv64.s +++ b/src/runtime/preempt_riscv64.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOV X1, -472(X2) ADD $-472, X2 MOV X3, 8(X2) diff --git a/src/runtime/preempt_s390x.s b/src/runtime/preempt_s390x.s index ca9e47cde1..c6f11571df 100644 --- a/src/runtime/preempt_s390x.s +++ b/src/runtime/preempt_s390x.s @@ -3,7 +3,8 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 IPM R10 MOVD R14, -248(R15) ADD $-248, R15 diff --git a/src/runtime/preempt_wasm.s b/src/runtime/preempt_wasm.s index 0cf57d3d22..da90e8aa6d 100644 --- a/src/runtime/preempt_wasm.s +++ b/src/runtime/preempt_wasm.s @@ -3,6 +3,7 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 // No async preemption on wasm UNDEF diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s index 758d543203..4a86b3371a 100644 --- a/src/runtime/race_amd64.s +++ b/src/runtime/race_amd64.s @@ -41,7 +41,9 @@ // func runtime·raceread(addr uintptr) // Called from instrumented code. -TEXT runtime·raceread(SB), NOSPLIT, $0-8 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would render runtime.getcallerpc ineffective. +TEXT runtime·raceread(SB), NOSPLIT, $0-8 MOVQ addr+0(FP), RARG1 MOVQ (SP), RARG2 // void __tsan_read(ThreadState *thr, void *addr, void *pc); @@ -65,7 +67,9 @@ TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 // func runtime·racewrite(addr uintptr) // Called from instrumented code. -TEXT runtime·racewrite(SB), NOSPLIT, $0-8 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would render runtime.getcallerpc ineffective. +TEXT runtime·racewrite(SB), NOSPLIT, $0-8 MOVQ addr+0(FP), RARG1 MOVQ (SP), RARG2 // void __tsan_write(ThreadState *thr, void *addr, void *pc); @@ -114,7 +118,9 @@ TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24 // func runtime·racewriterange(addr, size uintptr) // Called from instrumented code. -TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would render runtime.getcallerpc ineffective. +TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG1 MOVQ size+8(FP), RARG2 MOVQ (SP), RARG3 diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index 681cd20274..37cb8dad03 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -380,7 +380,8 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 POPQ BP RET -TEXT runtime·sigtramp(SB),NOSPLIT,$72 +// Defined as ABIInternal since it does not use the stack-based Go ABI. +TEXT runtime·sigtramp(SB),NOSPLIT,$72 // Save callee-saved C registers, since the caller may be a C signal handler. MOVQ BX, bx-8(SP) MOVQ BP, bp-16(SP) // save in case GOEXPERIMENT=noframepointer is set @@ -407,7 +408,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$72 // Used instead of sigtramp in programs that use cgo. // Arguments from kernel are in DI, SI, DX. -TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stack-based Go ABI. +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // If no traceback function, do usual sigtramp. MOVQ runtime·cgoTraceback(SB), AX TESTQ AX, AX @@ -450,12 +452,12 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // The first three arguments, and the fifth, are already in registers. // Set the two remaining arguments now. MOVQ runtime·cgoTraceback(SB), CX - MOVQ $runtime·sigtramp(SB), R9 + MOVQ $runtime·sigtramp(SB), R9 MOVQ _cgo_callers(SB), AX JMP AX sigtramp: - JMP runtime·sigtramp(SB) + JMP runtime·sigtramp(SB) sigtrampnog: // Signal arrived on a non-Go thread. If this is SIGPROF, get a @@ -486,7 +488,8 @@ sigtrampnog: // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/x86_64/sigaction.c // The code that cares about the precise instructions used is: // https://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/i386/linux-unwind.h?revision=219188&view=markup -TEXT runtime·sigreturn(SB),NOSPLIT,$0 +// Defined as ABIInternal since it does not use the stack-based Go ABI. +TEXT runtime·sigreturn(SB),NOSPLIT,$0 MOVQ $SYS_rt_sigreturn, AX SYSCALL INT $3 // not reached -- cgit v1.3 From 7191f1136b1526703c5af7fc04ff948e3a6c26b9 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 30 Oct 2020 10:36:31 -0700 Subject: cmd/compile: fix reassignVisitor reassignVisitor was short-circuiting on assignment statements after checking the LHS, but there might be further assignment statements nested within the RHS expressions. Fixes #42284. Change-Id: I175eef87513b973ed5ebe6a6527adb9766dde6cf Reviewed-on: https://go-review.googlesource.com/c/go/+/266618 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le Reviewed-by: David Chase TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/inl.go | 2 -- test/fixedbugs/issue42284.dir/a.go | 23 +++++++++++++++++++++++ test/fixedbugs/issue42284.dir/b.go | 15 +++++++++++++++ test/fixedbugs/issue42284.go | 7 +++++++ 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 test/fixedbugs/issue42284.dir/a.go create mode 100644 test/fixedbugs/issue42284.dir/b.go create mode 100644 test/fixedbugs/issue42284.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 5b58908299..8a5c6d8666 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -839,14 +839,12 @@ func (v *reassignVisitor) visit(n *Node) *Node { if n.Left == v.name && n != v.name.Name.Defn { return n } - return nil case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE: for _, p := range n.List.Slice() { if p == v.name && n != v.name.Name.Defn { return n } } - return nil } if a := v.visit(n.Left); a != nil { return a diff --git a/test/fixedbugs/issue42284.dir/a.go b/test/fixedbugs/issue42284.dir/a.go new file mode 100644 index 0000000000..e1271af32d --- /dev/null +++ b/test/fixedbugs/issue42284.dir/a.go @@ -0,0 +1,23 @@ +// Copyright 2020 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 a + +type I interface{ M() } +type T int + +func (T) M() {} // ERROR "can inline T.M" + +func F(i I) I { // ERROR "can inline F" "leaking param: i to result ~r1 level=0" + i = nil + return i +} + +func g() { // ERROR "can inline g" + // BAD: T(0) could be stack allocated. + i := F(T(0)) // ERROR "inlining call to F" "T\(0\) escapes to heap" + + // Testing that we do NOT devirtualize here: + i.M() +} diff --git a/test/fixedbugs/issue42284.dir/b.go b/test/fixedbugs/issue42284.dir/b.go new file mode 100644 index 0000000000..3305166db0 --- /dev/null +++ b/test/fixedbugs/issue42284.dir/b.go @@ -0,0 +1,15 @@ +// Copyright 2020 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 b + +import "./a" + +func g() { // ERROR "can inline g" + // BAD: T(0) could be stack allocated. + i := a.F(a.T(0)) // ERROR "inlining call to a.F" "a.T\(0\) escapes to heap" + + // Testing that we do NOT devirtualize here: + i.M() +} diff --git a/test/fixedbugs/issue42284.go b/test/fixedbugs/issue42284.go new file mode 100644 index 0000000000..e5d6173f5c --- /dev/null +++ b/test/fixedbugs/issue42284.go @@ -0,0 +1,7 @@ +// errorcheckdir -0 -m + +// Copyright 2020 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 ignored -- cgit v1.3 From 84d7a85089009332756c18e876ec91f96b362ebf Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Wed, 21 Oct 2020 20:15:48 -0400 Subject: cmd/compile: delete register maps, completely Remove go115ReduceLiveness feature gating flag, along with code that only needed when go115ReduceLiveness is false. Change-Id: I7571913cc74cbd17b330a0ee0160fefc9eeee66e Reviewed-on: https://go-review.googlesource.com/c/go/+/264338 Trust: Cherry Zhang Run-TryBot: Cherry Zhang Reviewed-by: Austin Clements --- src/cmd/compile/fmtmap_test.go | 2 - src/cmd/compile/internal/gc/gsubr.go | 40 +--- src/cmd/compile/internal/gc/obj.go | 2 +- src/cmd/compile/internal/gc/plive.go | 421 ++++++----------------------------- src/cmd/compile/internal/gc/ssa.go | 2 +- src/cmd/internal/obj/link.go | 1 - src/cmd/internal/obj/plist.go | 22 +- src/cmd/internal/objabi/funcdata.go | 9 +- 8 files changed, 87 insertions(+), 412 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/fmtmap_test.go b/src/cmd/compile/fmtmap_test.go index 179c60187f..0811df7f7b 100644 --- a/src/cmd/compile/fmtmap_test.go +++ b/src/cmd/compile/fmtmap_test.go @@ -105,10 +105,8 @@ var knownFormats = map[string]string{ "cmd/compile/internal/ssa.GCNode %v": "", "cmd/compile/internal/ssa.ID %d": "", "cmd/compile/internal/ssa.ID %v": "", - "cmd/compile/internal/ssa.LocPair %s": "", "cmd/compile/internal/ssa.LocalSlot %s": "", "cmd/compile/internal/ssa.LocalSlot %v": "", - "cmd/compile/internal/ssa.Location %T": "", "cmd/compile/internal/ssa.Location %s": "", "cmd/compile/internal/ssa.Op %s": "", "cmd/compile/internal/ssa.Op %v": "", diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index ce5182f203..864ada1d3c 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -70,12 +70,8 @@ func newProgs(fn *Node, worker int) *Progs { pp.pos = fn.Pos pp.settext(fn) // PCDATA tables implicitly start with index -1. - pp.prevLive = LivenessIndex{-1, -1, false} - if go115ReduceLiveness { - pp.nextLive = pp.prevLive - } else { - pp.nextLive = LivenessInvalid - } + pp.prevLive = LivenessIndex{-1, false} + pp.nextLive = pp.prevLive return pp } @@ -120,31 +116,15 @@ func (pp *Progs) Prog(as obj.As) *obj.Prog { Addrconst(&p.From, objabi.PCDATA_StackMapIndex) Addrconst(&p.To, int64(idx)) } - if !go115ReduceLiveness { + if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint { + // Emit unsafe-point marker. + pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint + p := pp.Prog(obj.APCDATA) + Addrconst(&p.From, objabi.PCDATA_UnsafePoint) if pp.nextLive.isUnsafePoint { - // Unsafe points are encoded as a special value in the - // register map. - pp.nextLive.regMapIndex = objabi.PCDATA_RegMapUnsafe - } - if pp.nextLive.regMapIndex != pp.prevLive.regMapIndex { - // Emit register map index change. - idx := pp.nextLive.regMapIndex - pp.prevLive.regMapIndex = idx - p := pp.Prog(obj.APCDATA) - Addrconst(&p.From, objabi.PCDATA_RegMapIndex) - Addrconst(&p.To, int64(idx)) - } - } else { - if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint { - // Emit unsafe-point marker. - pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint - p := pp.Prog(obj.APCDATA) - Addrconst(&p.From, objabi.PCDATA_UnsafePoint) - if pp.nextLive.isUnsafePoint { - Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe) - } else { - Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe) - } + Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe) + } else { + Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe) } } diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 226eb45252..32aa7c5bb1 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -312,7 +312,7 @@ func addGCLocals() { if fn == nil { continue } - for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals, fn.GCRegs} { + for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals} { if gcsym != nil && !gcsym.OnList() { ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK) } diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go index b471accb65..a48173e0d6 100644 --- a/src/cmd/compile/internal/gc/plive.go +++ b/src/cmd/compile/internal/gc/plive.go @@ -24,16 +24,6 @@ import ( "strings" ) -// go115ReduceLiveness disables register maps and only produces stack -// maps at call sites. -// -// In Go 1.15, we changed debug call injection to use conservative -// scanning instead of precise pointer maps, so these are no longer -// necessary. -// -// Keep in sync with runtime/preempt.go:go115ReduceLiveness. -const go115ReduceLiveness = true - // OpVarDef is an annotation for the liveness analysis, marking a place // where a complete initialization (definition) of a variable begins. // Since the liveness analysis can see initialization of single-word @@ -96,15 +86,15 @@ type BlockEffects struct { // // uevar: upward exposed variables (used before set in block) // varkill: killed variables (set in block) - uevar varRegVec - varkill varRegVec + uevar bvec + varkill bvec // Computed during Liveness.solve using control flow information: // // livein: variables live at block entry // liveout: variables live at block exit - livein varRegVec - liveout varRegVec + livein bvec + liveout bvec } // A collection of global state used by liveness analysis. @@ -128,16 +118,14 @@ type Liveness struct { // current Block during Liveness.epilogue. Indexed in Value // order for that block. Additionally, for the entry block // livevars[0] is the entry bitmap. Liveness.compact moves - // these to stackMaps and regMaps. - livevars []varRegVec + // these to stackMaps. + livevars []bvec // livenessMap maps from safe points (i.e., CALLs) to their // liveness map indexes. livenessMap LivenessMap stackMapSet bvecSet stackMaps []bvec - regMapSet map[liveRegMask]int - regMaps []liveRegMask cache progeffectscache } @@ -158,7 +146,7 @@ func (m *LivenessMap) reset() { delete(m.vals, k) } } - m.deferreturn = LivenessInvalid + m.deferreturn = LivenessDontCare } func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) { @@ -166,27 +154,17 @@ func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) { } func (m LivenessMap) Get(v *ssa.Value) LivenessIndex { - if !go115ReduceLiveness { - // All safe-points are in the map, so if v isn't in - // the map, it's an unsafe-point. - if idx, ok := m.vals[v.ID]; ok { - return idx - } - return LivenessInvalid - } - // If v isn't in the map, then it's a "don't care" and not an // unsafe-point. if idx, ok := m.vals[v.ID]; ok { return idx } - return LivenessIndex{StackMapDontCare, StackMapDontCare, false} + return LivenessIndex{StackMapDontCare, false} } // LivenessIndex stores the liveness map information for a Value. type LivenessIndex struct { stackMapIndex int - regMapIndex int // only for !go115ReduceLiveness // isUnsafePoint indicates that this is an unsafe-point. // @@ -197,8 +175,10 @@ type LivenessIndex struct { isUnsafePoint bool } -// LivenessInvalid indicates an unsafe point with no stack map. -var LivenessInvalid = LivenessIndex{StackMapDontCare, StackMapDontCare, true} // only for !go115ReduceLiveness +// LivenessDontCare indicates that the liveness information doesn't +// matter. Currently it is used in deferreturn liveness when we don't +// actually need it. It should never be emitted to the PCDATA stream. +var LivenessDontCare = LivenessIndex{StackMapDontCare, true} // StackMapDontCare indicates that the stack map index at a Value // doesn't matter. @@ -212,46 +192,12 @@ func (idx LivenessIndex) StackMapValid() bool { return idx.stackMapIndex != StackMapDontCare } -func (idx LivenessIndex) RegMapValid() bool { - return idx.regMapIndex != StackMapDontCare -} - type progeffectscache struct { retuevar []int32 tailuevar []int32 initialized bool } -// varRegVec contains liveness bitmaps for variables and registers. -type varRegVec struct { - vars bvec - regs liveRegMask -} - -func (v *varRegVec) Eq(v2 varRegVec) bool { - return v.vars.Eq(v2.vars) && v.regs == v2.regs -} - -func (v *varRegVec) Copy(v2 varRegVec) { - v.vars.Copy(v2.vars) - v.regs = v2.regs -} - -func (v *varRegVec) Clear() { - v.vars.Clear() - v.regs = 0 -} - -func (v *varRegVec) Or(v1, v2 varRegVec) { - v.vars.Or(v1.vars, v2.vars) - v.regs = v1.regs | v2.regs -} - -func (v *varRegVec) AndNot(v1, v2 varRegVec) { - v.vars.AndNot(v1.vars, v2.vars) - v.regs = v1.regs &^ v2.regs -} - // livenessShouldTrack reports whether the liveness analysis // should track the variable n. // We don't care about variables that have no pointers, @@ -400,110 +346,6 @@ func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) { } } -// regEffects returns the registers affected by v. -func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) { - if go115ReduceLiveness { - return 0, 0 - } - if v.Op == ssa.OpPhi { - // All phi node arguments must come from the same - // register and the result must also go to that - // register, so there's no overall effect. - return 0, 0 - } - addLocs := func(mask liveRegMask, v *ssa.Value, ptrOnly bool) liveRegMask { - if int(v.ID) >= len(lv.f.RegAlloc) { - // v has no allocated registers. - return mask - } - loc := lv.f.RegAlloc[v.ID] - if loc == nil { - // v has no allocated registers. - return mask - } - if v.Op == ssa.OpGetG { - // GetG represents the G register, which is a - // pointer, but not a valid GC register. The - // current G is always reachable, so it's okay - // to ignore this register. - return mask - } - - // Collect registers and types from v's location. - var regs [2]*ssa.Register - nreg := 0 - switch loc := loc.(type) { - case ssa.LocalSlot: - return mask - case *ssa.Register: - if ptrOnly && !v.Type.HasPointers() { - return mask - } - regs[0] = loc - nreg = 1 - case ssa.LocPair: - // The value will have TTUPLE type, and the - // children are nil or *ssa.Register. - if v.Type.Etype != types.TTUPLE { - v.Fatalf("location pair %s has non-tuple type %v", loc, v.Type) - } - for i, loc1 := range &loc { - if loc1 == nil { - continue - } - if ptrOnly && !v.Type.FieldType(i).HasPointers() { - continue - } - regs[nreg] = loc1.(*ssa.Register) - nreg++ - } - default: - v.Fatalf("weird RegAlloc location: %s (%T)", loc, loc) - } - - // Add register locations to vars. - for _, reg := range regs[:nreg] { - if reg.GCNum() == -1 { - if ptrOnly { - v.Fatalf("pointer in non-pointer register %v", reg) - } else { - continue - } - } - mask |= 1 << uint(reg.GCNum()) - } - return mask - } - - // v clobbers all registers it writes to (whether or not the - // write is pointer-typed). - kill = addLocs(0, v, false) - for _, arg := range v.Args { - // v uses all registers is reads from, but we only - // care about marking those containing pointers. - uevar = addLocs(uevar, arg, true) - } - return uevar, kill -} - -type liveRegMask uint32 // only if !go115ReduceLiveness - -func (m liveRegMask) niceString(config *ssa.Config) string { - if m == 0 { - return "" - } - str := "" - for i, reg := range config.GCRegMap { - if m&(1<= f.NumBlocks() { lv.be = lc.be[:f.NumBlocks()] } - lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessInvalid} + lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessDontCare} lc.livenessMap.vals = nil } if lv.be == nil { @@ -546,10 +386,10 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt for _, b := range f.Blocks { be := lv.blockEffects(b) - be.uevar = varRegVec{vars: bulk.next()} - be.varkill = varRegVec{vars: bulk.next()} - be.livein = varRegVec{vars: bulk.next()} - be.liveout = varRegVec{vars: bulk.next()} + be.uevar = bulk.next() + be.varkill = bulk.next() + be.livein = bulk.next() + be.liveout = bulk.next() } lv.livenessMap.reset() @@ -637,20 +477,6 @@ func onebitwalktype1(t *types.Type, off int64, bv bvec) { } } -// usedRegs returns the maximum width of the live register map. -func (lv *Liveness) usedRegs() int32 { - var any liveRegMask - for _, live := range lv.regMaps { - any |= live - } - i := int32(0) - for any != 0 { - any >>= 1 - i++ - } - return i -} - // Generates live pointer value maps for arguments and local variables. The // this argument and the in arguments are always assumed live. The vars // argument is a slice of *Nodes. @@ -851,31 +677,16 @@ func (lv *Liveness) markUnsafePoints() { // particular, call Values can have a stack map in case the callee // grows the stack, but not themselves be a safe-point. func (lv *Liveness) hasStackMap(v *ssa.Value) bool { - // The runtime only has safe-points in function prologues, so - // we only need stack maps at call sites. go:nosplit functions - // are similar. - if go115ReduceLiveness || compiling_runtime || lv.f.NoSplit { - if !v.Op.IsCall() { - return false - } - // typedmemclr and typedmemmove are write barriers and - // deeply non-preemptible. They are unsafe points and - // hence should not have liveness maps. - if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) { - return false - } - return true + if !v.Op.IsCall() { + return false } - - switch v.Op { - case ssa.OpInitMem, ssa.OpArg, ssa.OpSP, ssa.OpSB, - ssa.OpSelect0, ssa.OpSelect1, ssa.OpGetG, - ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive, - ssa.OpPhi: - // These don't produce code (see genssa). + // typedmemclr and typedmemmove are write barriers and + // deeply non-preemptible. They are unsafe points and + // hence should not have liveness maps. + if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) { return false } - return !lv.unsafePoints.Get(int32(v.ID)) + return true } // Initializes the sets for solving the live variables. Visits all the @@ -891,17 +702,13 @@ func (lv *Liveness) prologue() { // effects with the each prog effects. for j := len(b.Values) - 1; j >= 0; j-- { pos, e := lv.valueEffects(b.Values[j]) - regUevar, regKill := lv.regEffects(b.Values[j]) if e&varkill != 0 { - be.varkill.vars.Set(pos) - be.uevar.vars.Unset(pos) + be.varkill.Set(pos) + be.uevar.Unset(pos) } - be.varkill.regs |= regKill - be.uevar.regs &^= regKill if e&uevar != 0 { - be.uevar.vars.Set(pos) + be.uevar.Set(pos) } - be.uevar.regs |= regUevar } } } @@ -911,8 +718,8 @@ func (lv *Liveness) solve() { // These temporary bitvectors exist to avoid successive allocations and // frees within the loop. nvars := int32(len(lv.vars)) - newlivein := varRegVec{vars: bvalloc(nvars)} - newliveout := varRegVec{vars: bvalloc(nvars)} + newlivein := bvalloc(nvars) + newliveout := bvalloc(nvars) // Walk blocks in postorder ordering. This improves convergence. po := lv.f.Postorder() @@ -930,11 +737,11 @@ func (lv *Liveness) solve() { switch b.Kind { case ssa.BlockRet: for _, pos := range lv.cache.retuevar { - newliveout.vars.Set(pos) + newliveout.Set(pos) } case ssa.BlockRetJmp: for _, pos := range lv.cache.tailuevar { - newliveout.vars.Set(pos) + newliveout.Set(pos) } case ssa.BlockExit: // panic exit - nothing to do @@ -969,7 +776,7 @@ func (lv *Liveness) solve() { // variables at each safe point locations. func (lv *Liveness) epilogue() { nvars := int32(len(lv.vars)) - liveout := varRegVec{vars: bvalloc(nvars)} + liveout := bvalloc(nvars) livedefer := bvalloc(nvars) // always-live variables // If there is a defer (that could recover), then all output @@ -1025,12 +832,11 @@ func (lv *Liveness) epilogue() { { // Reserve an entry for function entry. live := bvalloc(nvars) - lv.livevars = append(lv.livevars, varRegVec{vars: live}) + lv.livevars = append(lv.livevars, live) } for _, b := range lv.f.Blocks { be := lv.blockEffects(b) - firstBitmapIndex := len(lv.livevars) // Walk forward through the basic block instructions and // allocate liveness maps for those instructions that need them. @@ -1040,7 +846,7 @@ func (lv *Liveness) epilogue() { } live := bvalloc(nvars) - lv.livevars = append(lv.livevars, varRegVec{vars: live}) + lv.livevars = append(lv.livevars, live) } // walk backward, construct maps at each safe point @@ -1056,21 +862,18 @@ func (lv *Liveness) epilogue() { live := &lv.livevars[index] live.Or(*live, liveout) - live.vars.Or(live.vars, livedefer) // only for non-entry safe points + live.Or(*live, livedefer) // only for non-entry safe points index-- } // Update liveness information. pos, e := lv.valueEffects(v) - regUevar, regKill := lv.regEffects(v) if e&varkill != 0 { - liveout.vars.Unset(pos) + liveout.Unset(pos) } - liveout.regs &^= regKill if e&uevar != 0 { - liveout.vars.Set(pos) + liveout.Set(pos) } - liveout.regs |= regUevar } if b == lv.f.Entry { @@ -1080,7 +883,7 @@ func (lv *Liveness) epilogue() { // Check to make sure only input variables are live. for i, n := range lv.vars { - if !liveout.vars.Get(int32(i)) { + if !liveout.Get(int32(i)) { continue } if n.Class() == PPARAM { @@ -1094,32 +897,16 @@ func (lv *Liveness) epilogue() { live.Or(*live, liveout) } - // Check that no registers are live across calls. - // For closure calls, the CALLclosure is the last use - // of the context register, so it's dead after the call. - index = int32(firstBitmapIndex) - for _, v := range b.Values { - if lv.hasStackMap(v) { - live := lv.livevars[index] - if v.Op.IsCall() && live.regs != 0 { - lv.printDebug() - v.Fatalf("%v register %s recorded as live at call", lv.fn.Func.Nname, live.regs.niceString(lv.f.Config)) - } - index++ - } - } - // The liveness maps for this block are now complete. Compact them. lv.compact(b) } // If we have an open-coded deferreturn call, make a liveness map for it. if lv.fn.Func.OpenCodedDeferDisallowed() { - lv.livenessMap.deferreturn = LivenessInvalid + lv.livenessMap.deferreturn = LivenessDontCare } else { lv.livenessMap.deferreturn = LivenessIndex{ stackMapIndex: lv.stackMapSet.add(livedefer), - regMapIndex: 0, // entry regMap, containing no live registers isUnsafePoint: false, } } @@ -1136,20 +923,10 @@ func (lv *Liveness) epilogue() { lv.f.Fatalf("%v %L recorded as live on entry", lv.fn.Func.Nname, n) } } - if !go115ReduceLiveness { - // Check that no registers are live at function entry. - // The context register, if any, comes from a - // LoweredGetClosurePtr operation first thing in the function, - // so it doesn't appear live at entry. - if regs := lv.regMaps[0]; regs != 0 { - lv.printDebug() - lv.f.Fatalf("%v register %s recorded as live on entry", lv.fn.Func.Nname, regs.niceString(lv.f.Config)) - } - } } // Compact coalesces identical bitmaps from lv.livevars into the sets -// lv.stackMapSet and lv.regMaps. +// lv.stackMapSet. // // Compact clears lv.livevars. // @@ -1165,45 +942,23 @@ func (lv *Liveness) epilogue() { // PCDATA tables cost about 100k. So for now we keep using a single index for // both bitmap lists. func (lv *Liveness) compact(b *ssa.Block) { - add := func(live varRegVec, isUnsafePoint bool) LivenessIndex { // only if !go115ReduceLiveness - // Deduplicate the stack map. - stackIndex := lv.stackMapSet.add(live.vars) - // Deduplicate the register map. - regIndex, ok := lv.regMapSet[live.regs] - if !ok { - regIndex = len(lv.regMapSet) - lv.regMapSet[live.regs] = regIndex - lv.regMaps = append(lv.regMaps, live.regs) - } - return LivenessIndex{stackIndex, regIndex, isUnsafePoint} - } pos := 0 if b == lv.f.Entry { // Handle entry stack map. - if !go115ReduceLiveness { - add(lv.livevars[0], false) - } else { - lv.stackMapSet.add(lv.livevars[0].vars) - } + lv.stackMapSet.add(lv.livevars[0]) pos++ } for _, v := range b.Values { - if go115ReduceLiveness { - hasStackMap := lv.hasStackMap(v) - isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID)) - idx := LivenessIndex{StackMapDontCare, StackMapDontCare, isUnsafePoint} - if hasStackMap { - idx.stackMapIndex = lv.stackMapSet.add(lv.livevars[pos].vars) - pos++ - } - if hasStackMap || isUnsafePoint { - lv.livenessMap.set(v, idx) - } - } else if lv.hasStackMap(v) { - isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID)) - lv.livenessMap.set(v, add(lv.livevars[pos], isUnsafePoint)) + hasStackMap := lv.hasStackMap(v) + isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID)) + idx := LivenessIndex{StackMapDontCare, isUnsafePoint} + if hasStackMap { + idx.stackMapIndex = lv.stackMapSet.add(lv.livevars[pos]) pos++ } + if hasStackMap || isUnsafePoint { + lv.livenessMap.set(v, idx) + } } // Reset livevars. @@ -1250,8 +1005,8 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) { Warnl(pos, s) } -func (lv *Liveness) printbvec(printed bool, name string, live varRegVec) bool { - if live.vars.IsEmpty() && live.regs == 0 { +func (lv *Liveness) printbvec(printed bool, name string, live bvec) bool { + if live.IsEmpty() { return printed } @@ -1264,19 +1019,18 @@ func (lv *Liveness) printbvec(printed bool, name string, live varRegVec) bool { comma := "" for i, n := range lv.vars { - if !live.vars.Get(int32(i)) { + if !live.Get(int32(i)) { continue } fmt.Printf("%s%s", comma, n.Sym.Name) comma = "," } - fmt.Printf("%s%s", comma, live.regs.niceString(lv.f.Config)) return true } -// printeffect is like printbvec, but for valueEffects and regEffects. -func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool, regMask liveRegMask) bool { - if !x && regMask == 0 { +// printeffect is like printbvec, but for valueEffects. +func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool { + if !x { return printed } if !printed { @@ -1288,15 +1042,7 @@ func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool, re if x { fmt.Printf("%s", lv.vars[pos].Sym.Name) } - for j, reg := range lv.f.Config.GCRegMap { - if regMask&(1< 32 { - // Our uint32 conversion below won't work. - Fatalf("GP registers overflow uint32") - } - - if regs.n > 0 { - for _, live := range lv.regMaps { - regs.Clear() - regs.b[0] = uint32(live) - roff = dbvec(®sSymTmp, roff, regs) - } - } - } - // Give these LSyms content-addressable names, // so that they can be de-duplicated. // This provides significant binary size savings. @@ -1502,11 +1219,7 @@ func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) { lsym.Set(obj.AttrContentAddressable, true) }) } - if !go115ReduceLiveness { - return makeSym(&argsSymTmp), makeSym(&liveSymTmp), makeSym(®sSymTmp) - } - // TODO(go115ReduceLiveness): Remove regsSym result - return makeSym(&argsSymTmp), makeSym(&liveSymTmp), nil + return makeSym(&argsSymTmp), makeSym(&liveSymTmp) } // Entry pointer for liveness analysis. Solves for the liveness of @@ -1553,7 +1266,7 @@ func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap { // Emit the live pointer map data structures ls := e.curfn.Func.lsym fninfo := ls.Func() - fninfo.GCArgs, fninfo.GCLocals, fninfo.GCRegs = lv.emit() + fninfo.GCArgs, fninfo.GCLocals = lv.emit() p := pp.Prog(obj.AFUNCDATA) Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps) @@ -1567,14 +1280,6 @@ func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap { p.To.Name = obj.NAME_EXTERN p.To.Sym = fninfo.GCLocals - if !go115ReduceLiveness { - p = pp.Prog(obj.AFUNCDATA) - Addrconst(&p.From, objabi.FUNCDATA_RegPointerMaps) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = fninfo.GCRegs - } - return lv.livenessMap } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 7388e4e3e8..67484904a9 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -6265,7 +6265,7 @@ func genssa(f *ssa.Func, pp *Progs) { // instruction. We won't use the actual liveness map on a // control instruction. Just mark it something that is // preemptible, unless this function is "all unsafe". - s.pp.nextLive = LivenessIndex{-1, -1, allUnsafe(f)} + s.pp.nextLive = LivenessIndex{-1, allUnsafe(f)} // Emit values in block thearch.SSAMarkMoves(&s, b) diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index c652e3adbb..8c8ff587ff 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -460,7 +460,6 @@ type FuncInfo struct { GCArgs *LSym GCLocals *LSym - GCRegs *LSym // Only if !go115ReduceLiveness StackObjects *LSym OpenCodedDeferInfo *LSym diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go index eb54c67f6a..2b096996f7 100644 --- a/src/cmd/internal/obj/plist.go +++ b/src/cmd/internal/obj/plist.go @@ -178,7 +178,7 @@ func (ctxt *Link) Globl(s *LSym, size int64, flag int) { // Prog generated. func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog { pcdata := ctxt.EmitEntryStackMap(s, p, newprog) - pcdata = ctxt.EmitEntryRegMap(s, pcdata, newprog) + pcdata = ctxt.EmitEntryUnsafePoint(s, pcdata, newprog) return pcdata } @@ -195,13 +195,13 @@ func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { return pcdata } -// Similar to EmitEntryLiveness, but just emit register map. -func (ctxt *Link) EmitEntryRegMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { +// Similar to EmitEntryLiveness, but just emit unsafe point map. +func (ctxt *Link) EmitEntryUnsafePoint(s *LSym, p *Prog, newprog ProgAlloc) *Prog { pcdata := Appendp(p, newprog) pcdata.Pos = s.Func().Text.Pos pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST - pcdata.From.Offset = objabi.PCDATA_RegMapIndex + pcdata.From.Offset = objabi.PCDATA_UnsafePoint pcdata.To.Type = TYPE_CONST pcdata.To.Offset = -1 @@ -216,9 +216,9 @@ func (ctxt *Link) StartUnsafePoint(p *Prog, newprog ProgAlloc) *Prog { pcdata := Appendp(p, newprog) pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST - pcdata.From.Offset = objabi.PCDATA_RegMapIndex + pcdata.From.Offset = objabi.PCDATA_UnsafePoint pcdata.To.Type = TYPE_CONST - pcdata.To.Offset = objabi.PCDATA_RegMapUnsafe + pcdata.To.Offset = objabi.PCDATA_UnsafePointUnsafe return pcdata } @@ -231,7 +231,7 @@ func (ctxt *Link) EndUnsafePoint(p *Prog, newprog ProgAlloc, oldval int64) *Prog pcdata := Appendp(p, newprog) pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST - pcdata.From.Offset = objabi.PCDATA_RegMapIndex + pcdata.From.Offset = objabi.PCDATA_UnsafePoint pcdata.To.Type = TYPE_CONST pcdata.To.Offset = oldval @@ -257,11 +257,11 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is prevPcdata := int64(-1) // entry PC data value prevRestart := int64(0) for p := prev.Link; p != nil; p, prev = p.Link, p { - if p.As == APCDATA && p.From.Offset == objabi.PCDATA_RegMapIndex { + if p.As == APCDATA && p.From.Offset == objabi.PCDATA_UnsafePoint { prevPcdata = p.To.Offset continue } - if prevPcdata == objabi.PCDATA_RegMapUnsafe { + if prevPcdata == objabi.PCDATA_UnsafePointUnsafe { continue // already unsafe } if isUnsafePoint(p) { @@ -288,7 +288,7 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is q := Appendp(prev, newprog) q.As = APCDATA q.From.Type = TYPE_CONST - q.From.Offset = objabi.PCDATA_RegMapIndex + q.From.Offset = objabi.PCDATA_UnsafePoint q.To.Type = TYPE_CONST q.To.Offset = val q.Pc = p.Pc @@ -305,7 +305,7 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is p = Appendp(p, newprog) p.As = APCDATA p.From.Type = TYPE_CONST - p.From.Offset = objabi.PCDATA_RegMapIndex + p.From.Offset = objabi.PCDATA_UnsafePoint p.To.Type = TYPE_CONST p.To.Offset = prevPcdata p.Pc = p.Link.Pc diff --git a/src/cmd/internal/objabi/funcdata.go b/src/cmd/internal/objabi/funcdata.go index c9480bf2f0..1c5e5e1c8c 100644 --- a/src/cmd/internal/objabi/funcdata.go +++ b/src/cmd/internal/objabi/funcdata.go @@ -11,14 +11,12 @@ package objabi // ../../../runtime/symtab.go. const ( - PCDATA_RegMapIndex = 0 // if !go115ReduceLiveness - PCDATA_UnsafePoint = 0 // if go115ReduceLiveness + PCDATA_UnsafePoint = 0 PCDATA_StackMapIndex = 1 PCDATA_InlTreeIndex = 2 FUNCDATA_ArgsPointerMaps = 0 FUNCDATA_LocalsPointerMaps = 1 - FUNCDATA_RegPointerMaps = 2 // if !go115ReduceLiveness FUNCDATA_StackObjects = 3 FUNCDATA_InlTree = 4 FUNCDATA_OpenCodedDeferInfo = 5 @@ -32,11 +30,6 @@ const ( // Special PCDATA values. const ( - // PCDATA_RegMapIndex values. - // - // Only if !go115ReduceLiveness. - PCDATA_RegMapUnsafe = PCDATA_UnsafePointUnsafe // Unsafe for async preemption - // PCDATA_UnsafePoint values. PCDATA_UnsafePointSafe = -1 // Safe for async preemption PCDATA_UnsafePointUnsafe = -2 // Unsafe for async preemption -- cgit v1.3 From 07e4f0fd4b0f215cdfa7d6ea50f3e6402762a1a9 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Thu, 29 Oct 2020 15:31:16 -0700 Subject: cmd/compile: fmt improvements for AST nodes and some comments on AST nodes Changed fmt.go to print out some extra information for various kinds of Nodes. This includes some extra (small) info in the %j (jconv) output, and some missing sections (such as Dcls and the body of a closure) in nodedump(). Also, added some extra doc comments for a few Node types in syntax.go Change-Id: I2ec7184e2abe0d5fbe3fb5a2506da7c7b06f2fb1 Reviewed-on: https://go-review.googlesource.com/c/go/+/266437 Run-TryBot: Dan Scales Trust: Dan Scales Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/fmt.go | 29 +++++++++++++++++++++++++++++ src/cmd/compile/internal/gc/syntax.go | 25 +++++++++++++++++-------- 2 files changed, 46 insertions(+), 8 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index d7ed1d2ff0..240b09bb6d 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -419,10 +419,17 @@ func (n *Node) format(s fmt.State, verb rune, mode fmtMode) { func (n *Node) jconv(s fmt.State, flag FmtFlag) { c := flag & FmtShort + // Useful to see which nodes in an AST printout are actually identical + fmt.Fprintf(s, " p(%p)", n) if c == 0 && n.Name != nil && n.Name.Vargen != 0 { fmt.Fprintf(s, " g(%d)", n.Name.Vargen) } + if c == 0 && n.Name != nil && n.Name.Defn != nil { + // Useful to see where Defn is set and what node it points to + fmt.Fprintf(s, " defn(%p)", n.Name.Defn) + } + if n.Pos.IsKnown() { pfx := "" switch n.Pos.IsStmt() { @@ -492,6 +499,15 @@ func (n *Node) jconv(s fmt.State, flag FmtFlag) { if n.Name.Assigned() { fmt.Fprint(s, " assigned") } + if n.Name.IsClosureVar() { + fmt.Fprint(s, " closurevar") + } + if n.Name.Captured() { + fmt.Fprint(s, " captured") + } + if n.Name.IsOutputParamHeapAddr() { + fmt.Fprint(s, " outputparamheapaddr") + } } if n.Bounded() { fmt.Fprint(s, " bounded") @@ -1710,6 +1726,9 @@ func (n *Node) nodedump(s fmt.State, flag FmtFlag, mode fmtMode) { } } + if n.Op == OCLOSURE && n.Func.Closure != nil && n.Func.Closure.Func.Nname.Sym != nil { + mode.Fprintf(s, " fnName %v", n.Func.Closure.Func.Nname.Sym) + } if n.Sym != nil && n.Op != ONAME { mode.Fprintf(s, " %v", n.Sym) } @@ -1725,6 +1744,16 @@ func (n *Node) nodedump(s fmt.State, flag FmtFlag, mode fmtMode) { if n.Right != nil { mode.Fprintf(s, "%v", n.Right) } + if n.Func != nil && n.Func.Closure != nil && n.Func.Closure.Nbody.Len() != 0 { + indent(s) + // The function associated with a closure + mode.Fprintf(s, "%v-clofunc%v", n.Op, n.Func.Closure) + } + if n.Func != nil && n.Func.Dcl != nil && len(n.Func.Dcl) != 0 { + indent(s) + // The dcls for a func or closure + mode.Fprintf(s, "%v-dcl%v", n.Op, asNodes(n.Func.Dcl)) + } if n.List.Len() != 0 { indent(s) mode.Fprintf(s, "%v-list%v", n.Op, n.List) diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 58de9b5e3f..649f7f4157 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -631,7 +631,7 @@ type Func struct { Ntype *Node // signature Top int // top context (ctxCallee, etc) Closure *Node // OCLOSURE <-> ODCLFUNC - Nname *Node + Nname *Node // The ONAME node associated with an ODCLFUNC (both have same Type) lsym *obj.LSym Inl *Inline @@ -773,7 +773,7 @@ const ( OCALLPART // Left.Right (method expression x.Method, not called) OCAP // cap(Left) OCLOSE // close(Left) - OCLOSURE // func Type { Body } (func literal) + OCLOSURE // func Type { Func.Closure.Nbody } (func literal) OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form) OMAPLIT // Type{List} (composite literal, Type is map) OSTRUCTLIT // Type{List} (composite literal, Type is struct) @@ -863,9 +863,14 @@ const ( OSIZEOF // unsafe.Sizeof(Left) // statements - OBLOCK // { List } (block of code) - OBREAK // break [Sym] - OCASE // case List: Nbody (List==nil means default) + OBLOCK // { List } (block of code) + OBREAK // break [Sym] + // OCASE: case List: Nbody (List==nil means default) + // For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL + // for nil), and, if a type-switch variable is specified, Rlist is an + // ONAME for the version of the type-switch variable with the specified + // type. + OCASE OCONTINUE // continue [Sym] ODEFER // defer Left (Left must be call) OEMPTY // no-op (empty statement) @@ -889,15 +894,19 @@ const ( ORETURN // return List OSELECT // select { List } (List is list of OCASE) OSWITCH // switch Ninit; Left { List } (List is a list of OCASE) - OTYPESW // Left = Right.(type) (appears as .Left of OSWITCH) + // OTYPESW: Left := Right.(type) (appears as .Left of OSWITCH) + // Left is nil if there is no type-switch variable + OTYPESW // types OTCHAN // chan int OTMAP // map[string]int OTSTRUCT // struct{} OTINTER // interface{} - OTFUNC // func() - OTARRAY // []int, [8]int, [N]int or [...]int + // OTFUNC: func() - Left is receiver field, List is list of param fields, Rlist is + // list of result fields. + OTFUNC + OTARRAY // []int, [8]int, [N]int or [...]int // misc ODDD // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}. -- cgit v1.3 From 063a91c0abef445154df1ba34ffb500eeccfe8bc Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 30 Oct 2020 12:58:28 -0700 Subject: cmd/compile: fix recognition of unnamed return variables In golang.org/cl/266199, I reused the existing code in inlining that recognizes anonymous variables. However, it turns out that code mistakenly recognizes anonymous return parameters as named when inlining a function from the same package. The issue is funcargs (which is only used for functions parsed from source) synthesizes ~r names for anonymous return parameters, but funcargs2 (which is only used for functions imported from export data) does not. This CL fixes the behavior so that anonymous return parameters are handled identically whether a function is inlined within the same package or across packages. It also adds a proper cross-package test case demonstrating #33160 is fixed in both cases. Change-Id: Iaa39a23f5666979a1f5ca6d09fc8c398e55b784c Reviewed-on: https://go-review.googlesource.com/c/go/+/266719 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le Reviewed-by: David Chase Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/inl.go | 2 +- src/cmd/compile/internal/logopt/logopt_test.go | 8 ++++---- test/fixedbugs/issue42284.dir/a.go | 9 ++++++++- test/fixedbugs/issue42284.dir/b.go | 5 ++++- 4 files changed, 17 insertions(+), 7 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 8a5c6d8666..253036fea6 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -1054,7 +1054,7 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { var retvars []*Node for i, t := range fn.Type.Results().Fields().Slice() { var m *Node - if n := asNode(t.Nname); n != nil && !n.isBlank() { + if n := asNode(t.Nname); n != nil && !n.isBlank() && !strings.HasPrefix(n.Sym.Name, "~r") { m = inlvar(n) m = typecheck(m, ctxExpr) inlvars[n] = m diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go index fca85c10fb..51bab49518 100644 --- a/src/cmd/compile/internal/logopt/logopt_test.go +++ b/src/cmd/compile/internal/logopt/logopt_test.go @@ -213,15 +213,15 @@ func s15a8(x *[15]int64) [15]int64 { `"relatedInformation":[`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+ - `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~r1 = y:"},`+ + `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~R0 = y:"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y.b (dot of pointer)"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+ - `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~r1 = \u003cN\u003e (assign-pair)"},`+ - `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r2 = ~r1:"},`+ - `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return (*int)(~r1) (return)"}]}`) + `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u003cN\u003e (assign-pair)"},`+ + `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r2 = ~R0:"},`+ + `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return (*int)(~R0) (return)"}]}`) }) } diff --git a/test/fixedbugs/issue42284.dir/a.go b/test/fixedbugs/issue42284.dir/a.go index e1271af32d..ffe9310be3 100644 --- a/test/fixedbugs/issue42284.dir/a.go +++ b/test/fixedbugs/issue42284.dir/a.go @@ -9,12 +9,19 @@ type T int func (T) M() {} // ERROR "can inline T.M" +func E() I { // ERROR "can inline E" + return T(0) // ERROR "T\(0\) escapes to heap" +} + func F(i I) I { // ERROR "can inline F" "leaking param: i to result ~r1 level=0" i = nil return i } -func g() { // ERROR "can inline g" +func g() { + h := E() // ERROR "inlining call to E" "T\(0\) does not escape" + h.M() // ERROR "devirtualizing h.M to T" + // BAD: T(0) could be stack allocated. i := F(T(0)) // ERROR "inlining call to F" "T\(0\) escapes to heap" diff --git a/test/fixedbugs/issue42284.dir/b.go b/test/fixedbugs/issue42284.dir/b.go index 3305166db0..652aa32122 100644 --- a/test/fixedbugs/issue42284.dir/b.go +++ b/test/fixedbugs/issue42284.dir/b.go @@ -6,7 +6,10 @@ package b import "./a" -func g() { // ERROR "can inline g" +func g() { + h := a.E() // ERROR "inlining call to a.E" "a.I\(a.T\(0\)\) does not escape" + h.M() // ERROR "devirtualizing h.M to a.T" + // BAD: T(0) could be stack allocated. i := a.F(a.T(0)) // ERROR "inlining call to a.F" "a.T\(0\) escapes to heap" -- cgit v1.3 From ac766e37182f36cd0a3247e44a4143d2d2132e42 Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Mon, 2 Nov 2020 16:58:38 +0000 Subject: runtime: make getMCache inlineable This change moves the responsibility of throwing if an mcache is not available to the caller, because the inlining cost of throw is set very high in the compiler. Even if it was reduced down to the cost of a usual function call, it would still be too expensive, so just move it out. This choice also makes sense in the context of #42339 since we're going to have to handle the case where we don't have an mcache to update stats in a few contexts anyhow. Also, add getMCache to the list of functions that should be inlined to prevent future regressions. getMCache is called on the allocation fast path and because its not inlined actually causes a significant regression (~10%) in some microbenchmarks. Fixes #42305. Change-Id: I64ac5e4f26b730bd4435ea1069a4a50f55411ced Reviewed-on: https://go-review.googlesource.com/c/go/+/267157 Trust: Michael Knyszek Run-TryBot: Michael Knyszek Reviewed-by: Michael Pratt TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/inl_test.go | 1 + src/runtime/malloc.go | 9 ++++++++- src/runtime/mcache.go | 7 ++----- src/runtime/mgcscavenge.go | 3 +++ src/runtime/mheap.go | 12 ++++++++++++ 5 files changed, 26 insertions(+), 6 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl_test.go b/src/cmd/compile/internal/gc/inl_test.go index afa6b98315..02735e50fb 100644 --- a/src/cmd/compile/internal/gc/inl_test.go +++ b/src/cmd/compile/internal/gc/inl_test.go @@ -51,6 +51,7 @@ func TestIntendedInlining(t *testing.T) { "funcPC", "getArgInfoFast", "getm", + "getMCache", "isDirectIface", "itabHashFunc", "noescape", diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index 4b798d129c..551acd0796 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -975,6 +975,9 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { shouldhelpgc := false dataSize := size c := getMCache() + if c == nil { + throw("mallocgc called without a P or outside bootstrapping") + } var span *mspan var x unsafe.Pointer noscan := typ == nil || typ.ptrdata == 0 @@ -1202,7 +1205,11 @@ func reflect_unsafe_NewArray(typ *_type, n int) unsafe.Pointer { } func profilealloc(mp *m, x unsafe.Pointer, size uintptr) { - getMCache().nextSample = nextSample() + c := getMCache() + if c == nil { + throw("profilealloc called without a P or outside bootstrapping") + } + c.nextSample = nextSample() mProf_Malloc(x, size) } diff --git a/src/runtime/mcache.go b/src/runtime/mcache.go index c9342a41c9..847a5dedf3 100644 --- a/src/runtime/mcache.go +++ b/src/runtime/mcache.go @@ -124,8 +124,8 @@ func freemcache(c *mcache) { // getMCache is a convenience function which tries to obtain an mcache. // -// Must be running with a P when called (so the caller must be in a -// non-preemptible state) or must be called during bootstrapping. +// Returns nil if we're not bootstrapping or we don't have a P. The caller's +// P must not change, so we must be in a non-preemptible state. func getMCache() *mcache { // Grab the mcache, since that's where stats live. pp := getg().m.p.ptr() @@ -136,9 +136,6 @@ func getMCache() *mcache { // mcache0 is cleared when bootstrapping is complete, // by procresize. c = mcache0 - if c == nil { - throw("getMCache called with no P or outside bootstrapping") - } } else { c = pp.mcache } diff --git a/src/runtime/mgcscavenge.go b/src/runtime/mgcscavenge.go index a242577bd9..ab4e28a60b 100644 --- a/src/runtime/mgcscavenge.go +++ b/src/runtime/mgcscavenge.go @@ -734,6 +734,9 @@ func (p *pageAlloc) scavengeRangeLocked(ci chunkIdx, base, npages uint) uintptr // Update consistent accounting too. c := getMCache() + if c == nil { + throw("scavengeRangeLocked called without a P or outside bootstrapping") + } stats := memstats.heapStats.acquire(c) atomic.Xaddint64(&stats.committed, -nbytes) atomic.Xaddint64(&stats.released, nbytes) diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 66a59cb999..6b29f34a82 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -1247,6 +1247,10 @@ HaveSpan: } // Update consistent stats. c := getMCache() + if c == nil { + // TODO(mknyszek): Remove this and handle this case to fix #42339. + throw("allocSpan called without P or outside bootstrapping") + } stats := memstats.heapStats.acquire(c) atomic.Xaddint64(&stats.committed, int64(scav)) atomic.Xaddint64(&stats.released, -int64(scav)) @@ -1341,6 +1345,10 @@ func (h *mheap) grow(npage uintptr) bool { // just add directly to heap_released. atomic.Xadd64(&memstats.heap_released, int64(asize)) c := getMCache() + if c == nil { + // TODO(mknyszek): Remove this and handle this case to fix #42339. + throw("grow called without P or outside bootstrapping") + } stats := memstats.heapStats.acquire(c) atomic.Xaddint64(&stats.released, int64(asize)) memstats.heapStats.release(c) @@ -1440,6 +1448,10 @@ func (h *mheap) freeSpanLocked(s *mspan, typ spanAllocType) { } // Update consistent stats. c := getMCache() + if c == nil { + // TODO(mknyszek): Remove this and handle this case to fix #42339. + throw("freeSpanLocked called without P or outside bootstrapping") + } stats := memstats.heapStats.acquire(c) switch typ { case spanAllocHeap: -- cgit v1.3 From 8eb846fd37eb7bded8a1cf6932be2c59069863e5 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Wed, 4 Nov 2020 00:26:15 +1100 Subject: cmd/compile,cmd/dist,cmd/go: enable pie buildmode for linux/riscv64 Enable pie as a buildmode for linux/riscv64, along with associated tests. Change-Id: I3fb0234d534dbeb96aa6cee6ae872304fbe02cf4 Reviewed-on: https://go-review.googlesource.com/c/go/+/267317 Trust: Joel Sing Reviewed-by: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/main.go | 2 +- src/cmd/dist/test.go | 2 +- src/cmd/go/go_test.go | 2 +- src/cmd/internal/sys/supported.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 8b94c7f71b..d1097e8236 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -130,7 +130,7 @@ func hidePanic() { // supportsDynlink reports whether or not the code generator for the given // architecture supports the -shared and -dynlink flags. func supportsDynlink(arch *sys.Arch) bool { - return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X) + return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) } // timing data for compiler phases diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index d12a52b1cc..f087aba2b9 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -1021,7 +1021,7 @@ func (t *tester) supportedBuildmode(mode string) bool { case "pie": switch pair { case "aix/ppc64", - "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", + "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", "android-amd64", "android-arm", "android-arm64", "android-386": return true case "darwin-amd64", "darwin-arm64": diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 7bbadd3974..91d5884036 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -2022,7 +2022,7 @@ func TestBuildmodePIE(t *testing.T) { platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "windows/386", "windows/amd64", "windows/arm": diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index 69d7591440..ef7c017bd4 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -86,7 +86,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "pie": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", -- cgit v1.3 From ecc3f5112ebaf23c4b1ac4c5eedfa406d82ecc9a Mon Sep 17 00:00:00 2001 From: Jonathan Swinney Date: Wed, 4 Nov 2020 16:18:23 +0000 Subject: cmd/compile: improve atomic swap intrinsics on arm64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARMv8.1 has added new instructions for atomic memory operations. This change builds on the previous change which added support for atomic add, 0a7ac93c27c9ade79fe0f66ae0bb81484c241ae5, to include similar support for atomic-compare-and-swap, atomic-swap, atomic-or, and atomic-and intrinsics. Since the new instructions are not guaranteed to be present, we guard their usages with a branch on a CPU feature. Peformance on an ARMv8.1 machine: name old time/op new time/op delta CompareAndSwap-16 37.9ns ±16% 24.1ns ± 4% -36.44% (p=0.000 n=10+9) CompareAndSwap64-16 38.6ns ±15% 24.1ns ± 3% -37.47% (p=0.000 n=10+10) name old time/op new time/op delta Swap-16 46.9ns ±32% 12.5ns ± 6% -73.40% (p=0.000 n=10+10) Swap64-16 53.4ns ± 1% 12.5ns ± 6% -76.56% (p=0.000 n=10+10) name old time/op new time/op delta Or8-16 8.81ns ± 0% 5.61ns ± 0% -36.32% (p=0.000 n=10+10) Or-16 7.21ns ± 0% 5.61ns ± 0% -22.19% (p=0.000 n=10+10) Or8Parallel-16 59.8ns ± 3% 12.5ns ± 2% -79.10% (p=0.000 n=10+10) OrParallel-16 51.7ns ± 3% 12.5ns ± 2% -75.84% (p=0.000 n=10+10) name old time/op new time/op delta And8-16 8.81ns ± 0% 5.61ns ± 0% -36.32% (p=0.000 n=10+10) And-16 7.21ns ± 0% 5.61ns ± 0% -22.19% (p=0.000 n=10+10) And8Parallel-16 59.1ns ± 6% 12.8ns ± 3% -78.33% (p=0.000 n=10+10) AndParallel-16 51.4ns ± 7% 12.8ns ± 3% -75.03% (p=0.000 n=10+10) Performance on an ARMv8.0 machine (no atomics instructions): name old time/op new time/op delta CompareAndSwap-16 61.3ns ± 0% 62.4ns ± 0% +1.70% (p=0.000 n=8+9) CompareAndSwap64-16 62.0ns ± 3% 61.3ns ± 2% ~ (p=0.093 n=10+10) name old time/op new time/op delta Swap-16 127ns ± 2% 131ns ± 2% +2.91% (p=0.001 n=10+10) Swap64-16 128ns ± 1% 131ns ± 2% +2.43% (p=0.001 n=10+10) name old time/op new time/op delta Or8-16 14.9ns ± 0% 15.3ns ± 0% +2.68% (p=0.000 n=10+10) Or-16 11.8ns ± 0% 12.3ns ± 0% +4.24% (p=0.000 n=10+10) Or8Parallel-16 137ns ± 1% 144ns ± 1% +4.97% (p=0.000 n=10+10) OrParallel-16 128ns ± 1% 136ns ± 1% +6.34% (p=0.000 n=10+10) name old time/op new time/op delta And8-16 14.9ns ± 0% 15.3ns ± 0% +2.68% (p=0.000 n=10+10) And-16 11.8ns ± 0% 12.3ns ± 0% +4.24% (p=0.000 n=10+10) And8Parallel-16 134ns ± 2% 141ns ± 1% +5.29% (p=0.000 n=10+10) AndParallel-16 125ns ± 2% 134ns ± 1% +7.10% (p=0.000 n=10+10) Fixes #39304 Change-Id: Idaca68701d4751650be6b4bedca3d57f51571712 Reviewed-on: https://go-review.googlesource.com/c/go/+/234217 Run-TryBot: Emmanuel Odeke TryBot-Result: Go Bot Reviewed-by: Cherry Zhang Trust: fannie zhang --- src/cmd/compile/internal/arm64/ssa.go | 125 ++++++++++++++++ src/cmd/compile/internal/gc/ssa.go | 109 ++++++++++---- src/cmd/compile/internal/ssa/gen/ARM64.rules | 13 +- src/cmd/compile/internal/ssa/gen/ARM64Ops.go | 35 +++++ src/cmd/compile/internal/ssa/gen/genericOps.go | 12 +- src/cmd/compile/internal/ssa/opGen.go | 200 +++++++++++++++++++++++++ src/cmd/compile/internal/ssa/rewriteARM64.go | 96 ++++++++++++ src/runtime/internal/atomic/bench_test.go | 51 +++++++ 8 files changed, 604 insertions(+), 37 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go index 5c695ef84c..22b28a9308 100644 --- a/src/cmd/compile/internal/arm64/ssa.go +++ b/src/cmd/compile/internal/arm64/ssa.go @@ -581,6 +581,24 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p2.From.Reg = arm64.REGTMP p2.To.Type = obj.TYPE_BRANCH gc.Patch(p2, p) + case ssa.OpARM64LoweredAtomicExchange64Variant, + ssa.OpARM64LoweredAtomicExchange32Variant: + swap := arm64.ASWPALD + if v.Op == ssa.OpARM64LoweredAtomicExchange32Variant { + swap = arm64.ASWPALW + } + r0 := v.Args[0].Reg() + r1 := v.Args[1].Reg() + out := v.Reg0() + + // SWPALD Rarg1, (Rarg0), Rout + p := s.Prog(swap) + p.From.Type = obj.TYPE_REG + p.From.Reg = r1 + p.To.Type = obj.TYPE_MEM + p.To.Reg = r0 + p.RegTo2 = out + case ssa.OpARM64LoweredAtomicAdd64, ssa.OpARM64LoweredAtomicAdd32: // LDAXR (Rarg0), Rout @@ -687,6 +705,56 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p5.To.Type = obj.TYPE_REG p5.To.Reg = out gc.Patch(p2, p5) + case ssa.OpARM64LoweredAtomicCas64Variant, + ssa.OpARM64LoweredAtomicCas32Variant: + // Rarg0: ptr + // Rarg1: old + // Rarg2: new + // MOV Rarg1, Rtmp + // CASAL Rtmp, (Rarg0), Rarg2 + // CMP Rarg1, Rtmp + // CSET EQ, Rout + cas := arm64.ACASALD + cmp := arm64.ACMP + mov := arm64.AMOVD + if v.Op == ssa.OpARM64LoweredAtomicCas32Variant { + cas = arm64.ACASALW + cmp = arm64.ACMPW + mov = arm64.AMOVW + } + r0 := v.Args[0].Reg() + r1 := v.Args[1].Reg() + r2 := v.Args[2].Reg() + out := v.Reg0() + + // MOV Rarg1, Rtmp + p := s.Prog(mov) + p.From.Type = obj.TYPE_REG + p.From.Reg = r1 + p.To.Type = obj.TYPE_REG + p.To.Reg = arm64.REGTMP + + // CASAL Rtmp, (Rarg0), Rarg2 + p1 := s.Prog(cas) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = arm64.REGTMP + p1.To.Type = obj.TYPE_MEM + p1.To.Reg = r0 + p1.RegTo2 = r2 + + // CMP Rarg1, Rtmp + p2 := s.Prog(cmp) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = r1 + p2.Reg = arm64.REGTMP + + // CSET EQ, Rout + p3 := s.Prog(arm64.ACSET) + p3.From.Type = obj.TYPE_REG + p3.From.Reg = arm64.COND_EQ + p3.To.Type = obj.TYPE_REG + p3.To.Reg = out + case ssa.OpARM64LoweredAtomicAnd8, ssa.OpARM64LoweredAtomicAnd32, ssa.OpARM64LoweredAtomicOr8, @@ -725,6 +793,63 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p3.From.Reg = arm64.REGTMP p3.To.Type = obj.TYPE_BRANCH gc.Patch(p3, p) + case ssa.OpARM64LoweredAtomicAnd8Variant, + ssa.OpARM64LoweredAtomicAnd32Variant: + atomic_clear := arm64.ALDCLRALW + if v.Op == ssa.OpARM64LoweredAtomicAnd8Variant { + atomic_clear = arm64.ALDCLRALB + } + r0 := v.Args[0].Reg() + r1 := v.Args[1].Reg() + out := v.Reg0() + + // MNV Rarg1 Rtemp + p := s.Prog(arm64.AMVN) + p.From.Type = obj.TYPE_REG + p.From.Reg = r1 + p.To.Type = obj.TYPE_REG + p.To.Reg = arm64.REGTMP + + // LDCLRALW Rtemp, (Rarg0), Rout + p1 := s.Prog(atomic_clear) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = arm64.REGTMP + p1.To.Type = obj.TYPE_MEM + p1.To.Reg = r0 + p1.RegTo2 = out + + // AND Rarg1, Rout + p2 := s.Prog(arm64.AAND) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = r1 + p2.To.Type = obj.TYPE_REG + p2.To.Reg = out + + case ssa.OpARM64LoweredAtomicOr8Variant, + ssa.OpARM64LoweredAtomicOr32Variant: + atomic_or := arm64.ALDORALW + if v.Op == ssa.OpARM64LoweredAtomicOr8Variant { + atomic_or = arm64.ALDORALB + } + r0 := v.Args[0].Reg() + r1 := v.Args[1].Reg() + out := v.Reg0() + + // LDORALW Rarg1, (Rarg0), Rout + p := s.Prog(atomic_or) + p.From.Type = obj.TYPE_REG + p.From.Reg = r1 + p.To.Type = obj.TYPE_MEM + p.To.Reg = r0 + p.RegTo2 = out + + // ORR Rarg1, Rout + p2 := s.Prog(arm64.AORR) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = r1 + p2.To.Type = obj.TYPE_REG + p2.To.Reg = out + case ssa.OpARM64MOVBreg, ssa.OpARM64MOVBUreg, ssa.OpARM64MOVHreg, diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 67484904a9..0b38e70cd2 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3458,31 +3458,19 @@ func init() { s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v) }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Xchg64", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { v := s.newValue3(ssa.OpAtomicExchange64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem()) s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v) }, - sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) - - addF("runtime/internal/atomic", "Xadd", - func(s *state, n *Node, args []*ssa.Value) *ssa.Value { - v := s.newValue3(ssa.OpAtomicAdd32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], args[1], s.mem()) - s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) - return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v) - }, - sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) - addF("runtime/internal/atomic", "Xadd64", - func(s *state, n *Node, args []*ssa.Value) *ssa.Value { - v := s.newValue3(ssa.OpAtomicAdd64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem()) - s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) - return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v) - }, sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) - makeXaddARM64 := func(op0 ssa.Op, op1 ssa.Op, ty types.EType) func(s *state, n *Node, args []*ssa.Value) *ssa.Value { + type atomicOpEmitter func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) + + makeAtomicGuardedIntrinsicARM64 := func(op0, op1 ssa.Op, typ, rtyp types.EType, emit atomicOpEmitter) intrinsicBuilder { + return func(s *state, n *Node, args []*ssa.Value) *ssa.Value { // Target Atomic feature is identified by dynamic detection addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), arm64HasATOMICS, s.sb) @@ -3495,33 +3483,60 @@ func init() { bEnd := s.f.NewBlock(ssa.BlockPlain) b.AddEdgeTo(bTrue) b.AddEdgeTo(bFalse) - b.Likely = ssa.BranchUnlikely // most machines don't have Atomics nowadays + b.Likely = ssa.BranchLikely // We have atomic instructions - use it directly. s.startBlock(bTrue) - v0 := s.newValue3(op1, types.NewTuple(types.Types[ty], types.TypeMem), args[0], args[1], s.mem()) - s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v0) - s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[ty], v0) + emit(s, n, args, op1, typ) s.endBlock().AddEdgeTo(bEnd) // Use original instruction sequence. s.startBlock(bFalse) - v1 := s.newValue3(op0, types.NewTuple(types.Types[ty], types.TypeMem), args[0], args[1], s.mem()) - s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v1) - s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[ty], v1) + emit(s, n, args, op0, typ) s.endBlock().AddEdgeTo(bEnd) // Merge results. s.startBlock(bEnd) - return s.variable(n, types.Types[ty]) + if rtyp == TNIL { + return nil + } else { + return s.variable(n, types.Types[rtyp]) + } } } + atomicXchgXaddEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) { + v := s.newValue3(op, types.NewTuple(types.Types[typ], types.TypeMem), args[0], args[1], s.mem()) + s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) + s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v) + } + addF("runtime/internal/atomic", "Xchg", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange32, ssa.OpAtomicExchange32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64), + sys.ARM64) + addF("runtime/internal/atomic", "Xchg64", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange64, ssa.OpAtomicExchange64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64), + sys.ARM64) + addF("runtime/internal/atomic", "Xadd", - makeXaddARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, TUINT32), + func(s *state, n *Node, args []*ssa.Value) *ssa.Value { + v := s.newValue3(ssa.OpAtomicAdd32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], args[1], s.mem()) + s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) + return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v) + }, + sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + addF("runtime/internal/atomic", "Xadd64", + func(s *state, n *Node, args []*ssa.Value) *ssa.Value { + v := s.newValue3(ssa.OpAtomicAdd64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem()) + s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) + return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v) + }, + sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + + addF("runtime/internal/atomic", "Xadd", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64), sys.ARM64) addF("runtime/internal/atomic", "Xadd64", - makeXaddARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, TUINT64), + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64), sys.ARM64) addF("runtime/internal/atomic", "Cas", @@ -3530,14 +3545,14 @@ func init() { s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v) }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Cas64", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { v := s.newValue4(ssa.OpAtomicCompareAndSwap64, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem()) s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v) }, - sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "CasRel", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem()) @@ -3546,18 +3561,31 @@ func init() { }, sys.PPC64) + atomicCasEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) { + v := s.newValue4(op, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem()) + s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) + s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v) + } + + addF("runtime/internal/atomic", "Cas", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap32, ssa.OpAtomicCompareAndSwap32Variant, TUINT32, TBOOL, atomicCasEmitterARM64), + sys.ARM64) + addF("runtime/internal/atomic", "Cas64", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap64, ssa.OpAtomicCompareAndSwap64Variant, TUINT64, TBOOL, atomicCasEmitterARM64), + sys.ARM64) + addF("runtime/internal/atomic", "And8", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd8, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) + sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X) addF("runtime/internal/atomic", "And", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) + sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X) addF("runtime/internal/atomic", "Or8", func(s *state, n *Node, args []*ssa.Value) *ssa.Value { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem()) @@ -3569,7 +3597,24 @@ func init() { s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) + sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X) + + atomicAndOrEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) { + s.vars[&memVar] = s.newValue3(op, types.TypeMem, args[0], args[1], s.mem()) + } + + addF("runtime/internal/atomic", "And8", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd8, ssa.OpAtomicAnd8Variant, TNIL, TNIL, atomicAndOrEmitterARM64), + sys.ARM64) + addF("runtime/internal/atomic", "And", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd32, ssa.OpAtomicAnd32Variant, TNIL, TNIL, atomicAndOrEmitterARM64), + sys.ARM64) + addF("runtime/internal/atomic", "Or8", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr8, ssa.OpAtomicOr8Variant, TNIL, TNIL, atomicAndOrEmitterARM64), + sys.ARM64) + addF("runtime/internal/atomic", "Or", + makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr32, ssa.OpAtomicOr32Variant, TNIL, TNIL, atomicAndOrEmitterARM64), + sys.ARM64) alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...) alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index 7e014fe0a8..9edc0c94b0 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -543,17 +543,24 @@ (AtomicStore64 ...) => (STLR ...) (AtomicStorePtrNoWB ...) => (STLR ...) -(AtomicExchange(32|64) ...) => (LoweredAtomicExchange(32|64) ...) -(AtomicAdd(32|64) ...) => (LoweredAtomicAdd(32|64) ...) +(AtomicExchange(32|64) ...) => (LoweredAtomicExchange(32|64) ...) +(AtomicAdd(32|64) ...) => (LoweredAtomicAdd(32|64) ...) (AtomicCompareAndSwap(32|64) ...) => (LoweredAtomicCas(32|64) ...) +(AtomicAdd(32|64)Variant ...) => (LoweredAtomicAdd(32|64)Variant ...) +(AtomicExchange(32|64)Variant ...) => (LoweredAtomicExchange(32|64)Variant ...) +(AtomicCompareAndSwap(32|64)Variant ...) => (LoweredAtomicCas(32|64)Variant ...) + // Currently the updated value is not used, but we need a register to temporarily hold it. (AtomicAnd8 ptr val mem) => (Select1 (LoweredAtomicAnd8 ptr val mem)) (AtomicAnd32 ptr val mem) => (Select1 (LoweredAtomicAnd32 ptr val mem)) (AtomicOr8 ptr val mem) => (Select1 (LoweredAtomicOr8 ptr val mem)) (AtomicOr32 ptr val mem) => (Select1 (LoweredAtomicOr32 ptr val mem)) -(AtomicAdd(32|64)Variant ...) => (LoweredAtomicAdd(32|64)Variant ...) +(AtomicAnd8Variant ptr val mem) => (Select1 (LoweredAtomicAnd8Variant ptr val mem)) +(AtomicAnd32Variant ptr val mem) => (Select1 (LoweredAtomicAnd32Variant ptr val mem)) +(AtomicOr8Variant ptr val mem) => (Select1 (LoweredAtomicOr8Variant ptr val mem)) +(AtomicOr32Variant ptr val mem) => (Select1 (LoweredAtomicOr32Variant ptr val mem)) // Write barrier. (WB ...) => (LoweredWB ...) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index fe9edbf933..87db2b7c9d 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -621,6 +621,12 @@ func init() { {name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + // atomic exchange variant. + // store arg1 to arg0. arg2=mem. returns . auxint must be zero. + // SWPALD Rarg1, (Rarg0), Rout + {name: "LoweredAtomicExchange64Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true}, + {name: "LoweredAtomicExchange32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true}, + // atomic add. // *arg0 += arg1. arg2=mem. returns . auxint must be zero. // LDAXR (Rarg0), Rout @@ -654,6 +660,21 @@ func init() { {name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + // atomic compare and swap variant. + // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero. + // if *arg0 == arg1 { + // *arg0 = arg2 + // return (true, memory) + // } else { + // return (false, memory) + // } + // MOV Rarg1, Rtmp + // CASAL Rtmp, (Rarg0), Rarg2 + // CMP Rarg1, Rtmp + // CSET EQ, Rout + {name: "LoweredAtomicCas64Variant", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicCas32Variant", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + // atomic and/or. // *arg0 &= (|=) arg1. arg2=mem. returns . auxint must be zero. // LDAXR (Rarg0), Rout @@ -665,6 +686,20 @@ func init() { {name: "LoweredAtomicOr8", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, {name: "LoweredAtomicOr32", argLength: 3, reg: gpxchg, resultNotInArgs: true, asm: "ORR", typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + // atomic and/or variant. + // *arg0 &= (|=) arg1. arg2=mem. returns . auxint must be zero. + // AND: + // MNV Rarg1, Rtemp + // LDANDALB Rtemp, (Rarg0), Rout + // AND Rarg1, Rout + // OR: + // LDORALB Rarg1, (Rarg0), Rout + // ORR Rarg1, Rout + {name: "LoweredAtomicAnd8Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicAnd32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicOr8Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, typ: "(UInt8,Mem)", faultOnNilArg0: true, hasSideEffects: true}, + {name: "LoweredAtomicOr32Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, typ: "(UInt32,Mem)", faultOnNilArg0: true, hasSideEffects: true}, + // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier // It saves all GP registers if necessary, // but clobbers R30 (LR) because it's a call. diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index db8d7ba0cf..9565199d51 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -574,8 +574,16 @@ var genericOps = []opData{ // These variants have the same semantics as above atomic operations. // But they are used for generating more efficient code on certain modern machines, with run-time CPU feature detection. // Currently, they are used on ARM64 only. - {name: "AtomicAdd32Variant", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory. - {name: "AtomicAdd64Variant", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory. + {name: "AtomicAdd32Variant", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory. + {name: "AtomicAdd64Variant", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory. + {name: "AtomicExchange32Variant", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory. + {name: "AtomicExchange64Variant", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory. + {name: "AtomicCompareAndSwap32Variant", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true if store happens and new memory. + {name: "AtomicCompareAndSwap64Variant", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true if store happens and new memory. + {name: "AtomicAnd8Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 &= arg1. arg2=memory. Returns memory. + {name: "AtomicAnd32Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 &= arg1. arg2=memory. Returns memory. + {name: "AtomicOr8Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory. + {name: "AtomicOr32Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory. // Clobber experiment op {name: "Clobber", argLength: 0, typ: "Void", aux: "SymOff", symEffect: "None"}, // write an invalid pointer value to the given pointer slot of a stack variable diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 25c1df12ee..c0b663cd8f 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1581,16 +1581,24 @@ const ( OpARM64STLRW OpARM64LoweredAtomicExchange64 OpARM64LoweredAtomicExchange32 + OpARM64LoweredAtomicExchange64Variant + OpARM64LoweredAtomicExchange32Variant OpARM64LoweredAtomicAdd64 OpARM64LoweredAtomicAdd32 OpARM64LoweredAtomicAdd64Variant OpARM64LoweredAtomicAdd32Variant OpARM64LoweredAtomicCas64 OpARM64LoweredAtomicCas32 + OpARM64LoweredAtomicCas64Variant + OpARM64LoweredAtomicCas32Variant OpARM64LoweredAtomicAnd8 OpARM64LoweredAtomicAnd32 OpARM64LoweredAtomicOr8 OpARM64LoweredAtomicOr32 + OpARM64LoweredAtomicAnd8Variant + OpARM64LoweredAtomicAnd32Variant + OpARM64LoweredAtomicOr8Variant + OpARM64LoweredAtomicOr32Variant OpARM64LoweredWB OpARM64LoweredPanicBoundsA OpARM64LoweredPanicBoundsB @@ -2881,6 +2889,14 @@ const ( OpAtomicOr32 OpAtomicAdd32Variant OpAtomicAdd64Variant + OpAtomicExchange32Variant + OpAtomicExchange64Variant + OpAtomicCompareAndSwap32Variant + OpAtomicCompareAndSwap64Variant + OpAtomicAnd8Variant + OpAtomicAnd32Variant + OpAtomicOr8Variant + OpAtomicOr32Variant OpClobber ) @@ -20994,6 +21010,38 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LoweredAtomicExchange64Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "LoweredAtomicExchange32Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "LoweredAtomicAdd64", argLen: 3, @@ -21098,6 +21146,44 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LoweredAtomicCas64Variant", + argLen: 4, + resultNotInArgs: true, + clobberFlags: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "LoweredAtomicCas32Variant", + argLen: 4, + resultNotInArgs: true, + clobberFlags: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "LoweredAtomicAnd8", argLen: 3, @@ -21170,6 +21256,72 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LoweredAtomicAnd8Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "LoweredAtomicAnd32Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "LoweredAtomicOr8Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "LoweredAtomicOr32Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "LoweredWB", auxType: auxSym, @@ -35874,6 +36026,54 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, generic: true, }, + { + name: "AtomicExchange32Variant", + argLen: 3, + hasSideEffects: true, + generic: true, + }, + { + name: "AtomicExchange64Variant", + argLen: 3, + hasSideEffects: true, + generic: true, + }, + { + name: "AtomicCompareAndSwap32Variant", + argLen: 4, + hasSideEffects: true, + generic: true, + }, + { + name: "AtomicCompareAndSwap64Variant", + argLen: 4, + hasSideEffects: true, + generic: true, + }, + { + name: "AtomicAnd8Variant", + argLen: 3, + hasSideEffects: true, + generic: true, + }, + { + name: "AtomicAnd32Variant", + argLen: 3, + hasSideEffects: true, + generic: true, + }, + { + name: "AtomicOr8Variant", + argLen: 3, + hasSideEffects: true, + generic: true, + }, + { + name: "AtomicOr32Variant", + argLen: 3, + hasSideEffects: true, + generic: true, + }, { name: "Clobber", auxType: auxSymOff, diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 9a5e976dea..353696bf39 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -426,20 +426,36 @@ func rewriteValueARM64(v *Value) bool { return true case OpAtomicAnd32: return rewriteValueARM64_OpAtomicAnd32(v) + case OpAtomicAnd32Variant: + return rewriteValueARM64_OpAtomicAnd32Variant(v) case OpAtomicAnd8: return rewriteValueARM64_OpAtomicAnd8(v) + case OpAtomicAnd8Variant: + return rewriteValueARM64_OpAtomicAnd8Variant(v) case OpAtomicCompareAndSwap32: v.Op = OpARM64LoweredAtomicCas32 return true + case OpAtomicCompareAndSwap32Variant: + v.Op = OpARM64LoweredAtomicCas32Variant + return true case OpAtomicCompareAndSwap64: v.Op = OpARM64LoweredAtomicCas64 return true + case OpAtomicCompareAndSwap64Variant: + v.Op = OpARM64LoweredAtomicCas64Variant + return true case OpAtomicExchange32: v.Op = OpARM64LoweredAtomicExchange32 return true + case OpAtomicExchange32Variant: + v.Op = OpARM64LoweredAtomicExchange32Variant + return true case OpAtomicExchange64: v.Op = OpARM64LoweredAtomicExchange64 return true + case OpAtomicExchange64Variant: + v.Op = OpARM64LoweredAtomicExchange64Variant + return true case OpAtomicLoad32: v.Op = OpARM64LDARW return true @@ -454,8 +470,12 @@ func rewriteValueARM64(v *Value) bool { return true case OpAtomicOr32: return rewriteValueARM64_OpAtomicOr32(v) + case OpAtomicOr32Variant: + return rewriteValueARM64_OpAtomicOr32Variant(v) case OpAtomicOr8: return rewriteValueARM64_OpAtomicOr8(v) + case OpAtomicOr8Variant: + return rewriteValueARM64_OpAtomicOr8Variant(v) case OpAtomicStore32: v.Op = OpARM64STLRW return true @@ -21363,6 +21383,25 @@ func rewriteValueARM64_OpAtomicAnd32(v *Value) bool { return true } } +func rewriteValueARM64_OpAtomicAnd32Variant(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (AtomicAnd32Variant ptr val mem) + // result: (Select1 (LoweredAtomicAnd32Variant ptr val mem)) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicAnd32Variant, types.NewTuple(typ.UInt32, types.TypeMem)) + v0.AddArg3(ptr, val, mem) + v.AddArg(v0) + return true + } +} func rewriteValueARM64_OpAtomicAnd8(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] @@ -21382,6 +21421,25 @@ func rewriteValueARM64_OpAtomicAnd8(v *Value) bool { return true } } +func rewriteValueARM64_OpAtomicAnd8Variant(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (AtomicAnd8Variant ptr val mem) + // result: (Select1 (LoweredAtomicAnd8Variant ptr val mem)) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicAnd8Variant, types.NewTuple(typ.UInt8, types.TypeMem)) + v0.AddArg3(ptr, val, mem) + v.AddArg(v0) + return true + } +} func rewriteValueARM64_OpAtomicOr32(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] @@ -21401,6 +21459,25 @@ func rewriteValueARM64_OpAtomicOr32(v *Value) bool { return true } } +func rewriteValueARM64_OpAtomicOr32Variant(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (AtomicOr32Variant ptr val mem) + // result: (Select1 (LoweredAtomicOr32Variant ptr val mem)) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicOr32Variant, types.NewTuple(typ.UInt32, types.TypeMem)) + v0.AddArg3(ptr, val, mem) + v.AddArg(v0) + return true + } +} func rewriteValueARM64_OpAtomicOr8(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] @@ -21420,6 +21497,25 @@ func rewriteValueARM64_OpAtomicOr8(v *Value) bool { return true } } +func rewriteValueARM64_OpAtomicOr8Variant(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (AtomicOr8Variant ptr val mem) + // result: (Select1 (LoweredAtomicOr8Variant ptr val mem)) + for { + ptr := v_0 + val := v_1 + mem := v_2 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpARM64LoweredAtomicOr8Variant, types.NewTuple(typ.UInt8, types.TypeMem)) + v0.AddArg3(ptr, val, mem) + v.AddArg(v0) + return true + } +} func rewriteValueARM64_OpAvg64u(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] diff --git a/src/runtime/internal/atomic/bench_test.go b/src/runtime/internal/atomic/bench_test.go index 434aa6d434..2476c06c52 100644 --- a/src/runtime/internal/atomic/bench_test.go +++ b/src/runtime/internal/atomic/bench_test.go @@ -142,3 +142,54 @@ func BenchmarkXadd64(b *testing.B) { } }) } + +func BenchmarkCas(b *testing.B) { + var x uint32 + x = 1 + ptr := &x + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + atomic.Cas(ptr, 1, 0) + atomic.Cas(ptr, 0, 1) + } + }) +} + +func BenchmarkCas64(b *testing.B) { + var x uint64 + x = 1 + ptr := &x + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + atomic.Cas64(ptr, 1, 0) + atomic.Cas64(ptr, 0, 1) + } + }) +} +func BenchmarkXchg(b *testing.B) { + var x uint32 + x = 1 + ptr := &x + b.RunParallel(func(pb *testing.PB) { + var y uint32 + y = 1 + for pb.Next() { + y = atomic.Xchg(ptr, y) + y += 1 + } + }) +} + +func BenchmarkXchg64(b *testing.B) { + var x uint64 + x = 1 + ptr := &x + b.RunParallel(func(pb *testing.PB) { + var y uint64 + y = 1 + for pb.Next() { + y = atomic.Xchg64(ptr, y) + y += 1 + } + }) +} -- cgit v1.3 From 5736eb0013cb8c9b67432c98b08f68e9f370810c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 29 Oct 2020 01:13:16 -0700 Subject: cmd/compile: support inlining of type switches This CL adds support for inlining type switches, including exporting and importing them. Type switches are represented mostly the same as expression switches. However, if the type switch guard includes a short variable declaration, then there are two differences: (1) there's an ONONAME (in the OTYPESW's Left) to represent the overall pseudo declaration; and (2) there's an ONAME (in each OCASE's Rlist) to represent the per-case variables. For simplicity, this CL simply writes out each variable separately using iimport/iiexport's normal Vargen mechanism for disambiguating identically named variables within a function. This could be improved somewhat, but inlinable type switches are probably too uncommon to merit the complexity. While here, remove "case OCASE" from typecheck1. We only type check "case" clauses as part of a "select" or "switch" statement, never as standalone statements. Fixes #37837 Change-Id: I8f42f6c9afdd821d6202af4a6bf1dbcbba0ef424 Reviewed-on: https://go-review.googlesource.com/c/go/+/266203 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Keith Randall Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/iexport.go | 40 ++++++++++++++++++++++++++----- src/cmd/compile/internal/gc/iimport.go | 41 +++++++++++++++++++++++++------- src/cmd/compile/internal/gc/inl.go | 4 ---- src/cmd/compile/internal/gc/typecheck.go | 5 ---- test/fixedbugs/issue37837.dir/a.go | 33 +++++++++++++++++++++++++ test/fixedbugs/issue37837.dir/b.go | 32 +++++++++++++++++++++++++ test/fixedbugs/issue37837.go | 7 ++++++ test/inline.go | 3 +-- 8 files changed, 140 insertions(+), 25 deletions(-) create mode 100644 test/fixedbugs/issue37837.dir/a.go create mode 100644 test/fixedbugs/issue37837.dir/b.go create mode 100644 test/fixedbugs/issue37837.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index 9bc1f64600..1f53d8ca7d 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -1138,13 +1138,10 @@ func (w *exportWriter) stmt(n *Node) { w.pos(n.Pos) w.stmtList(n.Ninit) w.exprsOrNil(n.Left, nil) - w.stmtList(n.List) + w.caseList(n) - case OCASE: - w.op(OCASE) - w.pos(n.Pos) - w.stmtList(n.List) - w.stmtList(n.Nbody) + // case OCASE: + // handled by caseList case OFALL: w.op(OFALL) @@ -1168,6 +1165,24 @@ func (w *exportWriter) stmt(n *Node) { } } +func (w *exportWriter) caseList(sw *Node) { + namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil + + cases := sw.List.Slice() + w.uint64(uint64(len(cases))) + for _, cas := range cases { + if cas.Op != OCASE { + Fatalf("expected OCASE, got %v", cas) + } + w.pos(cas.Pos) + w.stmtList(cas.List) + if namedTypeSwitch { + w.localName(cas.Rlist.First()) + } + w.stmtList(cas.Nbody) + } +} + func (w *exportWriter) exprList(list Nodes) { for _, n := range list.Slice() { w.expr(n) @@ -1232,6 +1247,19 @@ func (w *exportWriter) expr(n *Node) { w.op(OTYPE) w.typ(n.Type) + case OTYPESW: + w.op(OTYPESW) + w.pos(n.Pos) + var s *types.Sym + if n.Left != nil { + if n.Left.Op != ONONAME { + Fatalf("expected ONONAME, got %v", n.Left) + } + s = n.Left.Sym + } + w.localIdent(s, 0) // declared pseudo-variable, if any + w.exprsOrNil(n.Right, nil) + // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: // should have been resolved by typechecking - handled by default case diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index 7f2b05f288..c0114d0e53 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -784,6 +784,28 @@ func (r *importReader) stmtList() []*Node { return list } +func (r *importReader) caseList(sw *Node) []*Node { + namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil + + cases := make([]*Node, r.uint64()) + for i := range cases { + cas := nodl(r.pos(), OCASE, nil, nil) + cas.List.Set(r.stmtList()) + if namedTypeSwitch { + // Note: per-case variables will have distinct, dotted + // names after import. That's okay: swt.go only needs + // Sym for diagnostics anyway. + caseVar := newnamel(cas.Pos, r.ident()) + declare(caseVar, dclcontext) + cas.Rlist.Set1(caseVar) + caseVar.Name.Defn = sw.Left + } + cas.Nbody.Set(r.stmtList()) + cases[i] = cas + } + return cases +} + func (r *importReader) exprList() []*Node { var list []*Node for { @@ -831,6 +853,14 @@ func (r *importReader) node() *Node { case OTYPE: return typenod(r.typ()) + case OTYPESW: + n := nodl(r.pos(), OTYPESW, nil, nil) + if s := r.ident(); s != nil { + n.Left = npos(n.Pos, newnoname(s)) + } + n.Right, _ = r.exprsOrNil() + return n + // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: // unreachable - should have been resolved by typechecking @@ -1025,16 +1055,11 @@ func (r *importReader) node() *Node { n := nodl(r.pos(), op, nil, nil) n.Ninit.Set(r.stmtList()) n.Left, _ = r.exprsOrNil() - n.List.Set(r.stmtList()) + n.List.Set(r.caseList(n)) return n - case OCASE: - n := nodl(r.pos(), OCASE, nil, nil) - n.List.Set(r.exprList()) - // TODO(gri) eventually we must declare variables for type switch - // statements (type switch statements are not yet exported) - n.Nbody.Set(r.stmtList()) - return n + // case OCASE: + // handled by caseList case OFALL: n := nodl(r.pos(), OFALL, nil, nil) diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 253036fea6..139572f652 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -392,13 +392,9 @@ func (v *hairyVisitor) visit(n *Node) bool { v.reason = "call to recover" return true - case OCALLPART: - // OCALLPART is inlineable, but no extra cost to the budget - case OCLOSURE, ORANGE, OSELECT, - OTYPESW, OGO, ODEFER, ODCLTYPE, // can't print yet diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 8ebeaf1330..cbba5ff79c 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2065,11 +2065,6 @@ func typecheck1(n *Node, top int) (res *Node) { n.Type = nil return n - case OCASE: - ok |= ctxStmt - typecheckslice(n.List.Slice(), ctxExpr) - typecheckslice(n.Nbody.Slice(), ctxStmt) - case ODCLFUNC: ok |= ctxStmt typecheckfunc(n) diff --git a/test/fixedbugs/issue37837.dir/a.go b/test/fixedbugs/issue37837.dir/a.go new file mode 100644 index 0000000000..49d830ffbc --- /dev/null +++ b/test/fixedbugs/issue37837.dir/a.go @@ -0,0 +1,33 @@ +// Copyright 2020 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 a + +func F(i interface{}) int { // ERROR "can inline F" "i does not escape" + switch i.(type) { + case nil: + return 0 + case int: + return 1 + case float64: + return 2 + default: + return 3 + } +} + +func G(i interface{}) interface{} { // ERROR "can inline G" "leaking param: i" + switch i := i.(type) { + case nil: // ERROR "moved to heap: i" + return &i + case int: // ERROR "moved to heap: i" + return &i + case float64: // ERROR "moved to heap: i" + return &i + case string, []byte: // ERROR "moved to heap: i" + return &i + default: // ERROR "moved to heap: i" + return &i + } +} diff --git a/test/fixedbugs/issue37837.dir/b.go b/test/fixedbugs/issue37837.dir/b.go new file mode 100644 index 0000000000..461f5c7a55 --- /dev/null +++ b/test/fixedbugs/issue37837.dir/b.go @@ -0,0 +1,32 @@ +// Copyright 2020 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 main + +import "./a" + +func main() { + // Test that inlined type switches without short variable + // declarations work correctly. + check(0, a.F(nil)) // ERROR "inlining call to a.F" + check(1, a.F(0)) // ERROR "inlining call to a.F" "does not escape" + check(2, a.F(0.0)) // ERROR "inlining call to a.F" "does not escape" + check(3, a.F("")) // ERROR "inlining call to a.F" "does not escape" + + // Test that inlined type switches with short variable + // declarations work correctly. + _ = a.G(nil).(*interface{}) // ERROR "inlining call to a.G" + _ = a.G(1).(*int) // ERROR "inlining call to a.G" "does not escape" + _ = a.G(2.0).(*float64) // ERROR "inlining call to a.G" "does not escape" + _ = (*a.G("").(*interface{})).(string) // ERROR "inlining call to a.G" "does not escape" + _ = (*a.G(([]byte)(nil)).(*interface{})).([]byte) // ERROR "inlining call to a.G" "does not escape" + _ = (*a.G(true).(*interface{})).(bool) // ERROR "inlining call to a.G" "does not escape" +} + +//go:noinline +func check(want, got int) { + if want != got { + println("want", want, "but got", got) + } +} diff --git a/test/fixedbugs/issue37837.go b/test/fixedbugs/issue37837.go new file mode 100644 index 0000000000..2e8abc5f05 --- /dev/null +++ b/test/fixedbugs/issue37837.go @@ -0,0 +1,7 @@ +// errorcheckandrundir -0 -m + +// Copyright 2020 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 ignored diff --git a/test/inline.go b/test/inline.go index 470414f883..d754f06e03 100644 --- a/test/inline.go +++ b/test/inline.go @@ -152,8 +152,7 @@ func switchBreak(x, y int) int { return n } -// can't currently inline functions with a type switch -func switchType(x interface{}) int { // ERROR "x does not escape" +func switchType(x interface{}) int { // ERROR "can inline switchType" "x does not escape" switch x.(type) { case int: return x.(int) -- cgit v1.3 From a444458112e4059e73c9a5a2bc5867f53bf9faa2 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 6 Nov 2020 22:46:18 -0500 Subject: cmd/compile: make sure linkname'd symbol is non-package When a variable symbol is both imported (possibly through inlining) and linkname'd, make sure its LSym is marked as non-package for symbol indexing in the object file, so it is resolved by name and dedup'd with the original definition. Fixes #42401. Change-Id: I8e90c0418c6f46a048945c5fdc06c022b77ed68d Reviewed-on: https://go-review.googlesource.com/c/go/+/268178 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/compile/internal/gc/gsubr.go | 6 ++++++ test/fixedbugs/issue42401.dir/a.go | 11 +++++++++++ test/fixedbugs/issue42401.dir/b.go | 24 ++++++++++++++++++++++++ test/fixedbugs/issue42401.go | 10 ++++++++++ 4 files changed, 51 insertions(+) create mode 100644 test/fixedbugs/issue42401.dir/a.go create mode 100644 test/fixedbugs/issue42401.dir/b.go create mode 100644 test/fixedbugs/issue42401.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index 864ada1d3c..d599a383e7 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -302,6 +302,12 @@ func ggloblnod(nam *Node) { if nam.Name.LibfuzzerExtraCounter() { s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER } + if nam.Sym.Linkname != "" { + // Make sure linkname'd symbol is non-package. When a symbol is + // both imported and linkname'd, s.Pkg may not set to "_" in + // types.Sym.Linksym because LSym already exists. Set it here. + s.Pkg = "_" + } } func ggloblsym(s *obj.LSym, width int32, flags int16) { diff --git a/test/fixedbugs/issue42401.dir/a.go b/test/fixedbugs/issue42401.dir/a.go new file mode 100644 index 0000000000..75f8e7f91f --- /dev/null +++ b/test/fixedbugs/issue42401.dir/a.go @@ -0,0 +1,11 @@ +// Copyright 2020 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 a + +var s string + +func init() { s = "a" } + +func Get() string { return s } diff --git a/test/fixedbugs/issue42401.dir/b.go b/test/fixedbugs/issue42401.dir/b.go new file mode 100644 index 0000000000..a834f4efe8 --- /dev/null +++ b/test/fixedbugs/issue42401.dir/b.go @@ -0,0 +1,24 @@ +// Copyright 2020 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 main + +import ( + "./a" + _ "unsafe" +) + +//go:linkname s a.s +var s string + +func main() { + if a.Get() != "a" { + panic("FAIL") + } + + s = "b" + if a.Get() != "b" { + panic("FAIL") + } +} diff --git a/test/fixedbugs/issue42401.go b/test/fixedbugs/issue42401.go new file mode 100644 index 0000000000..794d5b01b5 --- /dev/null +++ b/test/fixedbugs/issue42401.go @@ -0,0 +1,10 @@ +// rundir + +// Copyright 2020 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. + +// Issue 42401: linkname doesn't work correctly when a variable symbol +// is both imported (possibly through inlining) and linkname'd. + +package ignored -- cgit v1.3 From 86954d5246339231dc0c45f5547c37a1c3650264 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 13 Nov 2020 14:42:45 -0500 Subject: cmd/compile: mark plugin-exported types as used in interface Plugin exports symbols as interfaces. Mark their types as used in interfaces, so their methods will be kept alive by the linker. Fixes #42579. Change-Id: If1b5aacc21510c20c25f88bb131bca61db6f1d56 Reviewed-on: https://go-review.googlesource.com/c/go/+/269819 Trust: Cherry Zhang Reviewed-by: Than McIntosh --- misc/cgo/testplugin/plugin_test.go | 14 ++++++++++++++ misc/cgo/testplugin/testdata/method/main.go | 26 ++++++++++++++++++++++++++ misc/cgo/testplugin/testdata/method/plugin.go | 13 +++++++++++++ src/cmd/compile/internal/gc/reflect.go | 6 +++++- 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 misc/cgo/testplugin/testdata/method/main.go create mode 100644 misc/cgo/testplugin/testdata/method/plugin.go (limited to 'src/cmd/compile/internal/gc') diff --git a/misc/cgo/testplugin/plugin_test.go b/misc/cgo/testplugin/plugin_test.go index 2875271c03..9055dbda04 100644 --- a/misc/cgo/testplugin/plugin_test.go +++ b/misc/cgo/testplugin/plugin_test.go @@ -196,3 +196,17 @@ func TestIssue25756(t *testing.T) { }) } } + +func TestMethod(t *testing.T) { + // Exported symbol's method must be live. + goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go") + goCmd(t, "build", "-o", "method.exe", "./method/main.go") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + cmd := exec.CommandContext(ctx, "./method.exe") + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out) + } +} diff --git a/misc/cgo/testplugin/testdata/method/main.go b/misc/cgo/testplugin/testdata/method/main.go new file mode 100644 index 0000000000..5e9189b450 --- /dev/null +++ b/misc/cgo/testplugin/testdata/method/main.go @@ -0,0 +1,26 @@ +// Copyright 2020 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. + +// Issue 42579: methods of symbols exported from plugin must be live. + +package main + +import ( + "plugin" + "reflect" +) + +func main() { + p, err := plugin.Open("plugin.so") + if err != nil { + panic(err) + } + + x, err := p.Lookup("X") + if err != nil { + panic(err) + } + + reflect.ValueOf(x).Elem().MethodByName("M").Call(nil) +} diff --git a/misc/cgo/testplugin/testdata/method/plugin.go b/misc/cgo/testplugin/testdata/method/plugin.go new file mode 100644 index 0000000000..240edd3bc4 --- /dev/null +++ b/misc/cgo/testplugin/testdata/method/plugin.go @@ -0,0 +1,13 @@ +// Copyright 2020 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 main + +func main() {} + +type T int + +func (T) M() { println("M") } + +var X T diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 21429af782..9401eba7a5 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -1591,8 +1591,12 @@ func dumptabs() { // typ typeOff // pointer to symbol // } nsym := dname(p.s.Name, "", nil, true) + tsym := dtypesym(p.t) ot = dsymptrOff(s, ot, nsym) - ot = dsymptrOff(s, ot, dtypesym(p.t)) + ot = dsymptrOff(s, ot, tsym) + // Plugin exports symbols as interfaces. Mark their types + // as UsedInIface. + tsym.Set(obj.AttrUsedInIface, true) } ggloblsym(s, int32(ot), int16(obj.RODATA)) -- cgit v1.3 From 4f63e0a1f88695eec9fc3116d6833b447bcd94a7 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Thu, 12 Nov 2020 09:33:34 -0800 Subject: cmd/compile: update comments only for Node types and some functions Improve the comments in syntax.go on Node structs and constants. Also, updated a few function header comments. Change-Id: I3e6e4a3c5678fc0b4e18844507b3460303ce1240 Reviewed-on: https://go-review.googlesource.com/c/go/+/269538 Reviewed-by: Matthew Dempsky Trust: Dan Scales --- src/cmd/compile/internal/gc/closure.go | 4 ++ src/cmd/compile/internal/gc/dcl.go | 2 + src/cmd/compile/internal/gc/inl.go | 23 ++++++------ src/cmd/compile/internal/gc/syntax.go | 68 ++++++++++++++++++++++------------ 4 files changed, 63 insertions(+), 34 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index 902d2e34a3..bd350f696e 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -71,6 +71,10 @@ func (p *noder) funcLit(expr *syntax.FuncLit) *Node { return clo } +// typecheckclosure typechecks an OCLOSURE node. It also creates the named +// function associated with the closure. +// TODO: This creation of the named function should probably really be done in a +// separate pass from type-checking. func typecheckclosure(clo *Node, top int) { xfunc := clo.Func.Closure // Set current associated iota value, so iota can be used inside diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index b8ca0d2e03..6e90eb4d65 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -257,6 +257,8 @@ func symfield(s *types.Sym, typ *types.Type) *Node { // oldname returns the Node that declares symbol s in the current scope. // If no such Node currently exists, an ONONAME Node is returned instead. +// Automatically creates a new closure variable if the referenced symbol was +// declared in a different (containing) function. func oldname(s *types.Sym) *Node { n := asNode(s.Def) if n == nil { diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 139572f652..d49a09458c 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -94,10 +94,11 @@ func typecheckinl(fn *Node) { typecheckslice(fn.Func.Inl.Body, ctxStmt) Curfn = savefn - // During typechecking, declarations are added to - // Curfn.Func.Dcl. Move them to Inl.Dcl for consistency with - // how local functions behave. (Append because typecheckinl - // may be called multiple times.) + // During expandInline (which imports fn.Func.Inl.Body), + // declarations are added to fn.Func.Dcl by funcHdr(). Move them + // to fn.Func.Inl.Dcl for consistency with how local functions + // behave. (Append because typecheckinl may be called multiple + // times.) fn.Func.Inl.Dcl = append(fn.Func.Inl.Dcl, fn.Func.Dcl...) fn.Func.Dcl = nil @@ -448,9 +449,9 @@ func (v *hairyVisitor) visit(n *Node) bool { v.visitList(n.Ninit) || v.visitList(n.Nbody) } -// Inlcopy and inlcopylist recursively copy the body of a function. -// Any name-like node of non-local class is marked for re-export by adding it to -// the exportlist. +// inlcopylist (together with inlcopy) recursively copies a list of nodes, except +// that it keeps the same ONAME, OTYPE, and OLITERAL nodes. It is used for copying +// the body and dcls of an inlineable function. func inlcopylist(ll []*Node) []*Node { s := make([]*Node, 0, len(ll)) for _, n := range ll { @@ -889,10 +890,10 @@ func inlParam(t *types.Field, as *Node, inlvars map[*Node]*Node) *Node { var inlgen int -// If n is a call, and fn is a function with an inlinable body, -// return an OINLCALL. -// On return ninit has the parameter assignments, the nbody is the -// inlined function body and list, rlist contain the input, output +// If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a +// function with an inlinable body, return an OINLCALL node that can replace n. +// The returned node's Ninit has the parameter assignments, the Nbody is the +// inlined function body, and (List, Rlist) contain the (input, output) // parameters. // The result of mkinlcall MUST be assigned back to n, e.g. // n.Left = mkinlcall(n.Left, fn, isddd) diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 649f7f4157..43358333b8 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -344,14 +344,22 @@ func (n *Node) CanBeAnSSASym() { // Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL). type Name struct { - Pack *Node // real package for import . names - Pkg *types.Pkg // pkg for OPACK nodes - Defn *Node // initializing assignment - Curfn *Node // function for local variables - Param *Param // additional fields for ONAME, OTYPE - Decldepth int32 // declaration loop depth, increased for every loop or label - Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one. - flags bitset16 + Pack *Node // real package for import . names + Pkg *types.Pkg // pkg for OPACK nodes + // For a local variable (not param) or extern, the initializing assignment (OAS or OAS2). + // For a closure var, the ONAME node of the outer captured variable + Defn *Node + // The ODCLFUNC node (for a static function/method or a closure) in which + // local variable or param is declared. + Curfn *Node + Param *Param // additional fields for ONAME, OTYPE + Decldepth int32 // declaration loop depth, increased for every loop or label + // Unique number for ONAME nodes within a function. Function outputs + // (results) are numbered starting at one, followed by function inputs + // (parameters), and then local variables. Vargen is used to distinguish + // local variables/params with the same name. + Vargen int32 + flags bitset16 } const ( @@ -608,10 +616,16 @@ func (p *Param) SetEmbedFiles(list []string) { // Func holds Node fields used only with function-like nodes. type Func struct { Shortname *types.Sym - Enter Nodes // for example, allocate and initialize memory for escaping parameters - Exit Nodes - Cvars Nodes // closure params - Dcl []*Node // autodcl for this func/closure + // Extra entry code for the function. For example, allocate and initialize + // memory for escaping parameters. However, just for OCLOSURE, Enter is a + // list of ONAME nodes of captured variables + Enter Nodes + Exit Nodes + // ONAME nodes for closure params, each should have closurevar set + Cvars Nodes + // ONAME nodes for all params/locals for this func/closure, does NOT + // include closurevars until transformclosure runs. + Dcl []*Node // Parents records the parent scope of each scope within a // function. The root scope (0) has no parent, so the i'th @@ -630,7 +644,7 @@ type Func struct { DebugInfo *ssa.FuncDebug Ntype *Node // signature Top int // top context (ctxCallee, etc) - Closure *Node // OCLOSURE <-> ODCLFUNC + Closure *Node // OCLOSURE <-> ODCLFUNC (see header comment above) Nname *Node // The ONAME node associated with an ODCLFUNC (both have same Type) lsym *obj.LSym @@ -680,6 +694,8 @@ const ( funcWrapper // is method wrapper funcNeedctxt // function uses context register (has closure variables) funcReflectMethod // function calls reflect.Type.Method or MethodByName + // true if closure inside a function; false if a simple function or a + // closure in a global variable initialization funcIsHiddenClosure funcHasDefer // contains a defer statement funcNilCheckDisabled // disable nil checks when compiling this function @@ -731,8 +747,10 @@ const ( OXXX Op = iota // names - ONAME // var or func name - ONONAME // unnamed arg or return value: f(int, string) (int, error) { etc } + ONAME // var or func name + // Unnamed arg or return value: f(int, string) (int, error) { etc } + // Also used for a qualified package identifier that hasn't been resolved yet. + ONONAME OTYPE // type name OPACK // import OLITERAL // literal @@ -752,14 +770,18 @@ const ( OSTR2BYTES // Type(Left) (Type is []byte, Left is a string) OSTR2BYTESTMP // Type(Left) (Type is []byte, Left is a string, ephemeral) OSTR2RUNES // Type(Left) (Type is []rune, Left is a string) - OAS // Left = Right or (if Colas=true) Left := Right - OAS2 // List = Rlist (x, y, z = a, b, c) - OAS2DOTTYPE // List = Right (x, ok = I.(int)) - OAS2FUNC // List = Right (x, y = f()) - OAS2MAPR // List = Right (x, ok = m["foo"]) - OAS2RECV // List = Right (x, ok = <-c) - OASOP // Left Etype= Right (x += y) - OCALL // Left(List) (function call, method call or type conversion) + // Left = Right or (if Colas=true) Left := Right + // If Colas, then Ninit includes a DCL node for Left. + OAS + // List = Rlist (x, y, z = a, b, c) or (if Colas=true) List := Rlist + // If Colas, then Ninit includes DCL nodes for List + OAS2 + OAS2DOTTYPE // List = Right (x, ok = I.(int)) + OAS2FUNC // List = Right (x, y = f()) + OAS2MAPR // List = Right (x, ok = m["foo"]) + OAS2RECV // List = Right (x, ok = <-c) + OASOP // Left Etype= Right (x += y) + OCALL // Left(List) (function call, method call or type conversion) // OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure. // Prior to walk, they are: Left(List), where List is all regular arguments. -- cgit v1.3 From 05082c90d5b35935ccc27acb070e00702df91a3a Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Mon, 17 Aug 2020 14:17:07 -0400 Subject: cmd/compile: clean up buggy DWARF inlined info PC ranges Repair the code that generates PC ranges for DWARF inlined routine instances to insure that if II Y is a child of II X within the inline tree, X's ranges include the ranges from Y. This is similar to what we're already doing for DWARF scopes. Updates #33188. Change-Id: I9bb552777fcd1ae93dc01872707667ad092b1dd9 Reviewed-on: https://go-review.googlesource.com/c/go/+/248724 Run-TryBot: Than McIntosh TryBot-Result: Go Bot Reviewed-by: Cherry Zhang Reviewed-by: David Chase Trust: Than McIntosh --- src/cmd/compile/internal/gc/dwinl.go | 92 ++++++++++++++++++++++++++++++++++++ src/cmd/internal/dwarf/dwarf.go | 31 +++++++----- 2 files changed, 110 insertions(+), 13 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/dwinl.go b/src/cmd/compile/internal/gc/dwinl.go index 5120fa1166..bb5ae61cbb 100644 --- a/src/cmd/compile/internal/gc/dwinl.go +++ b/src/cmd/compile/internal/gc/dwinl.go @@ -8,6 +8,7 @@ import ( "cmd/internal/dwarf" "cmd/internal/obj" "cmd/internal/src" + "fmt" "strings" ) @@ -170,12 +171,32 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls { addRange(inlcalls.Calls, start, fnsym.Size, curii, imap) } + // Issue 33188: if II foo is a child of II bar, then ensure that + // bar's ranges include the ranges of foo (the loop above will produce + // disjoint ranges). + for k, c := range inlcalls.Calls { + if c.Root { + unifyCallRanges(inlcalls, k) + } + } + // Debugging if Debug_gendwarfinl != 0 { dumpInlCalls(inlcalls) dumpInlVars(dwVars) } + // Perform a consistency check on inlined routine PC ranges + // produced by unifyCallRanges above. In particular, complain in + // cases where you have A -> B -> C (e.g. C is inlined into B, and + // B is inlined into A) and the ranges for B are not enclosed + // within the ranges for A, or C within B. + for k, c := range inlcalls.Calls { + if c.Root { + checkInlCall(fnsym.Name, inlcalls, fnsym.Size, k, -1) + } + } + return inlcalls } @@ -355,3 +376,74 @@ func dumpInlVars(dwvars []*dwarf.Var) { Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ) } } + +func rangesContains(par []dwarf.Range, rng dwarf.Range) (bool, string) { + for _, r := range par { + if rng.Start >= r.Start && rng.End <= r.End { + return true, "" + } + } + msg := fmt.Sprintf("range [%d,%d) not contained in {", rng.Start, rng.End) + for _, r := range par { + msg += fmt.Sprintf(" [%d,%d)", r.Start, r.End) + } + msg += " }" + return false, msg +} + +func rangesContainsAll(parent, child []dwarf.Range) (bool, string) { + for _, r := range child { + c, m := rangesContains(parent, r) + if !c { + return false, m + } + } + return true, "" +} + +// checkInlCall verifies that the PC ranges for inline info 'idx' are +// enclosed/contained within the ranges of its parent inline (or if +// this is a root/toplevel inline, checks that the ranges fall within +// the extent of the top level function). A panic is issued if a +// malformed range is found. +func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx, parentIdx int) { + + // Callee + ic := inlCalls.Calls[idx] + callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name + calleeRanges := ic.Ranges + + // Caller + caller := funcName + parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}} + if parentIdx != -1 { + pic := inlCalls.Calls[parentIdx] + caller = Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name + parentRanges = pic.Ranges + } + + // Callee ranges contained in caller ranges? + c, m := rangesContainsAll(parentRanges, calleeRanges) + if !c { + Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m) + } + + // Now visit kids + for _, k := range ic.Children { + checkInlCall(funcName, inlCalls, funcSize, k, idx) + } +} + +// unifyCallRanges ensures that the ranges for a given inline +// transitively include all of the ranges for its child inlines. +func unifyCallRanges(inlcalls dwarf.InlCalls, idx int) { + ic := &inlcalls.Calls[idx] + for _, childIdx := range ic.Children { + // First make sure child ranges are unified. + unifyCallRanges(inlcalls, childIdx) + + // Then merge child ranges into ranges for this inline. + cic := inlcalls.Calls[childIdx] + ic.Ranges = dwarf.MergeRanges(ic.Ranges, cic.Ranges) + } +} diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index b2fd5262bb..e1a70ef853 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -101,26 +101,26 @@ func EnableLogging(doit bool) { logDwarf = doit } -// UnifyRanges merges the list of ranges of c into the list of ranges of s -func (s *Scope) UnifyRanges(c *Scope) { - out := make([]Range, 0, len(s.Ranges)+len(c.Ranges)) - +// MergeRanges creates a new range list by merging the ranges from +// its two arguments, then returns the new list. +func MergeRanges(in1, in2 []Range) []Range { + out := make([]Range, 0, len(in1)+len(in2)) i, j := 0, 0 for { var cur Range - if i < len(s.Ranges) && j < len(c.Ranges) { - if s.Ranges[i].Start < c.Ranges[j].Start { - cur = s.Ranges[i] + if i < len(in2) && j < len(in1) { + if in2[i].Start < in1[j].Start { + cur = in2[i] i++ } else { - cur = c.Ranges[j] + cur = in1[j] j++ } - } else if i < len(s.Ranges) { - cur = s.Ranges[i] + } else if i < len(in2) { + cur = in2[i] i++ - } else if j < len(c.Ranges) { - cur = c.Ranges[j] + } else if j < len(in1) { + cur = in1[j] j++ } else { break @@ -133,7 +133,12 @@ func (s *Scope) UnifyRanges(c *Scope) { } } - s.Ranges = out + return out +} + +// UnifyRanges merges the ranges from 'c' into the list of ranges for 's'. +func (s *Scope) UnifyRanges(c *Scope) { + s.Ranges = MergeRanges(s.Ranges, c.Ranges) } // AppendRange adds r to s, if r is non-empty. -- cgit v1.3 From 5b0ec1a6ac0e644c89940e0fe5f79863ad2eafaa Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 18 Nov 2020 12:08:59 -0800 Subject: cmd/compile: fix panic in field tracking logic Within the frontend, we generally don't guarantee uniqueness of anonymous types. For example, each struct type literal gets represented by its own types.Type instance. However, the field tracking code was using the struct type as a map key. This broke in golang.org/cl/256457, because that CL started changing the inlined parameter variables from using the types.Type of the declared parameter to that of the call site argument. These are always identical types (e.g., types.Identical would report true), but they can be different pointer values, causing the map lookup to fail. The easiest fix is to simply get rid of the map and instead use Node.Opt for tracking the types.Field. To mitigate against more latent field tracking failures (e.g., if any other code were to start trying to use Opt on ODOT/ODOTPTR fields), we store this field unconditionally. I also expect having the types.Field will be useful to other frontend code in the future. Finally, to make it easier to test field tracking without having to run make.bash with GOEXPERIMENT=fieldtrack, this commit adds a -d=fieldtrack flag as an alternative way to enable field tracking within the compiler. See also #42681. Fixes #42686. Change-Id: I6923d206d5e2cab1e6798cba36cae96c1eeaea55 Reviewed-on: https://go-review.googlesource.com/c/go/+/271217 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Cherry Zhang Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/main.go | 1 + src/cmd/compile/internal/gc/typecheck.go | 15 ++------------- src/cmd/compile/internal/gc/walk.go | 5 ++++- test/fixedbugs/issue42686.go | 11 +++++++++++ 4 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 test/fixedbugs/issue42686.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index d1097e8236..f0a913275a 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -89,6 +89,7 @@ var debugtab = []struct { {"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl}, {"softfloat", "force compiler to emit soft-float code", &Debug_softfloat}, {"defer", "print information about defer compilation", &Debug_defer}, + {"fieldtrack", "enable fieldtracking", &objabi.Fieldtrack_enabled}, } const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=] diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index cbba5ff79c..c0b05035f0 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -6,7 +6,6 @@ package gc import ( "cmd/compile/internal/types" - "cmd/internal/objabi" "fmt" "strings" ) @@ -2442,15 +2441,6 @@ func derefall(t *types.Type) *types.Type { return t } -type typeSymKey struct { - t *types.Type - s *types.Sym -} - -// dotField maps (*types.Type, *types.Sym) pairs to the corresponding struct field (*types.Type with Etype==TFIELD). -// It is a cache for use during usefield in walk.go, only enabled when field tracking. -var dotField = map[typeSymKey]*types.Field{} - func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field { s := n.Sym @@ -2481,9 +2471,6 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field { } n.Xoffset = f1.Offset n.Type = f1.Type - if objabi.Fieldtrack_enabled > 0 { - dotField[typeSymKey{t.Orig, s}] = f1 - } if t.IsInterface() { if n.Left.Type.IsPtr() { n.Left = nod(ODEREF, n.Left, nil) // implicitstar @@ -2492,6 +2479,8 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field { } n.Op = ODOTINTER + } else { + n.SetOpt(f1) } return f1 diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 82898c8167..a7b6e7fcb3 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -3734,10 +3734,13 @@ func usefield(n *Node) { if t.IsPtr() { t = t.Elem() } - field := dotField[typeSymKey{t.Orig, n.Sym}] + field := n.Opt().(*types.Field) if field == nil { Fatalf("usefield %v %v without paramfld", n.Left.Type, n.Sym) } + if field.Sym != n.Sym || field.Offset != n.Xoffset { + Fatalf("field inconsistency: %v,%v != %v,%v", field.Sym, field.Offset, n.Sym, n.Xoffset) + } if !strings.Contains(field.Note, "go:\"track\"") { return } diff --git a/test/fixedbugs/issue42686.go b/test/fixedbugs/issue42686.go new file mode 100644 index 0000000000..962bdd35cb --- /dev/null +++ b/test/fixedbugs/issue42686.go @@ -0,0 +1,11 @@ +// compile -d=fieldtrack + +// Copyright 2020 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 p + +func a(x struct{ f int }) { _ = x.f } + +func b() { a(struct{ f int }{}) } -- cgit v1.3 From 35693d037f9d1c30d6de1fafd08e8c923a415ab8 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 18 Nov 2020 12:50:46 -0800 Subject: cmd/compile: fix miscompilation during inlining When inlining a function call expression, it's possible that the function callee subexpression has side effects that need to be preserved. This used to not be an issue, because inlining wouldn't recognize these as inlinable anyway. But golang.org/cl/266199 extended the inlining logic to recognize more cases, but did not notice that the actual inlining code was discarding side effects. Issue identified by danscales@. Fixes #42703. Change-Id: I95f8fc076b6ca4e9362e80ec26dad9d87a5bc44a Reviewed-on: https://go-review.googlesource.com/c/go/+/271219 Reviewed-by: Dan Scales Trust: Dan Scales Trust: Matthew Dempsky --- src/cmd/compile/internal/gc/inl.go | 15 +++++++++++++++ test/fixedbugs/issue42703.go | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/fixedbugs/issue42703.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index d49a09458c..419056985f 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -963,6 +963,21 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { ninit := n.Ninit + // For normal function calls, the function callee expression + // may contain side effects (e.g., added by addinit during + // inlconv2expr or inlconv2list). Make sure to preserve these, + // if necessary (#42703). + if n.Op == OCALLFUNC { + callee := n.Left + for callee.Op == OCONVNOP { + ninit.AppendNodes(&callee.Ninit) + callee = callee.Left + } + if callee.Op != ONAME && callee.Op != OCLOSURE { + Fatalf("unexpected callee expression: %v", callee) + } + } + // Make temp names to use instead of the originals. inlvars := make(map[*Node]*Node) diff --git a/test/fixedbugs/issue42703.go b/test/fixedbugs/issue42703.go new file mode 100644 index 0000000000..15f7a915e6 --- /dev/null +++ b/test/fixedbugs/issue42703.go @@ -0,0 +1,19 @@ +// run + +// Copyright 2020 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 main + +var ok [2]bool + +func main() { + f()() + if !ok[0] || !ok[1] { + panic("FAIL") + } +} + +func f() func() { ok[0] = true; return g } +func g() { ok[1] = true } -- cgit v1.3 From c31540364c2bc3bdb7800cfc344d85c2c9df3893 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Wed, 18 Nov 2020 16:42:31 -0800 Subject: cmd/compile: flag "-d=dumpptrs" to print Node ptrs in Dump output The printing of the ptr values can mean that two dump outputs can't easily be compared for the identical structure, so adding the "-d=dumpptrs" option to make printing of Node pointer values be an option. Change-Id: I0e92b02f069e9de2e6fa036a7841645d13cdd7a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/271339 Trust: Dan Scales Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/fmt.go | 8 +++++--- src/cmd/compile/internal/gc/main.go | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 240b09bb6d..f92f5d0e88 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -419,13 +419,15 @@ func (n *Node) format(s fmt.State, verb rune, mode fmtMode) { func (n *Node) jconv(s fmt.State, flag FmtFlag) { c := flag & FmtShort - // Useful to see which nodes in an AST printout are actually identical - fmt.Fprintf(s, " p(%p)", n) + // Useful to see which nodes in a Node Dump/dumplist are actually identical + if Debug_dumpptrs != 0 { + fmt.Fprintf(s, " p(%p)", n) + } if c == 0 && n.Name != nil && n.Name.Vargen != 0 { fmt.Fprintf(s, " g(%d)", n.Name.Vargen) } - if c == 0 && n.Name != nil && n.Name.Defn != nil { + if Debug_dumpptrs != 0 && c == 0 && n.Name != nil && n.Name.Defn != nil { // Useful to see where Defn is set and what node it points to fmt.Fprintf(s, " defn(%p)", n.Name.Defn) } diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index f0a913275a..a6963a3d66 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -46,6 +46,7 @@ var ( Debug_closure int Debug_compilelater int debug_dclstack int + Debug_dumpptrs int Debug_libfuzzer int Debug_panic int Debug_slice int @@ -75,6 +76,7 @@ var debugtab = []struct { {"compilelater", "compile functions as late as possible", &Debug_compilelater}, {"disablenil", "disable nil checks", &disable_checknil}, {"dclstack", "run internal dclstack check", &debug_dclstack}, + {"dumpptrs", "show Node pointer values in Dump/dumplist output", &Debug_dumpptrs}, {"gcprog", "print dump of GC programs", &Debug_gcprog}, {"libfuzzer", "coverage instrumentation for libfuzzer", &Debug_libfuzzer}, {"nil", "print information about nil checks", &Debug_checknil}, -- cgit v1.3 From 07cba70d5794747044ce5f2f3b34de139193e2a5 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Mon, 16 Nov 2020 21:28:26 -0500 Subject: cmd/compile, runtime: use __msan_memmove for moving data, split msanread to fields Currently, for data moving, we generate an msanread of the source, followed by an msanwrite of the destination. msanread checks the source is initialized. This has a problem: if the source is an aggregate type containing alignment paddings, the padding bytes may not be thought as initialized by MSAN. If we copy the aggregate type by value, if it counts as a read, MSAN reports using uninitialized data. This CL changes it to use __msan_memmove for data copying, which tells MSAN to propagate initialized-ness but not check for it. Caveat: technically __msan_memmove is not a public API of MSAN, although the C compiler does generate direct calls to it. Also, when instrumenting a load of a struct, split the instrumentation to fields, instead of generating an msanread for the whole struct. This skips padding bytes, which may not be considered initialized in MSAN. Fixes #42820. Change-Id: Id861c8bbfd94cfcccefcc58eaf9e4eb43b4d85c6 Reviewed-on: https://go-review.googlesource.com/c/go/+/270859 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Austin Clements --- misc/cgo/testsanitizers/msan_test.go | 1 + misc/cgo/testsanitizers/testdata/msan7.go | 38 ++++++++++++ src/cmd/compile/internal/gc/builtin.go | 42 ++++++------- src/cmd/compile/internal/gc/builtin/runtime.go | 1 + src/cmd/compile/internal/gc/go.go | 1 + src/cmd/compile/internal/gc/ssa.go | 83 ++++++++++++++++++++++---- src/runtime/msan.go | 6 +- src/runtime/msan_amd64.s | 9 +++ src/runtime/msan_arm64.s | 10 ++++ 9 files changed, 158 insertions(+), 33 deletions(-) create mode 100644 misc/cgo/testsanitizers/testdata/msan7.go (limited to 'src/cmd/compile/internal/gc') diff --git a/misc/cgo/testsanitizers/msan_test.go b/misc/cgo/testsanitizers/msan_test.go index 88b90d3d70..5e2f9759ba 100644 --- a/misc/cgo/testsanitizers/msan_test.go +++ b/misc/cgo/testsanitizers/msan_test.go @@ -28,6 +28,7 @@ func TestMSAN(t *testing.T) { {src: "msan4.go"}, {src: "msan5.go"}, {src: "msan6.go"}, + {src: "msan7.go"}, {src: "msan_fail.go", wantErr: true}, } for _, tc := range cases { diff --git a/misc/cgo/testsanitizers/testdata/msan7.go b/misc/cgo/testsanitizers/testdata/msan7.go new file mode 100644 index 0000000000..2f29fd21b2 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan7.go @@ -0,0 +1,38 @@ +// Copyright 2020 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 main + +// Test passing C struct to exported Go function. + +/* +#include +#include + +// T is a C struct with alignment padding after b. +// The padding bytes are not considered initialized by MSAN. +// It is big enough to be passed on stack in C ABI (and least +// on AMD64). +typedef struct { char b; uintptr_t x, y; } T; + +extern void F(T); + +// Use weak as a hack to permit defining a function even though we use export. +void CF(int x) __attribute__ ((weak)); +void CF(int x) { + T *t = malloc(sizeof(T)); + t->b = (char)x; + t->x = x; + t->y = x; + F(*t); +} +*/ +import "C" + +//export F +func F(t C.T) { println(t.b, t.x, t.y) } + +func main() { + C.CF(C.int(0)) +} diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go index fd95b657b2..e04f23e229 100644 --- a/src/cmd/compile/internal/gc/builtin.go +++ b/src/cmd/compile/internal/gc/builtin.go @@ -184,16 +184,17 @@ var runtimeDecls = [...]struct { {"racewriterange", funcTag, 121}, {"msanread", funcTag, 121}, {"msanwrite", funcTag, 121}, - {"checkptrAlignment", funcTag, 122}, - {"checkptrArithmetic", funcTag, 124}, - {"libfuzzerTraceCmp1", funcTag, 126}, - {"libfuzzerTraceCmp2", funcTag, 128}, - {"libfuzzerTraceCmp4", funcTag, 129}, - {"libfuzzerTraceCmp8", funcTag, 130}, - {"libfuzzerTraceConstCmp1", funcTag, 126}, - {"libfuzzerTraceConstCmp2", funcTag, 128}, - {"libfuzzerTraceConstCmp4", funcTag, 129}, - {"libfuzzerTraceConstCmp8", funcTag, 130}, + {"msanmove", funcTag, 122}, + {"checkptrAlignment", funcTag, 123}, + {"checkptrArithmetic", funcTag, 125}, + {"libfuzzerTraceCmp1", funcTag, 127}, + {"libfuzzerTraceCmp2", funcTag, 129}, + {"libfuzzerTraceCmp4", funcTag, 130}, + {"libfuzzerTraceCmp8", funcTag, 131}, + {"libfuzzerTraceConstCmp1", funcTag, 127}, + {"libfuzzerTraceConstCmp2", funcTag, 129}, + {"libfuzzerTraceConstCmp4", funcTag, 130}, + {"libfuzzerTraceConstCmp8", funcTag, 131}, {"x86HasPOPCNT", varTag, 6}, {"x86HasSSE41", varTag, 6}, {"x86HasFMA", varTag, 6}, @@ -202,7 +203,7 @@ var runtimeDecls = [...]struct { } func runtimeTypes() []*types.Type { - var typs [131]*types.Type + var typs [132]*types.Type typs[0] = types.Bytetype typs[1] = types.NewPtr(typs[0]) typs[2] = types.Types[TANY] @@ -325,14 +326,15 @@ func runtimeTypes() []*types.Type { typs[119] = functype(nil, []*Node{anonfield(typs[65])}, []*Node{anonfield(typs[20])}) typs[120] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])}) typs[121] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil) - typs[122] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil) - typs[123] = types.NewSlice(typs[7]) - typs[124] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[123])}, nil) - typs[125] = types.Types[TUINT8] - typs[126] = functype(nil, []*Node{anonfield(typs[125]), anonfield(typs[125])}, nil) - typs[127] = types.Types[TUINT16] - typs[128] = functype(nil, []*Node{anonfield(typs[127]), anonfield(typs[127])}, nil) - typs[129] = functype(nil, []*Node{anonfield(typs[65]), anonfield(typs[65])}, nil) - typs[130] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil) + typs[122] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5]), anonfield(typs[5])}, nil) + typs[123] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil) + typs[124] = types.NewSlice(typs[7]) + typs[125] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[124])}, nil) + typs[126] = types.Types[TUINT8] + typs[127] = functype(nil, []*Node{anonfield(typs[126]), anonfield(typs[126])}, nil) + typs[128] = types.Types[TUINT16] + typs[129] = functype(nil, []*Node{anonfield(typs[128]), anonfield(typs[128])}, nil) + typs[130] = functype(nil, []*Node{anonfield(typs[65]), anonfield(typs[65])}, nil) + typs[131] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil) return typs[:] } diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go index aac2de38c6..acb69c7b28 100644 --- a/src/cmd/compile/internal/gc/builtin/runtime.go +++ b/src/cmd/compile/internal/gc/builtin/runtime.go @@ -237,6 +237,7 @@ func racewriterange(addr, size uintptr) // memory sanitizer func msanread(addr, size uintptr) func msanwrite(addr, size uintptr) +func msanmove(dst, src, size uintptr) func checkptrAlignment(unsafe.Pointer, *byte, uintptr) func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer) diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index da6b6d6e72..274930bd15 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -309,6 +309,7 @@ var ( growslice, msanread, msanwrite, + msanmove, newobject, newproc, panicdivide, diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 0b38e70cd2..65b9291b76 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -79,6 +79,7 @@ func initssaconfig() { growslice = sysfunc("growslice") msanread = sysfunc("msanread") msanwrite = sysfunc("msanwrite") + msanmove = sysfunc("msanmove") newobject = sysfunc("newobject") newproc = sysfunc("newproc") panicdivide = sysfunc("panicdivide") @@ -966,7 +967,45 @@ func (s *state) newValueOrSfCall2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Valu return s.newValue2(op, t, arg0, arg1) } -func (s *state) instrument(t *types.Type, addr *ssa.Value, wr bool) { +type instrumentKind uint8 + +const ( + instrumentRead = iota + instrumentWrite + instrumentMove +) + +func (s *state) instrument(t *types.Type, addr *ssa.Value, kind instrumentKind) { + s.instrument2(t, addr, nil, kind) +} + +// instrumentFields instruments a read/write operation on addr. +// If it is instrumenting for MSAN and t is a struct type, it instruments +// operation for each field, instead of for the whole struct. +func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrumentKind) { + if !flag_msan || !t.IsStruct() { + s.instrument(t, addr, kind) + return + } + for _, f := range t.Fields().Slice() { + if f.Sym.IsBlank() { + continue + } + offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), f.Offset, addr) + s.instrumentFields(f.Type, offptr, kind) + } +} + +func (s *state) instrumentMove(t *types.Type, dst, src *ssa.Value) { + if flag_msan { + s.instrument2(t, dst, src, instrumentMove) + } else { + s.instrument(t, src, instrumentRead) + s.instrument(t, dst, instrumentWrite) + } +} + +func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrumentKind) { if !s.curfn.Func.InstrumentBody() { return } @@ -983,33 +1022,54 @@ func (s *state) instrument(t *types.Type, addr *ssa.Value, wr bool) { var fn *obj.LSym needWidth := false + if addr2 != nil && kind != instrumentMove { + panic("instrument2: non-nil addr2 for non-move instrumentation") + } + if flag_msan { - fn = msanread - if wr { + switch kind { + case instrumentRead: + fn = msanread + case instrumentWrite: fn = msanwrite + case instrumentMove: + fn = msanmove + default: + panic("unreachable") } needWidth = true } else if flag_race && t.NumComponents(types.CountBlankFields) > 1 { // for composite objects we have to write every address // because a write might happen to any subobject. // composites with only one element don't have subobjects, though. - fn = racereadrange - if wr { + switch kind { + case instrumentRead: + fn = racereadrange + case instrumentWrite: fn = racewriterange + default: + panic("unreachable") } needWidth = true } else if flag_race { // for non-composite objects we can write just the start // address, as any write must write the first byte. - fn = raceread - if wr { + switch kind { + case instrumentRead: + fn = raceread + case instrumentWrite: fn = racewrite + default: + panic("unreachable") } } else { panic("unreachable") } args := []*ssa.Value{addr} + if addr2 != nil { + args = append(args, addr2) + } if needWidth { args = append(args, s.constInt(types.Types[TUINTPTR], w)) } @@ -1017,7 +1077,7 @@ func (s *state) instrument(t *types.Type, addr *ssa.Value, wr bool) { } func (s *state) load(t *types.Type, src *ssa.Value) *ssa.Value { - s.instrument(t, src, false) + s.instrumentFields(t, src, instrumentRead) return s.rawLoad(t, src) } @@ -1030,15 +1090,14 @@ func (s *state) store(t *types.Type, dst, val *ssa.Value) { } func (s *state) zero(t *types.Type, dst *ssa.Value) { - s.instrument(t, dst, true) + s.instrument(t, dst, instrumentWrite) store := s.newValue2I(ssa.OpZero, types.TypeMem, t.Size(), dst, s.mem()) store.Aux = t s.vars[&memVar] = store } func (s *state) move(t *types.Type, dst, src *ssa.Value) { - s.instrument(t, src, false) - s.instrument(t, dst, true) + s.instrumentMove(t, dst, src) store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.mem()) store.Aux = t s.vars[&memVar] = store @@ -5248,7 +5307,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . // do *left = right for type t. func (s *state) storeType(t *types.Type, left, right *ssa.Value, skip skipMask, leftIsStmt bool) { - s.instrument(t, left, true) + s.instrument(t, left, instrumentWrite) if skip == 0 && (!t.HasPointers() || ssa.IsStackAddr(left)) { // Known to not have write barrier. Store the whole type. diff --git a/src/runtime/msan.go b/src/runtime/msan.go index c0f3957e28..6a5960b0a8 100644 --- a/src/runtime/msan.go +++ b/src/runtime/msan.go @@ -50,8 +50,12 @@ func msanmalloc(addr unsafe.Pointer, sz uintptr) //go:noescape func msanfree(addr unsafe.Pointer, sz uintptr) -// These are called from msan_amd64.s +//go:noescape +func msanmove(dst, src unsafe.Pointer, sz uintptr) + +// These are called from msan_GOARCH.s //go:cgo_import_static __msan_read_go //go:cgo_import_static __msan_write_go //go:cgo_import_static __msan_malloc_go //go:cgo_import_static __msan_free_go +//go:cgo_import_static __msan_memmove diff --git a/src/runtime/msan_amd64.s b/src/runtime/msan_amd64.s index cbe739df53..669e9ca73f 100644 --- a/src/runtime/msan_amd64.s +++ b/src/runtime/msan_amd64.s @@ -58,6 +58,15 @@ TEXT runtime·msanfree(SB), NOSPLIT, $0-16 MOVQ $__msan_free_go(SB), AX JMP msancall<>(SB) +// func runtime·msanmove(dst, src unsafe.Pointer, sz uintptr) +TEXT runtime·msanmove(SB), NOSPLIT, $0-24 + MOVQ dst+0(FP), RARG0 + MOVQ src+8(FP), RARG1 + MOVQ size+16(FP), RARG2 + // void __msan_memmove(void *dst, void *src, uintptr_t sz); + MOVQ $__msan_memmove(SB), AX + JMP msancall<>(SB) + // Switches SP to g0 stack and calls (AX). Arguments already set. TEXT msancall<>(SB), NOSPLIT, $0-0 get_tls(R12) diff --git a/src/runtime/msan_arm64.s b/src/runtime/msan_arm64.s index 5e29f1aefb..f19906cfc8 100644 --- a/src/runtime/msan_arm64.s +++ b/src/runtime/msan_arm64.s @@ -9,6 +9,7 @@ #define RARG0 R0 #define RARG1 R1 +#define RARG2 R2 #define FARG R3 // func runtime·domsanread(addr unsafe.Pointer, sz uintptr) @@ -45,6 +46,15 @@ TEXT runtime·msanfree(SB), NOSPLIT, $0-16 MOVD $__msan_free_go(SB), FARG JMP msancall<>(SB) +// func runtime·msanmove(dst, src unsafe.Pointer, sz uintptr) +TEXT runtime·msanmove(SB), NOSPLIT, $0-24 + MOVD dst+0(FP), RARG0 + MOVD src+8(FP), RARG1 + MOVD size+16(FP), RARG2 + // void __msan_memmove(void *dst, void *src, uintptr_t sz); + MOVD $__msan_memmove(SB), FARG + JMP msancall<>(SB) + // Switches SP to g0 stack and calls (FARG). Arguments already set. TEXT msancall<>(SB), NOSPLIT, $0-0 MOVD RSP, R19 // callee-saved -- cgit v1.3 From 14305527f686ced0de8d08b3a62bd96fe6359481 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 10 Dec 2020 12:21:45 -0800 Subject: cmd/compile: fix select statement evaluation order corner case The Go spec requires that select case clauses be evaluated in order, which is stricter than normal ordering semantics. cmd/compile handled this correctly for send clauses, but was not correctly handling receive clauses that involved bare variable references. Discovered with @cuonglm. Fixes #43111. Change-Id: Iec93b6514dd771875b084ba49c15d7f4531b4a6f Reviewed-on: https://go-review.googlesource.com/c/go/+/277132 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/order.go | 2 +- test/fixedbugs/issue43111.go | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 test/fixedbugs/issue43111.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 863de5b6c7..30e1535c09 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -891,7 +891,7 @@ func (o *Order) stmt(n *Node) { // c is always evaluated; x and ok are only evaluated when assigned. r.Right.Left = o.expr(r.Right.Left, nil) - if r.Right.Left.Op != ONAME { + if !r.Right.Left.IsAutoTmp() { r.Right.Left = o.copyExpr(r.Right.Left, r.Right.Left.Type, false) } diff --git a/test/fixedbugs/issue43111.go b/test/fixedbugs/issue43111.go new file mode 100644 index 0000000000..76d7beb084 --- /dev/null +++ b/test/fixedbugs/issue43111.go @@ -0,0 +1,70 @@ +// run + +// Copyright 2020 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 main + +var ch chan int +var x int + +func f() int { + close(ch) + ch = nil + return 0 +} + +func g() int { + ch = nil + x = 0 + return 0 +} + +func main() { + var nilch chan int + var v int + var ok bool + _, _ = v, ok + + ch = make(chan int) + select { + case <-ch: + case nilch <- f(): + } + + ch = make(chan int) + select { + case v = <-ch: + case nilch <- f(): + } + + ch = make(chan int) + select { + case v := <-ch: _ = v + case nilch <- f(): + } + + ch = make(chan int) + select { + case v, ok = <-ch: + case nilch <- f(): + } + + ch = make(chan int) + select { + case v, ok := <-ch: _, _ = v, ok + case nilch <- f(): + } + + ch1 := make(chan int, 1) + ch = ch1 + x = 42 + select { + case ch <- x: + case nilch <- g(): + } + if got := <-ch1; got != 42 { + panic(got) + } +} -- cgit v1.3 From 0a02371b0576964e81c3b40d328db9a3ef3b031b Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 14 Dec 2020 09:45:44 +0700 Subject: cmd/compile: set correct type for OpIData MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since CL 270057, there're many attempts to fix the expand_calls pass with interface{}-typed. But all of them did not fix the root cause. The main issue is during SSA conversion in gc/ssa.go, for empty interface case, we make its type as n.Type, instead of BytePtr. To fix these, we can just use BytePtr for now, since when itab fields are treated as scalar. No significal changes on compiler speed, size. cmd/compile/internal/ssa expandCalls.func6 9488 -> 9232 (-2.70%) file before after Δ % cmd/compile/internal/ssa.s 3992893 3992637 -256 -0.006% total 20500447 20500191 -256 -0.001% Fixes #43112 Updates #42784 Updates #42727 Updates #42568 Change-Id: I0b15d9434e0be5448453e61f98ef9c2d6cd93792 Reviewed-on: https://go-review.googlesource.com/c/go/+/276952 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/ssa.go | 4 +-- src/cmd/compile/internal/ssa/expand_calls.go | 8 +----- test/fixedbugs/issue43112.go | 41 ++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 test/fixedbugs/issue43112.go (limited to 'src/cmd/compile/internal/gc') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 65b9291b76..5b74754b53 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -5925,7 +5925,7 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) { // Load type out of itab, build interface with existing idata. off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(Widthptr), itab) typ := s.load(byteptr, off) - idata := s.newValue1(ssa.OpIData, n.Type, iface) + idata := s.newValue1(ssa.OpIData, byteptr, iface) res = s.newValue2(ssa.OpIMake, n.Type, typ, idata) return } @@ -5947,7 +5947,7 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) { bOk.AddEdgeTo(bEnd) bFail.AddEdgeTo(bEnd) s.startBlock(bEnd) - idata := s.newValue1(ssa.OpIData, n.Type, iface) + idata := s.newValue1(ssa.OpIData, byteptr, iface) res = s.newValue2(ssa.OpIMake, n.Type, s.variable(&typVar, byteptr), idata) resok = cond delete(s.vars, &typVar) diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index f266e49327..fbde19d94c 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -196,9 +196,6 @@ func expandCalls(f *Func) { } if leaf.Op == OpIData { leafType = removeTrivialWrapperTypes(leaf.Type) - if leafType.IsEmptyInterface() { - leafType = typ.BytePtr - } } aux := selector.Aux auxInt := selector.AuxInt + offset @@ -247,12 +244,9 @@ func expandCalls(f *Func) { // i.e., the struct select is generated and remains in because it is not applied to an actual structure. // The OpLoad was created to load the single field of the IData // This case removes that StructSelect. - if leafType != selector.Type && !selector.Type.IsEmptyInterface() { // empty interface for #42727 + if leafType != selector.Type { f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString()) } - if selector.Type.IsEmptyInterface() { - selector.Type = typ.BytePtr - } leaf.copyOf(selector) for _, s := range namedSelects[selector] { locs = append(locs, f.Names[s.locIndex]) diff --git a/test/fixedbugs/issue43112.go b/test/fixedbugs/issue43112.go new file mode 100644 index 0000000000..e36627a015 --- /dev/null +++ b/test/fixedbugs/issue43112.go @@ -0,0 +1,41 @@ +// compile + +// Copyright 2020 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 p + +type Symbol interface{} + +type Value interface { + String() string +} + +type Object interface { + String() string +} + +type Scope struct { + outer *Scope + elems map[string]Object +} + +func (s *Scope) findouter(name string) (*Scope, Object) { + return s.outer.findouter(name) +} + +func (s *Scope) Resolve(name string) (sym Symbol) { + if _, obj := s.findouter(name); obj != nil { + sym = obj.(Symbol) + } + return +} + +type ScopeName struct { + scope *Scope +} + +func (n *ScopeName) Get(name string) (Value, error) { + return n.scope.Resolve(name).(Value), nil +} -- cgit v1.3