cmd/compile: use type hash from itab field instead of type field

It is one less dependent load away, and right next to another
field in the itab we also load as part of the type switch or
type assert.

Change-Id: If7aaa7814c47bd79a6c7ed4232ece0bc1d63550e
Reviewed-on: https://go-review.googlesource.com/c/go/+/533117
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Keith Randall 2023-09-25 18:00:10 -07:00 committed by Keith Randall
parent 778880b008
commit e0948d825d
4 changed files with 58 additions and 24 deletions

View File

@ -313,17 +313,19 @@ func NewJumpTableStmt(pos src.XPos, idx Node) *JumpTableStmt {
// An InterfaceSwitchStmt is used to implement type switches. // An InterfaceSwitchStmt is used to implement type switches.
// Its semantics are: // Its semantics are:
// //
// if RuntimeType implements Descriptor.Cases[0] { // if RuntimeType implements Descriptor.Cases[0] {
// Case, Itab = 0, itab<RuntimeType, Descriptor.Cases[0]> // Case, Itab = 0, itab<RuntimeType, Descriptor.Cases[0]>
// } else if RuntimeType implements Descriptor.Cases[1] { // } else if RuntimeType implements Descriptor.Cases[1] {
// Case, Itab = 1, itab<RuntimeType, Descriptor.Cases[1]> // Case, Itab = 1, itab<RuntimeType, Descriptor.Cases[1]>
// ... // ...
// } else if RuntimeType implements Descriptor.Cases[N-1] { // } else if RuntimeType implements Descriptor.Cases[N-1] {
// Case, Itab = N-1, itab<RuntimeType, Descriptor.Cases[N-1]> // Case, Itab = N-1, itab<RuntimeType, Descriptor.Cases[N-1]>
// } else { // } else {
// Case, Itab = len(cases), nil // Case, Itab = len(cases), nil
// } // }
//
// RuntimeType must be a non-nil *runtime._type. // RuntimeType must be a non-nil *runtime._type.
// Hash must be the hash field of RuntimeType (or its copy loaded from an itab).
// Descriptor must represent an abi.InterfaceSwitch global variable. // Descriptor must represent an abi.InterfaceSwitch global variable.
type InterfaceSwitchStmt struct { type InterfaceSwitchStmt struct {
miniStmt miniStmt
@ -331,14 +333,16 @@ type InterfaceSwitchStmt struct {
Case Node Case Node
Itab Node Itab Node
RuntimeType Node RuntimeType Node
Hash Node
Descriptor *obj.LSym Descriptor *obj.LSym
} }
func NewInterfaceSwitchStmt(pos src.XPos, case_, itab, runtimeType Node, descriptor *obj.LSym) *InterfaceSwitchStmt { func NewInterfaceSwitchStmt(pos src.XPos, case_, itab, runtimeType, hash Node, descriptor *obj.LSym) *InterfaceSwitchStmt {
n := &InterfaceSwitchStmt{ n := &InterfaceSwitchStmt{
Case: case_, Case: case_,
Itab: itab, Itab: itab,
RuntimeType: runtimeType, RuntimeType: runtimeType,
Hash: hash,
Descriptor: descriptor, Descriptor: descriptor,
} }
n.pos = pos n.pos = pos

View File

@ -2025,6 +2025,7 @@ func (s *state) stmt(n ir.Node) {
typs := s.f.Config.Types typs := s.f.Config.Types
t := s.expr(n.RuntimeType) t := s.expr(n.RuntimeType)
h := s.expr(n.Hash)
d := s.newValue1A(ssa.OpAddr, typs.BytePtr, n.Descriptor, s.sb) d := s.newValue1A(ssa.OpAddr, typs.BytePtr, n.Descriptor, s.sb)
// Check the cache first. // Check the cache first.
@ -2061,10 +2062,9 @@ func (s *state) stmt(n ir.Node) {
cache := s.newValue1(ssa.OpSelect0, typs.BytePtr, atomicLoad) cache := s.newValue1(ssa.OpSelect0, typs.BytePtr, atomicLoad)
s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, atomicLoad) s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, atomicLoad)
// Load hash from type. // Initialize hash variable.
hash := s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, 2*s.config.PtrSize, t), s.mem()) s.vars[hashVar] = s.newValue1(zext, typs.Uintptr, h)
hash = s.newValue1(zext, typs.Uintptr, hash)
s.vars[hashVar] = hash
// Load mask from cache. // Load mask from cache.
mask := s.newValue2(ssa.OpLoad, typs.Uintptr, cache, s.mem()) mask := s.newValue2(ssa.OpLoad, typs.Uintptr, cache, s.mem())
// Jump to loop head. // Jump to loop head.
@ -6703,8 +6703,13 @@ func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, targ
cache := s.newValue1(ssa.OpSelect0, typs.BytePtr, atomicLoad) cache := s.newValue1(ssa.OpSelect0, typs.BytePtr, atomicLoad)
s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, atomicLoad) s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, atomicLoad)
// Load hash from type. // Load hash from type or itab.
hash := s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, 2*s.config.PtrSize, typ), s.mem()) var hash *ssa.Value
if src.IsEmptyInterface() {
hash = s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, 2*s.config.PtrSize, typ), s.mem())
} else {
hash = s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, 2*s.config.PtrSize, itab), s.mem())
}
hash = s.newValue1(zext, typs.Uintptr, hash) hash = s.newValue1(zext, typs.Uintptr, hash)
s.vars[hashVar] = hash s.vars[hashVar] = hash
// Load mask from cache. // Load mask from cache.

View File

@ -547,7 +547,7 @@ func walkSwitchType(sw *ir.SwitchStmt) {
typeArg = itabType(srcItab) typeArg = itabType(srcItab)
} }
caseVar := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT]) caseVar := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
isw := ir.NewInterfaceSwitchStmt(base.Pos, caseVar, s.itabName, typeArg, lsym) isw := ir.NewInterfaceSwitchStmt(base.Pos, caseVar, s.itabName, typeArg, dotHash, lsym)
sw.Compiled.Append(isw) sw.Compiled.Append(isw)
// Switch on the result of the call (or cache lookup). // Switch on the result of the call (or cache lookup).

View File

@ -129,11 +129,27 @@ type IJ interface {
I I
J J
} }
type K interface {
baz()
}
// use a runtime call for type switches to interface types. // use a runtime call for type switches to interface types.
func interfaceSwitch(x any) int { func interfaceSwitch(x any) int {
// amd64:`CALL\truntime.interfaceSwitch`,`MOVL\t16\(.*\)`,`MOVQ\t8\(.*\)(.*\*8)` // amd64:`CALL\truntime.interfaceSwitch`,`MOVL\t16\(AX\)`,`MOVQ\t8\(.*\)(.*\*8)`
// arm64:`CALL\truntime.interfaceSwitch`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)` // arm64:`CALL\truntime.interfaceSwitch`,`LDAR`,`MOVWU\t16\(R0\)`,`MOVD\t\(R.*\)\(R.*\)`
switch x.(type) {
case I:
return 1
case J:
return 2
default:
return 3
}
}
func interfaceSwitch2(x K) int {
// amd64:`CALL\truntime.interfaceSwitch`,`MOVL\t16\(AX\)`,`MOVQ\t8\(.*\)(.*\*8)`
// arm64:`CALL\truntime.interfaceSwitch`,`LDAR`,`MOVWU\t16\(R0\)`,`MOVD\t\(R.*\)\(R.*\)`
switch x.(type) { switch x.(type) {
case I: case I:
return 1 return 1
@ -145,8 +161,17 @@ func interfaceSwitch(x any) int {
} }
func interfaceCast(x any) int { func interfaceCast(x any) int {
// amd64:`CALL\truntime.typeAssert`,`MOVL\t16\(.*\)`,`MOVQ\t8\(.*\)(.*\*1)` // amd64:`CALL\truntime.typeAssert`,`MOVL\t16\(AX\)`,`MOVQ\t8\(.*\)(.*\*1)`
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)` // arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU\t16\(R0\)`,`MOVD\t\(R.*\)\(R.*\)`
if _, ok := x.(I); ok {
return 3
}
return 5
}
func interfaceCast2(x K) int {
// amd64:`CALL\truntime.typeAssert`,`MOVL\t16\(AX\)`,`MOVQ\t8\(.*\)(.*\*1)`
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU\t16\(R0\)`,`MOVD\t\(R.*\)\(R.*\)`
if _, ok := x.(I); ok { if _, ok := x.(I); ok {
return 3 return 3
} }
@ -154,7 +179,7 @@ func interfaceCast(x any) int {
} }
func interfaceConv(x IJ) I { func interfaceConv(x IJ) I {
// amd64:`CALL\truntime.typeAssert`,`MOVL\t16\(.*\)`,`MOVQ\t8\(.*\)(.*\*1)` // amd64:`CALL\truntime.typeAssert`,`MOVL\t16\(AX\)`,`MOVQ\t8\(.*\)(.*\*1)`
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)` // arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU\t16\(R0\)`,`MOVD\t\(R.*\)\(R.*\)`
return x return x
} }