cmd/compile: handle sole component for 1-byte type interface conversion

For 1-byte type, we have a special case for converting to interface
type. But we missed an optimization for sole component-ed types, this CL
add that one.

goos: linux
goarch: amd64
cpu: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
Benchmark_BoolField-8    	1000000000	         0.6473 ns/op
Benchmark_ByteField-8    	1000000000	         0.6094 ns/op
Benchmark_Uint8Field-8   	1000000000	         0.6385 ns/op
Benchmark_Int16Field-8   	785179434	         1.481 ns/op
Benchmark_Int32Field-8   	796127782	         1.539 ns/op
Benchmark_Int64Field-8   	718815478	         1.657 ns/op

Fixes #49879

Change-Id: Idc0e9d3ff738c8c8081b8e8d65093dacf2bcf392
Reviewed-on: https://go-review.googlesource.com/c/go/+/367755
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Cuong Manh Le 2021-12-01 00:28:25 +07:00
parent 8ab42a945a
commit 3fd8b8627f
2 changed files with 32 additions and 1 deletions

View File

@ -1628,6 +1628,7 @@ func (t *Type) NumComponents(countBlank componentsIncludeBlankFields) int64 {
// SoleComponent returns the only primitive component in t,
// if there is exactly one. Otherwise, it returns nil.
// Components are counted as in NumComponents, including blank fields.
// Keep in sync with cmd/compile/internal/walk/convert.go:soleComponent.
func (t *Type) SoleComponent() *Type {
switch t.kind {
case TSTRUCT:

View File

@ -118,6 +118,12 @@ func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node {
return n
}
isInteger := fromType.IsInteger()
isBool := fromType.IsBoolean()
if sc := fromType.SoleComponent(); sc != nil {
isInteger = sc.IsInteger()
isBool = sc.IsBoolean()
}
// Try a bunch of cases to avoid an allocation.
var value ir.Node
switch {
@ -125,10 +131,11 @@ func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node {
// n is zero-sized. Use zerobase.
cheapExpr(n, init) // Evaluate n for side-effects. See issue 19246.
value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()):
case isBool || fromType.Size() == 1 && isInteger:
// n is a bool/byte. Use staticuint64s[n * 8] on little-endian
// and staticuint64s[n * 8 + 7] on big-endian.
n = cheapExpr(n, init)
n = soleComponent(init, n)
// byteindex widens n so that the multiplication doesn't overflow.
index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(3))
if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
@ -392,6 +399,29 @@ func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
return types.Txxx, types.Txxx
}
func soleComponent(init *ir.Nodes, n ir.Node) ir.Node {
if n.Type().SoleComponent() == nil {
return n
}
// Keep in sync with cmd/compile/internal/types/type.go:Type.SoleComponent.
for {
switch {
case n.Type().IsStruct():
if n.Type().Field(0).Sym.IsBlank() {
// Treat blank fields as the zero value as the Go language requires.
n = typecheck.Temp(n.Type().Field(0).Type)
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n, nil))
return n
}
n = typecheck.Expr(ir.NewSelectorExpr(n.Pos(), ir.OXDOT, n, n.Type().Field(0).Sym))
case n.Type().IsArray():
n = typecheck.Expr(ir.NewIndexExpr(n.Pos(), n, ir.NewInt(0)))
default:
return n
}
}
}
// byteindex converts n, which is byte-sized, to an int used to index into an array.
// We cannot use conv, because we allow converting bool to int here,
// which is forbidden in user code.