Commit 35b1dcc2 authored by Russ Cox's avatar Russ Cox

cmd/internal/gc: clean up componentgen

This is primarily about making the code clearer, but as part of the cleanup
componentgen is now much more consistent about what it does and does
not attempt.

The new limit is to 8 move instructions.

The old limit was either 3 or 4 small things but in the details it was
quite inconsistent: ints, interfaces, strings, and slices all counted as small;
it handled a struct containing two ints, but not a struct containing a struct
containing two ints; it handled slices and interfaces and a struct containing
a slice but not a struct containing an interface; and so on.

The new code runs at about the same speed as the old code if limited to 4 moves,
but that's much more restrictive when the pieces are strings or interfaces.
With the limit raised to 8 moves, this CL is sometimes a significant improvement:

benchmark                          old ns/op      new ns/op      delta
BenchmarkBinaryTree17              4361174290     4362870005     +0.04%
BenchmarkFannkuch11                3008201483     2974408533     -1.12%
BenchmarkFmtFprintfEmpty           79.0           79.5           +0.63%
BenchmarkFmtFprintfString          281            261            -7.12%
BenchmarkFmtFprintfInt             264            262            -0.76%
BenchmarkFmtFprintfIntInt          447            443            -0.89%
BenchmarkFmtFprintfPrefixedInt     354            361            +1.98%
BenchmarkFmtFprintfFloat           500            452            -9.60%
BenchmarkFmtManyArgs               1688           1693           +0.30%
BenchmarkGobDecode                 11718456       11741179       +0.19%
BenchmarkGobEncode                 10144620       10161627       +0.17%
BenchmarkGzip                      437631642      435271877      -0.54%
BenchmarkGunzip                    109468858      110173606      +0.64%
BenchmarkHTTPClientServer          76248          75362          -1.16%
BenchmarkJSONEncode                24160474       23753091       -1.69%
BenchmarkJSONDecode                84470041       82902026       -1.86%
BenchmarkMandelbrot200             4676857        4687040        +0.22%
BenchmarkGoParse                   4954602        4923965        -0.62%
BenchmarkRegexpMatchEasy0_32       151            151            +0.00%
BenchmarkRegexpMatchEasy0_1K       450            452            +0.44%
BenchmarkRegexpMatchEasy1_32       131            130            -0.76%
BenchmarkRegexpMatchEasy1_1K       713            695            -2.52%
BenchmarkRegexpMatchMedium_32      227            218            -3.96%
BenchmarkRegexpMatchMedium_1K      63911          62966          -1.48%
BenchmarkRegexpMatchHard_32        3163           3026           -4.33%
BenchmarkRegexpMatchHard_1K        93985          90266          -3.96%
BenchmarkRevcomp                   650697093      649211600      -0.23%
BenchmarkTemplate                  107049170      106804076      -0.23%
BenchmarkTimeParse                 448            452            +0.89%
BenchmarkTimeFormat                468            460            -1.71%

Change-Id: I08563133883e88bb9db9e9e4dee438a5af2787da
Reviewed-on: https://go-review.googlesource.com/9004Reviewed-by: 's avatarJosh Bleecher Snyder <josharian@gmail.com>
parent 181e26b9
...@@ -1129,52 +1129,19 @@ func checklabels() { ...@@ -1129,52 +1129,19 @@ func checklabels() {
// elements of basic type are also supported. // elements of basic type are also supported.
// nr is nil when assigning a zero value. // nr is nil when assigning a zero value.
func Componentgen(nr *Node, nl *Node) bool { func Componentgen(nr *Node, nl *Node) bool {
var nodl, nodr Node // Count number of moves required to move components.
const maxMoves = 8
switch nl.Type.Etype { n := 0
default: visitComponents(nl.Type, 0, func(t *Type, offset int64) bool {
n++
return n <= maxMoves
})
if n > maxMoves {
return false return false
case TARRAY:
t := nl.Type
// Slices are ok.
if Isslice(t) {
break
}
// Small arrays are ok.
if t.Bound > 0 && t.Bound <= 3 && !Isfat(t.Type) {
break
}
return false
case TSTRUCT:
// Small structs with non-fat types are ok.
// Zero-sized structs are treated separately elsewhere.
fldcount := int64(0)
for t := nl.Type.Type; t != nil; t = t.Down {
if Isfat(t.Type) && !Isslice(t) {
return false
}
if t.Etype != TFIELD {
Fatal("componentgen: not a TFIELD: %v", Tconv(t, obj.FmtLong))
}
fldcount++
}
if fldcount == 0 || fldcount > 4 {
return false
}
case TSTRING, TINTER:
break
} }
isConstString := Isconst(nr, CTSTR) isConstString := Isconst(nr, CTSTR)
nodl = *nl nodl := *nl
if !cadable(nl) { if !cadable(nl) {
if nr != nil && !cadable(nr) && !isConstString { if nr != nil && !cadable(nr) && !isConstString {
return false return false
...@@ -1182,195 +1149,195 @@ func Componentgen(nr *Node, nl *Node) bool { ...@@ -1182,195 +1149,195 @@ func Componentgen(nr *Node, nl *Node) bool {
Igen(nl, &nodl, nil) Igen(nl, &nodl, nil)
defer Regfree(&nodl) defer Regfree(&nodl)
} }
lbase := nodl.Xoffset
if nr != nil { // Must call emitVardef on every path out of this function,
nodr = *nr // but only after evaluating rhs.
if !cadable(nr) && !isConstString { emitVardef := func() {
Igen(nr, &nodr, nil) // Emit vardef if needed.
defer Regfree(&nodr) if nl.Op == ONAME {
switch nl.Type.Etype {
case TARRAY, TSTRING, TINTER, TSTRUCT:
Gvardef(nl)
}
} }
} else { }
// Special case: zeroing.
var nodr Node
if nr == nil {
// When zeroing, prepare a register containing zero. // When zeroing, prepare a register containing zero.
// TODO(rsc): Check that this is actually generating the best code.
if Thearch.REGZERO != 0 { if Thearch.REGZERO != 0 {
// cpu has a dedicated zero register // cpu has a dedicated zero register
Nodreg(&nodr, Types[TUINT], Thearch.REGZERO) Nodreg(&nodr, Types[TUINT], Thearch.REGZERO)
} else { } else {
// no dedicated zero register // no dedicated zero register
var tmp Node var zero Node
Nodconst(&tmp, nl.Type, 0) Nodconst(&zero, nl.Type, 0)
Regalloc(&nodr, Types[TUINT], nil) Regalloc(&nodr, Types[TUINT], nil)
Thearch.Gmove(&tmp, &nodr) Thearch.Gmove(&zero, &nodr)
defer Regfree(&nodr) defer Regfree(&nodr)
} }
}
// nl and nr are 'cadable' which basically means they are names (variables) now.
// If they are the same variable, don't generate any code, because the
// VARDEF we generate will mark the old value as dead incorrectly.
// (And also the assignments are useless.)
if nr != nil && nl.Op == ONAME && nr.Op == ONAME && nl == nr {
return true
}
switch nl.Type.Etype { emitVardef()
default: visitComponents(nl.Type, 0, func(t *Type, offset int64) bool {
return false nodl.Type = t
nodl.Xoffset = lbase + offset
case TARRAY: nodr.Type = t
// componentgen for arrays. if Isfloat[t.Etype] {
if nl.Op == ONAME { // TODO(rsc): Cache zero register like we do for integers?
Gvardef(nl) Clearslim(&nodl)
} } else {
t := nl.Type Thearch.Gmove(&nodr, &nodl)
if !Isslice(t) {
nodl.Type = t.Type
nodr.Type = nodl.Type
for fldcount := int64(0); fldcount < t.Bound; fldcount++ {
if nr == nil {
Clearslim(&nodl)
} else {
Thearch.Gmove(&nodr, &nodl)
}
nodl.Xoffset += t.Type.Width
nodr.Xoffset += t.Type.Width
} }
return true return true
} })
return true
// componentgen for slices. }
nodl.Xoffset += int64(Array_array)
nodl.Type = Ptrto(nl.Type.Type) // Special case: assignment of string constant.
if isConstString {
if nr != nil { emitVardef()
nodr.Xoffset += int64(Array_array)
nodr.Type = nodl.Type
}
// base
nodl.Type = Ptrto(Types[TUINT8])
Regalloc(&nodr, Types[Tptr], nil)
p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr)
Datastring(nr.Val.U.Sval, &p.From)
p.From.Type = obj.TYPE_ADDR
Thearch.Gmove(&nodr, &nodl) Thearch.Gmove(&nodr, &nodl)
Regfree(&nodr)
nodl.Xoffset += int64(Array_nel) - int64(Array_array) // length
nodl.Type = Types[Simtype[TUINT]] nodl.Type = Types[Simtype[TUINT]]
nodl.Xoffset += int64(Array_nel) - int64(Array_array)
if nr != nil { Nodconst(&nodr, nodl.Type, int64(len(nr.Val.U.Sval)))
nodr.Xoffset += int64(Array_nel) - int64(Array_array)
nodr.Type = nodl.Type
}
Thearch.Gmove(&nodr, &nodl) Thearch.Gmove(&nodr, &nodl)
return true
}
nodl.Xoffset += int64(Array_cap) - int64(Array_nel) // General case: copy nl = nr.
nodl.Type = Types[Simtype[TUINT]] nodr = *nr
if !cadable(nr) {
if nr != nil { Igen(nr, &nodr, nil)
nodr.Xoffset += int64(Array_cap) - int64(Array_nel) defer Regfree(&nodr)
nodr.Type = nodl.Type }
} rbase := nodr.Xoffset
// Don't generate any code for complete copy of a variable into itself.
// It's useless, and the VARDEF will incorrectly mark the old value as dead.
// (This check assumes that the arguments passed to componentgen did not
// themselves come from Igen, or else we could have Op==ONAME but
// with a Type and Xoffset describing an individual field, not the entire
// variable.)
if nl.Op == ONAME && nr.Op == ONAME && nl == nr {
return true
}
emitVardef()
visitComponents(nl.Type, 0, func(t *Type, offset int64) bool {
nodl.Type = t
nodl.Xoffset = lbase + offset
nodr.Type = t
nodr.Xoffset = rbase + offset
Thearch.Gmove(&nodr, &nodl) Thearch.Gmove(&nodr, &nodl)
return true return true
})
return true
}
case TSTRING: // visitComponents walks the individual components of the type t,
if nl.Op == ONAME { // walking into array elements, struct fields, the real and imaginary
Gvardef(nl) // parts of complex numbers, and on 32-bit systems the high and
// low halves of 64-bit integers.
// It calls f for each such component, passing the component (aka element)
// type and memory offset, assuming t starts at startOffset.
// If f ever returns false, visitComponents returns false without any more
// calls to f. Otherwise visitComponents returns true.
func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset int64) bool) bool {
switch t.Etype {
case TINT64:
if Widthreg == 8 {
break
} }
nodl.Xoffset += int64(Array_array) // NOTE: Assuming little endian (signed top half at offset 4).
nodl.Type = Ptrto(Types[TUINT8]) // We don't have any 32-bit big-endian systems.
if Thearch.Thechar != '5' && Thearch.Thechar != '8' {
if isConstString { Fatal("unknown 32-bit architecture")
Regalloc(&nodr, Types[Tptr], nil)
p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr)
Datastring(nr.Val.U.Sval, &p.From)
p.From.Type = obj.TYPE_ADDR
Regfree(&nodr)
} else if nr != nil {
nodr.Xoffset += int64(Array_array)
nodr.Type = nodl.Type
} }
return f(Types[TUINT32], startOffset) &&
f(Types[TINT32], startOffset+4)
Thearch.Gmove(&nodr, &nodl) case TUINT64:
if Widthreg == 8 {
break
}
return f(Types[TUINT32], startOffset) &&
f(Types[TUINT32], startOffset+4)
nodl.Xoffset += int64(Array_nel) - int64(Array_array) case TCOMPLEX64:
nodl.Type = Types[Simtype[TUINT]] return f(Types[TFLOAT32], startOffset) &&
f(Types[TFLOAT32], startOffset+4)
if isConstString { case TCOMPLEX128:
Nodconst(&nodr, nodl.Type, int64(len(nr.Val.U.Sval))) return f(Types[TFLOAT64], startOffset) &&
} else if nr != nil { f(Types[TFLOAT64], startOffset+8)
nodr.Xoffset += int64(Array_nel) - int64(Array_array)
nodr.Type = nodl.Type
}
Thearch.Gmove(&nodr, &nodl) case TINTER:
return f(Ptrto(Types[TUINT8]), startOffset) &&
f(Ptrto(Types[TUINT8]), startOffset+int64(Widthptr))
return true return true
case TINTER: case TSTRING:
if nl.Op == ONAME { return f(Ptrto(Types[TUINT8]), startOffset) &&
Gvardef(nl) f(Types[Simtype[TUINT]], startOffset+int64(Widthptr))
}
nodl.Xoffset += int64(Array_array)
nodl.Type = Ptrto(Types[TUINT8])
if nr != nil { case TARRAY:
nodr.Xoffset += int64(Array_array) if Isslice(t) {
nodr.Type = nodl.Type return f(Ptrto(t.Type), startOffset+int64(Array_array)) &&
f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) &&
f(Types[Simtype[TUINT]], startOffset+int64(Array_cap))
} }
Thearch.Gmove(&nodr, &nodl) // Short-circuit [1e6]struct{}.
if t.Type.Width == 0 {
nodl.Xoffset += int64(Array_nel) - int64(Array_array) return true
nodl.Type = Ptrto(Types[TUINT8])
if nr != nil {
nodr.Xoffset += int64(Array_nel) - int64(Array_array)
nodr.Type = nodl.Type
} }
Thearch.Gmove(&nodr, &nodl) for i := int64(0); i < t.Bound; i++ {
if !visitComponents(t.Type, startOffset+i*t.Type.Width, f) {
return false
}
}
return true return true
case TSTRUCT: case TSTRUCT:
if nl.Op == ONAME { if t.Type != nil && t.Type.Width != 0 {
Gvardef(nl) // NOTE(rsc): If this happens, the right thing to do is to say
} // startOffset -= t.Type.Width
loffset := nodl.Xoffset // but I want to see if it does.
roffset := nodr.Xoffset // The old version of componentgen handled this,
// in code introduced in CL 6932045 to fix issue #4518.
// funarg structs may not begin at offset zero. // But the test case in issue 4518 does not trigger this anymore,
if nl.Type.Etype == TSTRUCT && nl.Type.Funarg != 0 && nl.Type.Type != nil { // so maybe this complication is no longer needed.
loffset -= nl.Type.Type.Width Fatal("struct not at offset 0")
} }
if nr != nil && nr.Type.Etype == TSTRUCT && nr.Type.Funarg != 0 && nr.Type.Type != nil {
roffset -= nr.Type.Type.Width for field := t.Type; field != nil; field = field.Down {
} if field.Etype != TFIELD {
Fatal("bad struct")
for t := nl.Type.Type; t != nil; t = t.Down { }
nodl.Xoffset = loffset + t.Width if !visitComponents(field.Type, startOffset+field.Width, f) {
nodl.Type = t.Type return false
if nr == nil {
Clearslim(&nodl)
} else {
nodr.Xoffset = roffset + t.Width
nodr.Type = nodl.Type
Thearch.Gmove(&nodr, &nodl)
} }
} }
return true return true
} }
return f(t, startOffset)
} }
func cadable(n *Node) bool { func cadable(n *Node) bool {
if !n.Addable { // Note: Not sure why you can have n.Op == ONAME without n.Addable, but you can.
// dont know how it happens, return n.Addable && n.Op == ONAME
// but it does
return false
}
switch n.Op {
case ONAME:
return true
}
return false
} }
...@@ -235,6 +235,11 @@ func getvariables(fn *Node) []*Node { ...@@ -235,6 +235,11 @@ func getvariables(fn *Node) []*Node {
// is the index in the variables list. // is the index in the variables list.
ll.N.Opt = nil ll.N.Opt = nil
// The compiler doesn't emit initializations for zero-width parameters or results.
if ll.N.Type.Width == 0 {
continue
}
ll.N.Curfn = Curfn ll.N.Curfn = Curfn
switch ll.N.Class { switch ll.N.Class {
case PAUTO: case PAUTO:
......
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