compiler: lower interfaces in a separate pass
This commit changes many things:
  * Most interface-related operations are moved into an optimization
    pass for more modularity. IR construction creates pseudo-calls which
    are lowered in this pass.
  * Type codes are assigned in this interface lowering pass, after DCE.
  * Type codes are sorted by usage: types more often used in type
    asserts are assigned lower numbers to ease jump table construction
    during machine code generation.
  * Interface assertions are optimized: they are replaced by constant
    false, comparison against a constant, or a typeswitch with only
    concrete types in the general case.
  * Interface calls are replaced with unreachable, direct calls, or a
    concrete type switch with direct calls depending on the number of
    implementing types. This hopefully makes some interface patterns
    zero-cost.
These changes lead to a ~0.5K reduction in code size on Cortex-M for
testdata/interface.go. It appears that a major cause for this is the
replacement of function pointers with direct calls, which are far more
susceptible to optimization. Also, not having a fixed global array of
function pointers greatly helps dead code elimination.
This change also makes future optimizations easier, like optimizations
on interface value comparisons.
			
			
Этот коммит содержится в:
		
							родитель
							
								
									e45c4ac182
								
							
						
					
					
						коммит
						b4c90f3677
					
				
					 13 изменённых файлов: 1039 добавлений и 535 удалений
				
			
		|  | @ -43,29 +43,30 @@ type Config struct { | ||||||
| 
 | 
 | ||||||
| type Compiler struct { | type Compiler struct { | ||||||
| 	Config | 	Config | ||||||
| 	mod              llvm.Module | 	mod                     llvm.Module | ||||||
| 	ctx              llvm.Context | 	ctx                     llvm.Context | ||||||
| 	builder          llvm.Builder | 	builder                 llvm.Builder | ||||||
| 	dibuilder        *llvm.DIBuilder | 	dibuilder               *llvm.DIBuilder | ||||||
| 	cu               llvm.Metadata | 	cu                      llvm.Metadata | ||||||
| 	difiles          map[string]llvm.Metadata | 	difiles                 map[string]llvm.Metadata | ||||||
| 	ditypes          map[string]llvm.Metadata | 	ditypes                 map[string]llvm.Metadata | ||||||
| 	machine          llvm.TargetMachine | 	machine                 llvm.TargetMachine | ||||||
| 	targetData       llvm.TargetData | 	targetData              llvm.TargetData | ||||||
| 	intType          llvm.Type | 	intType                 llvm.Type | ||||||
| 	i8ptrType        llvm.Type // for convenience | 	i8ptrType               llvm.Type // for convenience | ||||||
| 	uintptrType      llvm.Type | 	uintptrType             llvm.Type | ||||||
| 	coroIdFunc       llvm.Value | 	coroIdFunc              llvm.Value | ||||||
| 	coroSizeFunc     llvm.Value | 	coroSizeFunc            llvm.Value | ||||||
| 	coroBeginFunc    llvm.Value | 	coroBeginFunc           llvm.Value | ||||||
| 	coroSuspendFunc  llvm.Value | 	coroSuspendFunc         llvm.Value | ||||||
| 	coroEndFunc      llvm.Value | 	coroEndFunc             llvm.Value | ||||||
| 	coroFreeFunc     llvm.Value | 	coroFreeFunc            llvm.Value | ||||||
| 	initFuncs        []llvm.Value | 	initFuncs               []llvm.Value | ||||||
| 	deferFuncs       []*ir.Function | 	deferFuncs              []*ir.Function | ||||||
| 	deferInvokeFuncs []InvokeDeferFunction | 	deferInvokeFuncs        []InvokeDeferFunction | ||||||
| 	ctxDeferFuncs    []ContextDeferFunction | 	ctxDeferFuncs           []ContextDeferFunction | ||||||
| 	ir               *ir.Program | 	interfaceInvokeWrappers []interfaceInvokeWrapper | ||||||
|  | 	ir                      *ir.Program | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Frame struct { | type Frame struct { | ||||||
|  | @ -489,6 +490,15 @@ func (c *Compiler) Compile(mainPath string) error { | ||||||
| 		c.builder.CreateRetVoid() | 		c.builder.CreateRetVoid() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Define the already declared functions that wrap methods for use in | ||||||
|  | 	// interfaces. | ||||||
|  | 	for _, state := range c.interfaceInvokeWrappers { | ||||||
|  | 		err = c.createInterfaceInvokeWrapper(state) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// After all packages are imported, add a synthetic initializer function | 	// After all packages are imported, add a synthetic initializer function | ||||||
| 	// that calls the initializer of each package. | 	// that calls the initializer of each package. | ||||||
| 	initFn := c.ir.GetFunction(c.ir.Program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)) | 	initFn := c.ir.GetFunction(c.ir.Program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)) | ||||||
|  | @ -534,13 +544,6 @@ func (c *Compiler) Compile(mainPath string) error { | ||||||
| 	} | 	} | ||||||
| 	c.builder.CreateRetVoid() | 	c.builder.CreateRetVoid() | ||||||
| 
 | 
 | ||||||
| 	// Add runtime type information for interfaces: interface calls and type |  | ||||||
| 	// asserts. |  | ||||||
| 	err = c.createInterfaceRTTI() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// see: https://reviews.llvm.org/D18355 | 	// see: https://reviews.llvm.org/D18355 | ||||||
| 	c.mod.AddNamedMetadataOperand("llvm.module.flags", | 	c.mod.AddNamedMetadataOperand("llvm.module.flags", | ||||||
| 		c.ctx.MDNode([]llvm.Metadata{ | 		c.ctx.MDNode([]llvm.Metadata{ | ||||||
|  | @ -995,17 +998,6 @@ func (c *Compiler) getInterpretedValue(prefix string, value ir.Value) (llvm.Valu | ||||||
| 		ptr := llvm.ConstInBoundsGEP(value.Global.LLVMGlobal, []llvm.Value{zero}) | 		ptr := llvm.ConstInBoundsGEP(value.Global.LLVMGlobal, []llvm.Value{zero}) | ||||||
| 		return ptr, nil | 		return ptr, nil | ||||||
| 
 | 
 | ||||||
| 	case *ir.InterfaceValue: |  | ||||||
| 		underlying := llvm.ConstPointerNull(c.i8ptrType) // could be any 0 value |  | ||||||
| 		if value.Elem != nil { |  | ||||||
| 			elem, err := c.getInterpretedValue(prefix, value.Elem) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return llvm.Value{}, err |  | ||||||
| 			} |  | ||||||
| 			underlying = elem |  | ||||||
| 		} |  | ||||||
| 		return c.parseMakeInterface(underlying, value.Type, prefix) |  | ||||||
| 
 |  | ||||||
| 	case *ir.MapValue: | 	case *ir.MapValue: | ||||||
| 		// Create initial bucket. | 		// Create initial bucket. | ||||||
| 		firstBucketGlobal, keySize, valueSize, err := c.initMapNewBucket(prefix, value.Type) | 		firstBucketGlobal, keySize, valueSize, err := c.initMapNewBucket(prefix, value.Type) | ||||||
|  |  | ||||||
							
								
								
									
										715
									
								
								compiler/interface-lowering.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										715
									
								
								compiler/interface-lowering.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,715 @@ | ||||||
|  | package compiler | ||||||
|  | 
 | ||||||
|  | // This file provides function to lower interface intrinsics to their final LLVM | ||||||
|  | // form, optimizing them in the process. | ||||||
|  | // | ||||||
|  | // During SSA construction, the following pseudo-calls are created: | ||||||
|  | //     runtime.makeInterface(typecode, methodSet) | ||||||
|  | //     runtime.typeAssert(typecode, assertedType) | ||||||
|  | //     runtime.interfaceImplements(typecode, interfaceMethodSet) | ||||||
|  | //     runtime.interfaceMethod(typecode, interfaceMethodSet, signature) | ||||||
|  | // See src/runtime/interface.go for details. | ||||||
|  | // These calls are to declared but not defined functions, so the optimizer will | ||||||
|  | // leave them alone. | ||||||
|  | // | ||||||
|  | // This pass lowers the above functions to their final form: | ||||||
|  | // | ||||||
|  | // makeInterface: | ||||||
|  | //     Replaced with a constant typecode. | ||||||
|  | // | ||||||
|  | // typeAssert: | ||||||
|  | //     Replaced with an icmp instruction so it can be directly used in a type | ||||||
|  | //     switch. This is very easy to optimize for LLVM: it will often translate a | ||||||
|  | //     type switch into a regular switch statement. | ||||||
|  | //     When this type assert is not possible (the type is never used in an | ||||||
|  | //     interface with makeInterface), this call is replaced with a constant | ||||||
|  | //     false to optimize the type assert away completely. | ||||||
|  | // | ||||||
|  | // interfaceImplements: | ||||||
|  | //     This call is translated into a call that checks whether the underlying | ||||||
|  | //     type is one of the types implementing this interface. | ||||||
|  | //     When there is only one type implementing this interface, the check is | ||||||
|  | //     replaced with a simple icmp instruction, just like a type assert. | ||||||
|  | //     When there is no type at all that implements this interface, it is | ||||||
|  | //     replaced with a constant false to optimize it completely. | ||||||
|  | // | ||||||
|  | // interfaceMethod: | ||||||
|  | //     This call is replaced with a call to a function that calls the | ||||||
|  | //     appropriate method depending on the underlying type. | ||||||
|  | //     When there is only one type implementing this interface, this call is | ||||||
|  | //     translated into a direct call of that method. | ||||||
|  | //     When there is no type implementing this interface, this code is marked | ||||||
|  | //     unreachable as there is no way such an interface could be constructed. | ||||||
|  | // | ||||||
|  | // Note that this way of implementing interfaces is very different from how the | ||||||
|  | // main Go compiler implements them. For more details on how the main Go | ||||||
|  | // compiler does it: https://research.swtch.com/interfaces | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"sort" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/aykevl/go-llvm" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // signatureInfo is a Go signature of an interface method. It does not represent | ||||||
|  | // any method in particular. | ||||||
|  | type signatureInfo struct { | ||||||
|  | 	name       string | ||||||
|  | 	methods    []*methodInfo | ||||||
|  | 	interfaces []*interfaceInfo | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // methodName takes a method name like "func String()" and returns only the | ||||||
|  | // name, which is "String" in this case. | ||||||
|  | func (s *signatureInfo) methodName() string { | ||||||
|  | 	if !strings.HasPrefix(s.name, "func ") { | ||||||
|  | 		panic("signature must start with \"func \"") | ||||||
|  | 	} | ||||||
|  | 	methodName := s.name[len("func "):] | ||||||
|  | 	if openingParen := strings.IndexByte(methodName, '('); openingParen < 0 { | ||||||
|  | 		panic("no opening paren in signature name") | ||||||
|  | 	} else { | ||||||
|  | 		return methodName[:openingParen] | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // methodInfo describes a single method on a concrete type. | ||||||
|  | type methodInfo struct { | ||||||
|  | 	*signatureInfo | ||||||
|  | 	function llvm.Value | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // typeInfo describes a single concrete Go type, which can be a basic or a named | ||||||
|  | // type. If it is a named type, it may have methods. | ||||||
|  | type typeInfo struct { | ||||||
|  | 	name                string | ||||||
|  | 	typecode            llvm.Value | ||||||
|  | 	methodSet           llvm.Value | ||||||
|  | 	num                 uint64 // the type number after lowering | ||||||
|  | 	countMakeInterfaces int    // how often this type is used in an interface | ||||||
|  | 	countTypeAsserts    int    // how often a type assert happens on this method | ||||||
|  | 	methods             []*methodInfo | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getMethod looks up the method on this type with the given signature and | ||||||
|  | // returns it. The method must exist on this type, otherwise getMethod will | ||||||
|  | // panic. | ||||||
|  | func (t *typeInfo) getMethod(signature *signatureInfo) *methodInfo { | ||||||
|  | 	for _, method := range t.methods { | ||||||
|  | 		if method.signatureInfo == signature { | ||||||
|  | 			return method | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	panic("could not find method") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // id returns the fully-qualified type name including import path, removing the | ||||||
|  | // $type suffix. | ||||||
|  | func (t *typeInfo) id() string { | ||||||
|  | 	if !strings.HasSuffix(t.name, "$type") { | ||||||
|  | 		panic("concrete type does not have $type suffix: " + t.name) | ||||||
|  | 	} | ||||||
|  | 	return t.name[:len(t.name)-len("$type")] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // typeInfoSlice implements sort.Slice, sorting the most commonly used types | ||||||
|  | // first. | ||||||
|  | type typeInfoSlice []*typeInfo | ||||||
|  | 
 | ||||||
|  | func (t typeInfoSlice) Len() int { return len(t) } | ||||||
|  | func (t typeInfoSlice) Less(i, j int) bool { | ||||||
|  | 	// Try to sort the most commonly used types first. | ||||||
|  | 	if t[i].countTypeAsserts != t[j].countTypeAsserts { | ||||||
|  | 		return t[i].countTypeAsserts < t[j].countTypeAsserts | ||||||
|  | 	} | ||||||
|  | 	if t[i].countMakeInterfaces != t[j].countMakeInterfaces { | ||||||
|  | 		return t[i].countMakeInterfaces < t[j].countMakeInterfaces | ||||||
|  | 	} | ||||||
|  | 	return t[i].name < t[j].name | ||||||
|  | } | ||||||
|  | func (t typeInfoSlice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } | ||||||
|  | 
 | ||||||
|  | // interfaceInfo keeps information about a Go interface type, including all | ||||||
|  | // methods it has. | ||||||
|  | type interfaceInfo struct { | ||||||
|  | 	name        string                        // name with $interface suffix | ||||||
|  | 	signatures  []*signatureInfo              // method set | ||||||
|  | 	types       typeInfoSlice                 // types this interface implements | ||||||
|  | 	assertFunc  llvm.Value                    // runtime.interfaceImplements replacement | ||||||
|  | 	methodFuncs map[*signatureInfo]llvm.Value // runtime.interfaceMethod replacements for each signature | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // id removes the $interface suffix from the name and returns the clean | ||||||
|  | // interface name including import path. | ||||||
|  | func (itf *interfaceInfo) id() string { | ||||||
|  | 	if !strings.HasSuffix(itf.name, "$interface") { | ||||||
|  | 		panic("interface type does not have $interface suffix: " + itf.name) | ||||||
|  | 	} | ||||||
|  | 	return itf.name[:len(itf.name)-len("$interface")] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // lowerInterfacesPass keeps state related to the interface lowering pass. The | ||||||
|  | // pass has been implemented as an object type because of its complexity, but | ||||||
|  | // should be seen as a regular function call (see LowerInterfaces). | ||||||
|  | type lowerInterfacesPass struct { | ||||||
|  | 	*Compiler | ||||||
|  | 	types      map[string]*typeInfo | ||||||
|  | 	signatures map[string]*signatureInfo | ||||||
|  | 	interfaces map[string]*interfaceInfo | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Lower all interface functions. They are emitted by the compiler as | ||||||
|  | // higher-level intrinsics that need some lowering before LLVM can work on them. | ||||||
|  | // This is done so that a few cleanup passes can run before assigning the final | ||||||
|  | // type codes. | ||||||
|  | func (c *Compiler) LowerInterfaces() { | ||||||
|  | 	p := &lowerInterfacesPass{ | ||||||
|  | 		Compiler:   c, | ||||||
|  | 		types:      make(map[string]*typeInfo), | ||||||
|  | 		signatures: make(map[string]*signatureInfo), | ||||||
|  | 		interfaces: make(map[string]*interfaceInfo), | ||||||
|  | 	} | ||||||
|  | 	p.run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // run runs the pass itself. | ||||||
|  | func (p *lowerInterfacesPass) run() { | ||||||
|  | 	// Count per type how often it is put in an interface. Also, collect all | ||||||
|  | 	// methods this type has (if it is named). | ||||||
|  | 	makeInterface := p.mod.NamedFunction("runtime.makeInterface") | ||||||
|  | 	makeInterfaceUses := getUses(makeInterface) | ||||||
|  | 	for _, use := range makeInterfaceUses { | ||||||
|  | 		typecode := use.Operand(0) | ||||||
|  | 		name := typecode.Name() | ||||||
|  | 		if t, ok := p.types[name]; !ok { | ||||||
|  | 			// This is the first time this type has been seen, add it to the | ||||||
|  | 			// list of types. | ||||||
|  | 			t = p.addType(typecode) | ||||||
|  | 			p.addTypeMethods(t, use.Operand(1)) | ||||||
|  | 		} else { | ||||||
|  | 			p.addTypeMethods(t, use.Operand(1)) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Count the number of MakeInterface instructions, for sorting the | ||||||
|  | 		// typecodes later. | ||||||
|  | 		p.types[name].countMakeInterfaces++ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Count per type how often it is type asserted on (e.g. in a switch | ||||||
|  | 	// statement). | ||||||
|  | 	typeAssert := p.mod.NamedFunction("runtime.typeAssert") | ||||||
|  | 	typeAssertUses := getUses(typeAssert) | ||||||
|  | 	for _, use := range typeAssertUses { | ||||||
|  | 		typecode := use.Operand(1) | ||||||
|  | 		name := typecode.Name() | ||||||
|  | 		if _, ok := p.types[name]; !ok { | ||||||
|  | 			p.addType(typecode) | ||||||
|  | 		} | ||||||
|  | 		p.types[name].countTypeAsserts++ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Find all interface method calls. | ||||||
|  | 	interfaceMethod := p.mod.NamedFunction("runtime.interfaceMethod") | ||||||
|  | 	interfaceMethodUses := getUses(interfaceMethod) | ||||||
|  | 	for _, use := range interfaceMethodUses { | ||||||
|  | 		methodSet := use.Operand(1).Operand(0) | ||||||
|  | 		name := methodSet.Name() | ||||||
|  | 		if _, ok := p.interfaces[name]; !ok { | ||||||
|  | 			p.addInterface(methodSet) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Find all interface type asserts. | ||||||
|  | 	interfaceImplements := p.mod.NamedFunction("runtime.interfaceImplements") | ||||||
|  | 	interfaceImplementsUses := getUses(interfaceImplements) | ||||||
|  | 	for _, use := range interfaceImplementsUses { | ||||||
|  | 		methodSet := use.Operand(1).Operand(0) | ||||||
|  | 		name := methodSet.Name() | ||||||
|  | 		if _, ok := p.interfaces[name]; !ok { | ||||||
|  | 			p.addInterface(methodSet) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Find all the interfaces that are implemented per type. | ||||||
|  | 	for _, t := range p.types { | ||||||
|  | 		// This type has no methods, so don't spend time calculating them. | ||||||
|  | 		if len(t.methods) == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Pre-calculate a set of signatures that this type has, for easy | ||||||
|  | 		// lookup/check. | ||||||
|  | 		typeSignatureSet := make(map[*signatureInfo]struct{}) | ||||||
|  | 		for _, method := range t.methods { | ||||||
|  | 			typeSignatureSet[method.signatureInfo] = struct{}{} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// A set of interfaces, mapped from the name to the info. | ||||||
|  | 		// When the name maps to a nil pointer, one of the methods of this type | ||||||
|  | 		// exists in the given interface but not all of them so this type | ||||||
|  | 		// doesn't implement the interface. | ||||||
|  | 		satisfiesInterfaces := make(map[string]*interfaceInfo) | ||||||
|  | 
 | ||||||
|  | 		for _, method := range t.methods { | ||||||
|  | 			for _, itf := range method.interfaces { | ||||||
|  | 				if _, ok := satisfiesInterfaces[itf.name]; ok { | ||||||
|  | 					// interface already checked with a different method | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				// check whether this interface satisfies this type | ||||||
|  | 				satisfies := true | ||||||
|  | 				for _, itfSignature := range itf.signatures { | ||||||
|  | 					if _, ok := typeSignatureSet[itfSignature]; !ok { | ||||||
|  | 						satisfiesInterfaces[itf.name] = nil // does not satisfy | ||||||
|  | 						satisfies = false | ||||||
|  | 						break | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				if !satisfies { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				satisfiesInterfaces[itf.name] = itf | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Add this type to all interfaces that satisfy this type. | ||||||
|  | 		for _, itf := range satisfiesInterfaces { | ||||||
|  | 			if itf == nil { | ||||||
|  | 				// Interface does not implement this type, but one of the | ||||||
|  | 				// methods on this type also exists on the interface. | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			itf.types = append(itf.types, t) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Sort all types added to the interfaces, to check for more common types | ||||||
|  | 	// first. | ||||||
|  | 	for _, itf := range p.interfaces { | ||||||
|  | 		sort.Sort(itf.types) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Replace all interface methods with their uses, if possible. | ||||||
|  | 	for _, use := range interfaceMethodUses { | ||||||
|  | 		typecode := use.Operand(0) | ||||||
|  | 		signature := p.signatures[use.Operand(2).Name()] | ||||||
|  | 
 | ||||||
|  | 		// If the interface was created in the same function, we can insert a | ||||||
|  | 		// direct call. This may not happen often but it is an easy | ||||||
|  | 		// optimization so let's do it anyway. | ||||||
|  | 		if !typecode.IsACallInst().IsNil() && typecode.CalledValue() == makeInterface { | ||||||
|  | 			name := typecode.Operand(0).Name() | ||||||
|  | 			typ := p.types[name] | ||||||
|  | 			p.replaceInvokeWithCall(use, typ, signature) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		methodSet := use.Operand(1).Operand(0) // global variable | ||||||
|  | 		itf := p.interfaces[methodSet.Name()] | ||||||
|  | 		if len(itf.types) == 0 { | ||||||
|  | 			// This method call is impossible: no type implements this | ||||||
|  | 			// interface. In fact, the previous type assert that got this | ||||||
|  | 			// interface value should already have returned false. | ||||||
|  | 			// Replace the function pointer with undef (which will then be | ||||||
|  | 			// called), indicating to the optimizer this code is unreachable. | ||||||
|  | 			use.ReplaceAllUsesWith(llvm.Undef(p.i8ptrType)) | ||||||
|  | 			use.EraseFromParentAsInstruction() | ||||||
|  | 		} else if len(itf.types) == 1 { | ||||||
|  | 			// There is only one implementation of the given type. | ||||||
|  | 			// Call that function directly. | ||||||
|  | 			p.replaceInvokeWithCall(use, itf.types[0], signature) | ||||||
|  | 		} else { | ||||||
|  | 			// There are multiple types implementing this interface, thus there | ||||||
|  | 			// are multiple possible functions to call. Delegate calling the | ||||||
|  | 			// right function to a special wrapper function. | ||||||
|  | 			bitcasts := getUses(use) | ||||||
|  | 			if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() { | ||||||
|  | 				panic("expected exactly one bitcast use of runtime.interfaceMethod") | ||||||
|  | 			} | ||||||
|  | 			bitcast := bitcasts[0] | ||||||
|  | 			calls := getUses(bitcast) | ||||||
|  | 			if len(calls) != 1 || calls[0].IsACallInst().IsNil() { | ||||||
|  | 				panic("expected exactly one call use of runtime.interfaceMethod") | ||||||
|  | 			} | ||||||
|  | 			call := calls[0] | ||||||
|  | 
 | ||||||
|  | 			// Set up parameters for the call. First copy the regular params... | ||||||
|  | 			params := make([]llvm.Value, call.OperandsCount()) | ||||||
|  | 			paramTypes := make([]llvm.Type, len(params)) | ||||||
|  | 			for i := 0; i < len(params)-1; i++ { | ||||||
|  | 				params[i] = call.Operand(i) | ||||||
|  | 				paramTypes[i] = params[i].Type() | ||||||
|  | 			} | ||||||
|  | 			// then add the typecode to the end of the list. | ||||||
|  | 			params[len(params)-1] = typecode | ||||||
|  | 			paramTypes[len(params)-1] = p.uintptrType | ||||||
|  | 
 | ||||||
|  | 			// Create a function that redirects the call to the destination | ||||||
|  | 			// call, after selecting the right concrete type. | ||||||
|  | 			redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes) | ||||||
|  | 
 | ||||||
|  | 			// Replace the old lookup/bitcast/call with the new call. | ||||||
|  | 			p.builder.SetInsertPointBefore(call) | ||||||
|  | 			retval := p.builder.CreateCall(redirector, params, "") | ||||||
|  | 			if retval.Type().TypeKind() != llvm.VoidTypeKind { | ||||||
|  | 				call.ReplaceAllUsesWith(retval) | ||||||
|  | 			} | ||||||
|  | 			call.EraseFromParentAsInstruction() | ||||||
|  | 			bitcast.EraseFromParentAsInstruction() | ||||||
|  | 			use.EraseFromParentAsInstruction() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Replace all typeasserts on interface types with matches on their concrete | ||||||
|  | 	// types, if possible. | ||||||
|  | 	for _, use := range interfaceImplementsUses { | ||||||
|  | 		actualType := use.Operand(0) | ||||||
|  | 		if !actualType.IsACallInst().IsNil() && actualType.CalledValue() == makeInterface { | ||||||
|  | 			// Type assert is in the same function that creates the interface | ||||||
|  | 			// value. This means the underlying type is already known so match | ||||||
|  | 			// on that. | ||||||
|  | 			// This may not happen often but it is an easy optimization. | ||||||
|  | 			name := actualType.Operand(0).Name() | ||||||
|  | 			typ := p.types[name] | ||||||
|  | 			p.builder.SetInsertPointBefore(use) | ||||||
|  | 			assertedType := p.builder.CreatePtrToInt(typ.typecode, p.uintptrType, "typeassert.typecode") | ||||||
|  | 			commaOk := p.builder.CreateICmp(llvm.IntEQ, assertedType, actualType, "typeassert.ok") | ||||||
|  | 			use.ReplaceAllUsesWith(commaOk) | ||||||
|  | 			use.EraseFromParentAsInstruction() | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		methodSet := use.Operand(1).Operand(0) // global variable | ||||||
|  | 		itf := p.interfaces[methodSet.Name()] | ||||||
|  | 		if len(itf.types) == 0 { | ||||||
|  | 			// There are no types implementing this interface, so this assert | ||||||
|  | 			// can never succeed. | ||||||
|  | 			// Signal this to the optimizer by branching on constant false. It | ||||||
|  | 			// should remove the "then" block. | ||||||
|  | 			use.ReplaceAllUsesWith(llvm.ConstInt(p.ctx.Int1Type(), 0, false)) | ||||||
|  | 			use.EraseFromParentAsInstruction() | ||||||
|  | 		} else if len(itf.types) == 1 { | ||||||
|  | 			// There is only one type implementing this interface. | ||||||
|  | 			// Transform this interface assert into comparison against a | ||||||
|  | 			// constant. | ||||||
|  | 			p.builder.SetInsertPointBefore(use) | ||||||
|  | 			assertedType := p.builder.CreatePtrToInt(itf.types[0].typecode, p.uintptrType, "typeassert.typecode") | ||||||
|  | 			commaOk := p.builder.CreateICmp(llvm.IntEQ, assertedType, actualType, "typeassert.ok") | ||||||
|  | 			use.ReplaceAllUsesWith(commaOk) | ||||||
|  | 			use.EraseFromParentAsInstruction() | ||||||
|  | 		} else { | ||||||
|  | 			// There are multiple possible types implementing this interface. | ||||||
|  | 			// Create a function that does a type switch on all available types | ||||||
|  | 			// that implement this interface. | ||||||
|  | 			fn := p.getInterfaceImplementsFunc(itf) | ||||||
|  | 			p.builder.SetInsertPointBefore(use) | ||||||
|  | 			commaOk := p.builder.CreateCall(fn, []llvm.Value{actualType}, "typeassert.ok") | ||||||
|  | 			use.ReplaceAllUsesWith(commaOk) | ||||||
|  | 			use.EraseFromParentAsInstruction() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Make a slice of types sorted by frequency of use. | ||||||
|  | 	typeSlice := make(typeInfoSlice, 0, len(p.types)) | ||||||
|  | 	for _, t := range p.types { | ||||||
|  | 		typeSlice = append(typeSlice, t) | ||||||
|  | 	} | ||||||
|  | 	sort.Sort(typeSlice) | ||||||
|  | 
 | ||||||
|  | 	// A type code must fit in 16 bits. | ||||||
|  | 	if len(typeSlice) >= 1<<16 { | ||||||
|  | 		panic("typecode does not fit in a uint16: too many types in this program") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Assign a type code for each type. | ||||||
|  | 	for i, t := range typeSlice { | ||||||
|  | 		t.num = uint64(i + 1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Replace each call to runtime.makeInterface with the constant type code. | ||||||
|  | 	for _, use := range makeInterfaceUses { | ||||||
|  | 		global := use.Operand(0) | ||||||
|  | 		t := p.types[global.Name()] | ||||||
|  | 		use.ReplaceAllUsesWith(llvm.ConstPtrToInt(t.typecode, p.uintptrType)) | ||||||
|  | 		use.EraseFromParentAsInstruction() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Replace each type assert with an actual type comparison or (if the type | ||||||
|  | 	// assert is impossible) the constant false. | ||||||
|  | 	for _, use := range typeAssertUses { | ||||||
|  | 		actualType := use.Operand(0) | ||||||
|  | 		assertedTypeGlobal := use.Operand(1) | ||||||
|  | 		t := p.types[assertedTypeGlobal.Name()] | ||||||
|  | 		var commaOk llvm.Value | ||||||
|  | 		if t.countMakeInterfaces == 0 { | ||||||
|  | 			// impossible type assert: optimize accordingly | ||||||
|  | 			commaOk = llvm.ConstInt(llvm.Int1Type(), 0, false) | ||||||
|  | 		} else { | ||||||
|  | 			// regular type assert | ||||||
|  | 			p.builder.SetInsertPointBefore(use) | ||||||
|  | 			commaOk = p.builder.CreateICmp(llvm.IntEQ, llvm.ConstPtrToInt(assertedTypeGlobal, p.uintptrType), actualType, "typeassert.ok") | ||||||
|  | 		} | ||||||
|  | 		use.ReplaceAllUsesWith(commaOk) | ||||||
|  | 		use.EraseFromParentAsInstruction() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Fill in each helper function for type asserts on interfaces | ||||||
|  | 	// (interface-to-interface matches). | ||||||
|  | 	for _, itf := range p.interfaces { | ||||||
|  | 		if !itf.assertFunc.IsNil() { | ||||||
|  | 			p.createInterfaceImplementsFunc(itf) | ||||||
|  | 		} | ||||||
|  | 		for signature := range itf.methodFuncs { | ||||||
|  | 			p.createInterfaceMethodFunc(itf, signature) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Replace all ptrtoint typecode placeholders with their final type code | ||||||
|  | 	// numbers. | ||||||
|  | 	for _, typ := range p.types { | ||||||
|  | 		for _, use := range getUses(typ.typecode) { | ||||||
|  | 			if use.IsConstant() && use.Opcode() == llvm.PtrToInt { | ||||||
|  | 				use.ReplaceAllUsesWith(llvm.ConstInt(p.uintptrType, typ.num, false)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Remove method sets of types. Unnecessary, but cleans up the IR for | ||||||
|  | 	// inspection. | ||||||
|  | 	for _, typ := range p.types { | ||||||
|  | 		if !typ.methodSet.IsNil() { | ||||||
|  | 			typ.methodSet.EraseFromParentAsGlobal() | ||||||
|  | 			typ.methodSet = llvm.Value{} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // addType retrieves Go type information based on a i16 global variable. | ||||||
|  | // Only the name of the i16 is relevant, the object itself is const-propagated | ||||||
|  | // and discared afterwards. | ||||||
|  | func (p *lowerInterfacesPass) addType(typecode llvm.Value) *typeInfo { | ||||||
|  | 	name := typecode.Name() | ||||||
|  | 	t := &typeInfo{ | ||||||
|  | 		name:     name, | ||||||
|  | 		typecode: typecode, | ||||||
|  | 	} | ||||||
|  | 	p.types[name] = t | ||||||
|  | 	return t | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // addTypeMethods reads the method set of the given type info struct. It | ||||||
|  | // retrieves the signatures and the references to the method functions | ||||||
|  | // themselves for later type<->interface matching. | ||||||
|  | func (p *lowerInterfacesPass) addTypeMethods(t *typeInfo, methodSet llvm.Value) { | ||||||
|  | 	if !t.methodSet.IsNil() || methodSet.IsNull() { | ||||||
|  | 		// no methods or methods already read | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	methodSet = methodSet.Operand(0) // get global from GEP | ||||||
|  | 
 | ||||||
|  | 	// This type has methods, collect all methods of this type. | ||||||
|  | 	t.methodSet = methodSet | ||||||
|  | 	set := methodSet.Initializer() // get value from global | ||||||
|  | 	for i := 0; i < set.Type().ArrayLength(); i++ { | ||||||
|  | 		methodData := llvm.ConstExtractValue(set, []uint32{uint32(i)}) | ||||||
|  | 		signatureName := llvm.ConstExtractValue(methodData, []uint32{0}).Name() | ||||||
|  | 		function := llvm.ConstExtractValue(methodData, []uint32{1}).Operand(0) | ||||||
|  | 		signature := p.getSignature(signatureName) | ||||||
|  | 		method := &methodInfo{ | ||||||
|  | 			function:      function, | ||||||
|  | 			signatureInfo: signature, | ||||||
|  | 		} | ||||||
|  | 		signature.methods = append(signature.methods, method) | ||||||
|  | 		t.methods = append(t.methods, method) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // addInterface reads information about an interface, which is the | ||||||
|  | // fully-qualified name and the signatures of all methods it has. | ||||||
|  | func (p *lowerInterfacesPass) addInterface(methodSet llvm.Value) { | ||||||
|  | 	name := methodSet.Name() | ||||||
|  | 	t := &interfaceInfo{ | ||||||
|  | 		name: name, | ||||||
|  | 	} | ||||||
|  | 	p.interfaces[name] = t | ||||||
|  | 	methodSet = methodSet.Initializer() // get global value from getelementptr | ||||||
|  | 	for i := 0; i < methodSet.Type().ArrayLength(); i++ { | ||||||
|  | 		signatureName := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)}).Name() | ||||||
|  | 		signature := p.getSignature(signatureName) | ||||||
|  | 		signature.interfaces = append(signature.interfaces, t) | ||||||
|  | 		t.signatures = append(t.signatures, signature) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getSignature returns a new *signatureInfo, creating it if it doesn't already | ||||||
|  | // exist. | ||||||
|  | func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo { | ||||||
|  | 	if _, ok := p.signatures[name]; !ok { | ||||||
|  | 		p.signatures[name] = &signatureInfo{ | ||||||
|  | 			name: name, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return p.signatures[name] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // replaceInvokeWithCall replaces a runtime.interfaceMethod + bitcast with a | ||||||
|  | // concrete method. This can be done when only one type implements the | ||||||
|  | // interface. | ||||||
|  | func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) { | ||||||
|  | 	bitcasts := getUses(use) | ||||||
|  | 	if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() { | ||||||
|  | 		panic("expected exactly one bitcast use of runtime.interfaceMethod") | ||||||
|  | 	} | ||||||
|  | 	bitcast := bitcasts[0] | ||||||
|  | 	function := typ.getMethod(signature).function | ||||||
|  | 	if bitcast.Type() != function.Type() { | ||||||
|  | 		p.builder.SetInsertPointBefore(use) | ||||||
|  | 		function = p.builder.CreateBitCast(function, bitcast.Type(), "") | ||||||
|  | 	} | ||||||
|  | 	bitcast.ReplaceAllUsesWith(function) | ||||||
|  | 	bitcast.EraseFromParentAsInstruction() | ||||||
|  | 	use.EraseFromParentAsInstruction() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getInterfaceImplementsFunc returns a function that checks whether a given | ||||||
|  | // interface type implements a given interface, by checking all possible types | ||||||
|  | // that implement this interface. | ||||||
|  | func (p *lowerInterfacesPass) getInterfaceImplementsFunc(itf *interfaceInfo) llvm.Value { | ||||||
|  | 	if !itf.assertFunc.IsNil() { | ||||||
|  | 		return itf.assertFunc | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Create the function and function signature. | ||||||
|  | 	// TODO: debug info | ||||||
|  | 	fnName := itf.id() + "$typeassert" | ||||||
|  | 	fnType := llvm.FunctionType(p.ctx.Int1Type(), []llvm.Type{p.uintptrType}, false) | ||||||
|  | 	itf.assertFunc = llvm.AddFunction(p.mod, fnName, fnType) | ||||||
|  | 	itf.assertFunc.Param(0).SetName("actualType") | ||||||
|  | 
 | ||||||
|  | 	// Type asserts will be made for each type, so increment the counter for | ||||||
|  | 	// those. | ||||||
|  | 	for _, typ := range itf.types { | ||||||
|  | 		typ.countTypeAsserts++ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return itf.assertFunc | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // createInterfaceImplementsFunc finishes the work of | ||||||
|  | // getInterfaceImplementsFunc, because it needs to run after types have a type | ||||||
|  | // code assigned. | ||||||
|  | // | ||||||
|  | // The type match is implemented using a big type switch over all possible | ||||||
|  | // types. | ||||||
|  | func (p *lowerInterfacesPass) createInterfaceImplementsFunc(itf *interfaceInfo) { | ||||||
|  | 	fn := itf.assertFunc | ||||||
|  | 	fn.SetLinkage(llvm.InternalLinkage) | ||||||
|  | 	fn.SetUnnamedAddr(true) | ||||||
|  | 
 | ||||||
|  | 	// TODO: debug info | ||||||
|  | 
 | ||||||
|  | 	// Create all used basic blocks. | ||||||
|  | 	entry := llvm.AddBasicBlock(fn, "entry") | ||||||
|  | 	thenBlock := llvm.AddBasicBlock(fn, "then") | ||||||
|  | 	elseBlock := llvm.AddBasicBlock(fn, "else") | ||||||
|  | 
 | ||||||
|  | 	// Add all possible types as cases. | ||||||
|  | 	p.builder.SetInsertPointAtEnd(entry) | ||||||
|  | 	actualType := fn.Param(0) | ||||||
|  | 	sw := p.builder.CreateSwitch(actualType, elseBlock, len(itf.types)) | ||||||
|  | 	for _, typ := range itf.types { | ||||||
|  | 		sw.AddCase(llvm.ConstInt(p.uintptrType, typ.num, false), thenBlock) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Fill 'then' block (type assert was successful). | ||||||
|  | 	p.builder.SetInsertPointAtEnd(thenBlock) | ||||||
|  | 	p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 1, false)) | ||||||
|  | 
 | ||||||
|  | 	// Fill 'else' block (type asserted failed). | ||||||
|  | 	p.builder.SetInsertPointAtEnd(elseBlock) | ||||||
|  | 	p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 0, false)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getInterfaceMethodFunc return a function that returns a function pointer for | ||||||
|  | // calling a method on an interface. It only declares the function, | ||||||
|  | // createInterfaceMethodFunc actually defines the function. | ||||||
|  | func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signature *signatureInfo, returnType llvm.Type, params []llvm.Type) llvm.Value { | ||||||
|  | 	if fn, ok := itf.methodFuncs[signature]; ok { | ||||||
|  | 		// This function has already been created. | ||||||
|  | 		return fn | ||||||
|  | 	} | ||||||
|  | 	if itf.methodFuncs == nil { | ||||||
|  | 		// initialize the above map | ||||||
|  | 		itf.methodFuncs = make(map[*signatureInfo]llvm.Value) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Construct the function name, which is of the form: | ||||||
|  | 	//     (main.Stringer).String | ||||||
|  | 	fnName := "(" + itf.id() + ")." + signature.methodName() | ||||||
|  | 	fnType := llvm.FunctionType(returnType, params, false) | ||||||
|  | 	fn := llvm.AddFunction(p.mod, fnName, fnType) | ||||||
|  | 	fn.LastParam().SetName("actualType") | ||||||
|  | 	itf.methodFuncs[signature] = fn | ||||||
|  | 	return fn | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // createInterfaceMethodFunc finishes the work of getInterfaceMethodFunc, | ||||||
|  | // because it needs to run after type codes have been assigned to concrete | ||||||
|  | // types. | ||||||
|  | // | ||||||
|  | // Matching the actual type is implemented using a big type switch over all | ||||||
|  | // possible types. | ||||||
|  | func (p *lowerInterfacesPass) createInterfaceMethodFunc(itf *interfaceInfo, signature *signatureInfo) { | ||||||
|  | 	fn := itf.methodFuncs[signature] | ||||||
|  | 	fn.SetLinkage(llvm.InternalLinkage) | ||||||
|  | 	fn.SetUnnamedAddr(true) | ||||||
|  | 
 | ||||||
|  | 	// TODO: debug info | ||||||
|  | 
 | ||||||
|  | 	// Create entry block. | ||||||
|  | 	entry := llvm.AddBasicBlock(fn, "entry") | ||||||
|  | 
 | ||||||
|  | 	// Create default block and make it unreachable (which it is, because all | ||||||
|  | 	// possible types are checked). | ||||||
|  | 	defaultBlock := llvm.AddBasicBlock(fn, "default") | ||||||
|  | 	p.builder.SetInsertPointAtEnd(defaultBlock) | ||||||
|  | 	p.builder.CreateUnreachable() | ||||||
|  | 
 | ||||||
|  | 	// Create type switch in entry block. | ||||||
|  | 	p.builder.SetInsertPointAtEnd(entry) | ||||||
|  | 	actualType := fn.LastParam() | ||||||
|  | 	sw := p.builder.CreateSwitch(actualType, defaultBlock, len(itf.types)) | ||||||
|  | 
 | ||||||
|  | 	// Collect the params that will be passed to the functions to call. | ||||||
|  | 	// These params exclude the receiver (which may actually consist of multiple | ||||||
|  | 	// parts). | ||||||
|  | 	params := make([]llvm.Value, fn.ParamsCount()-2) | ||||||
|  | 	for i := range params { | ||||||
|  | 		params[i] = fn.Param(i + 1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Define all possible functions that can be called. | ||||||
|  | 	for _, typ := range itf.types { | ||||||
|  | 		bb := llvm.AddBasicBlock(fn, typ.id()) | ||||||
|  | 		sw.AddCase(llvm.ConstInt(p.uintptrType, typ.num, false), bb) | ||||||
|  | 
 | ||||||
|  | 		// The function we will redirect to when the interface has this type. | ||||||
|  | 		function := typ.getMethod(signature).function | ||||||
|  | 
 | ||||||
|  | 		p.builder.SetInsertPointAtEnd(bb) | ||||||
|  | 		receiver := fn.FirstParam() | ||||||
|  | 		if receiver.Type() != function.FirstParam().Type() { | ||||||
|  | 			// When the receiver is a pointer, it is not wrapped. This means the | ||||||
|  | 			// i8* has to be cast to the correct pointer type of the target | ||||||
|  | 			// function. | ||||||
|  | 			receiver = p.builder.CreateBitCast(receiver, function.FirstParam().Type(), "") | ||||||
|  | 		} | ||||||
|  | 		retval := p.builder.CreateCall(function, append([]llvm.Value{receiver}, params...), "") | ||||||
|  | 		if retval.Type().TypeKind() == llvm.VoidTypeKind { | ||||||
|  | 			p.builder.CreateRetVoid() | ||||||
|  | 		} else { | ||||||
|  | 			p.builder.CreateRet(retval) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,5 +1,10 @@ | ||||||
| package compiler | package compiler | ||||||
| 
 | 
 | ||||||
|  | // This file transforms interface-related instructions (*ssa.MakeInterface, | ||||||
|  | // *ssa.TypeAssert, calls on interface types) to an intermediate IR form, to be | ||||||
|  | // lowered to the final form by the interface lowering pass. See | ||||||
|  | // interface-lowering.go for more details. | ||||||
|  | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"go/types" | 	"go/types" | ||||||
|  | @ -32,10 +37,10 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global str | ||||||
| 			// Allocate on the heap and put a pointer in the interface. | 			// Allocate on the heap and put a pointer in the interface. | ||||||
| 			// TODO: escape analysis. | 			// TODO: escape analysis. | ||||||
| 			sizeValue := llvm.ConstInt(c.uintptrType, size, false) | 			sizeValue := llvm.ConstInt(c.uintptrType, size, false) | ||||||
| 			alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "") | 			alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "makeinterface.alloc") | ||||||
| 			itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "") | 			itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "makeinterface.cast.value") | ||||||
| 			c.builder.CreateStore(val, itfValueCast) | 			c.builder.CreateStore(val, itfValueCast) | ||||||
| 			itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "") | 			itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "makeinterface.cast.i8ptr") | ||||||
| 		} | 		} | ||||||
| 	} else if size == 0 { | 	} else if size == 0 { | ||||||
| 		itfValue = llvm.ConstPointerNull(c.i8ptrType) | 		itfValue = llvm.ConstPointerNull(c.i8ptrType) | ||||||
|  | @ -43,38 +48,136 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global str | ||||||
| 		// Directly place the value in the interface. | 		// Directly place the value in the interface. | ||||||
| 		switch val.Type().TypeKind() { | 		switch val.Type().TypeKind() { | ||||||
| 		case llvm.IntegerTypeKind: | 		case llvm.IntegerTypeKind: | ||||||
| 			itfValue = c.builder.CreateIntToPtr(val, c.i8ptrType, "") | 			itfValue = c.builder.CreateIntToPtr(val, c.i8ptrType, "makeinterface.cast.int") | ||||||
| 		case llvm.PointerTypeKind: | 		case llvm.PointerTypeKind: | ||||||
| 			itfValue = c.builder.CreateBitCast(val, c.i8ptrType, "") | 			itfValue = c.builder.CreateBitCast(val, c.i8ptrType, "makeinterface.cast.ptr") | ||||||
| 		case llvm.StructTypeKind: | 		case llvm.StructTypeKind: | ||||||
| 			// A bitcast would be useful here, but bitcast doesn't allow | 			// A bitcast would be useful here, but bitcast doesn't allow | ||||||
| 			// aggregate types. So we'll bitcast it using an alloca. | 			// aggregate types. So we'll bitcast it using an alloca. | ||||||
| 			// Hopefully this will get optimized away. | 			// Hopefully this will get optimized away. | ||||||
| 			mem := c.builder.CreateAlloca(c.i8ptrType, "") | 			mem := c.builder.CreateAlloca(c.i8ptrType, "makeinterface.cast.struct") | ||||||
| 			memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(val.Type(), 0), "") | 			memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(val.Type(), 0), "makeinterface.cast.struct.cast") | ||||||
| 			c.builder.CreateStore(val, memStructPtr) | 			c.builder.CreateStore(val, memStructPtr) | ||||||
| 			itfValue = c.builder.CreateLoad(mem, "") | 			itfValue = c.builder.CreateLoad(mem, "makeinterface.cast.load") | ||||||
| 		default: | 		default: | ||||||
| 			return llvm.Value{}, errors.New("todo: makeinterface: cast small type to i8*") | 			return llvm.Value{}, errors.New("todo: makeinterface: cast small type to i8*") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	itfTypeNum, _ := c.ir.TypeNum(typ) | 	itfTypeCodeGlobal := c.getTypeCode(typ) | ||||||
| 	if itfTypeNum >= 1<<16 { | 	itfMethodSetGlobal, err := c.getTypeMethodSet(typ) | ||||||
| 		return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") | 	if err != nil { | ||||||
|  | 		return llvm.Value{}, nil | ||||||
| 	} | 	} | ||||||
| 	itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), []llvm.Value{llvm.ConstInt(c.ctx.Int16Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType)}) | 	itfTypeCode := c.createRuntimeCall("makeInterface", []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}, "makeinterface.typecode") | ||||||
|  | 	itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface")) | ||||||
|  | 	itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "") | ||||||
| 	itf = c.builder.CreateInsertValue(itf, itfValue, 1, "") | 	itf = c.builder.CreateInsertValue(itf, itfValue, 1, "") | ||||||
| 	return itf, nil | 	return itf, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // getTypeCode returns a reference to a type code. | ||||||
|  | // It returns a pointer to an external global which should be replaced with the | ||||||
|  | // real type in the interface lowering pass. | ||||||
|  | func (c *Compiler) getTypeCode(typ types.Type) llvm.Value { | ||||||
|  | 	global := c.mod.NamedGlobal(typ.String() + "$type") | ||||||
|  | 	if global.IsNil() { | ||||||
|  | 		global = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), typ.String()+"$type") | ||||||
|  | 		global.SetGlobalConstant(true) | ||||||
|  | 	} | ||||||
|  | 	return global | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getTypeMethodSet returns a reference (GEP) to a global method set. This | ||||||
|  | // method set should be unreferenced after the interface lowering pass. | ||||||
|  | func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) { | ||||||
|  | 	global := c.mod.NamedGlobal(typ.String() + "$methodset") | ||||||
|  | 	zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) | ||||||
|  | 	if !global.IsNil() { | ||||||
|  | 		// the method set already exists | ||||||
|  | 		return llvm.ConstGEP(global, []llvm.Value{zero, zero}), nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ms := c.ir.Program.MethodSets.MethodSet(typ) | ||||||
|  | 	if ms.Len() == 0 { | ||||||
|  | 		// no methods, so can leave that one out | ||||||
|  | 		return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0)), nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	methods := make([]llvm.Value, ms.Len()) | ||||||
|  | 	interfaceMethodInfoType := c.mod.GetTypeByName("runtime.interfaceMethodInfo") | ||||||
|  | 	for i := 0; i < ms.Len(); i++ { | ||||||
|  | 		method := ms.At(i) | ||||||
|  | 		signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func)) | ||||||
|  | 		f := c.ir.GetFunction(c.ir.Program.MethodValue(method)) | ||||||
|  | 		if f.LLVMFn.IsNil() { | ||||||
|  | 			// compiler error, so panic | ||||||
|  | 			panic("cannot find function: " + f.LinkName()) | ||||||
|  | 		} | ||||||
|  | 		fn, err := c.getInterfaceInvokeWrapper(f) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return llvm.Value{}, err | ||||||
|  | 		} | ||||||
|  | 		methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{ | ||||||
|  | 			signatureGlobal, | ||||||
|  | 			llvm.ConstBitCast(fn, c.i8ptrType), | ||||||
|  | 		}) | ||||||
|  | 		methods[i] = methodInfo | ||||||
|  | 	} | ||||||
|  | 	arrayType := llvm.ArrayType(interfaceMethodInfoType, len(methods)) | ||||||
|  | 	value := llvm.ConstArray(interfaceMethodInfoType, methods) | ||||||
|  | 	global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset") | ||||||
|  | 	global.SetInitializer(value) | ||||||
|  | 	global.SetGlobalConstant(true) | ||||||
|  | 	global.SetLinkage(llvm.PrivateLinkage) | ||||||
|  | 	return llvm.ConstGEP(global, []llvm.Value{zero, zero}), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getInterfaceMethodSet returns a global variable with the method set of the | ||||||
|  | // given named interface type. This method set is used by the interface lowering | ||||||
|  | // pass. | ||||||
|  | func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value { | ||||||
|  | 	global := c.mod.NamedGlobal(typ.String() + "$interface") | ||||||
|  | 	zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) | ||||||
|  | 	if !global.IsNil() { | ||||||
|  | 		// method set already exist, return it | ||||||
|  | 		return llvm.ConstGEP(global, []llvm.Value{zero, zero}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Every method is a *i16 reference indicating the signature of this method. | ||||||
|  | 	methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods()) | ||||||
|  | 	for i := range methods { | ||||||
|  | 		method := typ.Underlying().(*types.Interface).Method(i) | ||||||
|  | 		methods[i] = c.getMethodSignature(method) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	value := llvm.ConstArray(methods[0].Type(), methods) | ||||||
|  | 	global = llvm.AddGlobal(c.mod, value.Type(), typ.String()+"$interface") | ||||||
|  | 	global.SetInitializer(value) | ||||||
|  | 	global.SetGlobalConstant(true) | ||||||
|  | 	global.SetLinkage(llvm.PrivateLinkage) | ||||||
|  | 	return llvm.ConstGEP(global, []llvm.Value{zero, zero}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getMethodSignature returns a global variable which is a reference to an | ||||||
|  | // external *i16 indicating the indicating the signature of this method. It is | ||||||
|  | // used during the interface lowering pass. | ||||||
|  | func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value { | ||||||
|  | 	signature := ir.MethodSignature(method) | ||||||
|  | 	signatureGlobal := c.mod.NamedGlobal("func " + signature) | ||||||
|  | 	if signatureGlobal.IsNil() { | ||||||
|  | 		signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature) | ||||||
|  | 		signatureGlobal.SetGlobalConstant(true) | ||||||
|  | 	} | ||||||
|  | 	return signatureGlobal | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // parseTypeAssert will emit the code for a typeassert, used in if statements | // parseTypeAssert will emit the code for a typeassert, used in if statements | ||||||
| // and in switch statements (Go SSA does not have type switches, only if/else | // and in type switches (Go SSA does not have type switches, only if/else | ||||||
| // chains). Note that even though the Go SSA does not contain type switches, | // chains). Note that even though the Go SSA does not contain type switches, | ||||||
| // LLVM will recognize the pattern and make it a real switch in many cases. | // LLVM will recognize the pattern and make it a real switch in many cases. | ||||||
| // | // | ||||||
| // Type asserts on concrete types are trivial: just compare type numbers. Type | // Type asserts on concrete types are trivial: just compare type numbers. Type | ||||||
| // asserts on interfaces are more difficult to implement and so are delegated to | // asserts on interfaces are more difficult, see the comments in the function. | ||||||
| // a runtime library function. |  | ||||||
| func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Value, error) { | func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Value, error) { | ||||||
| 	itf, err := c.parseExpr(frame, expr.X) | 	itf, err := c.parseExpr(frame, expr.X) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | @ -91,38 +194,24 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Val | ||||||
| 
 | 
 | ||||||
| 	actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") | 	actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") | ||||||
| 	commaOk := llvm.Value{} | 	commaOk := llvm.Value{} | ||||||
| 	if itf, ok := expr.AssertedType.Underlying().(*types.Interface); ok { | 	if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok { | ||||||
| 		// Type assert on interface type. | 		// Type assert on interface type. | ||||||
| 		// This is slightly non-trivial: at runtime the list of methods | 		// This pseudo call will be lowered in the interface lowering pass to a | ||||||
| 		// needs to be checked to see whether it implements the interface. | 		// real call which checks whether the provided typecode is any of the | ||||||
| 		// At the same time, the interface value itself is unchanged. | 		// concrete types that implements this interface. | ||||||
| 		itfTypeNum := c.ir.InterfaceNum(itf) | 		// This is very different from how interface asserts are implemented in | ||||||
| 		itfTypeNumValue := llvm.ConstInt(c.ctx.Int16Type(), uint64(itfTypeNum), false) | 		// the main Go compiler, where the runtime checks whether the type | ||||||
| 		commaOk = c.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, itfTypeNumValue}, "") | 		// implements each method of the interface. See: | ||||||
|  | 		// https://research.swtch.com/interfaces | ||||||
|  | 		methodSet := c.getInterfaceMethodSet(expr.AssertedType.(*types.Named)) | ||||||
|  | 		commaOk = c.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "") | ||||||
| 
 | 
 | ||||||
| 	} else { | 	} else { | ||||||
| 		// Type assert on concrete type. | 		// Type assert on concrete type. | ||||||
| 		// This is easy: just compare the type number. | 		// Call runtime.typeAssert, which will be lowered to a simple icmp or | ||||||
| 		assertedTypeNum, typeExists := c.ir.TypeNum(expr.AssertedType) | 		// const false in the interface lowering pass. | ||||||
| 		if !typeExists { | 		assertedTypeCodeGlobal := c.getTypeCode(expr.AssertedType) | ||||||
| 			// Static analysis has determined this type assert will never apply. | 		commaOk = c.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode") | ||||||
| 			// Using undef here so that LLVM knows we'll never get here and |  | ||||||
| 			// can optimize accordingly. |  | ||||||
| 			undef := llvm.Undef(assertedType) |  | ||||||
| 			commaOk := llvm.ConstInt(c.ctx.Int1Type(), 0, false) |  | ||||||
| 			if expr.CommaOk { |  | ||||||
| 				return c.ctx.ConstStruct([]llvm.Value{undef, commaOk}, false), nil |  | ||||||
| 			} else { |  | ||||||
| 				c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "") |  | ||||||
| 				return undef, nil |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if assertedTypeNum >= 1<<16 { |  | ||||||
| 			return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		assertedTypeNumValue := llvm.ConstInt(c.ctx.Int16Type(), uint64(assertedTypeNum), false) |  | ||||||
| 		commaOk = c.builder.CreateICmp(llvm.IntEQ, assertedTypeNumValue, actualTypeNum, "") |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Add 2 new basic blocks (that should get optimized away): one for the | 	// Add 2 new basic blocks (that should get optimized away): one for the | ||||||
|  | @ -171,16 +260,14 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Val | ||||||
| 				valueOk = c.builder.CreatePtrToInt(valuePtr, assertedType, "typeassert.value.ok") | 				valueOk = c.builder.CreatePtrToInt(valuePtr, assertedType, "typeassert.value.ok") | ||||||
| 			case llvm.PointerTypeKind: | 			case llvm.PointerTypeKind: | ||||||
| 				valueOk = c.builder.CreateBitCast(valuePtr, assertedType, "typeassert.value.ok") | 				valueOk = c.builder.CreateBitCast(valuePtr, assertedType, "typeassert.value.ok") | ||||||
| 			case llvm.StructTypeKind: | 			default: // struct, float, etc. | ||||||
| 				// A bitcast would be useful here, but bitcast doesn't allow | 				// A bitcast would be useful here, but bitcast doesn't allow | ||||||
| 				// aggregate types. So we'll bitcast it using an alloca. | 				// aggregate types. So we'll bitcast it using an alloca. | ||||||
| 				// Hopefully this will get optimized away. | 				// Hopefully this will get optimized away. | ||||||
| 				mem := c.builder.CreateAlloca(c.i8ptrType, "") | 				mem := c.builder.CreateAlloca(c.i8ptrType, "") | ||||||
| 				c.builder.CreateStore(valuePtr, mem) | 				c.builder.CreateStore(valuePtr, mem) | ||||||
| 				memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(assertedType, 0), "") | 				memCast := c.builder.CreateBitCast(mem, llvm.PointerType(assertedType, 0), "") | ||||||
| 				valueOk = c.builder.CreateLoad(memStructPtr, "typeassert.value.ok") | 				valueOk = c.builder.CreateLoad(memCast, "typeassert.value.ok") | ||||||
| 			default: |  | ||||||
| 				return llvm.Value{}, errors.New("todo: typeassert: bitcast small types") |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -231,7 +318,8 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu | ||||||
| 	typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode") | 	typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode") | ||||||
| 	values := []llvm.Value{ | 	values := []llvm.Value{ | ||||||
| 		typecode, | 		typecode, | ||||||
| 		llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false), | 		c.getInterfaceMethodSet(instr.Value.Type().(*types.Named)), | ||||||
|  | 		c.getMethodSignature(instr.Method), | ||||||
| 	} | 	} | ||||||
| 	fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func") | 	fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func") | ||||||
| 	fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast") | 	fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast") | ||||||
|  | @ -255,142 +343,40 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu | ||||||
| 	return fnCast, args, nil | 	return fnCast, args, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Initialize runtime type information, for interfaces. | // interfaceInvokeWrapper keeps some state between getInterfaceInvokeWrapper and | ||||||
| // See src/runtime/interface.go for more details. | // createInterfaceInvokeWrapper. The former is called during IR construction | ||||||
| func (c *Compiler) createInterfaceRTTI() error { | // itself and the latter is called when finishing up the IR. | ||||||
| 	dynamicTypes := c.ir.AllDynamicTypes() | type interfaceInvokeWrapper struct { | ||||||
| 	numDynamicTypes := 0 | 	fn           *ir.Function | ||||||
| 	for _, meta := range dynamicTypes { | 	wrapper      llvm.Value | ||||||
| 		numDynamicTypes += len(meta.Methods) | 	receiverType llvm.Type | ||||||
| 	} |  | ||||||
| 	ranges := make([]llvm.Value, 0, len(dynamicTypes)) |  | ||||||
| 	funcPointers := make([]llvm.Value, 0, numDynamicTypes) |  | ||||||
| 	signatures := make([]llvm.Value, 0, numDynamicTypes) |  | ||||||
| 	startIndex := 0 |  | ||||||
| 	rangeType := c.mod.GetTypeByName("runtime.methodSetRange") |  | ||||||
| 	for _, meta := range dynamicTypes { |  | ||||||
| 		rangeValues := []llvm.Value{ |  | ||||||
| 			llvm.ConstInt(c.ctx.Int16Type(), uint64(startIndex), false), |  | ||||||
| 			llvm.ConstInt(c.ctx.Int16Type(), uint64(len(meta.Methods)), false), |  | ||||||
| 		} |  | ||||||
| 		rangeValue := llvm.ConstNamedStruct(rangeType, rangeValues) |  | ||||||
| 		ranges = append(ranges, rangeValue) |  | ||||||
| 		methods := make([]*types.Selection, 0, len(meta.Methods)) |  | ||||||
| 		for _, method := range meta.Methods { |  | ||||||
| 			methods = append(methods, method) |  | ||||||
| 		} |  | ||||||
| 		c.ir.SortMethods(methods) |  | ||||||
| 		for _, method := range methods { |  | ||||||
| 			f := c.ir.GetFunction(c.ir.Program.MethodValue(method)) |  | ||||||
| 			if f.LLVMFn.IsNil() { |  | ||||||
| 				return errors.New("cannot find function: " + f.LinkName()) |  | ||||||
| 			} |  | ||||||
| 			fn, err := c.wrapInterfaceInvoke(f) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 			fnPtr := llvm.ConstBitCast(fn, c.i8ptrType) |  | ||||||
| 			funcPointers = append(funcPointers, fnPtr) |  | ||||||
| 			signatureNum := c.ir.MethodNum(method.Obj().(*types.Func)) |  | ||||||
| 			signature := llvm.ConstInt(c.ctx.Int16Type(), uint64(signatureNum), false) |  | ||||||
| 			signatures = append(signatures, signature) |  | ||||||
| 		} |  | ||||||
| 		startIndex += len(meta.Methods) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	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(c.ctx.Int16Type(), uint64(i), false) |  | ||||||
| 		interfaceLengths[i] = llvm.ConstInt(c.ctx.Int8Type(), uint64(itfType.Type.NumMethods()), false) |  | ||||||
| 		funcs := make([]*types.Func, itfType.Type.NumMethods()) |  | ||||||
| 		for i := range funcs { |  | ||||||
| 			funcs[i] = itfType.Type.Method(i) |  | ||||||
| 		} |  | ||||||
| 		c.ir.SortFuncs(funcs) |  | ||||||
| 		for _, f := range funcs { |  | ||||||
| 			id := llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.MethodNum(f)), false) |  | ||||||
| 			interfaceMethods = append(interfaceMethods, id) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if len(ranges) >= 1<<16 { |  | ||||||
| 		return errors.New("method call numbers do not fit in a 16-bit integer") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Replace the pre-created arrays with the generated arrays. |  | ||||||
| 	rangeArray := llvm.ConstArray(rangeType, ranges) |  | ||||||
| 	rangeArrayNewGlobal := llvm.AddGlobal(c.mod, rangeArray.Type(), "runtime.methodSetRanges.tmp") |  | ||||||
| 	rangeArrayNewGlobal.SetInitializer(rangeArray) |  | ||||||
| 	rangeArrayNewGlobal.SetLinkage(llvm.InternalLinkage) |  | ||||||
| 	rangeArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetRanges") |  | ||||||
| 	rangeArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(rangeArrayNewGlobal, rangeArrayOldGlobal.Type())) |  | ||||||
| 	rangeArrayOldGlobal.EraseFromParentAsGlobal() |  | ||||||
| 	rangeArrayNewGlobal.SetName("runtime.methodSetRanges") |  | ||||||
| 	funcArray := llvm.ConstArray(c.i8ptrType, funcPointers) |  | ||||||
| 	funcArrayNewGlobal := llvm.AddGlobal(c.mod, funcArray.Type(), "runtime.methodSetFunctions.tmp") |  | ||||||
| 	funcArrayNewGlobal.SetInitializer(funcArray) |  | ||||||
| 	funcArrayNewGlobal.SetLinkage(llvm.InternalLinkage) |  | ||||||
| 	funcArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetFunctions") |  | ||||||
| 	funcArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(funcArrayNewGlobal, funcArrayOldGlobal.Type())) |  | ||||||
| 	funcArrayOldGlobal.EraseFromParentAsGlobal() |  | ||||||
| 	funcArrayNewGlobal.SetName("runtime.methodSetFunctions") |  | ||||||
| 	signatureArray := llvm.ConstArray(c.ctx.Int16Type(), signatures) |  | ||||||
| 	signatureArrayNewGlobal := llvm.AddGlobal(c.mod, signatureArray.Type(), "runtime.methodSetSignatures.tmp") |  | ||||||
| 	signatureArrayNewGlobal.SetInitializer(signatureArray) |  | ||||||
| 	signatureArrayNewGlobal.SetLinkage(llvm.InternalLinkage) |  | ||||||
| 	signatureArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetSignatures") |  | ||||||
| 	signatureArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(signatureArrayNewGlobal, signatureArrayOldGlobal.Type())) |  | ||||||
| 	signatureArrayOldGlobal.EraseFromParentAsGlobal() |  | ||||||
| 	signatureArrayNewGlobal.SetName("runtime.methodSetSignatures") |  | ||||||
| 	interfaceIndexArray := llvm.ConstArray(c.ctx.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(c.ctx.Int8Type(), interfaceLengths) |  | ||||||
| 	interfaceLengthsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceLengthsArray.Type(), "runtime.interfaceLengths.tmp") |  | ||||||
| 	interfaceLengthsArrayNewGlobal.SetInitializer(interfaceLengthsArray) |  | ||||||
| 	interfaceLengthsArrayNewGlobal.SetLinkage(llvm.InternalLinkage) |  | ||||||
| 	interfaceLengthsArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceLengths") |  | ||||||
| 	interfaceLengthsArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceLengthsArrayNewGlobal, interfaceLengthsArrayOldGlobal.Type())) |  | ||||||
| 	interfaceLengthsArrayOldGlobal.EraseFromParentAsGlobal() |  | ||||||
| 	interfaceLengthsArrayNewGlobal.SetName("runtime.interfaceLengths") |  | ||||||
| 	interfaceMethodsArray := llvm.ConstArray(c.ctx.Int16Type(), interfaceMethods) |  | ||||||
| 	interfaceMethodsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceMethodsArray.Type(), "runtime.interfaceMethods.tmp") |  | ||||||
| 	interfaceMethodsArrayNewGlobal.SetInitializer(interfaceMethodsArray) |  | ||||||
| 	interfaceMethodsArrayNewGlobal.SetLinkage(llvm.InternalLinkage) |  | ||||||
| 	interfaceMethodsArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceMethods") |  | ||||||
| 	interfaceMethodsArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceMethodsArrayNewGlobal, interfaceMethodsArrayOldGlobal.Type())) |  | ||||||
| 	interfaceMethodsArrayOldGlobal.EraseFromParentAsGlobal() |  | ||||||
| 	interfaceMethodsArrayNewGlobal.SetName("runtime.interfaceMethods") |  | ||||||
| 
 |  | ||||||
| 	c.mod.NamedGlobal("runtime.firstTypeWithMethods").SetInitializer(llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.FirstDynamicType()), false)) |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Wrap an interface method function pointer. The wrapper takes in a pointer to | // Wrap an interface method function pointer. The wrapper takes in a pointer to | ||||||
| // the underlying value, dereferences it, and calls the real method. This | // the underlying value, dereferences it, and calls the real method. This | ||||||
| // wrapper is only needed when the interface value actually doesn't fit in a | // wrapper is only needed when the interface value actually doesn't fit in a | ||||||
| // pointer and a pointer to the value must be created. | // pointer and a pointer to the value must be created. | ||||||
| func (c *Compiler) wrapInterfaceInvoke(f *ir.Function) (llvm.Value, error) { | func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) (llvm.Value, error) { | ||||||
|  | 	wrapperName := f.LinkName() + "$invoke" | ||||||
|  | 	wrapper := c.mod.NamedFunction(wrapperName) | ||||||
|  | 	if !wrapper.IsNil() { | ||||||
|  | 		// Wrapper already created. Return it directly. | ||||||
|  | 		return wrapper, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Get the expanded receiver type. | ||||||
| 	receiverType, err := c.getLLVMType(f.Params[0].Type()) | 	receiverType, err := c.getLLVMType(f.Params[0].Type()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return llvm.Value{}, err | 		return llvm.Value{}, err | ||||||
| 	} | 	} | ||||||
| 	expandedReceiverType := c.expandFormalParamType(receiverType) | 	expandedReceiverType := c.expandFormalParamType(receiverType) | ||||||
| 
 | 
 | ||||||
| 	if c.targetData.TypeAllocSize(receiverType) <= c.targetData.TypeAllocSize(c.i8ptrType) && len(expandedReceiverType) == 1 { | 	// Does this method even need any wrapping? | ||||||
| 		// nothing to wrap | 	if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind { | ||||||
|  | 		// Nothing to wrap. | ||||||
|  | 		// Casting a function signature to a different signature and calling it | ||||||
|  | 		// with a receiver pointer bitcasted to *i8 (as done in calls on an | ||||||
|  | 		// interface) is hopefully a safe (defined) operation. | ||||||
| 		return f.LLVMFn, nil | 		return f.LLVMFn, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -398,16 +384,30 @@ func (c *Compiler) wrapInterfaceInvoke(f *ir.Function) (llvm.Value, error) { | ||||||
| 	fnType := f.LLVMFn.Type().ElementType() | 	fnType := f.LLVMFn.Type().ElementType() | ||||||
| 	paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...) | 	paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...) | ||||||
| 	wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false) | 	wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false) | ||||||
| 	wrapper := llvm.AddFunction(c.mod, f.LinkName()+"$invoke", wrapFnType) | 	wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType) | ||||||
|  | 	c.interfaceInvokeWrappers = append(c.interfaceInvokeWrappers, interfaceInvokeWrapper{ | ||||||
|  | 		fn:           f, | ||||||
|  | 		wrapper:      wrapper, | ||||||
|  | 		receiverType: receiverType, | ||||||
|  | 	}) | ||||||
|  | 	return wrapper, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper, | ||||||
|  | // see that function for details. | ||||||
|  | func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) error { | ||||||
|  | 	wrapper := state.wrapper | ||||||
|  | 	fn := state.fn | ||||||
|  | 	receiverType := state.receiverType | ||||||
| 	wrapper.SetLinkage(llvm.InternalLinkage) | 	wrapper.SetLinkage(llvm.InternalLinkage) | ||||||
| 	wrapper.SetUnnamedAddr(true) | 	wrapper.SetUnnamedAddr(true) | ||||||
| 
 | 
 | ||||||
| 	// add debug info | 	// add debug info if needed | ||||||
| 	if c.Debug { | 	if c.Debug { | ||||||
| 		pos := c.ir.Program.Fset.Position(f.Pos()) | 		pos := c.ir.Program.Fset.Position(fn.Pos()) | ||||||
| 		difunc, err := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line) | 		difunc, err := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return llvm.Value{}, err | 			return err | ||||||
| 		} | 		} | ||||||
| 		c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) | 		c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) | ||||||
| 	} | 	} | ||||||
|  | @ -424,7 +424,7 @@ func (c *Compiler) wrapInterfaceInvoke(f *ir.Function) (llvm.Value, error) { | ||||||
| 		// Load the underlying value. | 		// Load the underlying value. | ||||||
| 		receiverPtrType := llvm.PointerType(receiverType, 0) | 		receiverPtrType := llvm.PointerType(receiverType, 0) | ||||||
| 		receiverPtr = c.builder.CreateBitCast(wrapper.Param(0), receiverPtrType, "receiver.ptr") | 		receiverPtr = c.builder.CreateBitCast(wrapper.Param(0), receiverPtrType, "receiver.ptr") | ||||||
| 	} else if len(expandedReceiverType) != 1 { | 	} else { | ||||||
| 		// The value is stored in the interface, but it is of type struct which | 		// The value is stored in the interface, but it is of type struct which | ||||||
| 		// is expanded to multiple parameters (e.g. {i8, i8}). So we have to | 		// is expanded to multiple parameters (e.g. {i8, i8}). So we have to | ||||||
| 		// receive the struct as parameter, expand it, and pass it on to the | 		// receive the struct as parameter, expand it, and pass it on to the | ||||||
|  | @ -435,19 +435,17 @@ func (c *Compiler) wrapInterfaceInvoke(f *ir.Function) (llvm.Value, error) { | ||||||
| 		alloca := c.builder.CreateAlloca(c.i8ptrType, "receiver.alloca") | 		alloca := c.builder.CreateAlloca(c.i8ptrType, "receiver.alloca") | ||||||
| 		c.builder.CreateStore(wrapper.Param(0), alloca) | 		c.builder.CreateStore(wrapper.Param(0), alloca) | ||||||
| 		receiverPtr = c.builder.CreateBitCast(alloca, llvm.PointerType(receiverType, 0), "receiver.ptr") | 		receiverPtr = c.builder.CreateBitCast(alloca, llvm.PointerType(receiverType, 0), "receiver.ptr") | ||||||
| 	} else { |  | ||||||
| 		panic("unreachable") |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	receiverValue := c.builder.CreateLoad(receiverPtr, "receiver") | 	receiverValue := c.builder.CreateLoad(receiverPtr, "receiver") | ||||||
| 	params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...) | 	params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...) | ||||||
| 	if fnType.ReturnType().TypeKind() == llvm.VoidTypeKind { | 	if fn.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind { | ||||||
| 		c.builder.CreateCall(f.LLVMFn, params, "") | 		c.builder.CreateCall(fn.LLVMFn, params, "") | ||||||
| 		c.builder.CreateRetVoid() | 		c.builder.CreateRetVoid() | ||||||
| 	} else { | 	} else { | ||||||
| 		ret := c.builder.CreateCall(f.LLVMFn, params, "ret") | 		ret := c.builder.CreateCall(fn.LLVMFn, params, "ret") | ||||||
| 		c.builder.CreateRet(ret) | 		c.builder.CreateRet(ret) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return wrapper, nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,12 +1,14 @@ | ||||||
| package compiler | package compiler | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
|  | 
 | ||||||
| 	"github.com/aykevl/go-llvm" | 	"github.com/aykevl/go-llvm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Run the LLVM optimizer over the module. | // Run the LLVM optimizer over the module. | ||||||
| // The inliner can be disabled (if necessary) by passing 0 to the inlinerThreshold. | // The inliner can be disabled (if necessary) by passing 0 to the inlinerThreshold. | ||||||
| func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) { | func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) error { | ||||||
| 	builder := llvm.NewPassManagerBuilder() | 	builder := llvm.NewPassManagerBuilder() | ||||||
| 	defer builder.Dispose() | 	defer builder.Dispose() | ||||||
| 	builder.SetOptLevel(optLevel) | 	builder.SetOptLevel(optLevel) | ||||||
|  | @ -40,7 +42,13 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) { | ||||||
| 		c.OptimizeMaps() | 		c.OptimizeMaps() | ||||||
| 		c.OptimizeStringToBytes() | 		c.OptimizeStringToBytes() | ||||||
| 		c.OptimizeAllocs() | 		c.OptimizeAllocs() | ||||||
| 		c.Verify() | 		c.LowerInterfaces() | ||||||
|  | 	} else { | ||||||
|  | 		// Must be run at any optimization level. | ||||||
|  | 		c.LowerInterfaces() | ||||||
|  | 	} | ||||||
|  | 	if err := c.Verify(); err != nil { | ||||||
|  | 		return errors.New("optimizations caused a verification failure") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Run module passes. | 	// Run module passes. | ||||||
|  | @ -48,6 +56,8 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) { | ||||||
| 	defer modPasses.Dispose() | 	defer modPasses.Dispose() | ||||||
| 	builder.Populate(modPasses) | 	builder.Populate(modPasses) | ||||||
| 	modPasses.Run(c.mod) | 	modPasses.Run(c.mod) | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Eliminate created but not used maps. | // Eliminate created but not used maps. | ||||||
|  | @ -299,6 +309,9 @@ func (c *Compiler) hasFlag(call, param llvm.Value, kind string) bool { | ||||||
| // Return a list of values (actually, instructions) where this value is used as | // Return a list of values (actually, instructions) where this value is used as | ||||||
| // an operand. | // an operand. | ||||||
| func getUses(value llvm.Value) []llvm.Value { | func getUses(value llvm.Value) []llvm.Value { | ||||||
|  | 	if value.IsNil() { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
| 	var uses []llvm.Value | 	var uses []llvm.Value | ||||||
| 	use := value.FirstUse() | 	use := value.FirstUse() | ||||||
| 	for !use.IsNil() { | 	for !use.IsNil() { | ||||||
|  |  | ||||||
|  | @ -300,6 +300,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re | ||||||
| 				ret = llvm.ConstInsertValue(ret, retLen, []uint32{1}) // len | 				ret = llvm.ConstInsertValue(ret, retLen, []uint32{1}) // len | ||||||
| 				ret = llvm.ConstInsertValue(ret, retLen, []uint32{2}) // cap | 				ret = llvm.ConstInsertValue(ret, retLen, []uint32{2}) // cap | ||||||
| 				fr.locals[inst] = &LocalValue{fr.Eval, ret} | 				fr.locals[inst] = &LocalValue{fr.Eval, ret} | ||||||
|  | 			case callee.Name() == "runtime.makeInterface": | ||||||
|  | 				fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstPtrToInt(inst.Operand(0), fr.TargetData.IntPtrType())} | ||||||
| 			case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic": | 			case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic": | ||||||
| 				// all print instructions, which necessarily have side | 				// all print instructions, which necessarily have side | ||||||
| 				// effects but no results | 				// effects but no results | ||||||
|  |  | ||||||
|  | @ -73,7 +73,12 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult { | ||||||
| 					result.updateSeverity(sideEffectAll) | 					result.updateSeverity(sideEffectAll) | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
|  | 				name := child.Name() | ||||||
| 				if child.IsDeclaration() { | 				if child.IsDeclaration() { | ||||||
|  | 					if name == "runtime.makeInterface" { | ||||||
|  | 						// Can be interpreted so does not have side effects. | ||||||
|  | 						continue | ||||||
|  | 					} | ||||||
| 					// External function call. Assume only limited side effects | 					// External function call. Assume only limited side effects | ||||||
| 					// (no affected globals, etc.). | 					// (no affected globals, etc.). | ||||||
| 					if result.hasLocalSideEffects(dirtyLocals, inst) { | 					if result.hasLocalSideEffects(dirtyLocals, inst) { | ||||||
|  |  | ||||||
|  | @ -278,8 +278,6 @@ func (p *Program) interpret(instrs []ssa.Instruction, paramKeys []*ssa.Parameter | ||||||
| 			} else { | 			} else { | ||||||
| 				return i, errors.New("todo: init IndexAddr index: " + instr.Index.String()) | 				return i, errors.New("todo: init IndexAddr index: " + instr.Index.String()) | ||||||
| 			} | 			} | ||||||
| 		case *ssa.MakeInterface: |  | ||||||
| 			locals[instr] = &InterfaceValue{instr.X.Type(), locals[instr.X]} |  | ||||||
| 		case *ssa.MakeMap: | 		case *ssa.MakeMap: | ||||||
| 			locals[instr] = &MapValue{instr.Type().Underlying().(*types.Map), nil, nil} | 			locals[instr] = &MapValue{instr.Type().Underlying().(*types.Map), nil, nil} | ||||||
| 		case *ssa.MapUpdate: | 		case *ssa.MapUpdate: | ||||||
|  | @ -388,7 +386,6 @@ func canInterpret(callee *ssa.Function) bool { | ||||||
| 		case *ssa.Extract: | 		case *ssa.Extract: | ||||||
| 		case *ssa.FieldAddr: | 		case *ssa.FieldAddr: | ||||||
| 		case *ssa.IndexAddr: | 		case *ssa.IndexAddr: | ||||||
| 		case *ssa.MakeInterface: |  | ||||||
| 		case *ssa.MakeMap: | 		case *ssa.MakeMap: | ||||||
| 		case *ssa.MapUpdate: | 		case *ssa.MapUpdate: | ||||||
| 		case *ssa.Return: | 		case *ssa.Return: | ||||||
|  | @ -447,8 +444,6 @@ func (p *Program) getZeroValue(t types.Type) (Value, error) { | ||||||
| 		return &ZeroBasicValue{typ}, nil | 		return &ZeroBasicValue{typ}, nil | ||||||
| 	case *types.Signature: | 	case *types.Signature: | ||||||
| 		return &FunctionValue{typ, nil}, nil | 		return &FunctionValue{typ, nil}, nil | ||||||
| 	case *types.Interface: |  | ||||||
| 		return &InterfaceValue{typ, nil}, nil |  | ||||||
| 	case *types.Map: | 	case *types.Map: | ||||||
| 		return &MapValue{typ, nil, nil}, nil | 		return &MapValue{typ, nil, nil}, nil | ||||||
| 	case *types.Pointer: | 	case *types.Pointer: | ||||||
|  | @ -492,11 +487,6 @@ type FunctionValue struct { | ||||||
| 	Elem *ssa.Function | 	Elem *ssa.Function | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type InterfaceValue struct { |  | ||||||
| 	Type types.Type |  | ||||||
| 	Elem Value |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type PointerBitCastValue struct { | type PointerBitCastValue struct { | ||||||
| 	Type types.Type | 	Type types.Type | ||||||
| 	Elem Value | 	Elem Value | ||||||
|  |  | ||||||
							
								
								
									
										91
									
								
								ir/ir.go
									
										
									
									
									
								
							
							
						
						
									
										91
									
								
								ir/ir.go
									
										
									
									
									
								
							|  | @ -19,21 +19,18 @@ import ( | ||||||
| // View on all functions, types, and globals in a program, with analysis | // View on all functions, types, and globals in a program, with analysis | ||||||
| // results. | // results. | ||||||
| type Program struct { | type Program struct { | ||||||
| 	Program              *ssa.Program | 	Program           *ssa.Program | ||||||
| 	mainPkg              *ssa.Package | 	mainPkg           *ssa.Package | ||||||
| 	Functions            []*Function | 	Functions         []*Function | ||||||
| 	functionMap          map[*ssa.Function]*Function | 	functionMap       map[*ssa.Function]*Function | ||||||
| 	Globals              []*Global | 	Globals           []*Global | ||||||
| 	globalMap            map[*ssa.Global]*Global | 	globalMap         map[*ssa.Global]*Global | ||||||
| 	comments             map[string]*ast.CommentGroup | 	comments          map[string]*ast.CommentGroup | ||||||
| 	NamedTypes           []*NamedType | 	NamedTypes        []*NamedType | ||||||
| 	needsScheduler       bool | 	needsScheduler    bool | ||||||
| 	goCalls              []*ssa.Go | 	goCalls           []*ssa.Go | ||||||
| 	typesWithMethods     map[string]*TypeWithMethods // see AnalyseInterfaceConversions | 	typesInInterfaces map[string]struct{} // see AnalyseInterfaceConversions | ||||||
| 	typesWithoutMethods  map[string]int              // see AnalyseInterfaceConversions | 	fpWithContext     map[string]struct{} // see AnalyseFunctionPointers | ||||||
| 	methodSignatureNames map[string]int              // see MethodNum |  | ||||||
| 	interfaces           map[string]*Interface       // see AnalyseInterfaceConversions |  | ||||||
| 	fpWithContext        map[string]struct{}         // see AnalyseFunctionPointers |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Function or method. | // Function or method. | ||||||
|  | @ -179,13 +176,11 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	p := &Program{ | 	p := &Program{ | ||||||
| 		Program:              program, | 		Program:     program, | ||||||
| 		mainPkg:              mainPkg, | 		mainPkg:     mainPkg, | ||||||
| 		functionMap:          make(map[*ssa.Function]*Function), | 		functionMap: make(map[*ssa.Function]*Function), | ||||||
| 		globalMap:            make(map[*ssa.Global]*Global), | 		globalMap:   make(map[*ssa.Global]*Global), | ||||||
| 		methodSignatureNames: make(map[string]int), | 		comments:    comments, | ||||||
| 		interfaces:           make(map[string]*Interface), |  | ||||||
| 		comments:             comments, |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, pkg := range packageList { | 	for _, pkg := range packageList { | ||||||
|  | @ -270,18 +265,6 @@ func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global { | ||||||
| 	return p.globalMap[ssaGlobal] | 	return p.globalMap[ssaGlobal] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SortMethods sorts the list of methods by method ID. |  | ||||||
| func (p *Program) SortMethods(methods []*types.Selection) { |  | ||||||
| 	m := &methodList{methods: methods, program: p} |  | ||||||
| 	sort.Sort(m) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // SortFuncs sorts the list of functions by method ID. |  | ||||||
| func (p *Program) SortFuncs(funcs []*types.Func) { |  | ||||||
| 	m := &funcList{funcs: funcs, program: p} |  | ||||||
| 	sort.Sort(m) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *Program) MainPkg() *ssa.Package { | func (p *Program) MainPkg() *ssa.Package { | ||||||
| 	return p.mainPkg | 	return p.mainPkg | ||||||
| } | } | ||||||
|  | @ -442,46 +425,6 @@ func (p *Program) IsVolatile(t types.Type) bool { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Wrapper type to implement sort.Interface for []*types.Selection. |  | ||||||
| type methodList struct { |  | ||||||
| 	methods []*types.Selection |  | ||||||
| 	program *Program |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *methodList) Len() int { |  | ||||||
| 	return len(m.methods) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *methodList) Less(i, j int) bool { |  | ||||||
| 	iid := m.program.MethodNum(m.methods[i].Obj().(*types.Func)) |  | ||||||
| 	jid := m.program.MethodNum(m.methods[j].Obj().(*types.Func)) |  | ||||||
| 	return iid < jid |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (m *methodList) Swap(i, j int) { |  | ||||||
| 	m.methods[i], m.methods[j] = m.methods[j], m.methods[i] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Wrapper type to implement sort.Interface for []*types.Func. |  | ||||||
| type funcList struct { |  | ||||||
| 	funcs   []*types.Func |  | ||||||
| 	program *Program |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (fl *funcList) Len() int { |  | ||||||
| 	return len(fl.funcs) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (fl *funcList) Less(i, j int) bool { |  | ||||||
| 	iid := fl.program.MethodNum(fl.funcs[i]) |  | ||||||
| 	jid := fl.program.MethodNum(fl.funcs[j]) |  | ||||||
| 	return iid < jid |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (fl *funcList) Swap(i, j int) { |  | ||||||
| 	fl.funcs[i], fl.funcs[j] = fl.funcs[j], fl.funcs[i] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Return true if this is a CGo-internal function that can be ignored. | // Return true if this is a CGo-internal function that can be ignored. | ||||||
| func isCGoInternal(name string) bool { | func isCGoInternal(name string) bool { | ||||||
| 	if strings.HasPrefix(name, "_Cgo_") || strings.HasPrefix(name, "_cgo") { | 	if strings.HasPrefix(name, "_Cgo_") || strings.HasPrefix(name, "_cgo") { | ||||||
|  |  | ||||||
							
								
								
									
										102
									
								
								ir/passes.go
									
										
									
									
									
								
							
							
						
						
									
										102
									
								
								ir/passes.go
									
										
									
									
									
								
							|  | @ -2,8 +2,6 @@ package ir | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"go/types" | 	"go/types" | ||||||
| 	"sort" |  | ||||||
| 	"strings" |  | ||||||
| 
 | 
 | ||||||
| 	"golang.org/x/tools/go/ssa" | 	"golang.org/x/tools/go/ssa" | ||||||
| ) | ) | ||||||
|  | @ -59,18 +57,6 @@ func Signature(sig *types.Signature) string { | ||||||
| 	return s | 	return s | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Convert an interface type to a string of all method strings, separated by |  | ||||||
| // "; ". For example: "Read([]byte) (int, error); Close() error" |  | ||||||
| func InterfaceKey(itf *types.Interface) string { |  | ||||||
| 	methodStrings := []string{} |  | ||||||
| 	for i := 0; i < itf.NumMethods(); i++ { |  | ||||||
| 		method := itf.Method(i) |  | ||||||
| 		methodStrings = append(methodStrings, MethodSignature(method)) |  | ||||||
| 	} |  | ||||||
| 	sort.Strings(methodStrings) |  | ||||||
| 	return strings.Join(methodStrings, ";") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Fill in parents of all functions. | // Fill in parents of all functions. | ||||||
| // | // | ||||||
| // All packages need to be added before this pass can run, or it will produce | // All packages need to be added before this pass can run, or it will produce | ||||||
|  | @ -117,30 +103,17 @@ func (p *Program) AnalyseCallgraph() { | ||||||
| 
 | 
 | ||||||
| // Find all types that are put in an interface. | // Find all types that are put in an interface. | ||||||
| func (p *Program) AnalyseInterfaceConversions() { | func (p *Program) AnalyseInterfaceConversions() { | ||||||
| 	// Clear, if AnalyseTypes has been called before. | 	// Clear, if AnalyseInterfaceConversions has been called before. | ||||||
| 	p.typesWithoutMethods = map[string]int{"nil": 0} | 	p.typesInInterfaces = map[string]struct{}{} | ||||||
| 	p.typesWithMethods = map[string]*TypeWithMethods{} |  | ||||||
| 
 | 
 | ||||||
| 	for _, f := range p.Functions { | 	for _, f := range p.Functions { | ||||||
| 		for _, block := range f.Blocks { | 		for _, block := range f.Blocks { | ||||||
| 			for _, instr := range block.Instrs { | 			for _, instr := range block.Instrs { | ||||||
| 				switch instr := instr.(type) { | 				switch instr := instr.(type) { | ||||||
| 				case *ssa.MakeInterface: | 				case *ssa.MakeInterface: | ||||||
| 					methods := getAllMethods(f.Prog, instr.X.Type()) |  | ||||||
| 					name := instr.X.Type().String() | 					name := instr.X.Type().String() | ||||||
| 					if _, ok := p.typesWithMethods[name]; !ok && len(methods) > 0 { | 					if _, ok := p.typesInInterfaces[name]; !ok { | ||||||
| 						t := &TypeWithMethods{ | 						p.typesInInterfaces[name] = struct{}{} | ||||||
| 							t:       instr.X.Type(), |  | ||||||
| 							Num:     len(p.typesWithMethods), |  | ||||||
| 							Methods: make(map[string]*types.Selection), |  | ||||||
| 						} |  | ||||||
| 						for _, sel := range methods { |  | ||||||
| 							name := MethodSignature(sel.Obj().(*types.Func)) |  | ||||||
| 							t.Methods[name] = sel |  | ||||||
| 						} |  | ||||||
| 						p.typesWithMethods[name] = t |  | ||||||
| 					} else if _, ok := p.typesWithoutMethods[name]; !ok && len(methods) == 0 { |  | ||||||
| 						p.typesWithoutMethods[name] = len(p.typesWithoutMethods) |  | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | @ -349,75 +322,10 @@ func (p *Program) IsBlocking(f *Function) bool { | ||||||
| 	return f.blocking | 	return f.blocking | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Return the type number and whether this type is actually used. Used in |  | ||||||
| // interface conversions (type is always used) and type asserts (type may not be |  | ||||||
| // used, meaning assert is always false in this program). |  | ||||||
| // |  | ||||||
| // May only be used after all packages have been added to the analyser. |  | ||||||
| func (p *Program) TypeNum(typ types.Type) (int, bool) { |  | ||||||
| 	if n, ok := p.typesWithoutMethods[typ.String()]; ok { |  | ||||||
| 		return n, true |  | ||||||
| 	} else if meta, ok := p.typesWithMethods[typ.String()]; ok { |  | ||||||
| 		return len(p.typesWithoutMethods) + meta.Num, true |  | ||||||
| 	} else { |  | ||||||
| 		return -1, false // type is never put in an interface |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // InterfaceNum returns the numeric interface ID of this type, for use in type |  | ||||||
| // asserts. |  | ||||||
| func (p *Program) InterfaceNum(itfType *types.Interface) int { |  | ||||||
| 	key := InterfaceKey(itfType) |  | ||||||
| 	if itf, ok := p.interfaces[key]; !ok { |  | ||||||
| 		num := len(p.interfaces) |  | ||||||
| 		p.interfaces[key] = &Interface{Num: num, Type: itfType} |  | ||||||
| 		return num |  | ||||||
| 	} else { |  | ||||||
| 		return itf.Num |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MethodNum returns the numeric ID of this method, to be used in method lookups |  | ||||||
| // on interfaces for example. |  | ||||||
| func (p *Program) MethodNum(method *types.Func) int { |  | ||||||
| 	name := MethodSignature(method) |  | ||||||
| 	if _, ok := p.methodSignatureNames[name]; !ok { |  | ||||||
| 		p.methodSignatureNames[name] = len(p.methodSignatureNames) |  | ||||||
| 	} |  | ||||||
| 	return p.methodSignatureNames[MethodSignature(method)] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // The start index of the first dynamic type that has methods. |  | ||||||
| // Types without methods always have a lower ID and types with methods have this |  | ||||||
| // or a higher ID. |  | ||||||
| // |  | ||||||
| // May only be used after all packages have been added to the analyser. |  | ||||||
| func (p *Program) FirstDynamicType() int { |  | ||||||
| 	return len(p.typesWithoutMethods) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Return all types with methods, sorted by type ID. |  | ||||||
| func (p *Program) AllDynamicTypes() []*TypeWithMethods { |  | ||||||
| 	l := make([]*TypeWithMethods, len(p.typesWithMethods)) |  | ||||||
| 	for _, m := range p.typesWithMethods { |  | ||||||
| 		l[m.Num] = m |  | ||||||
| 	} |  | ||||||
| 	return l |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Return all interface types, sorted by interface ID. |  | ||||||
| func (p *Program) AllInterfaces() []*Interface { |  | ||||||
| 	l := make([]*Interface, len(p.interfaces)) |  | ||||||
| 	for _, itf := range p.interfaces { |  | ||||||
| 		l[itf.Num] = itf |  | ||||||
| 	} |  | ||||||
| 	return l |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *Program) FunctionNeedsContext(f *Function) bool { | func (p *Program) FunctionNeedsContext(f *Function) bool { | ||||||
| 	if !f.addressTaken { | 	if !f.addressTaken { | ||||||
| 		if f.Signature.Recv() != nil { | 		if f.Signature.Recv() != nil { | ||||||
| 			_, hasInterfaceConversion := p.TypeNum(f.Signature.Recv().Type()) | 			_, hasInterfaceConversion := p.typesInInterfaces[f.Signature.Recv().Type().String()] | ||||||
| 			if hasInterfaceConversion && p.SignatureNeedsContext(f.Signature) { | 			if hasInterfaceConversion && p.SignatureNeedsContext(f.Signature) { | ||||||
| 				return true | 				return true | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
							
								
								
									
										32
									
								
								main.go
									
										
									
									
									
								
							
							
						
						
									
										32
									
								
								main.go
									
										
									
									
									
								
							|  | @ -64,7 +64,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act | ||||||
| 		fmt.Println(c.IR()) | 		fmt.Println(c.IR()) | ||||||
| 	} | 	} | ||||||
| 	if err := c.Verify(); err != nil { | 	if err := c.Verify(); err != nil { | ||||||
| 		return err | 		return errors.New("verification error after IR construction") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if config.initInterp { | 	if config.initInterp { | ||||||
|  | @ -73,13 +73,13 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		if err := c.Verify(); err != nil { | 		if err := c.Verify(); err != nil { | ||||||
| 			return err | 			return errors.New("verification error after interpreting runtime.initAll") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	c.ApplyFunctionSections() // -ffunction-sections | 	c.ApplyFunctionSections() // -ffunction-sections | ||||||
| 	if err := c.Verify(); err != nil { | 	if err := c.Verify(); err != nil { | ||||||
| 		return err | 		return errors.New("verification error after applying function sections") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Browsers cannot handle external functions that have type i64 because it | 	// Browsers cannot handle external functions that have type i64 because it | ||||||
|  | @ -92,7 +92,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		if err := c.Verify(); err != nil { | 		if err := c.Verify(); err != nil { | ||||||
| 			return err | 			return errors.New("verification error after running the wasm i64 hack") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -100,20 +100,23 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act | ||||||
| 	// exactly. | 	// exactly. | ||||||
| 	switch config.opt { | 	switch config.opt { | ||||||
| 	case "none:", "0": | 	case "none:", "0": | ||||||
| 		c.Optimize(0, 0, 0) // -O0 | 		err = c.Optimize(0, 0, 0) // -O0 | ||||||
| 	case "1": | 	case "1": | ||||||
| 		c.Optimize(1, 0, 0) // -O1 | 		err = c.Optimize(1, 0, 0) // -O1 | ||||||
| 	case "2": | 	case "2": | ||||||
| 		c.Optimize(2, 0, 225) // -O2 | 		err = c.Optimize(2, 0, 225) // -O2 | ||||||
| 	case "s": | 	case "s": | ||||||
| 		c.Optimize(2, 1, 225) // -Os | 		err = c.Optimize(2, 1, 225) // -Os | ||||||
| 	case "z": | 	case "z": | ||||||
| 		c.Optimize(2, 2, 5) // -Oz, default | 		err = c.Optimize(2, 2, 5) // -Oz, default | ||||||
| 	default: | 	default: | ||||||
| 		return errors.New("unknown optimization level: -opt=" + config.opt) | 		err = errors.New("unknown optimization level: -opt=" + config.opt) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
| 	} | 	} | ||||||
| 	if err := c.Verify(); err != nil { | 	if err := c.Verify(); err != nil { | ||||||
| 		return err | 		return errors.New("verification failure after LLVM optimization passes") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// On the AVR, pointers can point either to flash or to RAM, but we don't | 	// On the AVR, pointers can point either to flash or to RAM, but we don't | ||||||
|  | @ -124,7 +127,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act | ||||||
| 	if strings.HasPrefix(spec.Triple, "avr") { | 	if strings.HasPrefix(spec.Triple, "avr") { | ||||||
| 		c.NonConstGlobals() | 		c.NonConstGlobals() | ||||||
| 		if err := c.Verify(); err != nil { | 		if err := c.Verify(); err != nil { | ||||||
| 			return err | 			return errors.New("verification error after making all globals non-constant on AVR") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -382,7 +385,10 @@ func Run(pkgName string) error { | ||||||
| 	// -Oz, which is the fastest optimization level (faster than -O0, -O1, -O2 | 	// -Oz, which is the fastest optimization level (faster than -O0, -O1, -O2 | ||||||
| 	// and -Os). Turn off the inliner, as the inliner increases optimization | 	// and -Os). Turn off the inliner, as the inliner increases optimization | ||||||
| 	// time. | 	// time. | ||||||
| 	c.Optimize(2, 2, 0) | 	err = c.Optimize(2, 2, 0) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	engine, err := llvm.NewExecutionEngine(c.Module()) | 	engine, err := llvm.NewExecutionEngine(c.Module()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  |  | ||||||
|  | @ -4,64 +4,12 @@ package runtime | ||||||
| // | // | ||||||
| // Interfaces are represented as a pair of {typecode, value}, where value can be | // Interfaces are represented as a pair of {typecode, value}, where value can be | ||||||
| // anything (including non-pointers). | // anything (including non-pointers). | ||||||
| // |  | ||||||
| // Signatures itself are not matched on strings, but on uniqued numbers that |  | ||||||
| // contain the name and the signature of the function (to save space), think of |  | ||||||
| // signatures as interned strings at compile time. |  | ||||||
| // |  | ||||||
| // The typecode is a small number unique for the Go type. All typecodes < |  | ||||||
| // firstTypeWithMethods do not have any methods and typecodes >= |  | ||||||
| // firstTypeWithMethods all have at least one method. This means that |  | ||||||
| // methodSetRanges does not need to contain types without methods and is thus |  | ||||||
| // indexed starting at a typecode with number firstTypeWithMethods. |  | ||||||
| // |  | ||||||
| // To further conserve some space, the methodSetRange (as the name indicates) |  | ||||||
| // doesn't contain a list of methods and function pointers directly, but instead |  | ||||||
| // just indexes into methodSetSignatures and methodSetFunctions which contains |  | ||||||
| // the mapping from uniqued signature to function pointer. |  | ||||||
| 
 | 
 | ||||||
| type _interface struct { | type _interface struct { | ||||||
| 	typecode uint16 | 	typecode uintptr | ||||||
| 	value    *uint8 | 	value    *uint8 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // This struct indicates the range of methods in the methodSetSignatures and |  | ||||||
| // methodSetFunctions arrays that belong to this named type. |  | ||||||
| type methodSetRange struct { |  | ||||||
| 	index  uint16 // start index into interfaceSignatures and interfaceFunctions |  | ||||||
| 	length uint16 // number of methods |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Global constants that will be set by the compiler. The arrays are of size 0, |  | ||||||
| // which is a dummy value, but will be bigger after the compiler has filled them |  | ||||||
| // in. |  | ||||||
| var ( |  | ||||||
| 	firstTypeWithMethods uint16            // the lowest typecode that has at least one method |  | ||||||
| 	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 |  | ||||||
| 	interfaceLengths     [0]uint8          // mapping from interface ID to the number of methods it has |  | ||||||
| 	interfaceMethods     [0]uint16         // the method an interface implements (list of method IDs) |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Get the function pointer for the method on the interface. |  | ||||||
| // This is a compiler intrinsic. |  | ||||||
| //go:nobounds |  | ||||||
| func interfaceMethod(typecode uint16, method uint16) *uint8 { |  | ||||||
| 	// This function doesn't do bounds checking as the supplied method must be |  | ||||||
| 	// in the list of signatures. The compiler will only emit |  | ||||||
| 	// runtime.interfaceMethod calls when the method actually exists on this |  | ||||||
| 	// interface (proven by the typechecker). |  | ||||||
| 	i := methodSetRanges[typecode-firstTypeWithMethods].index |  | ||||||
| 	for { |  | ||||||
| 		if methodSetSignatures[i] == method { |  | ||||||
| 			return methodSetFunctions[i] |  | ||||||
| 		} |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Return true iff both interfaces are equal. | // Return true iff both interfaces are equal. | ||||||
| func interfaceEqual(x, y _interface) bool { | func interfaceEqual(x, y _interface) bool { | ||||||
| 	if x.typecode != y.typecode { | 	if x.typecode != y.typecode { | ||||||
|  | @ -76,67 +24,37 @@ func interfaceEqual(x, y _interface) bool { | ||||||
| 	panic("unimplemented: interface equality") | 	panic("unimplemented: interface equality") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Return true iff the type implements all methods needed by the interface. This | // interfaceTypeAssert is called when a type assert without comma-ok still | ||||||
| // means the type satisfies the interface. | // returns false. | ||||||
| // This is a compiler intrinsic. |  | ||||||
| //go:nobounds |  | ||||||
| func interfaceImplements(typecode, interfaceNum uint16) bool { |  | ||||||
| 	// 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 |  | ||||||
| 
 |  | ||||||
| 	// Iterate over all methods of the interface: |  | ||||||
| 	for itfIndex < itfIndexEnd { |  | ||||||
| 		methodId := interfaceMethods[itfIndex] |  | ||||||
| 		if methodIndex >= methodIndexEnd { |  | ||||||
| 			// Reached the end of the list of methods, so interface doesn't |  | ||||||
| 			// implement this type. |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 		if methodId == methodSetSignatures[methodIndex] { |  | ||||||
| 			// Found a matching method, continue to the next method. |  | ||||||
| 			itfIndex++ |  | ||||||
| 			methodIndex++ |  | ||||||
| 			continue |  | ||||||
| 		} else if methodId > methodSetSignatures[methodIndex] { |  | ||||||
| 			// The method didn't match, but method ID of the concrete type was |  | ||||||
| 			// lower than that of the interface, so probably it has a method the |  | ||||||
| 			// interface doesn't implement. |  | ||||||
| 			// Move on to the next method of the concrete type. |  | ||||||
| 			methodIndex++ |  | ||||||
| 			continue |  | ||||||
| 		} else { |  | ||||||
| 			// The concrete type is missing a method. This means the type assert |  | ||||||
| 			// fails. |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Found a method for each expected method in the interface. This type |  | ||||||
| 	// assert is successful. |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func interfaceTypeAssert(ok bool) { | func interfaceTypeAssert(ok bool) { | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		runtimePanic("type assert failed") | 		runtimePanic("type assert failed") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // The following declarations are only used during IR construction. They are | ||||||
|  | // lowered to inline IR in the interface lowering pass. | ||||||
|  | // See compiler/interface-lowering.go for details. | ||||||
|  | 
 | ||||||
|  | type interfaceMethodInfo struct { | ||||||
|  | 	signature *uint8 // external *i8 with a name identifying the Go function signature | ||||||
|  | 	funcptr   *uint8 // bitcast from the actual function pointer | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Pseudo function call used while putting a concrete value in an interface, | ||||||
|  | // that must be lowered to a constant uintptr. | ||||||
|  | func makeInterface(typecode *uint8, methodSet *interfaceMethodInfo) uintptr | ||||||
|  | 
 | ||||||
|  | // Pseudo function call used during a type assert. It is used during interface | ||||||
|  | // lowering, to assign the lowest type numbers to the types with the most type | ||||||
|  | // asserts. Also, it is replaced with const false if this type assert can never | ||||||
|  | // happen. | ||||||
|  | func typeAssert(actualType uintptr, assertedType *uint8) bool | ||||||
|  | 
 | ||||||
|  | // Pseudo function call that returns whether a given type implements all methods | ||||||
|  | // of the given interface. | ||||||
|  | func interfaceImplements(typecode uintptr, interfaceMethodSet **uint8) bool | ||||||
|  | 
 | ||||||
|  | // Pseudo function that returns a function pointer to the method to call. | ||||||
|  | // See the interface lowering pass for how this is lowered to a real call. | ||||||
|  | func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) *uint8 | ||||||
|  |  | ||||||
|  | @ -205,7 +205,14 @@ func printitf(msg interface{}) { | ||||||
| 		// cast to underlying type | 		// cast to underlying type | ||||||
| 		itf := *(*_interface)(unsafe.Pointer(&msg)) | 		itf := *(*_interface)(unsafe.Pointer(&msg)) | ||||||
| 		putchar('(') | 		putchar('(') | ||||||
| 		print(itf.typecode) | 		switch unsafe.Sizeof(itf.typecode) { | ||||||
|  | 		case 2: | ||||||
|  | 			printuint16(uint16(itf.typecode)) | ||||||
|  | 		case 4: | ||||||
|  | 			printuint32(uint32(itf.typecode)) | ||||||
|  | 		case 8: | ||||||
|  | 			printuint64(uint64(itf.typecode)) | ||||||
|  | 		} | ||||||
| 		putchar(':') | 		putchar(':') | ||||||
| 		print(itf.value) | 		print(itf.value) | ||||||
| 		putchar(')') | 		putchar(')') | ||||||
|  |  | ||||||
							
								
								
									
										7
									
								
								testdata/interface.go
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										7
									
								
								testdata/interface.go
									
										
									
									
										предоставленный
									
									
								
							|  | @ -26,6 +26,8 @@ func main() { | ||||||
| 
 | 
 | ||||||
| func printItf(val interface{}) { | func printItf(val interface{}) { | ||||||
| 	switch val := val.(type) { | 	switch val := val.(type) { | ||||||
|  | 	case Unmatched: | ||||||
|  | 		panic("matched the unmatchable") | ||||||
| 	case Doubler: | 	case Doubler: | ||||||
| 		println("is Doubler:", val.Double()) | 		println("is Doubler:", val.Double()) | ||||||
| 	case Tuple: | 	case Tuple: | ||||||
|  | @ -127,3 +129,8 @@ func (p SmallPair) Nth(n int) uint32 { | ||||||
| func (p SmallPair) Print() { | func (p SmallPair) Print() { | ||||||
| 	println("SmallPair.Print:", p.a, p.b) | 	println("SmallPair.Print:", p.a, p.b) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // There is no type that matches this method. | ||||||
|  | type Unmatched interface { | ||||||
|  | 	NeverImplementedMethod() | ||||||
|  | } | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem