// An InterfaceSwitchStmt is used to implement type switches.
// Its semantics are:
//
-// if RuntimeType implements Descriptor.Cases[0] {
-// Case, Itab = 0, itab<RuntimeType, Descriptor.Cases[0]>
-// } else if RuntimeType implements Descriptor.Cases[1] {
-// Case, Itab = 1, itab<RuntimeType, Descriptor.Cases[1]>
-// ...
-// } else if RuntimeType implements Descriptor.Cases[N-1] {
-// Case, Itab = N-1, itab<RuntimeType, Descriptor.Cases[N-1]>
-// } else {
-// Case, Itab = len(cases), nil
-// }
+// if RuntimeType implements Descriptor.Cases[0] {
+// Case, Itab = 0, itab<RuntimeType, Descriptor.Cases[0]>
+// } else if RuntimeType implements Descriptor.Cases[1] {
+// Case, Itab = 1, itab<RuntimeType, Descriptor.Cases[1]>
+// ...
+// } else if RuntimeType implements Descriptor.Cases[N-1] {
+// Case, Itab = N-1, itab<RuntimeType, Descriptor.Cases[N-1]>
+// } else {
+// Case, Itab = len(cases), nil
+// }
+//
// 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.
type InterfaceSwitchStmt struct {
miniStmt
Case Node
Itab Node
RuntimeType Node
+ Hash Node
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{
Case: case_,
Itab: itab,
RuntimeType: runtimeType,
+ Hash: hash,
Descriptor: descriptor,
}
n.pos = pos
typs := s.f.Config.Types
t := s.expr(n.RuntimeType)
+ h := s.expr(n.Hash)
d := s.newValue1A(ssa.OpAddr, typs.BytePtr, n.Descriptor, s.sb)
// Check the cache first.
cache := s.newValue1(ssa.OpSelect0, typs.BytePtr, atomicLoad)
s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, atomicLoad)
- // Load hash from type.
- hash := s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, 2*s.config.PtrSize, t), s.mem())
- hash = s.newValue1(zext, typs.Uintptr, hash)
- s.vars[hashVar] = hash
+ // Initialize hash variable.
+ s.vars[hashVar] = s.newValue1(zext, typs.Uintptr, h)
+
// Load mask from cache.
mask := s.newValue2(ssa.OpLoad, typs.Uintptr, cache, s.mem())
// Jump to loop head.
cache := s.newValue1(ssa.OpSelect0, typs.BytePtr, atomicLoad)
s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, atomicLoad)
- // Load hash from type.
- hash := s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, 2*s.config.PtrSize, typ), s.mem())
+ // Load hash from type or itab.
+ 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)
s.vars[hashVar] = hash
// Load mask from cache.
typeArg = itabType(srcItab)
}
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)
// Switch on the result of the call (or cache lookup).
I
J
}
+type K interface {
+ baz()
+}
// use a runtime call for type switches to interface types.
func interfaceSwitch(x any) int {
- // amd64:`CALL\truntime.interfaceSwitch`,`MOVL\t16\(.*\)`,`MOVQ\t8\(.*\)(.*\*8)`
- // arm64:`CALL\truntime.interfaceSwitch`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)`
+ // 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) {
+ 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) {
case I:
return 1
}
func interfaceCast(x any) int {
- // amd64:`CALL\truntime.typeAssert`,`MOVL\t16\(.*\)`,`MOVQ\t8\(.*\)(.*\*1)`
- // arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)`
+ // 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 {
+ 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 {
return 3
}
}
func interfaceConv(x IJ) I {
- // amd64:`CALL\truntime.typeAssert`,`MOVL\t16\(.*\)`,`MOVQ\t8\(.*\)(.*\*1)`
- // arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)`
+ // amd64:`CALL\truntime.typeAssert`,`MOVL\t16\(AX\)`,`MOVQ\t8\(.*\)(.*\*1)`
+ // arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU\t16\(R0\)`,`MOVD\t\(R.*\)\(R.*\)`
return x
}