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 { ...@@ -50,16 +50,16 @@ var runtimeDecls = [...]struct {
{"slicestringcopy", funcTag, 50}, {"slicestringcopy", funcTag, 50},
{"convI2I", funcTag, 51}, {"convI2I", funcTag, 51},
{"convT2E", funcTag, 52}, {"convT2E", funcTag, 52},
{"convT2E16", funcTag, 52}, {"convT2E16", funcTag, 51},
{"convT2E32", funcTag, 52}, {"convT2E32", funcTag, 51},
{"convT2E64", funcTag, 52}, {"convT2E64", funcTag, 51},
{"convT2Estring", funcTag, 52}, {"convT2Estring", funcTag, 52},
{"convT2Eslice", funcTag, 52}, {"convT2Eslice", funcTag, 52},
{"convT2Enoptr", funcTag, 52}, {"convT2Enoptr", funcTag, 52},
{"convT2I", funcTag, 52}, {"convT2I", funcTag, 52},
{"convT2I16", funcTag, 52}, {"convT2I16", funcTag, 51},
{"convT2I32", funcTag, 52}, {"convT2I32", funcTag, 51},
{"convT2I64", funcTag, 52}, {"convT2I64", funcTag, 51},
{"convT2Istring", funcTag, 52}, {"convT2Istring", funcTag, 52},
{"convT2Islice", funcTag, 52}, {"convT2Islice", funcTag, 52},
{"convT2Inoptr", funcTag, 52}, {"convT2Inoptr", funcTag, 52},
......
...@@ -62,17 +62,17 @@ func slicestringcopy(to any, fr any) int ...@@ -62,17 +62,17 @@ func slicestringcopy(to any, fr any) int
func convI2I(typ *byte, elem any) (ret any) func convI2I(typ *byte, elem any) (ret any)
func convT2E(typ *byte, elem *any) (ret any) func convT2E(typ *byte, elem *any) (ret any)
func convT2E16(typ *byte, elem *any) (ret any) func convT2E16(typ *byte, val any) (ret any)
func convT2E32(typ *byte, elem *any) (ret any) func convT2E32(typ *byte, val any) (ret any)
func convT2E64(typ *byte, elem *any) (ret any) func convT2E64(typ *byte, val any) (ret any)
func convT2Estring(typ *byte, elem *any) (ret any) func convT2Estring(typ *byte, elem *any) (ret any)
func convT2Eslice(typ *byte, elem *any) (ret any) func convT2Eslice(typ *byte, elem *any) (ret any)
func convT2Enoptr(typ *byte, elem *any) (ret any) func convT2Enoptr(typ *byte, elem *any) (ret any)
func convT2I(tab *byte, elem *any) (ret any) func convT2I(tab *byte, elem *any) (ret any)
func convT2I16(tab *byte, elem *any) (ret any) func convT2I16(tab *byte, val any) (ret any)
func convT2I32(tab *byte, elem *any) (ret any) func convT2I32(tab *byte, val any) (ret any)
func convT2I64(tab *byte, elem *any) (ret any) func convT2I64(tab *byte, val any) (ret any)
func convT2Istring(tab *byte, elem *any) (ret any) func convT2Istring(tab *byte, elem *any) (ret any)
func convT2Islice(tab *byte, elem *any) (ret any) func convT2Islice(tab *byte, elem *any) (ret any)
func convT2Inoptr(tab *byte, elem *any) (ret any) func convT2Inoptr(tab *byte, elem *any) (ret any)
......
...@@ -388,51 +388,51 @@ func walkexprlistcheap(s []*Node, init *Nodes) { ...@@ -388,51 +388,51 @@ func walkexprlistcheap(s []*Node, init *Nodes) {
} }
} }
// Build name of function for interface conversion. // convFuncName builds the runtime function name for interface conversion.
// Not all names are possible // It also reports whether the function expects the data by address.
// (e.g., we'll never generate convE2E or convE2I or convI2E). // Not all names are possible. For example, we never generate convE2E or convE2I.
func convFuncName(from, to *types.Type) string { func convFuncName(from, to *types.Type) (fnname string, needsaddr bool) {
tkind := to.Tie() tkind := to.Tie()
switch from.Tie() { switch from.Tie() {
case 'I': case 'I':
switch tkind { switch tkind {
case 'I': case 'I':
return "convI2I" return "convI2I", false
} }
case 'T': case 'T':
switch tkind { switch tkind {
case 'E': case 'E':
switch { switch {
case from.Size() == 2 && from.Align == 2: case from.Size() == 2 && from.Align == 2:
return "convT2E16" return "convT2E16", false
case from.Size() == 4 && from.Align == 4 && !types.Haspointers(from): 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): case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !types.Haspointers(from):
return "convT2E64" return "convT2E64", false
case from.IsString(): case from.IsString():
return "convT2Estring" return "convT2Estring", true
case from.IsSlice(): case from.IsSlice():
return "convT2Eslice" return "convT2Eslice", true
case !types.Haspointers(from): case !types.Haspointers(from):
return "convT2Enoptr" return "convT2Enoptr", true
} }
return "convT2E" return "convT2E", true
case 'I': case 'I':
switch { switch {
case from.Size() == 2 && from.Align == 2: case from.Size() == 2 && from.Align == 2:
return "convT2I16" return "convT2I16", false
case from.Size() == 4 && from.Align == 4 && !types.Haspointers(from): 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): case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !types.Haspointers(from):
return "convT2I64" return "convT2I64", false
case from.IsString(): case from.IsString():
return "convT2Istring" return "convT2Istring", true
case from.IsSlice(): case from.IsSlice():
return "convT2Islice" return "convT2Islice", true
case !types.Haspointers(from): case !types.Haspointers(from):
return "convT2Inoptr" return "convT2Inoptr", true
} }
return "convT2I" return "convT2I", true
} }
} }
Fatalf("unknown conv func %c2%c", from.Tie(), to.Tie()) Fatalf("unknown conv func %c2%c", from.Tie(), to.Tie())
...@@ -980,24 +980,24 @@ opswitch: ...@@ -980,24 +980,24 @@ opswitch:
} }
} }
if n.Left.Type.IsInterface() { fnname, needsaddr := convFuncName(n.Left.Type, n.Type)
ll = append(ll, n.Left) v := n.Left
} else { if needsaddr {
// regular types are passed by reference to avoid C vararg calls // Types of large or unknown size are passed by reference.
// orderexpr arranged for n.Left to be a temporary for all // Orderexpr arranged for n.Left to be a temporary for all
// the conversions it could see. comparison of an interface // the conversions it could see. Comparison of an interface
// with a non-interface, especially in a switch on interface value // with a non-interface, especially in a switch on interface value
// with non-interface cases, is not visible to orderstmt, so we // with non-interface cases, is not visible to orderstmt, so we
// have to fall back on allocating a temp here. // have to fall back on allocating a temp here.
if islvalue(n.Left) { if !islvalue(v) {
ll = append(ll, nod(OADDR, n.Left, nil)) v = copyexpr(v, v.Type, init)
} else {
ll = append(ll, nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil))
} }
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) fn = substArgTypes(fn, n.Left.Type, n.Type)
dowidth(fn.Type) dowidth(fn.Type)
n = nod(OCALL, fn, nil) n = nod(OCALL, fn, nil)
......
...@@ -294,57 +294,39 @@ func convT2E(t *_type, elem unsafe.Pointer) (e eface) { ...@@ -294,57 +294,39 @@ func convT2E(t *_type, elem unsafe.Pointer) (e eface) {
return return
} }
func convT2E16(t *_type, elem unsafe.Pointer) (e eface) { func convT2E16(t *_type, val uint16) (e eface) {
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E16))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer var x unsafe.Pointer
if *(*uint16)(elem) == 0 { if val == 0 {
x = unsafe.Pointer(&zeroVal[0]) x = unsafe.Pointer(&zeroVal[0])
} else { } else {
x = mallocgc(2, t, false) x = mallocgc(2, t, false)
*(*uint16)(x) = *(*uint16)(elem) *(*uint16)(x) = val
} }
e._type = t e._type = t
e.data = x e.data = x
return return
} }
func convT2E32(t *_type, elem unsafe.Pointer) (e eface) { func convT2E32(t *_type, val uint32) (e eface) {
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E32))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer var x unsafe.Pointer
if *(*uint32)(elem) == 0 { if val == 0 {
x = unsafe.Pointer(&zeroVal[0]) x = unsafe.Pointer(&zeroVal[0])
} else { } else {
x = mallocgc(4, t, false) x = mallocgc(4, t, false)
*(*uint32)(x) = *(*uint32)(elem) *(*uint32)(x) = val
} }
e._type = t e._type = t
e.data = x e.data = x
return return
} }
func convT2E64(t *_type, elem unsafe.Pointer) (e eface) { func convT2E64(t *_type, val uint64) (e eface) {
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E64))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer var x unsafe.Pointer
if *(*uint64)(elem) == 0 { if val == 0 {
x = unsafe.Pointer(&zeroVal[0]) x = unsafe.Pointer(&zeroVal[0])
} else { } else {
x = mallocgc(8, t, false) x = mallocgc(8, t, false)
*(*uint64)(x) = *(*uint64)(elem) *(*uint64)(x) = val
} }
e._type = t e._type = t
e.data = x e.data = x
...@@ -418,60 +400,42 @@ func convT2I(tab *itab, elem unsafe.Pointer) (i iface) { ...@@ -418,60 +400,42 @@ func convT2I(tab *itab, elem unsafe.Pointer) (i iface) {
return return
} }
func convT2I16(tab *itab, elem unsafe.Pointer) (i iface) { func convT2I16(tab *itab, val uint16) (i iface) {
t := tab._type t := tab._type
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I16))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer var x unsafe.Pointer
if *(*uint16)(elem) == 0 { if val == 0 {
x = unsafe.Pointer(&zeroVal[0]) x = unsafe.Pointer(&zeroVal[0])
} else { } else {
x = mallocgc(2, t, false) x = mallocgc(2, t, false)
*(*uint16)(x) = *(*uint16)(elem) *(*uint16)(x) = val
} }
i.tab = tab i.tab = tab
i.data = x i.data = x
return return
} }
func convT2I32(tab *itab, elem unsafe.Pointer) (i iface) { func convT2I32(tab *itab, val uint32) (i iface) {
t := tab._type t := tab._type
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I32))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer var x unsafe.Pointer
if *(*uint32)(elem) == 0 { if val == 0 {
x = unsafe.Pointer(&zeroVal[0]) x = unsafe.Pointer(&zeroVal[0])
} else { } else {
x = mallocgc(4, t, false) x = mallocgc(4, t, false)
*(*uint32)(x) = *(*uint32)(elem) *(*uint32)(x) = val
} }
i.tab = tab i.tab = tab
i.data = x i.data = x
return return
} }
func convT2I64(tab *itab, elem unsafe.Pointer) (i iface) { func convT2I64(tab *itab, val uint64) (i iface) {
t := tab._type t := tab._type
if raceenabled {
raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I64))
}
if msanenabled {
msanread(elem, t.size)
}
var x unsafe.Pointer var x unsafe.Pointer
if *(*uint64)(elem) == 0 { if val == 0 {
x = unsafe.Pointer(&zeroVal[0]) x = unsafe.Pointer(&zeroVal[0])
} else { } else {
x = mallocgc(8, t, false) x = mallocgc(8, t, false)
*(*uint64)(x) = *(*uint64)(elem) *(*uint64)(x) = val
} }
i.tab = tab i.tab = tab
i.data = x 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