Commit 9c066bab authored by David Chase's avatar David Chase

cmd/compile: mark temps with new AutoTemp flag, and use it.

This is an extension of
https://go-review.googlesource.com/c/31662/
to mark all the temporaries, not just the ssa-generated ones.

Before-and-after ls -l `go tool -n compile` shows a 3%
reduction in size (or rather, a prior 3% inflation for
failing to filter temps out properly.)

Replaced name-dependent "is it a temp?" tests with calls to
*Node.IsAutoTmp(), which depends on AutoTemp.  Also replace
calls to istemp(n) with n.IsAutoTmp(), to reduce duplication
and clean up function name space.  Generated temporaries
now come with a "." prefix to avoid (apparently harmless)
clashes with legal Go variable names.

Fixes #17644.
Fixes #17240.

Change-Id: If1417f29c79a7275d7303ddf859b51472890fd43
Reviewed-on: https://go-review.googlesource.com/32255
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarMatthew Dempsky <mdempsky@google.com>
parent eec1e5d9
...@@ -115,6 +115,11 @@ func moveToHeap(n *Node) { ...@@ -115,6 +115,11 @@ func moveToHeap(n *Node) {
heapaddr.Sym = lookup("&" + n.Sym.Name) heapaddr.Sym = lookup("&" + n.Sym.Name)
heapaddr.Orig.Sym = heapaddr.Sym heapaddr.Orig.Sym = heapaddr.Sym
// Unset AutoTemp to persist the &foo variable name through SSA to
// liveness analysis.
// TODO(mdempsky/drchase): Cleaner solution?
heapaddr.Name.AutoTemp = false
// Parameters have a local stack copy used at function start/end // Parameters have a local stack copy used at function start/end
// in addition to the copy in the heap that may live longer than // in addition to the copy in the heap that may live longer than
// the function. // the function.
...@@ -192,8 +197,9 @@ func tempname(nn *Node, t *Type) { ...@@ -192,8 +197,9 @@ func tempname(nn *Node, t *Type) {
} }
// give each tmp a different name so that there // give each tmp a different name so that there
// a chance to registerizer them // a chance to registerizer them.
s := lookupN("autotmp_", statuniqgen) // Add a preceding . to avoid clash with legal names.
s := lookupN(".autotmp_", statuniqgen)
statuniqgen++ statuniqgen++
n := nod(ONAME, nil, nil) n := nod(ONAME, nil, nil)
n.Sym = s n.Sym = s
...@@ -204,6 +210,7 @@ func tempname(nn *Node, t *Type) { ...@@ -204,6 +210,7 @@ func tempname(nn *Node, t *Type) {
n.Ullman = 1 n.Ullman = 1
n.Esc = EscNever n.Esc = EscNever
n.Name.Curfn = Curfn n.Name.Curfn = Curfn
n.Name.AutoTemp = true
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
dowidth(t) dowidth(t)
......
...@@ -6,7 +6,6 @@ package gc ...@@ -6,7 +6,6 @@ package gc
import ( import (
"fmt" "fmt"
"strings"
) )
// Rewrite tree to use separate statements to enforce // Rewrite tree to use separate statements to enforce
...@@ -170,14 +169,6 @@ func ordersafeexpr(n *Node, order *Order) *Node { ...@@ -170,14 +169,6 @@ func ordersafeexpr(n *Node, order *Order) *Node {
} }
} }
// Istemp reports whether n is a temporary variable.
func istemp(n *Node) bool {
if n.Op != ONAME {
return false
}
return strings.HasPrefix(n.Sym.Name, "autotmp_")
}
// Isaddrokay reports whether it is okay to pass n's address to runtime routines. // Isaddrokay reports whether it is okay to pass n's address to runtime routines.
// Taking the address of a variable makes the liveness and optimization analyses // Taking the address of a variable makes the liveness and optimization analyses
// lose track of where the variable's lifetime ends. To avoid hurting the analyses // lose track of where the variable's lifetime ends. To avoid hurting the analyses
...@@ -185,7 +176,7 @@ func istemp(n *Node) bool { ...@@ -185,7 +176,7 @@ func istemp(n *Node) bool {
// because we emit explicit VARKILL instructions marking the end of those // because we emit explicit VARKILL instructions marking the end of those
// temporaries' lifetimes. // temporaries' lifetimes.
func isaddrokay(n *Node) bool { func isaddrokay(n *Node) bool {
return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || istemp(n)) return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || n.IsAutoTmp())
} }
// Orderaddrtemp ensures that n is okay to pass by address to runtime routines. // Orderaddrtemp ensures that n is okay to pass by address to runtime routines.
...@@ -438,10 +429,10 @@ func ordermapassign(n *Node, order *Order) { ...@@ -438,10 +429,10 @@ func ordermapassign(n *Node, order *Order) {
for i1, n1 := range n.List.Slice() { for i1, n1 := range n.List.Slice() {
if n1.Op == OINDEXMAP { if n1.Op == OINDEXMAP {
m = n1 m = n1
if !istemp(m.Left) { if !m.Left.IsAutoTmp() {
m.Left = ordercopyexpr(m.Left, m.Left.Type, order, 0) m.Left = ordercopyexpr(m.Left, m.Left.Type, order, 0)
} }
if !istemp(m.Right) { if !m.Right.IsAutoTmp() {
m.Right = ordercopyexpr(m.Right, m.Right.Type, order, 0) m.Right = ordercopyexpr(m.Right, m.Right.Type, order, 0)
} }
n.List.SetIndex(i1, ordertemp(m.Type, order, false)) n.List.SetIndex(i1, ordertemp(m.Type, order, false))
...@@ -902,11 +893,11 @@ func orderstmt(n *Node, order *Order) { ...@@ -902,11 +893,11 @@ func orderstmt(n *Node, order *Order) {
// r->left is c, r->right is x, both are always evaluated. // r->left is c, r->right is x, both are always evaluated.
r.Left = orderexpr(r.Left, order, nil) r.Left = orderexpr(r.Left, order, nil)
if !istemp(r.Left) { if !r.Left.IsAutoTmp() {
r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0) r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0)
} }
r.Right = orderexpr(r.Right, order, nil) r.Right = orderexpr(r.Right, order, nil)
if !istemp(r.Right) { if !r.Right.IsAutoTmp() {
r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0) r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0)
} }
} }
......
...@@ -422,6 +422,9 @@ func compile(fn *Node) { ...@@ -422,6 +422,9 @@ func compile(fn *Node) {
} }
fallthrough fallthrough
case PPARAM, PPARAMOUT: case PPARAM, PPARAMOUT:
if n.IsAutoTmp() { // skip debugging info for temporaries
continue
}
p := Gins(obj.ATYPE, n, nil) p := Gins(obj.ATYPE, n, nil)
p.From.Sym = obj.Linklookup(Ctxt, n.Sym.Name, 0) p.From.Sym = obj.Linklookup(Ctxt, n.Sym.Name, 0)
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
......
...@@ -437,7 +437,7 @@ func isartificial(n *Node) bool { ...@@ -437,7 +437,7 @@ func isartificial(n *Node) bool {
} }
// autotmp's are always local // autotmp's are always local
if strings.HasPrefix(n.Sym.Name, "autotmp_") { if n.IsAutoTmp() {
return true return true
} }
......
...@@ -9,7 +9,6 @@ import ( ...@@ -9,7 +9,6 @@ import (
"fmt" "fmt"
"html" "html"
"os" "os"
"strings"
"cmd/compile/internal/ssa" "cmd/compile/internal/ssa"
"cmd/internal/obj" "cmd/internal/obj"
...@@ -3987,8 +3986,8 @@ func (s *state) addNamedValue(n *Node, v *ssa.Value) { ...@@ -3987,8 +3986,8 @@ func (s *state) addNamedValue(n *Node, v *ssa.Value) {
// Don't track our dummy nodes (&memVar etc.). // Don't track our dummy nodes (&memVar etc.).
return return
} }
if strings.HasPrefix(n.Sym.Name, "autotmp_") { if n.IsAutoTmp() {
// Don't track autotmp_ variables. // Don't track temporary variables.
return return
} }
if n.Class == PPARAMOUT { if n.Class == PPARAMOUT {
...@@ -4569,6 +4568,7 @@ func (e *ssaExport) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot { ...@@ -4569,6 +4568,7 @@ func (e *ssaExport) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
} }
// namedAuto returns a new AUTO variable with the given name and type. // namedAuto returns a new AUTO variable with the given name and type.
// These are exposed to the debugger.
func (e *ssaExport) namedAuto(name string, typ ssa.Type) ssa.GCNode { func (e *ssaExport) namedAuto(name string, typ ssa.Type) ssa.GCNode {
t := typ.(*Type) t := typ.(*Type)
s := &Sym{Name: name, Pkg: localpkg} s := &Sym{Name: name, Pkg: localpkg}
......
...@@ -72,6 +72,15 @@ type Node struct { ...@@ -72,6 +72,15 @@ type Node struct {
flags uint8 // TODO: store more bool fields in this flag field flags uint8 // TODO: store more bool fields in this flag field
} }
// IsAutoTmp indicates if n was created by the compiler as a temporary,
// based on the setting of the .AutoTemp flag in n's Name.
func (n *Node) IsAutoTmp() bool {
if n == nil || n.Op != ONAME {
return false
}
return n.Name.AutoTemp
}
const ( const (
hasBreak = 1 << iota hasBreak = 1 << iota
isClosureVar isClosureVar
...@@ -188,6 +197,7 @@ type Name struct { ...@@ -188,6 +197,7 @@ type Name struct {
Byval bool // is the variable captured by value or by reference Byval bool // is the variable captured by value or by reference
Needzero bool // if it contains pointers, needs to be zeroed on function entry Needzero bool // if it contains pointers, needs to be zeroed on function entry
Keepalive bool // mark value live across unknown assembly call Keepalive bool // mark value live across unknown assembly call
AutoTemp bool // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
} }
type Param struct { type Param struct {
......
...@@ -1966,7 +1966,7 @@ OpSwitch: ...@@ -1966,7 +1966,7 @@ OpSwitch:
typecheckas(n) typecheckas(n)
// Code that creates temps does not bother to set defn, so do it here. // Code that creates temps does not bother to set defn, so do it here.
if n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") { if n.Left.Op == ONAME && n.Left.IsAutoTmp() {
n.Left.Name.Defn = n n.Left.Name.Defn = n
} }
break OpSwitch break OpSwitch
......
...@@ -2208,7 +2208,7 @@ func isstack(n *Node) bool { ...@@ -2208,7 +2208,7 @@ func isstack(n *Node) bool {
// If n is *autotmp and autotmp = &foo, replace n with foo. // If n is *autotmp and autotmp = &foo, replace n with foo.
// We introduce such temps when initializing struct literals. // We introduce such temps when initializing struct literals.
if n.Op == OIND && n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") { if n.Op == OIND && n.Left.Op == ONAME && n.Left.IsAutoTmp() {
defn := n.Left.Name.Defn defn := n.Left.Name.Defn
if defn != nil && defn.Op == OAS && defn.Right.Op == OADDR { if defn != nil && defn.Op == OAS && defn.Right.Op == OADDR {
n = defn.Right.Left n = defn.Right.Left
......
...@@ -9,7 +9,6 @@ package dwarf ...@@ -9,7 +9,6 @@ package dwarf
import ( import (
"fmt" "fmt"
"strings"
) )
// InfoPrefix is the prefix for all the symbols containing DWARF info entries. // InfoPrefix is the prefix for all the symbols containing DWARF info entries.
...@@ -577,9 +576,6 @@ func PutFunc(ctxt Context, s Sym, name string, external bool, startPC Sym, size ...@@ -577,9 +576,6 @@ func PutFunc(ctxt Context, s Sym, name string, external bool, startPC Sym, size
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0) putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
names := make(map[string]bool) names := make(map[string]bool)
for v := vars; v != nil; v = v.Link { for v := vars; v != nil; v = v.Link {
if strings.Contains(v.Name, ".autotmp_") {
continue
}
var n string var n string
if names[v.Name] { if names[v.Name] {
n = fmt.Sprintf("%s#%d", v.Name, len(names)) n = fmt.Sprintf("%s#%d", v.Name, len(names))
......
...@@ -17,14 +17,14 @@ type T struct{ M string } ...@@ -17,14 +17,14 @@ type T struct{ M string }
var b bool var b bool
func f1(q *Q, xx []byte) interface{} { // ERROR "live at entry to f1: xx" "live at call to newobject: xx" "live at call to writebarrierptr: &xx" func f1(q *Q, xx []byte) interface{} { // ERROR "live at call to newobject: xx$" "live at call to writebarrierptr: &xx$" "live at entry to f1: xx$"
// xx was copied from the stack to the heap on the previous line: // xx was copied from the stack to the heap on the previous line:
// xx was live for the first two prints but then it switched to &xx // xx was live for the first two prints but then it switched to &xx
// being live. We should not see plain xx again. // being live. We should not see plain xx again.
if b { if b {
global = &xx // ERROR "live at call to writebarrierptr: &xx$" global = &xx // ERROR "live at call to writebarrierptr: &xx$"
} }
xx, _, err := f2(xx, 5) // ERROR "live at call to writebarrierptr: err.data err.type$" "live at call to f2: &xx$" xx, _, err := f2(xx, 5) // ERROR "live at call to f2: &xx$" "live at call to writebarrierptr: err.data err.type$"
if err != nil { if err != nil {
return err return err
} }
...@@ -32,7 +32,7 @@ func f1(q *Q, xx []byte) interface{} { // ERROR "live at entry to f1: xx" "live ...@@ -32,7 +32,7 @@ func f1(q *Q, xx []byte) interface{} { // ERROR "live at entry to f1: xx" "live
} }
//go:noinline //go:noinline
func f2(d []byte, n int) (odata, res []byte, e interface{}) { // ERROR "live at entry to f2: d" func f2(d []byte, n int) (odata, res []byte, e interface{}) { // ERROR "live at entry to f2: d$"
if n > len(d) { if n > len(d) {
return d, nil, &T{M: "hello"} // ERROR "live at call to newobject: d" "live at call to writebarrierptr: d" return d, nil, &T{M: "hello"} // ERROR "live at call to newobject: d" "live at call to writebarrierptr: d"
} }
......
This diff is collapsed.
...@@ -20,20 +20,20 @@ type T40 struct { ...@@ -20,20 +20,20 @@ type T40 struct {
func newT40() *T40 { func newT40() *T40 {
ret := T40{} ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret" ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret$"
return &ret return &ret
} }
func bad40() { func bad40() {
t := newT40() // ERROR "live at call to makemap: autotmp_.* ret" t := newT40() // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
printnl() // ERROR "live at call to printnl: autotmp_.* ret" printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
_ = t _ = t
} }
func good40() { func good40() {
ret := T40{} ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: autotmp_.* ret" ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
t := &ret t := &ret
printnl() // ERROR "live at call to printnl: autotmp_.* ret" printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
_ = t _ = t
} }
...@@ -19,10 +19,10 @@ func f(uintptr) // ERROR "f assuming arg#1 is unsafe uintptr" ...@@ -19,10 +19,10 @@ func f(uintptr) // ERROR "f assuming arg#1 is unsafe uintptr"
func g() { func g() {
var t int var t int
f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: autotmp" "g &t does not escape" f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: .?autotmp" "g &t does not escape"
} }
func h() { func h() {
var v int var v int
syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: autotmp" "h &v does not escape" syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: .?autotmp" "h &v does not escape"
} }
...@@ -22,10 +22,10 @@ func F2(a ...uintptr) {} // ERROR "escaping ...uintptr" "a does not escape" ...@@ -22,10 +22,10 @@ func F2(a ...uintptr) {} // ERROR "escaping ...uintptr" "a does not escape"
func G() { func G() {
var t int // ERROR "moved to heap" var t int // ERROR "moved to heap"
F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: autotmp" "&t escapes to heap" F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: .?autotmp" "&t escapes to heap"
} }
func H() { func H() {
var v int // ERROR "moved to heap" var v int // ERROR "moved to heap"
F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: autotmp" "live at call to F2: autotmp" "escapes to heap" F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap"
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment