Commit 22ff9521 authored by ChrisALiles's avatar ChrisALiles Committed by Josh Bleecher Snyder

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/102636Reviewed-by: 's avatarJosh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent e0d37a33
......@@ -50,16 +50,16 @@ var runtimeDecls = [...]struct {
{"slicestringcopy", funcTag, 50},
{"convI2I", funcTag, 51},
{"convT2E", funcTag, 52},
{"convT2E16", funcTag, 52},
{"convT2E32", funcTag, 52},
{"convT2E64", funcTag, 52},
{"convT2E16", funcTag, 51},
{"convT2E32", funcTag, 51},
{"convT2E64", funcTag, 51},
{"convT2Estring", funcTag, 52},
{"convT2Eslice", funcTag, 52},
{"convT2Enoptr", funcTag, 52},
{"convT2I", funcTag, 52},
{"convT2I16", funcTag, 52},
{"convT2I32", funcTag, 52},
{"convT2I64", funcTag, 52},
{"convT2I16", funcTag, 51},
{"convT2I32", funcTag, 51},
{"convT2I64", funcTag, 51},
{"convT2Istring", funcTag, 52},
{"convT2Islice", funcTag, 52},
{"convT2Inoptr", funcTag, 52},
......
......@@ -62,17 +62,17 @@ func slicestringcopy(to any, fr any) int
func convI2I(typ *byte, elem any) (ret any)
func convT2E(typ *byte, elem *any) (ret any)
func convT2E16(typ *byte, elem *any) (ret any)
func convT2E32(typ *byte, elem *any) (ret any)
func convT2E64(typ *byte, elem *any) (ret any)
func convT2E16(typ *byte, val any) (ret any)
func convT2E32(typ *byte, val any) (ret any)
func convT2E64(typ *byte, val any) (ret any)
func convT2Estring(typ *byte, elem *any) (ret any)
func convT2Eslice(typ *byte, elem *any) (ret any)
func convT2Enoptr(typ *byte, elem *any) (ret any)
func convT2I(tab *byte, elem *any) (ret any)
func convT2I16(tab *byte, elem *any) (ret any)
func convT2I32(tab *byte, elem *any) (ret any)
func convT2I64(tab *byte, elem *any) (ret any)
func convT2I16(tab *byte, val any) (ret any)
func convT2I32(tab *byte, val any) (ret any)
func convT2I64(tab *byte, val any) (ret any)
func convT2Istring(tab *byte, elem *any) (ret any)
func convT2Islice(tab *byte, elem *any) (ret any)
func convT2Inoptr(tab *byte, elem *any) (ret any)
......
......@@ -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)
......
......@@ -294,57 +294,39 @@ func convT2E(t *_type, elem unsafe.Pointer) (e eface) {
return
}
func convT2E16(t *_type, elem unsafe.Pointer) (e eface) {
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E16))
}
if msanenabled {
msanread(elem, t.size)
}
func convT2E16(t *_type, val uint16) (e eface) {
var x unsafe.Pointer
if *(*uint16)(elem) == 0 {
if val == 0 {
x = unsafe.Pointer(&zeroVal[0])
} else {
x = mallocgc(2, t, false)
*(*uint16)(x) = *(*uint16)(elem)
*(*uint16)(x) = val
}
e._type = t
e.data = x
return
}
func convT2E32(t *_type, elem unsafe.Pointer) (e eface) {
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E32))
}
if msanenabled {
msanread(elem, t.size)
}
func convT2E32(t *_type, val uint32) (e eface) {
var x unsafe.Pointer
if *(*uint32)(elem) == 0 {
if val == 0 {
x = unsafe.Pointer(&zeroVal[0])
} else {
x = mallocgc(4, t, false)
*(*uint32)(x) = *(*uint32)(elem)
*(*uint32)(x) = val
}
e._type = t
e.data = x
return
}
func convT2E64(t *_type, elem unsafe.Pointer) (e eface) {
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E64))
}
if msanenabled {
msanread(elem, t.size)
}
func convT2E64(t *_type, val uint64) (e eface) {
var x unsafe.Pointer
if *(*uint64)(elem) == 0 {
if val == 0 {
x = unsafe.Pointer(&zeroVal[0])
} else {
x = mallocgc(8, t, false)
*(*uint64)(x) = *(*uint64)(elem)
*(*uint64)(x) = val
}
e._type = t
e.data = x
......@@ -418,60 +400,42 @@ func convT2I(tab *itab, elem unsafe.Pointer) (i iface) {
return
}
func convT2I16(tab *itab, elem unsafe.Pointer) (i iface) {
func convT2I16(tab *itab, val uint16) (i iface) {
t := tab._type
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I16))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer
if *(*uint16)(elem) == 0 {
if val == 0 {
x = unsafe.Pointer(&zeroVal[0])
} else {
x = mallocgc(2, t, false)
*(*uint16)(x) = *(*uint16)(elem)
*(*uint16)(x) = val
}
i.tab = tab
i.data = x
return
}
func convT2I32(tab *itab, elem unsafe.Pointer) (i iface) {
func convT2I32(tab *itab, val uint32) (i iface) {
t := tab._type
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I32))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer
if *(*uint32)(elem) == 0 {
if val == 0 {
x = unsafe.Pointer(&zeroVal[0])
} else {
x = mallocgc(4, t, false)
*(*uint32)(x) = *(*uint32)(elem)
*(*uint32)(x) = val
}
i.tab = tab
i.data = x
return
}
func convT2I64(tab *itab, elem unsafe.Pointer) (i iface) {
func convT2I64(tab *itab, val uint64) (i iface) {
t := tab._type
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I64))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer
if *(*uint64)(elem) == 0 {
if val == 0 {
x = unsafe.Pointer(&zeroVal[0])
} else {
x = mallocgc(8, t, false)
*(*uint64)(x) = *(*uint64)(elem)
*(*uint64)(x) = val
}
i.tab = tab
i.data = x
......
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