diff --git a/compiler.go b/compiler.go index d90fe789..a863d406 100644 --- a/compiler.go +++ b/compiler.go @@ -446,12 +446,14 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { } interfaceTypes := c.ir.AllInterfaces() + interfaceIndex := make([]llvm.Value, len(interfaceTypes)) interfaceLengths := make([]llvm.Value, len(interfaceTypes)) interfaceMethods := make([]llvm.Value, 0) for i, itfType := range interfaceTypes { if itfType.Type.NumMethods() > 0xff { return errors.New("too many methods for interface " + itfType.Type.String()) } + interfaceIndex[i] = llvm.ConstInt(llvm.Int16Type(), uint64(i), false) interfaceLengths[i] = llvm.ConstInt(llvm.Int8Type(), uint64(itfType.Type.NumMethods()), false) funcs := make([]*types.Func, itfType.Type.NumMethods()) for i := range funcs { @@ -493,6 +495,14 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { signatureArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(signatureArrayNewGlobal, signatureArrayOldGlobal.Type())) signatureArrayOldGlobal.EraseFromParentAsGlobal() signatureArrayNewGlobal.SetName("runtime.methodSetSignatures") + interfaceIndexArray := llvm.ConstArray(llvm.Int16Type(), interfaceIndex) + interfaceIndexArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceIndexArray.Type(), "runtime.interfaceIndex.tmp") + interfaceIndexArrayNewGlobal.SetInitializer(interfaceIndexArray) + interfaceIndexArrayNewGlobal.SetLinkage(llvm.InternalLinkage) + interfaceIndexArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceIndex") + interfaceIndexArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceIndexArrayNewGlobal, interfaceIndexArrayOldGlobal.Type())) + interfaceIndexArrayOldGlobal.EraseFromParentAsGlobal() + interfaceIndexArrayNewGlobal.SetName("runtime.interfaceIndex") interfaceLengthsArray := llvm.ConstArray(llvm.Int8Type(), interfaceLengths) interfaceLengthsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceLengthsArray.Type(), "runtime.interfaceLengths.tmp") interfaceLengthsArrayNewGlobal.SetInitializer(interfaceLengthsArray) diff --git a/src/runtime/interface.go b/src/runtime/interface.go index 455788d6..de279872 100644 --- a/src/runtime/interface.go +++ b/src/runtime/interface.go @@ -37,7 +37,7 @@ type methodSetRange struct { // in. var ( firstTypeWithMethods uint16 // the lowest typecode that has at least one method - methodSetRanges [0]methodSetRange // indexes into methodSetSignatures and methodSetFunctions + methodSetRanges [0]methodSetRange // indices into methodSetSignatures and methodSetFunctions methodSetSignatures [0]uint16 // uniqued method ID methodSetFunctions [0]*uint8 // function pointer of method interfaceIndex [0]uint16 // mapping from interface ID to an index in interfaceMethods @@ -81,15 +81,28 @@ func interfaceEqual(x, y _interface) bool { // This is a compiler intrinsic. //go:nobounds func interfaceImplements(typecode, interfaceNum uint16) bool { - // method set indexes of the concrete type + // method set indices of the interface + itfIndex := interfaceIndex[interfaceNum] + itfIndexEnd := itfIndex + uint16(interfaceLengths[interfaceNum]) + + if itfIndex == itfIndexEnd { + // This interface has no methods, so it satisfies all types. + // TODO: this should be figured out at compile time (as it is known at + // compile time), so that this check is unnecessary at runtime. + return true + } + + if typecode < firstTypeWithMethods { + // Type has no methods while the interface has (checked above), so this + // type does not satisfy this interface. + return false + } + + // method set indices of the concrete type methodSet := methodSetRanges[typecode-firstTypeWithMethods] methodIndex := methodSet.index methodIndexEnd := methodSet.index + methodSet.length - // method set indexes of the interface - itfIndex := interfaceIndex[interfaceNum] - itfIndexEnd := itfIndex + uint16(interfaceLengths[interfaceNum]) - // Iterate over all methods of the interface: for itfIndex < itfIndexEnd { methodId := interfaceMethods[itfIndex]