cmd/compile: pass arguments to convt2E/I integer functions by value

The motivation is avoid generating a pointer to the data being
converted so it can be garbage collected.
The change also slightly reduces binary size by shrinking call sites.

Fixes #24286

Benchmark results:
name                   old time/op  new time/op  delta
ConvT2ESmall-4         2.86ns ± 0%  2.80ns ± 1%  -2.12%  (p=0.000 n=29+28)
ConvT2EUintptr-4       2.88ns ± 1%  2.88ns ± 0%  -0.20%  (p=0.002 n=28+30)
ConvT2ELarge-4         19.6ns ± 0%  20.4ns ± 1%  +4.22%  (p=0.000 n=19+30)
ConvT2ISmall-4         3.01ns ± 0%  2.85ns ± 0%  -5.32%  (p=0.000 n=24+28)
ConvT2IUintptr-4       3.00ns ± 1%  2.87ns ± 0%  -4.44%  (p=0.000 n=29+25)
ConvT2ILarge-4         20.4ns ± 1%  21.3ns ± 1%  +4.41%  (p=0.000 n=30+26)
ConvT2Ezero/zero/16-4  2.84ns ± 1%  2.99ns ± 0%  +5.38%  (p=0.000 n=30+25)
ConvT2Ezero/zero/32-4  2.83ns ± 2%  3.00ns ± 0%  +5.91%  (p=0.004 n=27+3)

Change-Id: I65016ec94c53f97c52113121cab582d0c342b7a8
Reviewed-on: https://go-review.googlesource.com/102636
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
ChrisALiles 2018-04-21 17:31:21 +10:00 committed by Josh Bleecher Snyder
parent e0d37a33ab
commit 22ff9521da
4 changed files with 61 additions and 97 deletions

View file

@ -388,51 +388,51 @@ func walkexprlistcheap(s []*Node, init *Nodes) {
}
}
// Build name of function for interface conversion.
// Not all names are possible
// (e.g., we'll never generate convE2E or convE2I or convI2E).
func convFuncName(from, to *types.Type) string {
// convFuncName builds the runtime function name for interface conversion.
// It also reports whether the function expects the data by address.
// Not all names are possible. For example, we never generate convE2E or convE2I.
func convFuncName(from, to *types.Type) (fnname string, needsaddr bool) {
tkind := to.Tie()
switch from.Tie() {
case 'I':
switch tkind {
case 'I':
return "convI2I"
return "convI2I", false
}
case 'T':
switch tkind {
case 'E':
switch {
case from.Size() == 2 && from.Align == 2:
return "convT2E16"
return "convT2E16", false
case from.Size() == 4 && from.Align == 4 && !types.Haspointers(from):
return "convT2E32"
return "convT2E32", false
case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !types.Haspointers(from):
return "convT2E64"
return "convT2E64", false
case from.IsString():
return "convT2Estring"
return "convT2Estring", true
case from.IsSlice():
return "convT2Eslice"
return "convT2Eslice", true
case !types.Haspointers(from):
return "convT2Enoptr"
return "convT2Enoptr", true
}
return "convT2E"
return "convT2E", true
case 'I':
switch {
case from.Size() == 2 && from.Align == 2:
return "convT2I16"
return "convT2I16", false
case from.Size() == 4 && from.Align == 4 && !types.Haspointers(from):
return "convT2I32"
return "convT2I32", false
case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !types.Haspointers(from):
return "convT2I64"
return "convT2I64", false
case from.IsString():
return "convT2Istring"
return "convT2Istring", true
case from.IsSlice():
return "convT2Islice"
return "convT2Islice", true
case !types.Haspointers(from):
return "convT2Inoptr"
return "convT2Inoptr", true
}
return "convT2I"
return "convT2I", true
}
}
Fatalf("unknown conv func %c2%c", from.Tie(), to.Tie())
@ -980,24 +980,24 @@ opswitch:
}
}
if n.Left.Type.IsInterface() {
ll = append(ll, n.Left)
} else {
// regular types are passed by reference to avoid C vararg calls
// orderexpr arranged for n.Left to be a temporary for all
// the conversions it could see. comparison of an interface
fnname, needsaddr := convFuncName(n.Left.Type, n.Type)
v := n.Left
if needsaddr {
// Types of large or unknown size are passed by reference.
// Orderexpr arranged for n.Left to be a temporary for all
// the conversions it could see. Comparison of an interface
// with a non-interface, especially in a switch on interface value
// with non-interface cases, is not visible to orderstmt, so we
// have to fall back on allocating a temp here.
if islvalue(n.Left) {
ll = append(ll, nod(OADDR, n.Left, nil))
} else {
ll = append(ll, nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil))
if !islvalue(v) {
v = copyexpr(v, v.Type, init)
}
dowidth(n.Left.Type)
v = nod(OADDR, v, nil)
}
ll = append(ll, v)
fn := syslook(convFuncName(n.Left.Type, n.Type))
dowidth(n.Left.Type)
fn := syslook(fnname)
fn = substArgTypes(fn, n.Left.Type, n.Type)
dowidth(fn.Type)
n = nod(OCALL, fn, nil)