all: refactor reflect package
This is a big commit that changes the way runtime type information is stored in
the binary. Instead of compressing it and storing it in a number of sidetables,
it is stored similar to how the Go compiler toolchain stores it (but still more
compactly).
This has a number of advantages:
  * It is much easier to add new features to reflect support. They can simply
    be added to these structs without requiring massive changes (especially in
    the reflect lowering pass).
  * It removes the reflect lowering pass, which was a large amount of hard to
    understand and debug code.
  * The reflect lowering pass also required merging all LLVM IR into one
    module, which is terrible for performance especially when compiling large
    amounts of code. See issue 2870 for details.
  * It is (probably!) easier to reason about for the compiler.
The downside is that it increases code size a bit, especially when reflect is
involved. I hope to fix some of that in later patches.
			
			
Этот коммит содержится в:
		
							родитель
							
								
									ebb410afd9
								
							
						
					
					
						коммит
						4e8453167f
					
				
					 27 изменённых файлов: 791 добавлений и 1346 удалений
				
			
		| 
						 | 
					@ -118,10 +118,6 @@ var (
 | 
				
			||||||
	//   pack:   data created when storing a constant in an interface for example
 | 
						//   pack:   data created when storing a constant in an interface for example
 | 
				
			||||||
	//   string: buffer behind strings
 | 
						//   string: buffer behind strings
 | 
				
			||||||
	packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
 | 
						packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Reflect sidetables. Created by the reflect lowering pass.
 | 
					 | 
				
			||||||
	// See src/reflect/sidetables.go.
 | 
					 | 
				
			||||||
	reflectDataRegexp = regexp.MustCompile(`^reflect\.[a-zA-Z]+Sidetable$`)
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// readProgramSizeFromDWARF reads the source location for each line of code and
 | 
					// readProgramSizeFromDWARF reads the source location for each line of code and
 | 
				
			||||||
| 
						 | 
					@ -375,7 +371,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
 | 
				
			||||||
			if section.Flags&elf.SHF_ALLOC == 0 {
 | 
								if section.Flags&elf.SHF_ALLOC == 0 {
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if packageSymbolRegexp.MatchString(symbol.Name) || reflectDataRegexp.MatchString(symbol.Name) {
 | 
								if packageSymbolRegexp.MatchString(symbol.Name) {
 | 
				
			||||||
				addresses = append(addresses, addressLine{
 | 
									addresses = append(addresses, addressLine{
 | 
				
			||||||
					Address:    symbol.Value,
 | 
										Address:    symbol.Value,
 | 
				
			||||||
					Length:     symbol.Size,
 | 
										Length:     symbol.Size,
 | 
				
			||||||
| 
						 | 
					@ -836,9 +832,8 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
 | 
				
			||||||
		} else if packageSymbolRegexp.MatchString(path) {
 | 
							} else if packageSymbolRegexp.MatchString(path) {
 | 
				
			||||||
			// Parse symbol names like main$alloc or runtime$string.
 | 
								// Parse symbol names like main$alloc or runtime$string.
 | 
				
			||||||
			packagePath = path[:strings.LastIndex(path, "$")]
 | 
								packagePath = path[:strings.LastIndex(path, "$")]
 | 
				
			||||||
		} else if reflectDataRegexp.MatchString(path) {
 | 
							} else if path == "<Go type>" {
 | 
				
			||||||
			// Parse symbol names like reflect.structTypesSidetable.
 | 
								packagePath = "Go types"
 | 
				
			||||||
			packagePath = "Go reflect data"
 | 
					 | 
				
			||||||
		} else if path == "<Go interface assert>" {
 | 
							} else if path == "<Go interface assert>" {
 | 
				
			||||||
			// Interface type assert, generated by the interface lowering pass.
 | 
								// Interface type assert, generated by the interface lowering pass.
 | 
				
			||||||
			packagePath = "Go interface assert"
 | 
								packagePath = "Go interface assert"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -340,12 +340,15 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
 | 
				
			||||||
	return c.mod, c.diagnostics
 | 
						return c.mod, c.diagnostics
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *compilerContext) getRuntimeType(name string) types.Type {
 | 
				
			||||||
 | 
						return c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
 | 
					// getLLVMRuntimeType obtains a named type from the runtime package and returns
 | 
				
			||||||
// it as a LLVM type, creating it if necessary. It is a shorthand for
 | 
					// it as a LLVM type, creating it if necessary. It is a shorthand for
 | 
				
			||||||
// getLLVMType(getRuntimeType(name)).
 | 
					// getLLVMType(getRuntimeType(name)).
 | 
				
			||||||
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
 | 
					func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
 | 
				
			||||||
	typ := c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type()
 | 
						return c.getLLVMType(c.getRuntimeType(name))
 | 
				
			||||||
	return c.getLLVMType(typ)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
 | 
					// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -271,7 +271,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
 | 
				
			||||||
		typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
 | 
							typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
 | 
				
			||||||
		receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
 | 
							receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
 | 
				
			||||||
		values = []llvm.Value{callback, next, typecode, receiverValue}
 | 
							values = []llvm.Value{callback, next, typecode, receiverValue}
 | 
				
			||||||
		valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
 | 
							valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
 | 
				
			||||||
		for _, arg := range instr.Call.Args {
 | 
							for _, arg := range instr.Call.Args {
 | 
				
			||||||
			val := b.getValue(arg)
 | 
								val := b.getValue(arg)
 | 
				
			||||||
			values = append(values, val)
 | 
								values = append(values, val)
 | 
				
			||||||
| 
						 | 
					@ -476,7 +476,7 @@ func (b *builder) createRunDefers() {
 | 
				
			||||||
				valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
 | 
									valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				//Expect typecode
 | 
									//Expect typecode
 | 
				
			||||||
				valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
 | 
									valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for _, arg := range callback.Args {
 | 
								for _, arg := range callback.Args {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package compiler
 | 
				
			||||||
// interface-lowering.go for more details.
 | 
					// interface-lowering.go for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"go/token"
 | 
						"go/token"
 | 
				
			||||||
	"go/types"
 | 
						"go/types"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
| 
						 | 
					@ -15,6 +16,49 @@ import (
 | 
				
			||||||
	"tinygo.org/x/go-llvm"
 | 
						"tinygo.org/x/go-llvm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Type kinds for basic types.
 | 
				
			||||||
 | 
					// They must match the constants for the Kind type in src/reflect/type.go.
 | 
				
			||||||
 | 
					var basicTypes = [...]uint8{
 | 
				
			||||||
 | 
						types.Bool:          1,
 | 
				
			||||||
 | 
						types.Int:           2,
 | 
				
			||||||
 | 
						types.Int8:          3,
 | 
				
			||||||
 | 
						types.Int16:         4,
 | 
				
			||||||
 | 
						types.Int32:         5,
 | 
				
			||||||
 | 
						types.Int64:         6,
 | 
				
			||||||
 | 
						types.Uint:          7,
 | 
				
			||||||
 | 
						types.Uint8:         8,
 | 
				
			||||||
 | 
						types.Uint16:        9,
 | 
				
			||||||
 | 
						types.Uint32:        10,
 | 
				
			||||||
 | 
						types.Uint64:        11,
 | 
				
			||||||
 | 
						types.Uintptr:       12,
 | 
				
			||||||
 | 
						types.Float32:       13,
 | 
				
			||||||
 | 
						types.Float64:       14,
 | 
				
			||||||
 | 
						types.Complex64:     15,
 | 
				
			||||||
 | 
						types.Complex128:    16,
 | 
				
			||||||
 | 
						types.String:        17,
 | 
				
			||||||
 | 
						types.UnsafePointer: 18,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// These must also match the constants for the Kind type in src/reflect/type.go.
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						typeKindChan      = 19
 | 
				
			||||||
 | 
						typeKindInterface = 20
 | 
				
			||||||
 | 
						typeKindPointer   = 21
 | 
				
			||||||
 | 
						typeKindSlice     = 22
 | 
				
			||||||
 | 
						typeKindArray     = 23
 | 
				
			||||||
 | 
						typeKindSignature = 24
 | 
				
			||||||
 | 
						typeKindMap       = 25
 | 
				
			||||||
 | 
						typeKindStruct    = 26
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Flags stored in the first byte of the struct field byte array. Must be kept
 | 
				
			||||||
 | 
					// up to date with src/reflect/type.go.
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						structFieldFlagAnonymous = 1 << iota
 | 
				
			||||||
 | 
						structFieldFlagHasTag
 | 
				
			||||||
 | 
						structFieldFlagIsExported
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
 | 
					// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
 | 
				
			||||||
// It tries to put the type in the interface value, but if that's not possible,
 | 
					// It tries to put the type in the interface value, but if that's not possible,
 | 
				
			||||||
// it will do an allocation of the right size and put that in the interface
 | 
					// it will do an allocation of the right size and put that in the interface
 | 
				
			||||||
| 
						 | 
					@ -23,10 +67,9 @@ import (
 | 
				
			||||||
// An interface value is a {typecode, value} tuple named runtime._interface.
 | 
					// An interface value is a {typecode, value} tuple named runtime._interface.
 | 
				
			||||||
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
 | 
					func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
 | 
				
			||||||
	itfValue := b.emitPointerPack([]llvm.Value{val})
 | 
						itfValue := b.emitPointerPack([]llvm.Value{val})
 | 
				
			||||||
	itfTypeCodeGlobal := b.getTypeCode(typ)
 | 
						itfType := b.getTypeCode(typ)
 | 
				
			||||||
	itfTypeCode := b.CreatePtrToInt(itfTypeCodeGlobal, b.uintptrType, "")
 | 
					 | 
				
			||||||
	itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
 | 
						itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
 | 
				
			||||||
	itf = b.CreateInsertValue(itf, itfTypeCode, 0, "")
 | 
						itf = b.CreateInsertValue(itf, itfType, 0, "")
 | 
				
			||||||
	itf = b.CreateInsertValue(itf, itfValue, 1, "")
 | 
						itf = b.CreateInsertValue(itf, itfValue, 1, "")
 | 
				
			||||||
	return itf
 | 
						return itf
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -41,118 +84,236 @@ func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getTypeCode returns a reference to a type code.
 | 
					// getTypeCode returns a reference to a type code.
 | 
				
			||||||
// It returns a pointer to an external global which should be replaced with the
 | 
					// A type code is a pointer to a constant global that describes the type.
 | 
				
			||||||
// real type in the interface lowering pass.
 | 
					// This function returns a pointer to the 'kind' field (which might not be the
 | 
				
			||||||
 | 
					// first field in the struct).
 | 
				
			||||||
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
 | 
					func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
 | 
				
			||||||
 | 
						ms := c.program.MethodSets.MethodSet(typ)
 | 
				
			||||||
 | 
						hasMethodSet := ms.Len() != 0
 | 
				
			||||||
 | 
						if _, ok := typ.Underlying().(*types.Interface); ok {
 | 
				
			||||||
 | 
							hasMethodSet = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	globalName := "reflect/types.type:" + getTypeCodeName(typ)
 | 
						globalName := "reflect/types.type:" + getTypeCodeName(typ)
 | 
				
			||||||
	global := c.mod.NamedGlobal(globalName)
 | 
						global := c.mod.NamedGlobal(globalName)
 | 
				
			||||||
	if global.IsNil() {
 | 
						if global.IsNil() {
 | 
				
			||||||
		// Create a new typecode global.
 | 
							var typeFields []llvm.Value
 | 
				
			||||||
		global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName)
 | 
							// Define the type fields. These must match the structs in
 | 
				
			||||||
		// Some type classes contain more information for underlying types or
 | 
							// src/reflect/type.go (ptrType, arrayType, etc). See the comment at the
 | 
				
			||||||
		// element types. Store it directly in the typecode global to make
 | 
							// top of src/reflect/type.go for more information on the layout of these structs.
 | 
				
			||||||
		// reflect lowering simpler.
 | 
							typeFieldTypes := []*types.Var{
 | 
				
			||||||
		var references llvm.Value
 | 
								types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
 | 
				
			||||||
		var length int64
 | 
							}
 | 
				
			||||||
		var methodSet llvm.Value
 | 
					 | 
				
			||||||
		var ptrTo llvm.Value
 | 
					 | 
				
			||||||
		var typeAssert llvm.Value
 | 
					 | 
				
			||||||
		switch typ := typ.(type) {
 | 
							switch typ := typ.(type) {
 | 
				
			||||||
 | 
							case *types.Basic:
 | 
				
			||||||
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
		case *types.Named:
 | 
							case *types.Named:
 | 
				
			||||||
			references = c.getTypeCode(typ.Underlying())
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
		case *types.Chan:
 | 
									types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
			references = c.getTypeCode(typ.Elem())
 | 
									types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							case *types.Chan, *types.Slice:
 | 
				
			||||||
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
		case *types.Pointer:
 | 
							case *types.Pointer:
 | 
				
			||||||
			references = c.getTypeCode(typ.Elem())
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
		case *types.Slice:
 | 
									types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
			references = c.getTypeCode(typ.Elem())
 | 
								)
 | 
				
			||||||
		case *types.Array:
 | 
							case *types.Array:
 | 
				
			||||||
			references = c.getTypeCode(typ.Elem())
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
			length = typ.Len()
 | 
									types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							case *types.Map:
 | 
				
			||||||
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
		case *types.Struct:
 | 
							case *types.Struct:
 | 
				
			||||||
			// Take a pointer to the typecodeID of the first field (if it exists).
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
			structGlobal := c.makeStructTypeFields(typ)
 | 
									types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
 | 
				
			||||||
			references = llvm.ConstBitCast(structGlobal, global.Type())
 | 
									types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
		case *types.Interface:
 | 
							case *types.Interface:
 | 
				
			||||||
			methodSetGlobal := c.getInterfaceMethodSet(typ)
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
			references = llvm.ConstBitCast(methodSetGlobal, global.Type())
 | 
									types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								// TODO: methods
 | 
				
			||||||
 | 
							case *types.Signature:
 | 
				
			||||||
 | 
								typeFieldTypes = append(typeFieldTypes,
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								// TODO: signature params and return values
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if _, ok := typ.Underlying().(*types.Interface); !ok {
 | 
							if hasMethodSet {
 | 
				
			||||||
			methodSet = c.getTypeMethodSet(typ)
 | 
								// This method set is appended at the start of the struct. It is
 | 
				
			||||||
		} else {
 | 
								// removed in the interface lowering pass.
 | 
				
			||||||
			typeAssert = c.getInterfaceImplementsFunc(typ)
 | 
								// TODO: don't remove these and instead do what upstream Go is doing
 | 
				
			||||||
			typeAssert = llvm.ConstPtrToInt(typeAssert, c.uintptrType)
 | 
								// instead. See: https://research.swtch.com/interfaces. This can
 | 
				
			||||||
 | 
								// likely be optimized in LLVM using
 | 
				
			||||||
 | 
								// https://llvm.org/docs/TypeMetadata.html.
 | 
				
			||||||
 | 
								typeFieldTypes = append([]*types.Var{
 | 
				
			||||||
 | 
									types.NewVar(token.NoPos, nil, "methodSet", types.Typ[types.UnsafePointer]),
 | 
				
			||||||
 | 
								}, typeFieldTypes...)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if _, ok := typ.Underlying().(*types.Pointer); !ok {
 | 
							globalType := types.NewStruct(typeFieldTypes, nil)
 | 
				
			||||||
			ptrTo = c.getTypeCode(types.NewPointer(typ))
 | 
							global = llvm.AddGlobal(c.mod, c.getLLVMType(globalType), globalName)
 | 
				
			||||||
 | 
							metabyte := getTypeKind(typ)
 | 
				
			||||||
 | 
							switch typ := typ.(type) {
 | 
				
			||||||
 | 
							case *types.Basic:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
 | 
				
			||||||
 | 
							case *types.Named:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{
 | 
				
			||||||
 | 
									c.getTypeCode(types.NewPointer(typ)), // ptrTo
 | 
				
			||||||
 | 
									c.getTypeCode(typ.Underlying()),      // underlying
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								metabyte |= 1 << 5 // "named" flag
 | 
				
			||||||
 | 
							case *types.Chan:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{
 | 
				
			||||||
 | 
									c.getTypeCode(types.NewPointer(typ)), // ptrTo
 | 
				
			||||||
 | 
									c.getTypeCode(typ.Elem()),            // elementType
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case *types.Slice:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{
 | 
				
			||||||
 | 
									c.getTypeCode(types.NewPointer(typ)), // ptrTo
 | 
				
			||||||
 | 
									c.getTypeCode(typ.Elem()),            // elementType
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case *types.Pointer:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{c.getTypeCode(typ.Elem())}
 | 
				
			||||||
 | 
							case *types.Array:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{
 | 
				
			||||||
 | 
									c.getTypeCode(types.NewPointer(typ)),                   // ptrTo
 | 
				
			||||||
 | 
									c.getTypeCode(typ.Elem()),                              // elementType
 | 
				
			||||||
 | 
									llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case *types.Map:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{
 | 
				
			||||||
 | 
									c.getTypeCode(types.NewPointer(typ)), // ptrTo
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case *types.Struct:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{
 | 
				
			||||||
 | 
									llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
 | 
				
			||||||
 | 
									c.getTypeCode(types.NewPointer(typ)),                             // ptrTo
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								structFieldType := c.getLLVMRuntimeType("structField")
 | 
				
			||||||
 | 
								var fields []llvm.Value
 | 
				
			||||||
 | 
								for i := 0; i < typ.NumFields(); i++ {
 | 
				
			||||||
 | 
									field := typ.Field(i)
 | 
				
			||||||
 | 
									var flags uint8
 | 
				
			||||||
 | 
									if field.Anonymous() {
 | 
				
			||||||
 | 
										flags |= structFieldFlagAnonymous
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if typ.Tag(i) != "" {
 | 
				
			||||||
 | 
										flags |= structFieldFlagHasTag
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if token.IsExported(field.Name()) {
 | 
				
			||||||
 | 
										flags |= structFieldFlagIsExported
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									data := string(flags) + field.Name() + "\x00"
 | 
				
			||||||
 | 
									if typ.Tag(i) != "" {
 | 
				
			||||||
 | 
										if len(typ.Tag(i)) > 0xff {
 | 
				
			||||||
 | 
											c.addError(field.Pos(), fmt.Sprintf("struct tag is %d bytes which is too long, max is 255", len(typ.Tag(i))))
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										data += string([]byte{byte(len(typ.Tag(i)))}) + typ.Tag(i)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									dataInitializer := c.ctx.ConstString(data, false)
 | 
				
			||||||
 | 
									dataGlobal := llvm.AddGlobal(c.mod, dataInitializer.Type(), globalName+"."+field.Name())
 | 
				
			||||||
 | 
									dataGlobal.SetInitializer(dataInitializer)
 | 
				
			||||||
 | 
									dataGlobal.SetAlignment(1)
 | 
				
			||||||
 | 
									dataGlobal.SetUnnamedAddr(true)
 | 
				
			||||||
 | 
									dataGlobal.SetLinkage(llvm.InternalLinkage)
 | 
				
			||||||
 | 
									dataGlobal.SetGlobalConstant(true)
 | 
				
			||||||
 | 
									fieldType := c.getTypeCode(field.Type())
 | 
				
			||||||
 | 
									fields = append(fields, llvm.ConstNamedStruct(structFieldType, []llvm.Value{
 | 
				
			||||||
 | 
										fieldType,
 | 
				
			||||||
 | 
										llvm.ConstGEP(dataGlobal.GlobalValueType(), dataGlobal, []llvm.Value{
 | 
				
			||||||
 | 
											llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 | 
				
			||||||
 | 
											llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 | 
				
			||||||
 | 
										}),
 | 
				
			||||||
 | 
									}))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
 | 
				
			||||||
 | 
							case *types.Interface:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
 | 
				
			||||||
 | 
								// TODO: methods
 | 
				
			||||||
 | 
							case *types.Signature:
 | 
				
			||||||
 | 
								typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
 | 
				
			||||||
 | 
								// TODO: params, return values, etc
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		globalValue := llvm.ConstNull(global.GlobalValueType())
 | 
							// Prepend metadata byte.
 | 
				
			||||||
		if !references.IsNil() {
 | 
							typeFields = append([]llvm.Value{
 | 
				
			||||||
			globalValue = c.builder.CreateInsertValue(globalValue, references, 0, "")
 | 
								llvm.ConstInt(c.ctx.Int8Type(), uint64(metabyte), false),
 | 
				
			||||||
		}
 | 
							}, typeFields...)
 | 
				
			||||||
		if length != 0 {
 | 
							if hasMethodSet {
 | 
				
			||||||
			lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
 | 
								typeFields = append([]llvm.Value{
 | 
				
			||||||
			globalValue = c.builder.CreateInsertValue(globalValue, lengthValue, 1, "")
 | 
									llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType),
 | 
				
			||||||
		}
 | 
								}, typeFields...)
 | 
				
			||||||
		if !methodSet.IsNil() {
 | 
					 | 
				
			||||||
			globalValue = c.builder.CreateInsertValue(globalValue, methodSet, 2, "")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !ptrTo.IsNil() {
 | 
					 | 
				
			||||||
			globalValue = c.builder.CreateInsertValue(globalValue, ptrTo, 3, "")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !typeAssert.IsNil() {
 | 
					 | 
				
			||||||
			globalValue = c.builder.CreateInsertValue(globalValue, typeAssert, 4, "")
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							alignment := c.targetData.TypeAllocSize(c.i8ptrType)
 | 
				
			||||||
 | 
							globalValue := c.ctx.ConstStruct(typeFields, false)
 | 
				
			||||||
		global.SetInitializer(globalValue)
 | 
							global.SetInitializer(globalValue)
 | 
				
			||||||
		global.SetLinkage(llvm.LinkOnceODRLinkage)
 | 
							global.SetLinkage(llvm.LinkOnceODRLinkage)
 | 
				
			||||||
		global.SetGlobalConstant(true)
 | 
							global.SetGlobalConstant(true)
 | 
				
			||||||
	}
 | 
							global.SetAlignment(int(alignment))
 | 
				
			||||||
	return global
 | 
							if c.Debug {
 | 
				
			||||||
}
 | 
								file := c.getDIFile("<Go type>")
 | 
				
			||||||
 | 
								diglobal := c.dibuilder.CreateGlobalVariableExpression(file, llvm.DIGlobalVariableExpression{
 | 
				
			||||||
// makeStructTypeFields creates a new global that stores all type information
 | 
									Name:        "type " + typ.String(),
 | 
				
			||||||
// related to this struct type, and returns the resulting global. This global is
 | 
									File:        file,
 | 
				
			||||||
// actually an array of all the fields in the structs.
 | 
									Line:        1,
 | 
				
			||||||
func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
 | 
									Type:        c.getDIType(globalType),
 | 
				
			||||||
	// The global is an array of runtime.structField structs.
 | 
									LocalToUnit: false,
 | 
				
			||||||
	runtimeStructField := c.getLLVMRuntimeType("structField")
 | 
									Expr:        c.dibuilder.CreateExpression(nil),
 | 
				
			||||||
	structGlobalType := llvm.ArrayType(runtimeStructField, typ.NumFields())
 | 
									AlignInBits: uint32(alignment * 8),
 | 
				
			||||||
	structGlobal := llvm.AddGlobal(c.mod, structGlobalType, "reflect/types.structFields")
 | 
					 | 
				
			||||||
	structGlobalValue := llvm.ConstNull(structGlobalType)
 | 
					 | 
				
			||||||
	for i := 0; i < typ.NumFields(); i++ {
 | 
					 | 
				
			||||||
		fieldGlobalValue := llvm.ConstNull(runtimeStructField)
 | 
					 | 
				
			||||||
		fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), 0, "")
 | 
					 | 
				
			||||||
		fieldNameType, fieldName := c.makeGlobalArray([]byte(typ.Field(i).Name()), "reflect/types.structFieldName", c.ctx.Int8Type())
 | 
					 | 
				
			||||||
		fieldName.SetLinkage(llvm.PrivateLinkage)
 | 
					 | 
				
			||||||
		fieldName.SetUnnamedAddr(true)
 | 
					 | 
				
			||||||
		fieldName = llvm.ConstGEP(fieldNameType, fieldName, []llvm.Value{
 | 
					 | 
				
			||||||
			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 | 
					 | 
				
			||||||
			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldName, 1, "")
 | 
					 | 
				
			||||||
		if typ.Tag(i) != "" {
 | 
					 | 
				
			||||||
			fieldTagType, fieldTag := c.makeGlobalArray([]byte(typ.Tag(i)), "reflect/types.structFieldTag", c.ctx.Int8Type())
 | 
					 | 
				
			||||||
			fieldTag.SetLinkage(llvm.PrivateLinkage)
 | 
					 | 
				
			||||||
			fieldTag.SetUnnamedAddr(true)
 | 
					 | 
				
			||||||
			fieldTag = llvm.ConstGEP(fieldTagType, fieldTag, []llvm.Value{
 | 
					 | 
				
			||||||
				llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 | 
					 | 
				
			||||||
				llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 | 
					 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldTag, 2, "")
 | 
								global.AddMetadata(0, diglobal)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if typ.Field(i).Embedded() {
 | 
					 | 
				
			||||||
			fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false)
 | 
					 | 
				
			||||||
			fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldEmbedded, 3, "")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		structGlobalValue = c.builder.CreateInsertValue(structGlobalValue, fieldGlobalValue, i, "")
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	structGlobal.SetInitializer(structGlobalValue)
 | 
						offset := uint64(0)
 | 
				
			||||||
	structGlobal.SetUnnamedAddr(true)
 | 
						if hasMethodSet {
 | 
				
			||||||
	structGlobal.SetLinkage(llvm.PrivateLinkage)
 | 
							// The pointer to the method set is always the first element of the
 | 
				
			||||||
	return structGlobal
 | 
							// global (if there is a method set). However, the pointer we return
 | 
				
			||||||
 | 
							// should point to the 'kind' field not the method set.
 | 
				
			||||||
 | 
							offset = 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
 | 
				
			||||||
 | 
							llvm.ConstInt(llvm.Int32Type(), 0, false),
 | 
				
			||||||
 | 
							llvm.ConstInt(llvm.Int32Type(), offset, false),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var basicTypes = [...]string{
 | 
					// getTypeKind returns the type kind for the given type, as defined by
 | 
				
			||||||
 | 
					// reflect.Kind.
 | 
				
			||||||
 | 
					func getTypeKind(t types.Type) uint8 {
 | 
				
			||||||
 | 
						switch t := t.Underlying().(type) {
 | 
				
			||||||
 | 
						case *types.Basic:
 | 
				
			||||||
 | 
							return basicTypes[t.Kind()]
 | 
				
			||||||
 | 
						case *types.Chan:
 | 
				
			||||||
 | 
							return typeKindChan
 | 
				
			||||||
 | 
						case *types.Interface:
 | 
				
			||||||
 | 
							return typeKindInterface
 | 
				
			||||||
 | 
						case *types.Pointer:
 | 
				
			||||||
 | 
							return typeKindPointer
 | 
				
			||||||
 | 
						case *types.Slice:
 | 
				
			||||||
 | 
							return typeKindSlice
 | 
				
			||||||
 | 
						case *types.Array:
 | 
				
			||||||
 | 
							return typeKindArray
 | 
				
			||||||
 | 
						case *types.Signature:
 | 
				
			||||||
 | 
							return typeKindSignature
 | 
				
			||||||
 | 
						case *types.Map:
 | 
				
			||||||
 | 
							return typeKindMap
 | 
				
			||||||
 | 
						case *types.Struct:
 | 
				
			||||||
 | 
							return typeKindStruct
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							panic("unknown type")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var basicTypeNames = [...]string{
 | 
				
			||||||
	types.Bool:          "bool",
 | 
						types.Bool:          "bool",
 | 
				
			||||||
	types.Int:           "int",
 | 
						types.Int:           "int",
 | 
				
			||||||
	types.Int8:          "int8",
 | 
						types.Int8:          "int8",
 | 
				
			||||||
| 
						 | 
					@ -183,7 +344,7 @@ func getTypeCodeName(t types.Type) string {
 | 
				
			||||||
	case *types.Array:
 | 
						case *types.Array:
 | 
				
			||||||
		return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
 | 
							return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
 | 
				
			||||||
	case *types.Basic:
 | 
						case *types.Basic:
 | 
				
			||||||
		return "basic:" + basicTypes[t.Kind()]
 | 
							return "basic:" + basicTypeNames[t.Kind()]
 | 
				
			||||||
	case *types.Chan:
 | 
						case *types.Chan:
 | 
				
			||||||
		return "chan:" + getTypeCodeName(t.Elem())
 | 
							return "chan:" + getTypeCodeName(t.Elem())
 | 
				
			||||||
	case *types.Interface:
 | 
						case *types.Interface:
 | 
				
			||||||
| 
						 | 
					@ -235,75 +396,40 @@ func getTypeCodeName(t types.Type) string {
 | 
				
			||||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
 | 
					// getTypeMethodSet returns a reference (GEP) to a global method set. This
 | 
				
			||||||
// method set should be unreferenced after the interface lowering pass.
 | 
					// method set should be unreferenced after the interface lowering pass.
 | 
				
			||||||
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
 | 
					func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
 | 
				
			||||||
	global := c.mod.NamedGlobal(typ.String() + "$methodset")
 | 
						globalName := typ.String() + "$methodset"
 | 
				
			||||||
	zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
 | 
						global := c.mod.NamedGlobal(globalName)
 | 
				
			||||||
	if !global.IsNil() {
 | 
						if global.IsNil() {
 | 
				
			||||||
		// the method set already exists
 | 
							ms := c.program.MethodSets.MethodSet(typ)
 | 
				
			||||||
		return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{zero, zero})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ms := c.program.MethodSets.MethodSet(typ)
 | 
							// Create method set.
 | 
				
			||||||
	if ms.Len() == 0 {
 | 
							var signatures, wrappers []llvm.Value
 | 
				
			||||||
		// no methods, so can leave that one out
 | 
							for i := 0; i < ms.Len(); i++ {
 | 
				
			||||||
		return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
 | 
								method := ms.At(i)
 | 
				
			||||||
	}
 | 
								signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
 | 
				
			||||||
 | 
								signatures = append(signatures, signatureGlobal)
 | 
				
			||||||
	methods := make([]llvm.Value, ms.Len())
 | 
								fn := c.program.MethodValue(method)
 | 
				
			||||||
	interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo")
 | 
								llvmFnType, llvmFn := c.getFunction(fn)
 | 
				
			||||||
	for i := 0; i < ms.Len(); i++ {
 | 
								if llvmFn.IsNil() {
 | 
				
			||||||
		method := ms.At(i)
 | 
									// compiler error, so panic
 | 
				
			||||||
		signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
 | 
									panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
 | 
				
			||||||
		fn := c.program.MethodValue(method)
 | 
								}
 | 
				
			||||||
		llvmFnType, llvmFn := c.getFunction(fn)
 | 
								wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn)
 | 
				
			||||||
		if llvmFn.IsNil() {
 | 
								wrappers = append(wrappers, wrapper)
 | 
				
			||||||
			// compiler error, so panic
 | 
					 | 
				
			||||||
			panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn)
 | 
					 | 
				
			||||||
		methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
 | 
					 | 
				
			||||||
			signatureGlobal,
 | 
					 | 
				
			||||||
			llvm.ConstPtrToInt(wrapper, c.uintptrType),
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		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.LinkOnceODRLinkage)
 | 
					 | 
				
			||||||
	return llvm.ConstGEP(arrayType, global, []llvm.Value{zero, zero})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getInterfaceMethodSet returns a global variable with the method set of the
 | 
							// Construct global value.
 | 
				
			||||||
// given named interface type. This method set is used by the interface lowering
 | 
							globalValue := c.ctx.ConstStruct([]llvm.Value{
 | 
				
			||||||
// pass.
 | 
								llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
 | 
				
			||||||
func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
 | 
								llvm.ConstArray(c.i8ptrType, signatures),
 | 
				
			||||||
	name := typ.String()
 | 
								c.ctx.ConstStruct(wrappers, false),
 | 
				
			||||||
	if _, ok := typ.(*types.Named); !ok {
 | 
							}, false)
 | 
				
			||||||
		// Anonymous interface.
 | 
							global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
 | 
				
			||||||
		name = "reflect/types.interface:" + name
 | 
							global.SetInitializer(globalValue)
 | 
				
			||||||
 | 
							global.SetGlobalConstant(true)
 | 
				
			||||||
 | 
							global.SetUnnamedAddr(true)
 | 
				
			||||||
 | 
							global.SetLinkage(llvm.LinkOnceODRLinkage)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	global := c.mod.NamedGlobal(name + "$interface")
 | 
						return global
 | 
				
			||||||
	zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
 | 
					 | 
				
			||||||
	if !global.IsNil() {
 | 
					 | 
				
			||||||
		// method set already exist, return it
 | 
					 | 
				
			||||||
		return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{zero, zero})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Every method is a *i8 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(c.i8ptrType, methods)
 | 
					 | 
				
			||||||
	global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
 | 
					 | 
				
			||||||
	global.SetInitializer(value)
 | 
					 | 
				
			||||||
	global.SetGlobalConstant(true)
 | 
					 | 
				
			||||||
	global.SetLinkage(llvm.LinkOnceODRLinkage)
 | 
					 | 
				
			||||||
	return llvm.ConstGEP(value.Type(), global, []llvm.Value{zero, zero})
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getMethodSignatureName returns a unique name (that can be used as the name of
 | 
					// getMethodSignatureName returns a unique name (that can be used as the name of
 | 
				
			||||||
| 
						 | 
					@ -443,7 +569,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll
 | 
				
			||||||
	fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
 | 
						fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
 | 
				
			||||||
	llvmFn := c.mod.NamedFunction(fnName)
 | 
						llvmFn := c.mod.NamedFunction(fnName)
 | 
				
			||||||
	if llvmFn.IsNil() {
 | 
						if llvmFn.IsNil() {
 | 
				
			||||||
		llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.uintptrType}, false)
 | 
							llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, false)
 | 
				
			||||||
		llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
 | 
							llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
 | 
				
			||||||
		c.addStandardDeclaredAttributes(llvmFn)
 | 
							c.addStandardDeclaredAttributes(llvmFn)
 | 
				
			||||||
		methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
 | 
							methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
 | 
				
			||||||
| 
						 | 
					@ -464,7 +590,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
 | 
				
			||||||
		for i := 0; i < sig.Params().Len(); i++ {
 | 
							for i := 0; i < sig.Params().Len(); i++ {
 | 
				
			||||||
			paramTuple = append(paramTuple, sig.Params().At(i))
 | 
								paramTuple = append(paramTuple, sig.Params().At(i))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr]))
 | 
							paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
 | 
				
			||||||
		llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
 | 
							llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
 | 
				
			||||||
		llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
 | 
							llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
 | 
				
			||||||
		c.addStandardDeclaredAttributes(llvmFn)
 | 
							c.addStandardDeclaredAttributes(llvmFn)
 | 
				
			||||||
| 
						 | 
					@ -601,7 +727,7 @@ func typestring(t types.Type) string {
 | 
				
			||||||
	case *types.Array:
 | 
						case *types.Array:
 | 
				
			||||||
		return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
 | 
							return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
 | 
				
			||||||
	case *types.Basic:
 | 
						case *types.Basic:
 | 
				
			||||||
		return basicTypes[t.Kind()]
 | 
							return basicTypeNames[t.Kind()]
 | 
				
			||||||
	case *types.Chan:
 | 
						case *types.Chan:
 | 
				
			||||||
		switch t.Dir() {
 | 
							switch t.Dir() {
 | 
				
			||||||
		case types.SendRecv:
 | 
							case types.SendRecv:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								compiler/testdata/defer-cortex-m-qemu.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										2
									
								
								compiler/testdata/defer-cortex-m-qemu.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -4,7 +4,7 @@ target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
 | 
				
			||||||
target triple = "thumbv7m-unknown-unknown-eabi"
 | 
					target triple = "thumbv7m-unknown-unknown-eabi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
 | 
					%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
 | 
				
			||||||
%runtime._interface = type { i32, ptr }
 | 
					%runtime._interface = type { ptr, ptr }
 | 
				
			||||||
%runtime._defer = type { i32, ptr }
 | 
					%runtime._defer = type { i32, ptr }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
 | 
					declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								compiler/testdata/gc.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										10
									
								
								compiler/testdata/gc.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -3,8 +3,7 @@ source_filename = "gc.go"
 | 
				
			||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
 | 
					target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
 | 
				
			||||||
target triple = "wasm32-unknown-wasi"
 | 
					target triple = "wasm32-unknown-wasi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%runtime.typecodeID = type { ptr, i32, ptr, ptr, i32 }
 | 
					%runtime._interface = type { ptr, ptr }
 | 
				
			||||||
%runtime._interface = type { i32, ptr }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@main.scalar1 = hidden global ptr null, align 4
 | 
					@main.scalar1 = hidden global ptr null, align 4
 | 
				
			||||||
@main.scalar2 = hidden global ptr null, align 4
 | 
					@main.scalar2 = hidden global ptr null, align 4
 | 
				
			||||||
| 
						 | 
					@ -22,8 +21,8 @@ target triple = "wasm32-unknown-wasi"
 | 
				
			||||||
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
 | 
					@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
 | 
				
			||||||
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
 | 
					@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
 | 
				
			||||||
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
 | 
					@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
 | 
				
			||||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { ptr null, i32 0, ptr null, ptr @"reflect/types.type:pointer:basic:complex128", i32 0 }
 | 
					@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 16, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
 | 
				
			||||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:basic:complex128", i32 0, ptr null, ptr null, i32 0 }
 | 
					@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:complex128" }, align 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
 | 
					declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,7 +128,8 @@ entry:
 | 
				
			||||||
  store double %v.r, ptr %0, align 8
 | 
					  store double %v.r, ptr %0, align 8
 | 
				
			||||||
  %.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
 | 
					  %.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
 | 
				
			||||||
  store double %v.i, ptr %.repack1, align 8
 | 
					  store double %v.i, ptr %.repack1, align 8
 | 
				
			||||||
  %1 = insertvalue %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:basic:complex128" to i32), ptr undef }, ptr %0, 1
 | 
					  %1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
 | 
				
			||||||
 | 
					  call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #2
 | 
				
			||||||
  call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
 | 
					  call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
 | 
				
			||||||
  ret %runtime._interface %1
 | 
					  ret %runtime._interface %1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								compiler/testdata/goroutine-cortex-m-qemu-tasks.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										22
									
								
								compiler/testdata/goroutine-cortex-m-qemu-tasks.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -145,34 +145,34 @@ entry:
 | 
				
			||||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
 | 
					declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
					define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
 | 
					  %0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
 | 
				
			||||||
  store ptr %itf.value, ptr %0, align 4
 | 
					  store ptr %itf.value, ptr %0, align 4
 | 
				
			||||||
  %1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1
 | 
					  %1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
 | 
				
			||||||
  store ptr @"main$string", ptr %1, align 4
 | 
					  store ptr @"main$string", ptr %1, align 4
 | 
				
			||||||
  %.repack1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1, i32 1
 | 
					  %.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1
 | 
				
			||||||
  store i32 4, ptr %.repack1, align 4
 | 
					  store i32 4, ptr %.repack1, align 4
 | 
				
			||||||
  %2 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 2
 | 
					  %2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
 | 
				
			||||||
  store i32 %itf.typecode, ptr %2, align 4
 | 
					  store ptr %itf.typecode, ptr %2, align 4
 | 
				
			||||||
  %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #8
 | 
					  %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #8
 | 
				
			||||||
  call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #8
 | 
					  call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #8
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, i32, ptr) #6
 | 
					declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 {
 | 
					define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %1 = load ptr, ptr %0, align 4
 | 
					  %1 = load ptr, ptr %0, align 4
 | 
				
			||||||
  %2 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 1
 | 
					  %2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
 | 
				
			||||||
  %3 = load ptr, ptr %2, align 4
 | 
					  %3 = load ptr, ptr %2, align 4
 | 
				
			||||||
  %4 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 2
 | 
					  %4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
 | 
				
			||||||
  %5 = load i32, ptr %4, align 4
 | 
					  %5 = load i32, ptr %4, align 4
 | 
				
			||||||
  %6 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 3
 | 
					  %6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
 | 
				
			||||||
  %7 = load i32, ptr %6, align 4
 | 
					  %7 = load ptr, ptr %6, align 4
 | 
				
			||||||
  call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, i32 %7, ptr undef) #8
 | 
					  call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #8
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								compiler/testdata/goroutine-wasm-asyncify.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										22
									
								
								compiler/testdata/goroutine-wasm-asyncify.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -154,35 +154,35 @@ entry:
 | 
				
			||||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
 | 
					declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
					define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %stackalloc = alloca i8, align 1
 | 
					  %stackalloc = alloca i8, align 1
 | 
				
			||||||
  %0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
 | 
					  %0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
 | 
				
			||||||
  call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
 | 
					  call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
 | 
				
			||||||
  store ptr %itf.value, ptr %0, align 4
 | 
					  store ptr %itf.value, ptr %0, align 4
 | 
				
			||||||
  %1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1
 | 
					  %1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
 | 
				
			||||||
  store ptr @"main$string", ptr %1, align 4
 | 
					  store ptr @"main$string", ptr %1, align 4
 | 
				
			||||||
  %.repack1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1, i32 1
 | 
					  %.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1
 | 
				
			||||||
  store i32 4, ptr %.repack1, align 4
 | 
					  store i32 4, ptr %.repack1, align 4
 | 
				
			||||||
  %2 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 2
 | 
					  %2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
 | 
				
			||||||
  store i32 %itf.typecode, ptr %2, align 4
 | 
					  store ptr %itf.typecode, ptr %2, align 4
 | 
				
			||||||
  call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8
 | 
					  call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, i32, ptr) #6
 | 
					declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 {
 | 
					define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %1 = load ptr, ptr %0, align 4
 | 
					  %1 = load ptr, ptr %0, align 4
 | 
				
			||||||
  %2 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 1
 | 
					  %2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
 | 
				
			||||||
  %3 = load ptr, ptr %2, align 4
 | 
					  %3 = load ptr, ptr %2, align 4
 | 
				
			||||||
  %4 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 2
 | 
					  %4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
 | 
				
			||||||
  %5 = load i32, ptr %4, align 4
 | 
					  %5 = load i32, ptr %4, align 4
 | 
				
			||||||
  %6 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 3
 | 
					  %6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
 | 
				
			||||||
  %7 = load i32, ptr %6, align 4
 | 
					  %7 = load ptr, ptr %6, align 4
 | 
				
			||||||
  call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, i32 %7, ptr undef) #8
 | 
					  call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #8
 | 
				
			||||||
  call void @runtime.deadlock(ptr undef) #8
 | 
					  call void @runtime.deadlock(ptr undef) #8
 | 
				
			||||||
  unreachable
 | 
					  unreachable
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										69
									
								
								compiler/testdata/interface.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										69
									
								
								compiler/testdata/interface.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -3,22 +3,17 @@ source_filename = "interface.go"
 | 
				
			||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
 | 
					target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
 | 
				
			||||||
target triple = "wasm32-unknown-wasi"
 | 
					target triple = "wasm32-unknown-wasi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%runtime.typecodeID = type { ptr, i32, ptr, ptr, i32 }
 | 
					%runtime._interface = type { ptr, ptr }
 | 
				
			||||||
%runtime._interface = type { i32, ptr }
 | 
					 | 
				
			||||||
%runtime._string = type { ptr, i32 }
 | 
					%runtime._string = type { ptr, i32 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { ptr null, i32 0, ptr null, ptr @"reflect/types.type:pointer:basic:int", i32 0 }
 | 
					@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 2, ptr @"reflect/types.type:pointer:basic:int" }, align 4
 | 
				
			||||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:basic:int", i32 0, ptr null, ptr null, i32 0 }
 | 
					@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:int" }, align 4
 | 
				
			||||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:named:error", i32 0, ptr null, ptr null, i32 0 }
 | 
					@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:named:error" }, align 4
 | 
				
			||||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, ptr null, ptr @"reflect/types.type:pointer:named:error", i32 ptrtoint (ptr @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
 | 
					@"reflect/types.type:named:error" = linkonce_odr constant { i8, ptr, ptr } { i8 52, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
 | 
				
			||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.interface:interface{Error() string}$interface", i32 0, ptr null, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", i32 ptrtoint (ptr @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
 | 
					@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
 | 
				
			||||||
@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
 | 
					@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
 | 
				
			||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x ptr] [ptr @"reflect/methods.Error() string"]
 | 
					@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
 | 
				
			||||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, ptr null, ptr null, i32 0 }
 | 
					@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
 | 
				
			||||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, ptr null, ptr null, i32 0 }
 | 
					 | 
				
			||||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.interface:interface{String() string}$interface", i32 0, ptr null, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", i32 ptrtoint (ptr @"interface:{String:func:{}{basic:string}}.$typeassert" to i32) }
 | 
					 | 
				
			||||||
@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1
 | 
					 | 
				
			||||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x ptr] [ptr @"reflect/methods.String() string"]
 | 
					 | 
				
			||||||
@"reflect/types.typeid:basic:int" = external constant i8
 | 
					@"reflect/types.typeid:basic:int" = external constant i8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
 | 
					declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
 | 
				
			||||||
| 
						 | 
					@ -35,42 +30,42 @@ entry:
 | 
				
			||||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
 | 
					define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %stackalloc = alloca i8, align 1
 | 
					  %stackalloc = alloca i8, align 1
 | 
				
			||||||
 | 
					  call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
 | 
					  call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:basic:int" to i32), ptr null }
 | 
					  ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
 | 
					define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %stackalloc = alloca i8, align 1
 | 
					  %stackalloc = alloca i8, align 1
 | 
				
			||||||
 | 
					  call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
 | 
					  call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:basic:int" to i32), ptr null }
 | 
					  ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
 | 
					define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %stackalloc = alloca i8, align 1
 | 
					  %stackalloc = alloca i8, align 1
 | 
				
			||||||
 | 
					  call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
 | 
					  call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:named:error" to i32), ptr null }
 | 
					  ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
 | 
					define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %stackalloc = alloca i8, align 1
 | 
					  %stackalloc = alloca i8, align 1
 | 
				
			||||||
 | 
					  call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
 | 
					  call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), ptr null }
 | 
					  ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden i1 @main.isInt(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
					define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
 | 
					  %typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
 | 
				
			||||||
  br i1 %typecode, label %typeassert.ok, label %typeassert.next
 | 
					  br i1 %typecode, label %typeassert.ok, label %typeassert.next
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeassert.next:                                  ; preds = %typeassert.ok, %entry
 | 
					typeassert.next:                                  ; preds = %typeassert.ok, %entry
 | 
				
			||||||
| 
						 | 
					@ -80,12 +75,12 @@ typeassert.ok:                                    ; preds = %entry
 | 
				
			||||||
  br label %typeassert.next
 | 
					  br label %typeassert.next
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @runtime.typeAssert(i32, ptr dereferenceable_or_null(1), ptr) #0
 | 
					declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden i1 @main.isError(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
					define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
 | 
					  %0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
 | 
				
			||||||
  br i1 %0, label %typeassert.ok, label %typeassert.next
 | 
					  br i1 %0, label %typeassert.ok, label %typeassert.next
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeassert.next:                                  ; preds = %typeassert.ok, %entry
 | 
					typeassert.next:                                  ; preds = %typeassert.ok, %entry
 | 
				
			||||||
| 
						 | 
					@ -95,10 +90,12 @@ typeassert.ok:                                    ; preds = %entry
 | 
				
			||||||
  br label %typeassert.next
 | 
					  br label %typeassert.next
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden i1 @main.isStringer(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
					define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
 | 
					  %0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
 | 
				
			||||||
  br i1 %0, label %typeassert.ok, label %typeassert.next
 | 
					  br i1 %0, label %typeassert.ok, label %typeassert.next
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeassert.next:                                  ; preds = %typeassert.ok, %entry
 | 
					typeassert.next:                                  ; preds = %typeassert.ok, %entry
 | 
				
			||||||
| 
						 | 
					@ -108,26 +105,28 @@ typeassert.ok:                                    ; preds = %entry
 | 
				
			||||||
  br label %typeassert.next
 | 
					  br label %typeassert.next
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden i8 @main.callFooMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
					define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, i32 %itf.typecode, ptr undef) #6
 | 
					  %0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #6
 | 
				
			||||||
  ret i8 %0
 | 
					  ret i8 %0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, i32, ptr) #4
 | 
					declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; Function Attrs: nounwind
 | 
					; Function Attrs: nounwind
 | 
				
			||||||
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
					define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %stackalloc = alloca i8, align 1
 | 
					  %stackalloc = alloca i8, align 1
 | 
				
			||||||
  %0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, i32 %itf.typecode, ptr undef) #6
 | 
					  %0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #6
 | 
				
			||||||
  %1 = extractvalue %runtime._string %0, 0
 | 
					  %1 = extractvalue %runtime._string %0, 0
 | 
				
			||||||
  call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6
 | 
					  call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6
 | 
				
			||||||
  ret %runtime._string %0
 | 
					  ret %runtime._string %0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, i32, ptr) #5
 | 
					declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
 | 
					attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
 | 
				
			||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
 | 
					attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -238,7 +238,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
				// which case this call won't even get to this point but will
 | 
									// which case this call won't even get to this point but will
 | 
				
			||||||
				// already be emitted in initAll.
 | 
									// already be emitted in initAll.
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" ||
 | 
								case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "runtime.hashmapInterfaceHash" ||
 | 
				
			||||||
				callFn.name == "os.runtime_args" || callFn.name == "internal/task.start" || callFn.name == "internal/task.Current":
 | 
									callFn.name == "os.runtime_args" || callFn.name == "internal/task.start" || callFn.name == "internal/task.Current":
 | 
				
			||||||
				// These functions should be run at runtime. Specifically:
 | 
									// These functions should be run at runtime. Specifically:
 | 
				
			||||||
				//   * Print and panic functions are best emitted directly without
 | 
									//   * Print and panic functions are best emitted directly without
 | 
				
			||||||
| 
						 | 
					@ -378,42 +378,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
				copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
 | 
									copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
 | 
				
			||||||
				dstObj.buffer = dstBuf
 | 
									dstObj.buffer = dstBuf
 | 
				
			||||||
				mem.put(dst.index(), dstObj)
 | 
									mem.put(dst.index(), dstObj)
 | 
				
			||||||
			case callFn.name == "(reflect.rawType).elem":
 | 
					 | 
				
			||||||
				if r.debug {
 | 
					 | 
				
			||||||
					fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				// Extract the type code global from the first parameter.
 | 
					 | 
				
			||||||
				typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					return nil, mem, r.errorAt(inst, err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				typecodeID := typecodeIDPtrToInt.Operand(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Get the type class.
 | 
					 | 
				
			||||||
				// See also: getClassAndValueFromTypeCode in transform/reflect.go.
 | 
					 | 
				
			||||||
				typecodeName := typecodeID.Name()
 | 
					 | 
				
			||||||
				const prefix = "reflect/types.type:"
 | 
					 | 
				
			||||||
				if !strings.HasPrefix(typecodeName, prefix) {
 | 
					 | 
				
			||||||
					panic("unexpected typecode name: " + typecodeName)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				id := typecodeName[len(prefix):]
 | 
					 | 
				
			||||||
				class := id[:strings.IndexByte(id, ':')]
 | 
					 | 
				
			||||||
				value := id[len(class)+1:]
 | 
					 | 
				
			||||||
				if class == "named" {
 | 
					 | 
				
			||||||
					// Get the underlying type.
 | 
					 | 
				
			||||||
					class = value[:strings.IndexByte(value, ':')]
 | 
					 | 
				
			||||||
					value = value[len(class)+1:]
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Elem() is only valid for certain type classes.
 | 
					 | 
				
			||||||
				switch class {
 | 
					 | 
				
			||||||
				case "chan", "pointer", "slice", "array":
 | 
					 | 
				
			||||||
					elementType := r.builder.CreateExtractValue(typecodeID.Initializer(), 0, "")
 | 
					 | 
				
			||||||
					uintptrType := r.mod.Context().IntType(int(mem.r.pointerSize) * 8)
 | 
					 | 
				
			||||||
					locals[inst.localIndex] = r.getValue(llvm.ConstPtrToInt(elementType, uintptrType))
 | 
					 | 
				
			||||||
				default:
 | 
					 | 
				
			||||||
					return nil, mem, r.errorAt(inst, fmt.Errorf("(reflect.Type).Elem() called on %s type", class))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			case callFn.name == "runtime.typeAssert":
 | 
								case callFn.name == "runtime.typeAssert":
 | 
				
			||||||
				// This function must be implemented manually as it is normally
 | 
									// This function must be implemented manually as it is normally
 | 
				
			||||||
				// implemented by the interface lowering pass.
 | 
									// implemented by the interface lowering pass.
 | 
				
			||||||
| 
						 | 
					@ -424,15 +388,22 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, mem, r.errorAt(inst, err)
 | 
										return nil, mem, r.errorAt(inst, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				actualTypePtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
 | 
									actualType, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, mem, r.errorAt(inst, err)
 | 
										return nil, mem, r.errorAt(inst, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if !actualTypePtrToInt.IsAConstantInt().IsNil() && actualTypePtrToInt.ZExtValue() == 0 {
 | 
									if !actualType.IsAConstantInt().IsNil() && actualType.ZExtValue() == 0 {
 | 
				
			||||||
					locals[inst.localIndex] = literalValue{uint8(0)}
 | 
										locals[inst.localIndex] = literalValue{uint8(0)}
 | 
				
			||||||
					break
 | 
										break
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				actualType := actualTypePtrToInt.Operand(0)
 | 
									// Strip pointer casts (bitcast, getelementptr).
 | 
				
			||||||
 | 
									for !actualType.IsAConstantExpr().IsNil() {
 | 
				
			||||||
 | 
										opcode := actualType.Opcode()
 | 
				
			||||||
 | 
										if opcode != llvm.GetElementPtr && opcode != llvm.BitCast {
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										actualType = actualType.Operand(0)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") {
 | 
									if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") {
 | 
				
			||||||
					locals[inst.localIndex] = literalValue{uint8(1)}
 | 
										locals[inst.localIndex] = literalValue{uint8(1)}
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
| 
						 | 
					@ -448,11 +419,12 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, mem, r.errorAt(inst, err)
 | 
										return nil, mem, r.errorAt(inst, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				methodSetPtr, err := mem.load(typecodePtr.addOffset(r.pointerSize*2), r.pointerSize).asPointer(r)
 | 
									methodSetPtr, err := mem.load(typecodePtr.addOffset(-int64(r.pointerSize)), r.pointerSize).asPointer(r)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, mem, r.errorAt(inst, err)
 | 
										return nil, mem, r.errorAt(inst, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
 | 
									methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
 | 
				
			||||||
 | 
									numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
 | 
				
			||||||
				llvmFn := inst.llvmInst.CalledValue()
 | 
									llvmFn := inst.llvmInst.CalledValue()
 | 
				
			||||||
				methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
 | 
									methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
 | 
				
			||||||
				methodSetString := methodSetAttr.GetStringValue()
 | 
									methodSetString := methodSetAttr.GetStringValue()
 | 
				
			||||||
| 
						 | 
					@ -460,9 +432,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
				// Make a set of all the methods on the concrete type, for
 | 
									// Make a set of all the methods on the concrete type, for
 | 
				
			||||||
				// easier checking in the next step.
 | 
									// easier checking in the next step.
 | 
				
			||||||
				concreteTypeMethods := map[string]struct{}{}
 | 
									concreteTypeMethods := map[string]struct{}{}
 | 
				
			||||||
				for i := 0; i < methodSet.Type().ArrayLength(); i++ {
 | 
									for i := 0; i < numMethods; i++ {
 | 
				
			||||||
					methodInfo := r.builder.CreateExtractValue(methodSet, i, "")
 | 
										methodInfo := r.builder.CreateExtractValue(methodSet, 1, "")
 | 
				
			||||||
					name := r.builder.CreateExtractValue(methodInfo, 0, "").Name()
 | 
										name := r.builder.CreateExtractValue(methodInfo, i, "").Name()
 | 
				
			||||||
					concreteTypeMethods[name] = struct{}{}
 | 
										concreteTypeMethods[name] = struct{}{}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -488,15 +460,16 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
					fmt.Fprintln(os.Stderr, indent+"invoke method:", operands[1:])
 | 
										fmt.Fprintln(os.Stderr, indent+"invoke method:", operands[1:])
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Load the type code of the interface value.
 | 
									// Load the type code and method set of the interface value.
 | 
				
			||||||
				typecodeIDBitCast, err := operands[len(operands)-2].toLLVMValue(inst.llvmInst.Operand(len(operands)-3).Type(), &mem)
 | 
									typecodePtr, err := operands[len(operands)-2].asPointer(r)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, mem, r.errorAt(inst, err)
 | 
										return nil, mem, r.errorAt(inst, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				typecodeID := typecodeIDBitCast.Operand(0).Initializer()
 | 
									methodSetPtr, err := mem.load(typecodePtr.addOffset(-int64(r.pointerSize)), r.pointerSize).asPointer(r)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
				// Load the method set, which is part of the typecodeID object.
 | 
										return nil, mem, r.errorAt(inst, err)
 | 
				
			||||||
				methodSet := stripPointerCasts(r.builder.CreateExtractValue(typecodeID, 2, "")).Initializer()
 | 
									}
 | 
				
			||||||
 | 
									methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// We don't need to load the interface method set.
 | 
									// We don't need to load the interface method set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -508,13 +481,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Iterate through all methods, looking for the one method that
 | 
									// Iterate through all methods, looking for the one method that
 | 
				
			||||||
				// should be returned.
 | 
									// should be returned.
 | 
				
			||||||
				numMethods := methodSet.Type().ArrayLength()
 | 
									numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
 | 
				
			||||||
				var method llvm.Value
 | 
									var method llvm.Value
 | 
				
			||||||
				for i := 0; i < numMethods; i++ {
 | 
									for i := 0; i < numMethods; i++ {
 | 
				
			||||||
					methodSignatureAgg := r.builder.CreateExtractValue(methodSet, i, "")
 | 
										methodSignatureAgg := r.builder.CreateExtractValue(methodSet, 1, "")
 | 
				
			||||||
					methodSignature := r.builder.CreateExtractValue(methodSignatureAgg, 0, "")
 | 
										methodSignature := r.builder.CreateExtractValue(methodSignatureAgg, i, "")
 | 
				
			||||||
					if methodSignature == signature {
 | 
										if methodSignature == signature {
 | 
				
			||||||
						method = r.builder.CreateExtractValue(methodSignatureAgg, 1, "").Operand(0)
 | 
											methodAgg := r.builder.CreateExtractValue(methodSet, 2, "")
 | 
				
			||||||
 | 
											method = r.builder.CreateExtractValue(methodAgg, i, "")
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if method.IsNil() {
 | 
									if method.IsNil() {
 | 
				
			||||||
| 
						 | 
					@ -685,7 +659,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ptr = ptr.addOffset(uint32(offset))
 | 
								ptr = ptr.addOffset(int64(offset))
 | 
				
			||||||
			locals[inst.localIndex] = ptr
 | 
								locals[inst.localIndex] = ptr
 | 
				
			||||||
			if r.debug {
 | 
								if r.debug {
 | 
				
			||||||
				fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr)
 | 
									fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr)
 | 
				
			||||||
| 
						 | 
					@ -784,7 +758,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 | 
				
			||||||
				case llvm.Add:
 | 
									case llvm.Add:
 | 
				
			||||||
					// This likely means this is part of a
 | 
										// This likely means this is part of a
 | 
				
			||||||
					// unsafe.Pointer(uintptr(ptr) + offset) pattern.
 | 
										// unsafe.Pointer(uintptr(ptr) + offset) pattern.
 | 
				
			||||||
					lhsPtr = lhsPtr.addOffset(uint32(rhs.Uint()))
 | 
										lhsPtr = lhsPtr.addOffset(int64(rhs.Uint()))
 | 
				
			||||||
					locals[inst.localIndex] = lhsPtr
 | 
										locals[inst.localIndex] = lhsPtr
 | 
				
			||||||
					continue
 | 
										continue
 | 
				
			||||||
				case llvm.Xor:
 | 
									case llvm.Xor:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -501,7 +501,7 @@ func (v pointerValue) offset() uint32 {
 | 
				
			||||||
// addOffset essentially does a GEP operation (pointer arithmetic): it adds the
 | 
					// addOffset essentially does a GEP operation (pointer arithmetic): it adds the
 | 
				
			||||||
// offset to the pointer. It also checks that the offset doesn't overflow the
 | 
					// offset to the pointer. It also checks that the offset doesn't overflow the
 | 
				
			||||||
// maximum offset size (which is 4GB).
 | 
					// maximum offset size (which is 4GB).
 | 
				
			||||||
func (v pointerValue) addOffset(offset uint32) pointerValue {
 | 
					func (v pointerValue) addOffset(offset int64) pointerValue {
 | 
				
			||||||
	result := pointerValue{v.pointer + uint64(offset)}
 | 
						result := pointerValue{v.pointer + uint64(offset)}
 | 
				
			||||||
	if checks && v.index() != result.index() {
 | 
						if checks && v.index() != result.index() {
 | 
				
			||||||
		panic("interp: offset out of range")
 | 
							panic("interp: offset out of range")
 | 
				
			||||||
| 
						 | 
					@ -815,7 +815,7 @@ func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
 | 
				
			||||||
					// as a ptrtoint, so that they can be used in certain
 | 
										// as a ptrtoint, so that they can be used in certain
 | 
				
			||||||
					// optimizations.
 | 
										// optimizations.
 | 
				
			||||||
					name := elementType.StructName()
 | 
										name := elementType.StructName()
 | 
				
			||||||
					if name == "runtime.typecodeID" || name == "runtime.funcValueWithSignature" {
 | 
										if name == "runtime.funcValueWithSignature" {
 | 
				
			||||||
						uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8)
 | 
											uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8)
 | 
				
			||||||
						field = llvm.ConstPtrToInt(field, uintptrType)
 | 
											field = llvm.ConstPtrToInt(field, uintptrType)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								interp/testdata/interface.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										15
									
								
								interp/testdata/interface.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -1,17 +1,16 @@
 | 
				
			||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 | 
					target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 | 
				
			||||||
target triple = "x86_64--linux"
 | 
					target triple = "x86_64--linux"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i64, %runtime.interfaceMethodInfo* }
 | 
					 | 
				
			||||||
%runtime.interfaceMethodInfo = type { i8*, i64 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@main.v1 = global i1 0
 | 
					@main.v1 = global i1 0
 | 
				
			||||||
@main.v2 = global i1 0
 | 
					@main.v2 = global i1 0
 | 
				
			||||||
@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0, %runtime.interfaceMethodInfo* null }
 | 
					@"reflect/types.type:named:main.foo" = private constant { i8, i8*, i8* } { i8 34, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:named:main.foo", i32 0, i32 0), i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:int", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					@"reflect/types.type:pointer:named:main.foo" = external constant { i8, i8* }
 | 
				
			||||||
@"reflect/types.typeid:named:main.foo" = external constant i8
 | 
					@"reflect/types.typeid:named:main.foo" = external constant i8
 | 
				
			||||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
 | 
					@"reflect/types.type:basic:int" = private constant { i8, i8* } { i8 2, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:basic:int", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					@"reflect/types.type:pointer:basic:int" = external constant { i8, i8* }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @runtime.typeAssert(i64, i8*, i8*, i8*)
 | 
					declare i1 @runtime.typeAssert(i8*, i8*, i8*, i8*)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define void @runtime.initAll() unnamed_addr {
 | 
					define void @runtime.initAll() unnamed_addr {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
| 
						 | 
					@ -22,9 +21,9 @@ entry:
 | 
				
			||||||
define internal void @main.init() unnamed_addr {
 | 
					define internal void @main.init() unnamed_addr {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  ; Test type asserts.
 | 
					  ; Test type asserts.
 | 
				
			||||||
  %typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null)
 | 
					  %typecode = call i1 @runtime.typeAssert(i8* getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:main.foo", i32 0, i32 0), i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null)
 | 
				
			||||||
  store i1 %typecode, i1* @main.v1
 | 
					  store i1 %typecode, i1* @main.v1
 | 
				
			||||||
  %typecode2 = call i1 @runtime.typeAssert(i64 0, i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null)
 | 
					  %typecode2 = call i1 @runtime.typeAssert(i8* null, i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null)
 | 
				
			||||||
  store i1 %typecode2, i1* @main.v2
 | 
					  store i1 %typecode2, i1* @main.v2
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import "unsafe"
 | 
				
			||||||
type visit struct {
 | 
					type visit struct {
 | 
				
			||||||
	a1  unsafe.Pointer
 | 
						a1  unsafe.Pointer
 | 
				
			||||||
	a2  unsafe.Pointer
 | 
						a2  unsafe.Pointer
 | 
				
			||||||
	typ rawType
 | 
						typ *rawType
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Tests for deep equality using reflected types. The map argument tracks
 | 
					// Tests for deep equality using reflected types. The map argument tracks
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,61 +0,0 @@
 | 
				
			||||||
package reflect
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"unsafe"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// This stores a varint for each named type. Named types are identified by their
 | 
					 | 
				
			||||||
// name instead of by their type. The named types stored in this struct are
 | 
					 | 
				
			||||||
// non-basic types: pointer, struct, and channel.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//go:extern reflect.namedNonBasicTypesSidetable
 | 
					 | 
				
			||||||
var namedNonBasicTypesSidetable uintptr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//go:extern reflect.structTypesSidetable
 | 
					 | 
				
			||||||
var structTypesSidetable byte
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//go:extern reflect.structNamesSidetable
 | 
					 | 
				
			||||||
var structNamesSidetable byte
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//go:extern reflect.arrayTypesSidetable
 | 
					 | 
				
			||||||
var arrayTypesSidetable byte
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// readStringSidetable reads a string from the given table (like
 | 
					 | 
				
			||||||
// structNamesSidetable) and returns this string. No heap allocation is
 | 
					 | 
				
			||||||
// necessary because it makes the string point directly to the raw bytes of the
 | 
					 | 
				
			||||||
// table.
 | 
					 | 
				
			||||||
func readStringSidetable(table unsafe.Pointer, index uintptr) string {
 | 
					 | 
				
			||||||
	nameLen, namePtr := readVarint(unsafe.Pointer(uintptr(table) + index))
 | 
					 | 
				
			||||||
	return *(*string)(unsafe.Pointer(&stringHeader{
 | 
					 | 
				
			||||||
		data: namePtr,
 | 
					 | 
				
			||||||
		len:  nameLen,
 | 
					 | 
				
			||||||
	}))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// readVarint decodes a varint as used in the encoding/binary package.
 | 
					 | 
				
			||||||
// It has an input pointer and returns the read varint and the pointer
 | 
					 | 
				
			||||||
// incremented to the next field in the data structure, just after the varint.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Details:
 | 
					 | 
				
			||||||
// https://github.com/golang/go/blob/e37a1b1c/src/encoding/binary/varint.go#L7-L25
 | 
					 | 
				
			||||||
func readVarint(buf unsafe.Pointer) (uintptr, unsafe.Pointer) {
 | 
					 | 
				
			||||||
	var n uintptr
 | 
					 | 
				
			||||||
	shift := uintptr(0)
 | 
					 | 
				
			||||||
	for {
 | 
					 | 
				
			||||||
		// Read the next byte in the buffer.
 | 
					 | 
				
			||||||
		c := *(*byte)(buf)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Decode the bits from this byte and add them to the output number.
 | 
					 | 
				
			||||||
		n |= uintptr(c&0x7f) << shift
 | 
					 | 
				
			||||||
		shift += 7
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Increment the buf pointer (pointer arithmetic!).
 | 
					 | 
				
			||||||
		buf = unsafe.Pointer(uintptr(buf) + 1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Check whether this is the last byte of this varint. The upper bit
 | 
					 | 
				
			||||||
		// (msb) indicates whether any bytes follow.
 | 
					 | 
				
			||||||
		if c>>7 == 0 {
 | 
					 | 
				
			||||||
			return n, buf
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -2,36 +2,72 @@
 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Type information of an interface is stored as a pointer to a global in the
 | 
				
			||||||
 | 
					// interface type (runtime._interface). This is called a type struct.
 | 
				
			||||||
 | 
					// It always starts with a byte that contains both the type kind and a few
 | 
				
			||||||
 | 
					// flags. In most cases it also contains a pointer to another type struct
 | 
				
			||||||
 | 
					// (ptrTo), that is the pointer type of the current type (for example, type int
 | 
				
			||||||
 | 
					// also has a pointer to the type *int). The exception is pointer types, to
 | 
				
			||||||
 | 
					// avoid infinite recursion.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The layouts specifically look like this:
 | 
				
			||||||
 | 
					// - basic types (Bool..UnsafePointer):
 | 
				
			||||||
 | 
					//     meta         uint8 // actually: kind + flags
 | 
				
			||||||
 | 
					//     ptrTo        *typeStruct
 | 
				
			||||||
 | 
					// - named types (see elemType):
 | 
				
			||||||
 | 
					//     meta         uint8
 | 
				
			||||||
 | 
					//     ptrTo        *typeStruct
 | 
				
			||||||
 | 
					//     underlying   *typeStruct // the underlying, non-named type
 | 
				
			||||||
 | 
					// - channels and slices (see elemType):
 | 
				
			||||||
 | 
					//     meta          uint8
 | 
				
			||||||
 | 
					//     ptrTo        *typeStruct
 | 
				
			||||||
 | 
					//     elementType  *typeStruct // the type that you get with .Elem()
 | 
				
			||||||
 | 
					// - pointer types (see ptrType, this doesn't include chan, map, etc):
 | 
				
			||||||
 | 
					//     meta         uint8
 | 
				
			||||||
 | 
					//     elementType  *typeStruct
 | 
				
			||||||
 | 
					// - array types (see arrayType)
 | 
				
			||||||
 | 
					//     meta         uint8
 | 
				
			||||||
 | 
					//     ptrTo        *typeStruct
 | 
				
			||||||
 | 
					//     elem         *typeStruct // element type of the array
 | 
				
			||||||
 | 
					//     arrayLen     uintptr     // length of the array (this is part of the type)
 | 
				
			||||||
 | 
					// - map types (this is still missing the key and element types)
 | 
				
			||||||
 | 
					//     meta         uint8
 | 
				
			||||||
 | 
					//     ptrTo        *typeStruct
 | 
				
			||||||
 | 
					// - struct types (see structType):
 | 
				
			||||||
 | 
					//     meta         uint8
 | 
				
			||||||
 | 
					//     numField     uint16
 | 
				
			||||||
 | 
					//     ptrTo        *typeStruct
 | 
				
			||||||
 | 
					//     fields       [...]structField // the remaining fields are all of type structField
 | 
				
			||||||
 | 
					// - interface types (this is missing the interface methods):
 | 
				
			||||||
 | 
					//     meta         uint8
 | 
				
			||||||
 | 
					//     ptrTo        *typeStruct
 | 
				
			||||||
 | 
					// - signature types (this is missing input and output parameters):
 | 
				
			||||||
 | 
					//     meta         uint8
 | 
				
			||||||
 | 
					//     ptrTo        *typeStruct
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The type struct is essentially a union of all the above types. Which it is,
 | 
				
			||||||
 | 
					// can be determined by looking at the meta byte.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package reflect
 | 
					package reflect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The compiler uses a compact encoding to store type information. Unlike the
 | 
					// Flags stored in the first byte of the struct field byte array. Must be kept
 | 
				
			||||||
// main Go compiler, most of the types are stored directly in the type code.
 | 
					// up to date with compiler/interface.go.
 | 
				
			||||||
//
 | 
					const (
 | 
				
			||||||
// Type code bit allocation:
 | 
						structFieldFlagAnonymous = 1 << iota
 | 
				
			||||||
// xxxxx0: basic types, where xxxxx is the basic type number (never 0).
 | 
						structFieldFlagHasTag
 | 
				
			||||||
//         The higher bits indicate the named type, if any.
 | 
						structFieldFlagIsExported
 | 
				
			||||||
//  nxxx1: complex types, where n indicates whether this is a named type (named
 | 
					)
 | 
				
			||||||
//         if set) and xxx contains the type kind number:
 | 
					 | 
				
			||||||
//             0 (0001): Chan
 | 
					 | 
				
			||||||
//             1 (0011): Interface
 | 
					 | 
				
			||||||
//             2 (0101): Pointer
 | 
					 | 
				
			||||||
//             3 (0111): Slice
 | 
					 | 
				
			||||||
//             4 (1001): Array
 | 
					 | 
				
			||||||
//             5 (1011): Func
 | 
					 | 
				
			||||||
//             6 (1101): Map
 | 
					 | 
				
			||||||
//             7 (1111): Struct
 | 
					 | 
				
			||||||
//         The higher bits are either the contents of the type depending on the
 | 
					 | 
				
			||||||
//         type (if n is clear) or indicate the number of the named type (if n
 | 
					 | 
				
			||||||
//         is set).
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Kind uintptr
 | 
					type Kind uint8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Copied from reflect/type.go
 | 
					// Copied from reflect/type.go
 | 
				
			||||||
// https://golang.org/src/reflect/type.go?s=8302:8316#L217
 | 
					// https://golang.org/src/reflect/type.go?s=8302:8316#L217
 | 
				
			||||||
 | 
					// These constants must match basicTypes and the typeKind* constants in
 | 
				
			||||||
 | 
					// compiler/interface.go
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	Invalid Kind = iota
 | 
						Invalid Kind = iota
 | 
				
			||||||
	Bool
 | 
						Bool
 | 
				
			||||||
| 
						 | 
					@ -124,11 +160,6 @@ func (k Kind) String() string {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// basicType returns a new Type for this kind if Kind is a basic type.
 | 
					 | 
				
			||||||
func (k Kind) basicType() rawType {
 | 
					 | 
				
			||||||
	return rawType(k << 1)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copied from reflect/type.go
 | 
					// Copied from reflect/type.go
 | 
				
			||||||
// https://go.dev/src/reflect/type.go?#L348
 | 
					// https://go.dev/src/reflect/type.go?#L348
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -346,8 +377,64 @@ type Type interface {
 | 
				
			||||||
	Out(i int) Type
 | 
						Out(i int) Type
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The typecode as used in an interface{}.
 | 
					// Constants for the 'meta' byte.
 | 
				
			||||||
type rawType uintptr
 | 
					const (
 | 
				
			||||||
 | 
						kindMask  = 31 // mask to apply to the meta byte to get the Kind value
 | 
				
			||||||
 | 
						flagNamed = 32 // flag that is set if this is a named type
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The base type struct. All type structs start with this.
 | 
				
			||||||
 | 
					type rawType struct {
 | 
				
			||||||
 | 
						meta uint8 // metadata byte, contains kind and flags (see contants above)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// All types that have an element type: named, chan, slice, array, map (but not
 | 
				
			||||||
 | 
					// pointer because it doesn't have ptrTo).
 | 
				
			||||||
 | 
					type elemType struct {
 | 
				
			||||||
 | 
						rawType
 | 
				
			||||||
 | 
						ptrTo *rawType
 | 
				
			||||||
 | 
						elem  *rawType
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ptrType struct {
 | 
				
			||||||
 | 
						rawType
 | 
				
			||||||
 | 
						elem *rawType
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type arrayType struct {
 | 
				
			||||||
 | 
						rawType
 | 
				
			||||||
 | 
						ptrTo    *rawType
 | 
				
			||||||
 | 
						elem     *rawType
 | 
				
			||||||
 | 
						arrayLen uintptr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Type for struct types. The numField value is intentionally put before ptrTo
 | 
				
			||||||
 | 
					// for better struct packing on 32-bit and 64-bit architectures. On these
 | 
				
			||||||
 | 
					// architectures, the ptrTo field still has the same offset as in all the other
 | 
				
			||||||
 | 
					// type structs.
 | 
				
			||||||
 | 
					// The fields array isn't necessarily 1 structField long, instead it is as long
 | 
				
			||||||
 | 
					// as numFields. The array is given a length of 1 to satisfy the Go type
 | 
				
			||||||
 | 
					// checker.
 | 
				
			||||||
 | 
					type structType struct {
 | 
				
			||||||
 | 
						rawType
 | 
				
			||||||
 | 
						numField uint16
 | 
				
			||||||
 | 
						ptrTo    *rawType
 | 
				
			||||||
 | 
						fields   [1]structField // the remaining fields are all of type structField
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type structField struct {
 | 
				
			||||||
 | 
						fieldType *rawType
 | 
				
			||||||
 | 
						data      unsafe.Pointer // various bits of information, packed in a byte array
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Equivalent to (go/types.Type).Underlying(): if this is a named type return
 | 
				
			||||||
 | 
					// the underlying type, else just return the type itself.
 | 
				
			||||||
 | 
					func (t *rawType) underlying() *rawType {
 | 
				
			||||||
 | 
						if t.meta&flagNamed != 0 {
 | 
				
			||||||
 | 
							return (*elemType)(unsafe.Pointer(t)).elem
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TypeOf(i interface{}) Type {
 | 
					func TypeOf(i interface{}) Type {
 | 
				
			||||||
	return ValueOf(i).typecode
 | 
						return ValueOf(i).typecode
 | 
				
			||||||
| 
						 | 
					@ -356,70 +443,45 @@ func TypeOf(i interface{}) Type {
 | 
				
			||||||
func PtrTo(t Type) Type { return PointerTo(t) }
 | 
					func PtrTo(t Type) Type { return PointerTo(t) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func PointerTo(t Type) Type {
 | 
					func PointerTo(t Type) Type {
 | 
				
			||||||
	if t.Kind() == Pointer {
 | 
						switch t.Kind() {
 | 
				
			||||||
 | 
						case Pointer:
 | 
				
			||||||
		panic("reflect: cannot make **T type")
 | 
							panic("reflect: cannot make **T type")
 | 
				
			||||||
 | 
						case Struct:
 | 
				
			||||||
 | 
							return (*structType)(unsafe.Pointer(t.(*rawType))).ptrTo
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return (*elemType)(unsafe.Pointer(t.(*rawType))).ptrTo
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ptrType := t.(rawType)<<5 | 5 // 0b0101 == 5
 | 
					 | 
				
			||||||
	if ptrType>>5 != t {
 | 
					 | 
				
			||||||
		panic("reflect: PointerTo type does not fit")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ptrType
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) String() string {
 | 
					func (t *rawType) String() string {
 | 
				
			||||||
	return "T"
 | 
						return "T"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) Kind() Kind {
 | 
					func (t *rawType) Kind() Kind {
 | 
				
			||||||
	if t%2 == 0 {
 | 
						return Kind(t.meta & kindMask)
 | 
				
			||||||
		// basic type
 | 
					 | 
				
			||||||
		return Kind((t >> 1) % 32)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return Kind(t>>1)%8 + 19
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Elem returns the element type for channel, slice and array types, the
 | 
					// Elem returns the element type for channel, slice and array types, the
 | 
				
			||||||
// pointed-to value for pointer types, and the key type for map types.
 | 
					// pointed-to value for pointer types, and the key type for map types.
 | 
				
			||||||
func (t rawType) Elem() Type {
 | 
					func (t *rawType) Elem() Type {
 | 
				
			||||||
	return t.elem()
 | 
						return t.elem()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) elem() rawType {
 | 
					func (t *rawType) elem() *rawType {
 | 
				
			||||||
	switch t.Kind() {
 | 
						underlying := t.underlying()
 | 
				
			||||||
	case Chan, Pointer, Slice:
 | 
						switch underlying.Kind() {
 | 
				
			||||||
		return t.stripPrefix()
 | 
						case Pointer:
 | 
				
			||||||
	case Array:
 | 
							return (*ptrType)(unsafe.Pointer(underlying)).elem
 | 
				
			||||||
		index := t.stripPrefix()
 | 
						case Chan, Slice, Array:
 | 
				
			||||||
		elem, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(index)))
 | 
							return (*elemType)(unsafe.Pointer(underlying)).elem
 | 
				
			||||||
		return rawType(elem)
 | 
					 | 
				
			||||||
	default: // not implemented: Map
 | 
						default: // not implemented: Map
 | 
				
			||||||
		panic("unimplemented: (reflect.Type).Elem()")
 | 
							panic("unimplemented: (reflect.Type).Elem()")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// stripPrefix removes the "prefix" (the low 5 bits of the type code) from
 | 
					 | 
				
			||||||
// the type code. If this is a named type, it will resolve the underlying type
 | 
					 | 
				
			||||||
// (which is the data for this named type). If it is not, the lower bits are
 | 
					 | 
				
			||||||
// simply shifted off.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// The behavior is only defined for non-basic types.
 | 
					 | 
				
			||||||
func (t rawType) stripPrefix() rawType {
 | 
					 | 
				
			||||||
	// Look at the 'n' bit in the type code (see the top of this file) to see
 | 
					 | 
				
			||||||
	// whether this is a named type.
 | 
					 | 
				
			||||||
	if (t>>4)%2 != 0 {
 | 
					 | 
				
			||||||
		// This is a named type. The data is stored in a sidetable.
 | 
					 | 
				
			||||||
		namedTypeNum := t >> 5
 | 
					 | 
				
			||||||
		n := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicTypesSidetable)) + uintptr(namedTypeNum)*unsafe.Sizeof(uintptr(0))))
 | 
					 | 
				
			||||||
		return rawType(n)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Not a named type, so the value is stored directly in the type code.
 | 
					 | 
				
			||||||
	return t >> 5
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Field returns the type of the i'th field of this struct type. It panics if t
 | 
					// Field returns the type of the i'th field of this struct type. It panics if t
 | 
				
			||||||
// is not a struct type.
 | 
					// is not a struct type.
 | 
				
			||||||
func (t rawType) Field(i int) StructField {
 | 
					func (t *rawType) Field(i int) StructField {
 | 
				
			||||||
	field := t.rawField(i)
 | 
						field := t.rawField(i)
 | 
				
			||||||
	return StructField{
 | 
						return StructField{
 | 
				
			||||||
		Name:      field.Name,
 | 
							Name:      field.Name,
 | 
				
			||||||
| 
						 | 
					@ -435,82 +497,87 @@ func (t rawType) Field(i int) StructField {
 | 
				
			||||||
// Type member to an interface.
 | 
					// Type member to an interface.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// For internal use only.
 | 
					// For internal use only.
 | 
				
			||||||
func (t rawType) rawField(i int) rawStructField {
 | 
					func (t *rawType) rawField(n int) rawStructField {
 | 
				
			||||||
	if t.Kind() != Struct {
 | 
						if t.Kind() != Struct {
 | 
				
			||||||
		panic(&TypeError{"Field"})
 | 
							panic(&TypeError{"Field"})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	structIdentifier := t.stripPrefix()
 | 
						descriptor := (*structType)(unsafe.Pointer(t.underlying()))
 | 
				
			||||||
 | 
						if uint(n) >= uint(descriptor.numField) {
 | 
				
			||||||
	numField, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
 | 
					 | 
				
			||||||
	if uint(i) >= uint(numField) {
 | 
					 | 
				
			||||||
		panic("reflect: field index out of range")
 | 
							panic("reflect: field index out of range")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Iterate over every field in the struct and update the StructField each
 | 
						// Iterate over all the fields to calculate the offset.
 | 
				
			||||||
	// time, until the target field has been reached. This is very much not
 | 
						// This offset could have been stored directly in the array (to make the
 | 
				
			||||||
	// efficient, but it is easy to implement.
 | 
						// lookup faster), but by calculating it on-the-fly a bit of storage can be
 | 
				
			||||||
	// Adding a jump table at the start to jump to the field directly would
 | 
						// saved.
 | 
				
			||||||
	// make this much faster, but that would also impact code size.
 | 
						field := &descriptor.fields[0]
 | 
				
			||||||
	field := rawStructField{}
 | 
						var offset uintptr = 0
 | 
				
			||||||
	offset := uintptr(0)
 | 
						for i := 0; i < n; i++ {
 | 
				
			||||||
	for fieldNum := 0; fieldNum <= i; fieldNum++ {
 | 
							offset += field.fieldType.Size()
 | 
				
			||||||
		// Read some flags of this field, like whether the field is an
 | 
					 | 
				
			||||||
		// embedded field.
 | 
					 | 
				
			||||||
		flagsByte := *(*uint8)(p)
 | 
					 | 
				
			||||||
		p = unsafe.Pointer(uintptr(p) + 1)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Read the type of this struct field.
 | 
							// Increment pointer to the next field.
 | 
				
			||||||
		var fieldTypeVal uintptr
 | 
							field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
 | 
				
			||||||
		fieldTypeVal, p = readVarint(p)
 | 
					 | 
				
			||||||
		fieldType := rawType(fieldTypeVal)
 | 
					 | 
				
			||||||
		field.Type = fieldType
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Move Offset forward to align it to this field's alignment.
 | 
							// Align the offset for the next field.
 | 
				
			||||||
		// Assume alignment is a power of two.
 | 
							offset = align(offset, uintptr(field.fieldType.Align()))
 | 
				
			||||||
		offset = align(offset, uintptr(fieldType.Align()))
 | 
					 | 
				
			||||||
		field.Offset = offset
 | 
					 | 
				
			||||||
		offset += fieldType.Size() // starting (unaligned) offset for next field
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Read the field name.
 | 
					 | 
				
			||||||
		var nameNum uintptr
 | 
					 | 
				
			||||||
		nameNum, p = readVarint(p)
 | 
					 | 
				
			||||||
		field.Name = readStringSidetable(unsafe.Pointer(&structNamesSidetable), nameNum)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// The first bit in the flagsByte indicates whether this is an embedded
 | 
					 | 
				
			||||||
		// field.
 | 
					 | 
				
			||||||
		field.Anonymous = flagsByte&1 != 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// The second bit indicates whether there is a tag.
 | 
					 | 
				
			||||||
		if flagsByte&2 != 0 {
 | 
					 | 
				
			||||||
			// There is a tag.
 | 
					 | 
				
			||||||
			var tagNum uintptr
 | 
					 | 
				
			||||||
			tagNum, p = readVarint(p)
 | 
					 | 
				
			||||||
			field.Tag = StructTag(readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum))
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			// There is no tag.
 | 
					 | 
				
			||||||
			field.Tag = ""
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// The third bit indicates whether this field is exported.
 | 
					 | 
				
			||||||
		if flagsByte&4 != 0 {
 | 
					 | 
				
			||||||
			// This field is exported.
 | 
					 | 
				
			||||||
			field.PkgPath = ""
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			// This field is unexported.
 | 
					 | 
				
			||||||
			// TODO: list the real package path here. Storing it should not
 | 
					 | 
				
			||||||
			// significantly impact binary size as there is only a limited
 | 
					 | 
				
			||||||
			// number of packages in any program.
 | 
					 | 
				
			||||||
			field.PkgPath = "<unimplemented>"
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return field
 | 
						data := field.data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Read some flags of this field, like whether the field is an embedded
 | 
				
			||||||
 | 
						// field. See structFieldFlagAnonymous and similar flags.
 | 
				
			||||||
 | 
						flagsByte := *(*byte)(data)
 | 
				
			||||||
 | 
						data = unsafe.Add(data, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Read the field name.
 | 
				
			||||||
 | 
						nameStart := data
 | 
				
			||||||
 | 
						var nameLen uintptr
 | 
				
			||||||
 | 
						for *(*byte)(data) != 0 {
 | 
				
			||||||
 | 
							nameLen++
 | 
				
			||||||
 | 
							data = unsafe.Add(data, 1) // C: data++
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						name := *(*string)(unsafe.Pointer(&stringHeader{
 | 
				
			||||||
 | 
							data: nameStart,
 | 
				
			||||||
 | 
							len:  nameLen,
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Read the field tag, if there is one.
 | 
				
			||||||
 | 
						var tag string
 | 
				
			||||||
 | 
						if flagsByte&structFieldFlagHasTag != 0 {
 | 
				
			||||||
 | 
							data = unsafe.Add(data, 1) // C: data+1
 | 
				
			||||||
 | 
							tagLen := uintptr(*(*byte)(data))
 | 
				
			||||||
 | 
							data = unsafe.Add(data, 1) // C: data+1
 | 
				
			||||||
 | 
							tag = *(*string)(unsafe.Pointer(&stringHeader{
 | 
				
			||||||
 | 
								data: data,
 | 
				
			||||||
 | 
								len:  tagLen,
 | 
				
			||||||
 | 
							}))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set the PkgPath to some (arbitrary) value if the package path is not
 | 
				
			||||||
 | 
						// exported.
 | 
				
			||||||
 | 
						pkgPath := ""
 | 
				
			||||||
 | 
						if flagsByte&structFieldFlagIsExported == 0 {
 | 
				
			||||||
 | 
							// This field is unexported.
 | 
				
			||||||
 | 
							// TODO: list the real package path here. Storing it should not
 | 
				
			||||||
 | 
							// significantly impact binary size as there is only a limited
 | 
				
			||||||
 | 
							// number of packages in any program.
 | 
				
			||||||
 | 
							pkgPath = "<unimplemented>"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rawStructField{
 | 
				
			||||||
 | 
							Name:      name,
 | 
				
			||||||
 | 
							PkgPath:   pkgPath,
 | 
				
			||||||
 | 
							Type:      field.fieldType,
 | 
				
			||||||
 | 
							Tag:       StructTag(tag),
 | 
				
			||||||
 | 
							Anonymous: flagsByte&structFieldFlagAnonymous != 0,
 | 
				
			||||||
 | 
							Offset:    offset,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Bits returns the number of bits that this type uses. It is only valid for
 | 
					// Bits returns the number of bits that this type uses. It is only valid for
 | 
				
			||||||
// arithmetic types (integers, floats, and complex numbers). For other types, it
 | 
					// arithmetic types (integers, floats, and complex numbers). For other types, it
 | 
				
			||||||
// will panic.
 | 
					// will panic.
 | 
				
			||||||
func (t rawType) Bits() int {
 | 
					func (t *rawType) Bits() int {
 | 
				
			||||||
	kind := t.Kind()
 | 
						kind := t.Kind()
 | 
				
			||||||
	if kind >= Int && kind <= Complex128 {
 | 
						if kind >= Int && kind <= Complex128 {
 | 
				
			||||||
		return int(t.Size()) * 8
 | 
							return int(t.Size()) * 8
 | 
				
			||||||
| 
						 | 
					@ -520,34 +587,26 @@ func (t rawType) Bits() int {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Len returns the number of elements in this array. It panics of the type kind
 | 
					// Len returns the number of elements in this array. It panics of the type kind
 | 
				
			||||||
// is not Array.
 | 
					// is not Array.
 | 
				
			||||||
func (t rawType) Len() int {
 | 
					func (t *rawType) Len() int {
 | 
				
			||||||
	if t.Kind() != Array {
 | 
						if t.Kind() != Array {
 | 
				
			||||||
		panic(TypeError{"Len"})
 | 
							panic(TypeError{"Len"})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// skip past the element type
 | 
						return int((*arrayType)(unsafe.Pointer(t.underlying())).arrayLen)
 | 
				
			||||||
	arrayIdentifier := t.stripPrefix()
 | 
					 | 
				
			||||||
	_, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(arrayIdentifier)))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Read the array length.
 | 
					 | 
				
			||||||
	arrayLen, _ := readVarint(p)
 | 
					 | 
				
			||||||
	return int(arrayLen)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NumField returns the number of fields of a struct type. It panics for other
 | 
					// NumField returns the number of fields of a struct type. It panics for other
 | 
				
			||||||
// type kinds.
 | 
					// type kinds.
 | 
				
			||||||
func (t rawType) NumField() int {
 | 
					func (t *rawType) NumField() int {
 | 
				
			||||||
	if t.Kind() != Struct {
 | 
						if t.Kind() != Struct {
 | 
				
			||||||
		panic(&TypeError{"NumField"})
 | 
							panic(&TypeError{"NumField"})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	structIdentifier := t.stripPrefix()
 | 
						return int((*structType)(unsafe.Pointer(t.underlying())).numField)
 | 
				
			||||||
	n, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
 | 
					 | 
				
			||||||
	return int(n)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Size returns the size in bytes of a given type. It is similar to
 | 
					// Size returns the size in bytes of a given type. It is similar to
 | 
				
			||||||
// unsafe.Sizeof.
 | 
					// unsafe.Sizeof.
 | 
				
			||||||
func (t rawType) Size() uintptr {
 | 
					func (t *rawType) Size() uintptr {
 | 
				
			||||||
	switch t.Kind() {
 | 
						switch t.Kind() {
 | 
				
			||||||
	case Bool, Int8, Uint8:
 | 
						case Bool, Int8, Uint8:
 | 
				
			||||||
		return 1
 | 
							return 1
 | 
				
			||||||
| 
						 | 
					@ -596,7 +655,7 @@ func (t rawType) Size() uintptr {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Align returns the alignment of this type. It is similar to calling
 | 
					// Align returns the alignment of this type. It is similar to calling
 | 
				
			||||||
// unsafe.Alignof.
 | 
					// unsafe.Alignof.
 | 
				
			||||||
func (t rawType) Align() int {
 | 
					func (t *rawType) Align() int {
 | 
				
			||||||
	switch t.Kind() {
 | 
						switch t.Kind() {
 | 
				
			||||||
	case Bool, Int8, Uint8:
 | 
						case Bool, Int8, Uint8:
 | 
				
			||||||
		return int(unsafe.Alignof(int8(0)))
 | 
							return int(unsafe.Alignof(int8(0)))
 | 
				
			||||||
| 
						 | 
					@ -648,14 +707,14 @@ func (t rawType) Align() int {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FieldAlign returns the alignment if this type is used in a struct field. It
 | 
					// FieldAlign returns the alignment if this type is used in a struct field. It
 | 
				
			||||||
// is currently an alias for Align() but this might change in the future.
 | 
					// is currently an alias for Align() but this might change in the future.
 | 
				
			||||||
func (t rawType) FieldAlign() int {
 | 
					func (t *rawType) FieldAlign() int {
 | 
				
			||||||
	return t.Align()
 | 
						return t.Align()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AssignableTo returns whether a value of type t can be assigned to a variable
 | 
					// AssignableTo returns whether a value of type t can be assigned to a variable
 | 
				
			||||||
// of type u.
 | 
					// of type u.
 | 
				
			||||||
func (t rawType) AssignableTo(u Type) bool {
 | 
					func (t *rawType) AssignableTo(u Type) bool {
 | 
				
			||||||
	if t == u.(rawType) {
 | 
						if t == u.(*rawType) {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if u.Kind() == Interface {
 | 
						if u.Kind() == Interface {
 | 
				
			||||||
| 
						 | 
					@ -664,7 +723,7 @@ func (t rawType) AssignableTo(u Type) bool {
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) Implements(u Type) bool {
 | 
					func (t *rawType) Implements(u Type) bool {
 | 
				
			||||||
	if u.Kind() != Interface {
 | 
						if u.Kind() != Interface {
 | 
				
			||||||
		panic("reflect: non-interface type passed to Type.Implements")
 | 
							panic("reflect: non-interface type passed to Type.Implements")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -672,7 +731,7 @@ func (t rawType) Implements(u Type) bool {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Comparable returns whether values of this type can be compared to each other.
 | 
					// Comparable returns whether values of this type can be compared to each other.
 | 
				
			||||||
func (t rawType) Comparable() bool {
 | 
					func (t *rawType) Comparable() bool {
 | 
				
			||||||
	switch t.Kind() {
 | 
						switch t.Kind() {
 | 
				
			||||||
	case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
 | 
						case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
| 
						 | 
					@ -713,31 +772,31 @@ func (t rawType) ChanDir() ChanDir {
 | 
				
			||||||
	panic("unimplemented: (reflect.Type).ChanDir()")
 | 
						panic("unimplemented: (reflect.Type).ChanDir()")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) ConvertibleTo(u Type) bool {
 | 
					func (t *rawType) ConvertibleTo(u Type) bool {
 | 
				
			||||||
	panic("unimplemented: (reflect.Type).ConvertibleTo()")
 | 
						panic("unimplemented: (reflect.Type).ConvertibleTo()")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) IsVariadic() bool {
 | 
					func (t *rawType) IsVariadic() bool {
 | 
				
			||||||
	panic("unimplemented: (reflect.Type).IsVariadic()")
 | 
						panic("unimplemented: (reflect.Type).IsVariadic()")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) NumIn() int {
 | 
					func (t *rawType) NumIn() int {
 | 
				
			||||||
	panic("unimplemented: (reflect.Type).NumIn()")
 | 
						panic("unimplemented: (reflect.Type).NumIn()")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) NumOut() int {
 | 
					func (t *rawType) NumOut() int {
 | 
				
			||||||
	panic("unimplemented: (reflect.Type).NumOut()")
 | 
						panic("unimplemented: (reflect.Type).NumOut()")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) NumMethod() int {
 | 
					func (t *rawType) NumMethod() int {
 | 
				
			||||||
	panic("unimplemented: (reflect.Type).NumMethod()")
 | 
						panic("unimplemented: (reflect.Type).NumMethod()")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) Name() string {
 | 
					func (t *rawType) Name() string {
 | 
				
			||||||
	panic("unimplemented: (reflect.Type).Name()")
 | 
						panic("unimplemented: (reflect.Type).Name()")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t rawType) Key() Type {
 | 
					func (t *rawType) Key() Type {
 | 
				
			||||||
	panic("unimplemented: (reflect.Type).Key()")
 | 
						panic("unimplemented: (reflect.Type).Key()")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -792,7 +851,7 @@ func (f StructField) IsExported() bool {
 | 
				
			||||||
type rawStructField struct {
 | 
					type rawStructField struct {
 | 
				
			||||||
	Name      string
 | 
						Name      string
 | 
				
			||||||
	PkgPath   string
 | 
						PkgPath   string
 | 
				
			||||||
	Type      rawType
 | 
						Type      *rawType
 | 
				
			||||||
	Tag       StructTag
 | 
						Tag       StructTag
 | 
				
			||||||
	Anonymous bool
 | 
						Anonymous bool
 | 
				
			||||||
	Offset    uintptr
 | 
						Offset    uintptr
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ const (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Value struct {
 | 
					type Value struct {
 | 
				
			||||||
	typecode rawType
 | 
						typecode *rawType
 | 
				
			||||||
	value    unsafe.Pointer
 | 
						value    unsafe.Pointer
 | 
				
			||||||
	flags    valueFlags
 | 
						flags    valueFlags
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -44,15 +44,15 @@ func Indirect(v Value) Value {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//go:linkname composeInterface runtime.composeInterface
 | 
					//go:linkname composeInterface runtime.composeInterface
 | 
				
			||||||
func composeInterface(rawType, unsafe.Pointer) interface{}
 | 
					func composeInterface(unsafe.Pointer, unsafe.Pointer) interface{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//go:linkname decomposeInterface runtime.decomposeInterface
 | 
					//go:linkname decomposeInterface runtime.decomposeInterface
 | 
				
			||||||
func decomposeInterface(i interface{}) (rawType, unsafe.Pointer)
 | 
					func decomposeInterface(i interface{}) (unsafe.Pointer, unsafe.Pointer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ValueOf(i interface{}) Value {
 | 
					func ValueOf(i interface{}) Value {
 | 
				
			||||||
	typecode, value := decomposeInterface(i)
 | 
						typecode, value := decomposeInterface(i)
 | 
				
			||||||
	return Value{
 | 
						return Value{
 | 
				
			||||||
		typecode: typecode,
 | 
							typecode: (*rawType)(typecode),
 | 
				
			||||||
		value:    value,
 | 
							value:    value,
 | 
				
			||||||
		flags:    valueFlagExported,
 | 
							flags:    valueFlagExported,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -85,7 +85,7 @@ func valueInterfaceUnsafe(v Value) interface{} {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		v.value = unsafe.Pointer(value)
 | 
							v.value = unsafe.Pointer(value)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return composeInterface(v.typecode, v.value)
 | 
						return composeInterface(unsafe.Pointer(v.typecode), v.value)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (v Value) Type() Type {
 | 
					func (v Value) Type() Type {
 | 
				
			||||||
| 
						 | 
					@ -136,7 +136,7 @@ func (v Value) IsZero() bool {
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// RawType returns the raw, underlying type code. It is used in the runtime
 | 
					// RawType returns the raw, underlying type code. It is used in the runtime
 | 
				
			||||||
// package and needs to be exported for the runtime package to access it.
 | 
					// package and needs to be exported for the runtime package to access it.
 | 
				
			||||||
func (v Value) RawType() rawType {
 | 
					func (v Value) RawType() *rawType {
 | 
				
			||||||
	return v.typecode
 | 
						return v.typecode
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -205,7 +205,7 @@ func (v Value) pointer() unsafe.Pointer {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (v Value) IsValid() bool {
 | 
					func (v Value) IsValid() bool {
 | 
				
			||||||
	return v.typecode != 0
 | 
						return v.typecode != nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (v Value) CanInterface() bool {
 | 
					func (v Value) CanInterface() bool {
 | 
				
			||||||
| 
						 | 
					@ -453,7 +453,7 @@ func (v Value) Elem() Value {
 | 
				
			||||||
	case Interface:
 | 
						case Interface:
 | 
				
			||||||
		typecode, value := decomposeInterface(*(*interface{})(v.value))
 | 
							typecode, value := decomposeInterface(*(*interface{})(v.value))
 | 
				
			||||||
		return Value{
 | 
							return Value{
 | 
				
			||||||
			typecode: typecode,
 | 
								typecode: (*rawType)(typecode),
 | 
				
			||||||
			value:    value,
 | 
								value:    value,
 | 
				
			||||||
			flags:    v.flags &^ valueFlagIndirect,
 | 
								flags:    v.flags &^ valueFlagIndirect,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -523,6 +523,8 @@ func (v Value) Field(i int) Value {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var uint8Type = TypeOf(uint8(0)).(*rawType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (v Value) Index(i int) Value {
 | 
					func (v Value) Index(i int) Value {
 | 
				
			||||||
	switch v.Kind() {
 | 
						switch v.Kind() {
 | 
				
			||||||
	case Slice:
 | 
						case Slice:
 | 
				
			||||||
| 
						 | 
					@ -550,7 +552,7 @@ func (v Value) Index(i int) Value {
 | 
				
			||||||
			panic("reflect: string index out of range")
 | 
								panic("reflect: string index out of range")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return Value{
 | 
							return Value{
 | 
				
			||||||
			typecode: Uint8.basicType(),
 | 
								typecode: uint8Type,
 | 
				
			||||||
			value:    unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(uintptr(s.data) + uintptr(i))))),
 | 
								value:    unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(uintptr(s.data) + uintptr(i))))),
 | 
				
			||||||
			flags:    v.flags & valueFlagExported,
 | 
								flags:    v.flags & valueFlagExported,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -803,7 +805,7 @@ func Zero(typ Type) Value {
 | 
				
			||||||
// new value of the given type.
 | 
					// new value of the given type.
 | 
				
			||||||
func New(typ Type) Value {
 | 
					func New(typ Type) Value {
 | 
				
			||||||
	return Value{
 | 
						return Value{
 | 
				
			||||||
		typecode: PtrTo(typ).(rawType),
 | 
							typecode: PtrTo(typ).(*rawType),
 | 
				
			||||||
		value:    alloc(typ.Size(), nil),
 | 
							value:    alloc(typ.Size(), nil),
 | 
				
			||||||
		flags:    valueFlagExported,
 | 
							flags:    valueFlagExported,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -506,7 +506,7 @@ func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
 | 
					func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
 | 
				
			||||||
	x := reflect.ValueOf(itf)
 | 
						x := reflect.ValueOf(itf)
 | 
				
			||||||
	if x.RawType() == 0 {
 | 
						if x.RawType() == nil {
 | 
				
			||||||
		return 0 // nil interface
 | 
							return 0 // nil interface
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,17 +11,17 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type _interface struct {
 | 
					type _interface struct {
 | 
				
			||||||
	typecode uintptr
 | 
						typecode unsafe.Pointer
 | 
				
			||||||
	value    unsafe.Pointer
 | 
						value    unsafe.Pointer
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//go:inline
 | 
					//go:inline
 | 
				
			||||||
func composeInterface(typecode uintptr, value unsafe.Pointer) _interface {
 | 
					func composeInterface(typecode, value unsafe.Pointer) _interface {
 | 
				
			||||||
	return _interface{typecode, value}
 | 
						return _interface{typecode, value}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//go:inline
 | 
					//go:inline
 | 
				
			||||||
func decomposeInterface(i _interface) (uintptr, unsafe.Pointer) {
 | 
					func decomposeInterface(i _interface) (unsafe.Pointer, unsafe.Pointer) {
 | 
				
			||||||
	return i.typecode, i.value
 | 
						return i.typecode, i.value
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@ func reflectValueEqual(x, y reflect.Value) bool {
 | 
				
			||||||
	// Note: doing a x.Type() == y.Type() comparison would not work here as that
 | 
						// Note: doing a x.Type() == y.Type() comparison would not work here as that
 | 
				
			||||||
	// would introduce an infinite recursion: comparing two reflect.Type values
 | 
						// would introduce an infinite recursion: comparing two reflect.Type values
 | 
				
			||||||
	// is done with this reflectValueEqual runtime call.
 | 
						// is done with this reflectValueEqual runtime call.
 | 
				
			||||||
	if x.RawType() == 0 || y.RawType() == 0 {
 | 
						if x.RawType() == nil || y.RawType() == nil {
 | 
				
			||||||
		// One of them is nil.
 | 
							// One of them is nil.
 | 
				
			||||||
		return x.RawType() == y.RawType()
 | 
							return x.RawType() == y.RawType()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -94,48 +94,13 @@ func interfaceTypeAssert(ok bool) {
 | 
				
			||||||
// lowered to inline IR in the interface lowering pass.
 | 
					// lowered to inline IR in the interface lowering pass.
 | 
				
			||||||
// See compiler/interface-lowering.go for details.
 | 
					// See compiler/interface-lowering.go for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type interfaceMethodInfo struct {
 | 
					 | 
				
			||||||
	signature *uint8  // external *i8 with a name identifying the Go function signature
 | 
					 | 
				
			||||||
	funcptr   uintptr // bitcast from the actual function pointer
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type typecodeID struct {
 | 
					 | 
				
			||||||
	// Depending on the type kind of this typecodeID, this pointer is something
 | 
					 | 
				
			||||||
	// different:
 | 
					 | 
				
			||||||
	// * basic types: null
 | 
					 | 
				
			||||||
	// * named type: the underlying type
 | 
					 | 
				
			||||||
	// * interface: null
 | 
					 | 
				
			||||||
	// * chan/pointer/slice/array: the element type
 | 
					 | 
				
			||||||
	// * struct: bitcast of global with structField array
 | 
					 | 
				
			||||||
	// * func/map: TODO
 | 
					 | 
				
			||||||
	references *typecodeID
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The array length, for array types.
 | 
					 | 
				
			||||||
	length uintptr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	methodSet *interfaceMethodInfo // nil or a GEP of an array
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The type that's a pointer to this type, nil if it is already a pointer.
 | 
					 | 
				
			||||||
	// Keeping the type struct alive here is important so that values from
 | 
					 | 
				
			||||||
	// reflect.New (which uses reflect.PtrTo) can be used in type asserts etc.
 | 
					 | 
				
			||||||
	ptrTo *typecodeID
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// typeAssert is a ptrtoint of a declared interface assert function.
 | 
					 | 
				
			||||||
	// It only exists to make the rtcalls pass easier.
 | 
					 | 
				
			||||||
	typeAssert uintptr
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// structField is used by the compiler to pass information to the interface
 | 
					 | 
				
			||||||
// lowering pass. It is not used in the final binary.
 | 
					 | 
				
			||||||
type structField struct {
 | 
					type structField struct {
 | 
				
			||||||
	typecode *typecodeID // type of this struct field
 | 
						typecode unsafe.Pointer // type of this struct field
 | 
				
			||||||
	name     *uint8      // pointer to char array
 | 
						data     *uint8         // pointer to byte array containing name, tag, and 'embedded' flag
 | 
				
			||||||
	tag      *uint8      // pointer to char array, or nil
 | 
					 | 
				
			||||||
	embedded bool
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Pseudo function call used during a type assert. It is used during interface
 | 
					// 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
 | 
					// 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
 | 
					// asserts. Also, it is replaced with const false if this type assert can never
 | 
				
			||||||
// happen.
 | 
					// happen.
 | 
				
			||||||
func typeAssert(actualType uintptr, assertedType *uint8) bool
 | 
					func typeAssert(actualType unsafe.Pointer, assertedType *uint8) bool
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,8 +13,7 @@ package transform
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// typeAssert:
 | 
					// typeAssert:
 | 
				
			||||||
//     Replaced with an icmp instruction so it can be directly used in a type
 | 
					//     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
 | 
					//     switch.
 | 
				
			||||||
//     type switch into a regular switch statement.
 | 
					 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// interface type assert:
 | 
					// interface type assert:
 | 
				
			||||||
//     These functions are defined by creating a big type switch over all the
 | 
					//     These functions are defined by creating a big type switch over all the
 | 
				
			||||||
| 
						 | 
					@ -54,10 +53,11 @@ type methodInfo struct {
 | 
				
			||||||
// typeInfo describes a single concrete Go type, which can be a basic or a named
 | 
					// 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. If it is a named type, it may have methods.
 | 
				
			||||||
type typeInfo struct {
 | 
					type typeInfo struct {
 | 
				
			||||||
	name      string
 | 
						name        string
 | 
				
			||||||
	typecode  llvm.Value
 | 
						typecode    llvm.Value
 | 
				
			||||||
	methodSet llvm.Value
 | 
						typecodeGEP llvm.Value
 | 
				
			||||||
	methods   []*methodInfo
 | 
						methodSet   llvm.Value
 | 
				
			||||||
 | 
						methods     []*methodInfo
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getMethod looks up the method on this type with the given signature and
 | 
					// getMethod looks up the method on this type with the given signature and
 | 
				
			||||||
| 
						 | 
					@ -91,6 +91,8 @@ type lowerInterfacesPass struct {
 | 
				
			||||||
	difiles     map[string]llvm.Metadata
 | 
						difiles     map[string]llvm.Metadata
 | 
				
			||||||
	ctx         llvm.Context
 | 
						ctx         llvm.Context
 | 
				
			||||||
	uintptrType llvm.Type
 | 
						uintptrType llvm.Type
 | 
				
			||||||
 | 
						targetData  llvm.TargetData
 | 
				
			||||||
 | 
						i8ptrType   llvm.Type
 | 
				
			||||||
	types       map[string]*typeInfo
 | 
						types       map[string]*typeInfo
 | 
				
			||||||
	signatures  map[string]*signatureInfo
 | 
						signatures  map[string]*signatureInfo
 | 
				
			||||||
	interfaces  map[string]*interfaceInfo
 | 
						interfaces  map[string]*interfaceInfo
 | 
				
			||||||
| 
						 | 
					@ -101,14 +103,17 @@ type lowerInterfacesPass struct {
 | 
				
			||||||
// before LLVM can work on them. This is done so that a few cleanup passes can
 | 
					// before LLVM can work on them. This is done so that a few cleanup passes can
 | 
				
			||||||
// run before assigning the final type codes.
 | 
					// run before assigning the final type codes.
 | 
				
			||||||
func LowerInterfaces(mod llvm.Module, config *compileopts.Config) error {
 | 
					func LowerInterfaces(mod llvm.Module, config *compileopts.Config) error {
 | 
				
			||||||
 | 
						ctx := mod.Context()
 | 
				
			||||||
	targetData := llvm.NewTargetData(mod.DataLayout())
 | 
						targetData := llvm.NewTargetData(mod.DataLayout())
 | 
				
			||||||
	defer targetData.Dispose()
 | 
						defer targetData.Dispose()
 | 
				
			||||||
	p := &lowerInterfacesPass{
 | 
						p := &lowerInterfacesPass{
 | 
				
			||||||
		mod:         mod,
 | 
							mod:         mod,
 | 
				
			||||||
		config:      config,
 | 
							config:      config,
 | 
				
			||||||
		builder:     mod.Context().NewBuilder(),
 | 
							builder:     ctx.NewBuilder(),
 | 
				
			||||||
		ctx:         mod.Context(),
 | 
							ctx:         ctx,
 | 
				
			||||||
 | 
							targetData:  targetData,
 | 
				
			||||||
		uintptrType: mod.Context().IntType(targetData.PointerSize() * 8),
 | 
							uintptrType: mod.Context().IntType(targetData.PointerSize() * 8),
 | 
				
			||||||
 | 
							i8ptrType:   llvm.PointerType(ctx.Int8Type(), 0),
 | 
				
			||||||
		types:       make(map[string]*typeInfo),
 | 
							types:       make(map[string]*typeInfo),
 | 
				
			||||||
		signatures:  make(map[string]*signatureInfo),
 | 
							signatures:  make(map[string]*signatureInfo),
 | 
				
			||||||
		interfaces:  make(map[string]*interfaceInfo),
 | 
							interfaces:  make(map[string]*interfaceInfo),
 | 
				
			||||||
| 
						 | 
					@ -151,11 +156,26 @@ func (p *lowerInterfacesPass) run() error {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				p.types[name] = t
 | 
									p.types[name] = t
 | 
				
			||||||
				initializer := global.Initializer()
 | 
									initializer := global.Initializer()
 | 
				
			||||||
				if initializer.IsNil() {
 | 
									firstField := p.builder.CreateExtractValue(initializer, 0, "")
 | 
				
			||||||
					continue
 | 
									if firstField.Type() != p.ctx.Int8Type() {
 | 
				
			||||||
 | 
										// This type has a method set at index 0. Change the GEP to
 | 
				
			||||||
 | 
										// point to index 1 (the meta byte).
 | 
				
			||||||
 | 
										t.typecodeGEP = llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
 | 
				
			||||||
 | 
											llvm.ConstInt(p.ctx.Int32Type(), 0, false),
 | 
				
			||||||
 | 
											llvm.ConstInt(p.ctx.Int32Type(), 1, false),
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
										methodSet := stripPointerCasts(firstField)
 | 
				
			||||||
 | 
										if !strings.HasSuffix(methodSet.Name(), "$methodset") {
 | 
				
			||||||
 | 
											panic("expected method set")
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										p.addTypeMethods(t, methodSet)
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// This type has no method set.
 | 
				
			||||||
 | 
										t.typecodeGEP = llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
 | 
				
			||||||
 | 
											llvm.ConstInt(p.ctx.Int32Type(), 0, false),
 | 
				
			||||||
 | 
											llvm.ConstInt(p.ctx.Int32Type(), 0, false),
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				methodSet := p.builder.CreateExtractValue(initializer, 2, "")
 | 
					 | 
				
			||||||
				p.addTypeMethods(t, methodSet)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -266,10 +286,10 @@ func (p *lowerInterfacesPass) run() error {
 | 
				
			||||||
		actualType := use.Operand(0)
 | 
							actualType := use.Operand(0)
 | 
				
			||||||
		name := strings.TrimPrefix(use.Operand(1).Name(), "reflect/types.typeid:")
 | 
							name := strings.TrimPrefix(use.Operand(1).Name(), "reflect/types.typeid:")
 | 
				
			||||||
		if t, ok := p.types[name]; ok {
 | 
							if t, ok := p.types[name]; ok {
 | 
				
			||||||
			// The type exists in the program, so lower to a regular integer
 | 
								// The type exists in the program, so lower to a regular pointer
 | 
				
			||||||
			// comparison.
 | 
								// comparison.
 | 
				
			||||||
			p.builder.SetInsertPointBefore(use)
 | 
								p.builder.SetInsertPointBefore(use)
 | 
				
			||||||
			commaOk := p.builder.CreateICmp(llvm.IntEQ, llvm.ConstPtrToInt(t.typecode, p.uintptrType), actualType, "typeassert.ok")
 | 
								commaOk := p.builder.CreateICmp(llvm.IntEQ, t.typecodeGEP, actualType, "typeassert.ok")
 | 
				
			||||||
			use.ReplaceAllUsesWith(commaOk)
 | 
								use.ReplaceAllUsesWith(commaOk)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// The type does not exist in the program, so lower to a constant
 | 
								// The type does not exist in the program, so lower to a constant
 | 
				
			||||||
| 
						 | 
					@ -283,15 +303,45 @@ func (p *lowerInterfacesPass) run() error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove all method sets, which are now unnecessary and inhibit later
 | 
						// Remove all method sets, which are now unnecessary and inhibit later
 | 
				
			||||||
	// optimizations if they are left in place. Also remove references to the
 | 
						// optimizations if they are left in place.
 | 
				
			||||||
	// interface type assert functions just to be sure.
 | 
						zero := llvm.ConstInt(p.ctx.Int32Type(), 0, false)
 | 
				
			||||||
	zeroUintptr := llvm.ConstNull(p.uintptrType)
 | 
					 | 
				
			||||||
	for _, t := range p.types {
 | 
						for _, t := range p.types {
 | 
				
			||||||
		initializer := t.typecode.Initializer()
 | 
							if !t.methodSet.IsNil() {
 | 
				
			||||||
		methodSet := p.builder.CreateExtractValue(initializer, 2, "")
 | 
								initializer := t.typecode.Initializer()
 | 
				
			||||||
		initializer = p.builder.CreateInsertValue(initializer, llvm.ConstNull(methodSet.Type()), 2, "")
 | 
								var newInitializerFields []llvm.Value
 | 
				
			||||||
		initializer = p.builder.CreateInsertValue(initializer, zeroUintptr, 4, "")
 | 
								for i := 1; i < initializer.Type().StructElementTypesCount(); i++ {
 | 
				
			||||||
		t.typecode.SetInitializer(initializer)
 | 
									newInitializerFields = append(newInitializerFields, p.builder.CreateExtractValue(initializer, i, ""))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								newInitializer := p.ctx.ConstStruct(newInitializerFields, false)
 | 
				
			||||||
 | 
								typecodeName := t.typecode.Name()
 | 
				
			||||||
 | 
								newGlobal := llvm.AddGlobal(p.mod, newInitializer.Type(), typecodeName+".tmp")
 | 
				
			||||||
 | 
								newGlobal.SetInitializer(newInitializer)
 | 
				
			||||||
 | 
								newGlobal.SetLinkage(t.typecode.Linkage())
 | 
				
			||||||
 | 
								newGlobal.SetGlobalConstant(true)
 | 
				
			||||||
 | 
								newGlobal.SetAlignment(t.typecode.Alignment())
 | 
				
			||||||
 | 
								for _, use := range getUses(t.typecode) {
 | 
				
			||||||
 | 
									if !use.IsAConstantExpr().IsNil() {
 | 
				
			||||||
 | 
										opcode := use.Opcode()
 | 
				
			||||||
 | 
										if opcode == llvm.GetElementPtr && use.OperandsCount() == 3 {
 | 
				
			||||||
 | 
											if use.Operand(1).ZExtValue() == 0 && use.Operand(2).ZExtValue() == 1 {
 | 
				
			||||||
 | 
												gep := p.builder.CreateInBoundsGEP(newGlobal.GlobalValueType(), newGlobal, []llvm.Value{zero, zero}, "")
 | 
				
			||||||
 | 
												use.ReplaceAllUsesWith(gep)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// Fallback.
 | 
				
			||||||
 | 
								if hasUses(t.typecode) {
 | 
				
			||||||
 | 
									bitcast := llvm.ConstBitCast(newGlobal, p.i8ptrType)
 | 
				
			||||||
 | 
									negativeOffset := -int64(p.targetData.TypeAllocSize(p.i8ptrType))
 | 
				
			||||||
 | 
									gep := p.builder.CreateInBoundsGEP(p.ctx.Int8Type(), bitcast, []llvm.Value{llvm.ConstInt(p.ctx.Int32Type(), uint64(negativeOffset), true)}, "")
 | 
				
			||||||
 | 
									bitcast2 := llvm.ConstBitCast(gep, t.typecode.Type())
 | 
				
			||||||
 | 
									t.typecode.ReplaceAllUsesWith(bitcast2)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								t.typecode.EraseFromParentAsGlobal()
 | 
				
			||||||
 | 
								newGlobal.SetName(typecodeName)
 | 
				
			||||||
 | 
								t.typecode = newGlobal
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
| 
						 | 
					@ -301,22 +351,22 @@ func (p *lowerInterfacesPass) run() error {
 | 
				
			||||||
// retrieves the signatures and the references to the method functions
 | 
					// retrieves the signatures and the references to the method functions
 | 
				
			||||||
// themselves for later type<->interface matching.
 | 
					// themselves for later type<->interface matching.
 | 
				
			||||||
func (p *lowerInterfacesPass) addTypeMethods(t *typeInfo, methodSet llvm.Value) {
 | 
					func (p *lowerInterfacesPass) addTypeMethods(t *typeInfo, methodSet llvm.Value) {
 | 
				
			||||||
	if !t.methodSet.IsNil() || methodSet.IsNull() {
 | 
						if !t.methodSet.IsNil() {
 | 
				
			||||||
		// no methods or methods already read
 | 
							// no methods or methods already read
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !methodSet.IsAConstantExpr().IsNil() && methodSet.Opcode() == llvm.GetElementPtr {
 | 
					 | 
				
			||||||
		methodSet = methodSet.Operand(0) // get global from GEP, for LLVM 14 (non-opaque pointers)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// This type has methods, collect all methods of this type.
 | 
						// This type has methods, collect all methods of this type.
 | 
				
			||||||
	t.methodSet = methodSet
 | 
						t.methodSet = methodSet
 | 
				
			||||||
	set := methodSet.Initializer() // get value from global
 | 
						set := methodSet.Initializer() // get value from global
 | 
				
			||||||
	for i := 0; i < set.Type().ArrayLength(); i++ {
 | 
						signatures := p.builder.CreateExtractValue(set, 1, "")
 | 
				
			||||||
		methodData := p.builder.CreateExtractValue(set, i, "")
 | 
						wrappers := p.builder.CreateExtractValue(set, 2, "")
 | 
				
			||||||
		signatureGlobal := p.builder.CreateExtractValue(methodData, 0, "")
 | 
						numMethods := signatures.Type().ArrayLength()
 | 
				
			||||||
 | 
						for i := 0; i < numMethods; i++ {
 | 
				
			||||||
 | 
							signatureGlobal := p.builder.CreateExtractValue(signatures, i, "")
 | 
				
			||||||
 | 
							function := p.builder.CreateExtractValue(wrappers, i, "")
 | 
				
			||||||
 | 
							function = stripPointerCasts(function) // strip bitcasts
 | 
				
			||||||
		signatureName := signatureGlobal.Name()
 | 
							signatureName := signatureGlobal.Name()
 | 
				
			||||||
		function := p.builder.CreateExtractValue(methodData, 1, "").Operand(0)
 | 
					 | 
				
			||||||
		signature := p.getSignature(signatureName)
 | 
							signature := p.getSignature(signatureName)
 | 
				
			||||||
		method := &methodInfo{
 | 
							method := &methodInfo{
 | 
				
			||||||
			function:      function,
 | 
								function:      function,
 | 
				
			||||||
| 
						 | 
					@ -401,7 +451,7 @@ func (p *lowerInterfacesPass) defineInterfaceImplementsFunc(fn llvm.Value, itf *
 | 
				
			||||||
	actualType := fn.Param(0)
 | 
						actualType := fn.Param(0)
 | 
				
			||||||
	for _, typ := range itf.types {
 | 
						for _, typ := range itf.types {
 | 
				
			||||||
		nextBlock := p.ctx.AddBasicBlock(fn, typ.name+".next")
 | 
							nextBlock := p.ctx.AddBasicBlock(fn, typ.name+".next")
 | 
				
			||||||
		cmp := p.builder.CreateICmp(llvm.IntEQ, actualType, llvm.ConstPtrToInt(typ.typecode, p.uintptrType), typ.name+".icmp")
 | 
							cmp := p.builder.CreateICmp(llvm.IntEQ, actualType, typ.typecodeGEP, typ.name+".icmp")
 | 
				
			||||||
		p.builder.CreateCondBr(cmp, thenBlock, nextBlock)
 | 
							p.builder.CreateCondBr(cmp, thenBlock, nextBlock)
 | 
				
			||||||
		p.builder.SetInsertPointAtEnd(nextBlock)
 | 
							p.builder.SetInsertPointAtEnd(nextBlock)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -440,7 +490,7 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
 | 
				
			||||||
		params[i] = fn.Param(i + 1)
 | 
							params[i] = fn.Param(i + 1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	params = append(params,
 | 
						params = append(params,
 | 
				
			||||||
		llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
 | 
							llvm.Undef(p.i8ptrType),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Start chain in the entry block.
 | 
						// Start chain in the entry block.
 | 
				
			||||||
| 
						 | 
					@ -472,7 +522,7 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
 | 
				
			||||||
		// Create type check (if/else).
 | 
							// Create type check (if/else).
 | 
				
			||||||
		bb := p.ctx.AddBasicBlock(fn, typ.name)
 | 
							bb := p.ctx.AddBasicBlock(fn, typ.name)
 | 
				
			||||||
		next := p.ctx.AddBasicBlock(fn, typ.name+".next")
 | 
							next := p.ctx.AddBasicBlock(fn, typ.name+".next")
 | 
				
			||||||
		cmp := p.builder.CreateICmp(llvm.IntEQ, actualType, llvm.ConstPtrToInt(typ.typecode, p.uintptrType), typ.name+".icmp")
 | 
							cmp := p.builder.CreateICmp(llvm.IntEQ, actualType, typ.typecodeGEP, typ.name+".icmp")
 | 
				
			||||||
		p.builder.CreateCondBr(cmp, bb, next)
 | 
							p.builder.CreateCondBr(cmp, bb, next)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// The function we will redirect to when the interface has this type.
 | 
							// The function we will redirect to when the interface has this type.
 | 
				
			||||||
| 
						 | 
					@ -522,7 +572,7 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
 | 
				
			||||||
	// method on a nil interface.
 | 
						// method on a nil interface.
 | 
				
			||||||
	nilPanic := p.mod.NamedFunction("runtime.nilPanic")
 | 
						nilPanic := p.mod.NamedFunction("runtime.nilPanic")
 | 
				
			||||||
	p.builder.CreateCall(nilPanic.GlobalValueType(), nilPanic, []llvm.Value{
 | 
						p.builder.CreateCall(nilPanic.GlobalValueType(), nilPanic, []llvm.Value{
 | 
				
			||||||
		llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
 | 
							llvm.Undef(p.i8ptrType),
 | 
				
			||||||
	}, "")
 | 
						}, "")
 | 
				
			||||||
	p.builder.CreateUnreachable()
 | 
						p.builder.CreateUnreachable()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
 | 
				
			||||||
		goPasses.Run(mod)
 | 
							goPasses.Run(mod)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Run TinyGo-specific interprocedural optimizations.
 | 
							// Run TinyGo-specific interprocedural optimizations.
 | 
				
			||||||
		LowerReflect(mod)
 | 
					 | 
				
			||||||
		OptimizeAllocs(mod, config.Options.PrintAllocs, func(pos token.Position, msg string) {
 | 
							OptimizeAllocs(mod, config.Options.PrintAllocs, func(pos token.Position, msg string) {
 | 
				
			||||||
			fmt.Fprintln(os.Stderr, pos.String()+": "+msg)
 | 
								fmt.Fprintln(os.Stderr, pos.String()+": "+msg)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
| 
						 | 
					@ -129,7 +128,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return []error{err}
 | 
								return []error{err}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		LowerReflect(mod)
 | 
					 | 
				
			||||||
		errs := LowerInterrupts(mod)
 | 
							errs := LowerInterrupts(mod)
 | 
				
			||||||
		if len(errs) > 0 {
 | 
							if len(errs) > 0 {
 | 
				
			||||||
			return errs
 | 
								return errs
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,567 +0,0 @@
 | 
				
			||||||
package transform
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// This file has some compiler support for run-time reflection using the reflect
 | 
					 | 
				
			||||||
// package. In particular, it encodes type information in type codes in such a
 | 
					 | 
				
			||||||
// way that the reflect package can decode the type from this information.
 | 
					 | 
				
			||||||
// Where needed, it also adds some side tables for looking up more information
 | 
					 | 
				
			||||||
// about a type, when that information cannot be stored directly in the type
 | 
					 | 
				
			||||||
// code.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Go has 26 different type kinds.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Type kinds are subdivided in basic types (see the list of basicTypes below)
 | 
					 | 
				
			||||||
// that are mostly numeric literals and non-basic (or "complex") types that are
 | 
					 | 
				
			||||||
// more difficult to encode. These non-basic types come in two forms:
 | 
					 | 
				
			||||||
//   * Prefix types (pointer, slice, interface, channel): these just add
 | 
					 | 
				
			||||||
//     something to an existing type. For example, a pointer like *int just adds
 | 
					 | 
				
			||||||
//     the fact that it's a pointer to an existing type (int).
 | 
					 | 
				
			||||||
//     These are encoded efficiently by adding a prefix to a type code.
 | 
					 | 
				
			||||||
//   * Types with multiple fields (struct, array, func, map). All of these have
 | 
					 | 
				
			||||||
//     multiple fields contained within. Most obviously structs can contain many
 | 
					 | 
				
			||||||
//     types as fields. Also arrays contain not just the element type but also
 | 
					 | 
				
			||||||
//     the length parameter which can be any arbitrary number and thus may not
 | 
					 | 
				
			||||||
//     fit in a type code.
 | 
					 | 
				
			||||||
//     These types are encoded using side tables.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// This distinction is also important for how named types are encoded. At the
 | 
					 | 
				
			||||||
// moment, named basic type just get a unique number assigned while named
 | 
					 | 
				
			||||||
// non-basic types have their underlying type stored in a sidetable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"encoding/binary"
 | 
					 | 
				
			||||||
	"go/ast"
 | 
					 | 
				
			||||||
	"math/big"
 | 
					 | 
				
			||||||
	"sort"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"tinygo.org/x/go-llvm"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// A list of basic types and their numbers. This list should be kept in sync
 | 
					 | 
				
			||||||
// with the list of Kind constants of type.go in the reflect package.
 | 
					 | 
				
			||||||
var basicTypes = map[string]int64{
 | 
					 | 
				
			||||||
	"bool":           1,
 | 
					 | 
				
			||||||
	"int":            2,
 | 
					 | 
				
			||||||
	"int8":           3,
 | 
					 | 
				
			||||||
	"int16":          4,
 | 
					 | 
				
			||||||
	"int32":          5,
 | 
					 | 
				
			||||||
	"int64":          6,
 | 
					 | 
				
			||||||
	"uint":           7,
 | 
					 | 
				
			||||||
	"uint8":          8,
 | 
					 | 
				
			||||||
	"uint16":         9,
 | 
					 | 
				
			||||||
	"uint32":         10,
 | 
					 | 
				
			||||||
	"uint64":         11,
 | 
					 | 
				
			||||||
	"uintptr":        12,
 | 
					 | 
				
			||||||
	"float32":        13,
 | 
					 | 
				
			||||||
	"float64":        14,
 | 
					 | 
				
			||||||
	"complex64":      15,
 | 
					 | 
				
			||||||
	"complex128":     16,
 | 
					 | 
				
			||||||
	"string":         17,
 | 
					 | 
				
			||||||
	"unsafe.Pointer": 18,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// A list of non-basic types. Adding 19 to this number will give the Kind as
 | 
					 | 
				
			||||||
// used in src/reflect/types.go, and it must be kept in sync with that list.
 | 
					 | 
				
			||||||
var nonBasicTypes = map[string]int64{
 | 
					 | 
				
			||||||
	"chan":      0,
 | 
					 | 
				
			||||||
	"interface": 1,
 | 
					 | 
				
			||||||
	"pointer":   2,
 | 
					 | 
				
			||||||
	"slice":     3,
 | 
					 | 
				
			||||||
	"array":     4,
 | 
					 | 
				
			||||||
	"func":      5,
 | 
					 | 
				
			||||||
	"map":       6,
 | 
					 | 
				
			||||||
	"struct":    7,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// typeCodeAssignmentState keeps some global state around for type code
 | 
					 | 
				
			||||||
// assignments, used to assign one unique type code to each Go type.
 | 
					 | 
				
			||||||
type typeCodeAssignmentState struct {
 | 
					 | 
				
			||||||
	// Builder used purely for constant operations (because LLVM 15 removed many
 | 
					 | 
				
			||||||
	// llvm.Const* functions).
 | 
					 | 
				
			||||||
	builder llvm.Builder
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// An integer that's incremented each time it's used to give unique IDs to
 | 
					 | 
				
			||||||
	// type codes that are not yet fully supported otherwise by the reflect
 | 
					 | 
				
			||||||
	// package (or are simply unused in the compiled program).
 | 
					 | 
				
			||||||
	fallbackIndex int
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// This is the length of an uintptr. Only used occasionally to know whether
 | 
					 | 
				
			||||||
	// a given number can be encoded as a varint.
 | 
					 | 
				
			||||||
	uintptrLen int
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Map of named types to their type code. It is important that named types
 | 
					 | 
				
			||||||
	// get unique IDs for each type.
 | 
					 | 
				
			||||||
	namedBasicTypes    map[string]int
 | 
					 | 
				
			||||||
	namedNonBasicTypes map[string]int
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Map of array types to their type code.
 | 
					 | 
				
			||||||
	arrayTypes               map[string]int
 | 
					 | 
				
			||||||
	arrayTypesSidetable      []byte
 | 
					 | 
				
			||||||
	needsArrayTypesSidetable bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Map of struct types to their type code.
 | 
					 | 
				
			||||||
	structTypes               map[string]int
 | 
					 | 
				
			||||||
	structTypesSidetable      []byte
 | 
					 | 
				
			||||||
	needsStructNamesSidetable bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Map of struct names and tags to their name string.
 | 
					 | 
				
			||||||
	structNames               map[string]int
 | 
					 | 
				
			||||||
	structNamesSidetable      []byte
 | 
					 | 
				
			||||||
	needsStructTypesSidetable bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// This byte array is stored in reflect.namedNonBasicTypesSidetable and is
 | 
					 | 
				
			||||||
	// used at runtime to get details about a named non-basic type.
 | 
					 | 
				
			||||||
	// Entries are varints (see makeVarint below and readVarint in
 | 
					 | 
				
			||||||
	// reflect/sidetables.go for the encoding): one varint per entry. The
 | 
					 | 
				
			||||||
	// integers in namedNonBasicTypes are indices into this array. Because these
 | 
					 | 
				
			||||||
	// are varints, most type codes are really small (just one byte).
 | 
					 | 
				
			||||||
	//
 | 
					 | 
				
			||||||
	// Note that this byte buffer is not created when it is not needed
 | 
					 | 
				
			||||||
	// (reflect.namedNonBasicTypesSidetable has no uses), see
 | 
					 | 
				
			||||||
	// needsNamedTypesSidetable.
 | 
					 | 
				
			||||||
	namedNonBasicTypesSidetable []uint64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// This indicates whether namedNonBasicTypesSidetable needs to be created at
 | 
					 | 
				
			||||||
	// all. If it is false, namedNonBasicTypesSidetable will contain simple
 | 
					 | 
				
			||||||
	// monotonically increasing numbers.
 | 
					 | 
				
			||||||
	needsNamedNonBasicTypesSidetable bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LowerReflect is used to assign a type code to each type in the program
 | 
					 | 
				
			||||||
// that is ever stored in an interface. It tries to use the smallest possible
 | 
					 | 
				
			||||||
// numbers to make the code that works with interfaces as small as possible.
 | 
					 | 
				
			||||||
func LowerReflect(mod llvm.Module) {
 | 
					 | 
				
			||||||
	// if reflect were not used, we could skip generating the sidetable
 | 
					 | 
				
			||||||
	// this does not help in practice, and is difficult to do correctly
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Obtain slice of all types in the program.
 | 
					 | 
				
			||||||
	type typeInfo struct {
 | 
					 | 
				
			||||||
		typecode llvm.Value
 | 
					 | 
				
			||||||
		name     string
 | 
					 | 
				
			||||||
		numUses  int
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var types []*typeInfo
 | 
					 | 
				
			||||||
	for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
 | 
					 | 
				
			||||||
		if strings.HasPrefix(global.Name(), "reflect/types.type:") {
 | 
					 | 
				
			||||||
			types = append(types, &typeInfo{
 | 
					 | 
				
			||||||
				typecode: global,
 | 
					 | 
				
			||||||
				name:     global.Name(),
 | 
					 | 
				
			||||||
				numUses:  len(getUses(global)),
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Sort the slice in a way that often used types are assigned a type code
 | 
					 | 
				
			||||||
	// first.
 | 
					 | 
				
			||||||
	sort.Slice(types, func(i, j int) bool {
 | 
					 | 
				
			||||||
		if types[i].numUses != types[j].numUses {
 | 
					 | 
				
			||||||
			return types[i].numUses < types[j].numUses
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// It would make more sense to compare the name in the other direction,
 | 
					 | 
				
			||||||
		// but for some reason that increases binary size. Could be a fluke, but
 | 
					 | 
				
			||||||
		// could also have some good reason (and possibly hint at a small
 | 
					 | 
				
			||||||
		// optimization).
 | 
					 | 
				
			||||||
		return types[i].name > types[j].name
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Assign typecodes the way the reflect package expects.
 | 
					 | 
				
			||||||
	targetData := llvm.NewTargetData(mod.DataLayout())
 | 
					 | 
				
			||||||
	defer targetData.Dispose()
 | 
					 | 
				
			||||||
	uintptrType := mod.Context().IntType(targetData.PointerSize() * 8)
 | 
					 | 
				
			||||||
	state := typeCodeAssignmentState{
 | 
					 | 
				
			||||||
		builder:                          mod.Context().NewBuilder(),
 | 
					 | 
				
			||||||
		fallbackIndex:                    1,
 | 
					 | 
				
			||||||
		uintptrLen:                       targetData.PointerSize() * 8,
 | 
					 | 
				
			||||||
		namedBasicTypes:                  make(map[string]int),
 | 
					 | 
				
			||||||
		namedNonBasicTypes:               make(map[string]int),
 | 
					 | 
				
			||||||
		arrayTypes:                       make(map[string]int),
 | 
					 | 
				
			||||||
		structTypes:                      make(map[string]int),
 | 
					 | 
				
			||||||
		structNames:                      make(map[string]int),
 | 
					 | 
				
			||||||
		needsNamedNonBasicTypesSidetable: len(getUses(mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0,
 | 
					 | 
				
			||||||
		needsStructTypesSidetable:        len(getUses(mod.NamedGlobal("reflect.structTypesSidetable"))) != 0,
 | 
					 | 
				
			||||||
		needsStructNamesSidetable:        len(getUses(mod.NamedGlobal("reflect.structNamesSidetable"))) != 0,
 | 
					 | 
				
			||||||
		needsArrayTypesSidetable:         len(getUses(mod.NamedGlobal("reflect.arrayTypesSidetable"))) != 0,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer state.builder.Dispose()
 | 
					 | 
				
			||||||
	for _, t := range types {
 | 
					 | 
				
			||||||
		num := state.getTypeCodeNum(t.typecode)
 | 
					 | 
				
			||||||
		if num.BitLen() > state.uintptrLen || !num.IsUint64() {
 | 
					 | 
				
			||||||
			// TODO: support this in some way, using a side table for example.
 | 
					 | 
				
			||||||
			// That's less efficient but better than not working at all.
 | 
					 | 
				
			||||||
			// Particularly important on systems with 16-bit pointers (e.g.
 | 
					 | 
				
			||||||
			// AVR).
 | 
					 | 
				
			||||||
			panic("compiler: could not store type code number inside interface type code")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Replace each use of the type code global with the constant type code.
 | 
					 | 
				
			||||||
		for _, use := range getUses(t.typecode) {
 | 
					 | 
				
			||||||
			if use.IsAConstantExpr().IsNil() {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			typecode := llvm.ConstInt(uintptrType, num.Uint64(), false)
 | 
					 | 
				
			||||||
			switch use.Opcode() {
 | 
					 | 
				
			||||||
			case llvm.PtrToInt:
 | 
					 | 
				
			||||||
				// Already of the correct type.
 | 
					 | 
				
			||||||
			case llvm.BitCast:
 | 
					 | 
				
			||||||
				// Could happen when stored in an interface (which is of type
 | 
					 | 
				
			||||||
				// i8*).
 | 
					 | 
				
			||||||
				typecode = llvm.ConstIntToPtr(typecode, use.Type())
 | 
					 | 
				
			||||||
			default:
 | 
					 | 
				
			||||||
				panic("unexpected constant expression")
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			use.ReplaceAllUsesWith(typecode)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Only create this sidetable when it is necessary.
 | 
					 | 
				
			||||||
	if state.needsNamedNonBasicTypesSidetable {
 | 
					 | 
				
			||||||
		global := replaceGlobalIntWithArray(mod, "reflect.namedNonBasicTypesSidetable", state.namedNonBasicTypesSidetable)
 | 
					 | 
				
			||||||
		global.SetLinkage(llvm.InternalLinkage)
 | 
					 | 
				
			||||||
		global.SetUnnamedAddr(true)
 | 
					 | 
				
			||||||
		global.SetGlobalConstant(true)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if state.needsArrayTypesSidetable {
 | 
					 | 
				
			||||||
		global := replaceGlobalIntWithArray(mod, "reflect.arrayTypesSidetable", state.arrayTypesSidetable)
 | 
					 | 
				
			||||||
		global.SetLinkage(llvm.InternalLinkage)
 | 
					 | 
				
			||||||
		global.SetUnnamedAddr(true)
 | 
					 | 
				
			||||||
		global.SetGlobalConstant(true)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if state.needsStructTypesSidetable {
 | 
					 | 
				
			||||||
		global := replaceGlobalIntWithArray(mod, "reflect.structTypesSidetable", state.structTypesSidetable)
 | 
					 | 
				
			||||||
		global.SetLinkage(llvm.InternalLinkage)
 | 
					 | 
				
			||||||
		global.SetUnnamedAddr(true)
 | 
					 | 
				
			||||||
		global.SetGlobalConstant(true)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if state.needsStructNamesSidetable {
 | 
					 | 
				
			||||||
		global := replaceGlobalIntWithArray(mod, "reflect.structNamesSidetable", state.structNamesSidetable)
 | 
					 | 
				
			||||||
		global.SetLinkage(llvm.InternalLinkage)
 | 
					 | 
				
			||||||
		global.SetUnnamedAddr(true)
 | 
					 | 
				
			||||||
		global.SetGlobalConstant(true)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Remove most objects created for interface and reflect lowering.
 | 
					 | 
				
			||||||
	// They would normally be removed anyway in later passes, but not always.
 | 
					 | 
				
			||||||
	// It also cleans up the IR for testing.
 | 
					 | 
				
			||||||
	for _, typ := range types {
 | 
					 | 
				
			||||||
		initializer := typ.typecode.Initializer()
 | 
					 | 
				
			||||||
		references := state.builder.CreateExtractValue(initializer, 0, "")
 | 
					 | 
				
			||||||
		typ.typecode.SetInitializer(llvm.ConstNull(initializer.Type()))
 | 
					 | 
				
			||||||
		if strings.HasPrefix(typ.name, "reflect/types.type:struct:") {
 | 
					 | 
				
			||||||
			// Structs have a 'references' field that is not a typecode but
 | 
					 | 
				
			||||||
			// a pointer to a runtime.structField array and therefore a
 | 
					 | 
				
			||||||
			// bitcast. This global should be erased separately, otherwise
 | 
					 | 
				
			||||||
			// typecode objects cannot be erased.
 | 
					 | 
				
			||||||
			structFields := references
 | 
					 | 
				
			||||||
			if !structFields.IsAConstantExpr().IsNil() && structFields.Opcode() == llvm.BitCast {
 | 
					 | 
				
			||||||
				structFields = structFields.Operand(0) // get global from bitcast, for LLVM 14 compatibility (non-opaque pointers)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			structFields.EraseFromParentAsGlobal()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getTypeCodeNum returns the typecode for a given type as expected by the
 | 
					 | 
				
			||||||
// reflect package. Also see getTypeCodeName, which serializes types to a string
 | 
					 | 
				
			||||||
// based on a types.Type value for this function.
 | 
					 | 
				
			||||||
func (state *typeCodeAssignmentState) getTypeCodeNum(typecode llvm.Value) *big.Int {
 | 
					 | 
				
			||||||
	// Note: see src/reflect/type.go for bit allocations.
 | 
					 | 
				
			||||||
	class, value := getClassAndValueFromTypeCode(typecode)
 | 
					 | 
				
			||||||
	name := ""
 | 
					 | 
				
			||||||
	if class == "named" {
 | 
					 | 
				
			||||||
		name = value
 | 
					 | 
				
			||||||
		typecode = state.builder.CreateExtractValue(typecode.Initializer(), 0, "")
 | 
					 | 
				
			||||||
		class, value = getClassAndValueFromTypeCode(typecode)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if class == "basic" {
 | 
					 | 
				
			||||||
		// Basic types follow the following bit pattern:
 | 
					 | 
				
			||||||
		//    ...xxxxx0
 | 
					 | 
				
			||||||
		// where xxxxx is allocated for the 18 possible basic types and all the
 | 
					 | 
				
			||||||
		// upper bits are used to indicate the named type.
 | 
					 | 
				
			||||||
		num, ok := basicTypes[value]
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			panic("invalid basic type: " + value)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if name != "" {
 | 
					 | 
				
			||||||
			// This type is named, set the upper bits to the name ID.
 | 
					 | 
				
			||||||
			num |= int64(state.getBasicNamedTypeNum(name)) << 5
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return big.NewInt(num << 1)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		// Non-baisc types use the following bit pattern:
 | 
					 | 
				
			||||||
		//    ...nxxx1
 | 
					 | 
				
			||||||
		// where xxx indicates the non-basic type. The upper bits contain
 | 
					 | 
				
			||||||
		// whatever the type contains. Types that wrap a single other type
 | 
					 | 
				
			||||||
		// (channel, interface, pointer, slice) just contain the bits of the
 | 
					 | 
				
			||||||
		// wrapped type. Other types (like struct) need more fields and thus
 | 
					 | 
				
			||||||
		// cannot be encoded as a simple prefix.
 | 
					 | 
				
			||||||
		var classNumber int64
 | 
					 | 
				
			||||||
		if n, ok := nonBasicTypes[class]; ok {
 | 
					 | 
				
			||||||
			classNumber = n
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			panic("unknown type kind: " + class)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		var num *big.Int
 | 
					 | 
				
			||||||
		lowBits := (classNumber << 1) + 1 // the 5 low bits of the typecode
 | 
					 | 
				
			||||||
		if name == "" {
 | 
					 | 
				
			||||||
			num = state.getNonBasicTypeCode(class, typecode)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			// We must return a named type here. But first check whether it
 | 
					 | 
				
			||||||
			// has already been defined.
 | 
					 | 
				
			||||||
			if index, ok := state.namedNonBasicTypes[name]; ok {
 | 
					 | 
				
			||||||
				num := big.NewInt(int64(index))
 | 
					 | 
				
			||||||
				num.Lsh(num, 5).Or(num, big.NewInt((classNumber<<1)+1+(1<<4)))
 | 
					 | 
				
			||||||
				return num
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			lowBits |= 1 << 4 // set the 'n' bit (see above)
 | 
					 | 
				
			||||||
			if !state.needsNamedNonBasicTypesSidetable {
 | 
					 | 
				
			||||||
				// Use simple small integers in this case, to make these numbers
 | 
					 | 
				
			||||||
				// smaller.
 | 
					 | 
				
			||||||
				index := len(state.namedNonBasicTypes) + 1
 | 
					 | 
				
			||||||
				state.namedNonBasicTypes[name] = index
 | 
					 | 
				
			||||||
				num = big.NewInt(int64(index))
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				// We need to store full type information.
 | 
					 | 
				
			||||||
				// First allocate a number in the named non-basic type
 | 
					 | 
				
			||||||
				// sidetable.
 | 
					 | 
				
			||||||
				index := len(state.namedNonBasicTypesSidetable)
 | 
					 | 
				
			||||||
				state.namedNonBasicTypesSidetable = append(state.namedNonBasicTypesSidetable, 0)
 | 
					 | 
				
			||||||
				state.namedNonBasicTypes[name] = index
 | 
					 | 
				
			||||||
				// Get the typecode of the underlying type (which could be the
 | 
					 | 
				
			||||||
				// element type in the case of pointers, for example).
 | 
					 | 
				
			||||||
				num = state.getNonBasicTypeCode(class, typecode)
 | 
					 | 
				
			||||||
				if num.BitLen() > state.uintptrLen || !num.IsUint64() {
 | 
					 | 
				
			||||||
					panic("cannot store value in sidetable")
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				// Now update the side table with the number we just
 | 
					 | 
				
			||||||
				// determined. We need this multi-step approach to avoid stack
 | 
					 | 
				
			||||||
				// overflow due to adding types recursively in the case of
 | 
					 | 
				
			||||||
				// linked lists (a pointer which points to a struct that
 | 
					 | 
				
			||||||
				// contains that same pointer).
 | 
					 | 
				
			||||||
				state.namedNonBasicTypesSidetable[index] = num.Uint64()
 | 
					 | 
				
			||||||
				num = big.NewInt(int64(index))
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// Concatenate the 'num' and 'lowBits' bitstrings.
 | 
					 | 
				
			||||||
		num.Lsh(num, 5).Or(num, big.NewInt(lowBits))
 | 
					 | 
				
			||||||
		return num
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getNonBasicTypeCode is used by getTypeCodeNum. It returns the upper bits of
 | 
					 | 
				
			||||||
// the type code used there in the type code.
 | 
					 | 
				
			||||||
func (state *typeCodeAssignmentState) getNonBasicTypeCode(class string, typecode llvm.Value) *big.Int {
 | 
					 | 
				
			||||||
	switch class {
 | 
					 | 
				
			||||||
	case "chan", "pointer", "slice":
 | 
					 | 
				
			||||||
		// Prefix-style type kinds. The upper bits contain the element type.
 | 
					 | 
				
			||||||
		sub := state.builder.CreateExtractValue(typecode.Initializer(), 0, "")
 | 
					 | 
				
			||||||
		return state.getTypeCodeNum(sub)
 | 
					 | 
				
			||||||
	case "array":
 | 
					 | 
				
			||||||
		// An array is basically a pair of (typecode, length) stored in a
 | 
					 | 
				
			||||||
		// sidetable.
 | 
					 | 
				
			||||||
		return big.NewInt(int64(state.getArrayTypeNum(typecode)))
 | 
					 | 
				
			||||||
	case "struct":
 | 
					 | 
				
			||||||
		// More complicated type kind. The upper bits contain the index to the
 | 
					 | 
				
			||||||
		// struct type in the struct types sidetable.
 | 
					 | 
				
			||||||
		return big.NewInt(int64(state.getStructTypeNum(typecode)))
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		// Type has not yet been implemented, so fall back by using a unique
 | 
					 | 
				
			||||||
		// number.
 | 
					 | 
				
			||||||
		num := big.NewInt(int64(state.fallbackIndex))
 | 
					 | 
				
			||||||
		state.fallbackIndex++
 | 
					 | 
				
			||||||
		return num
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getClassAndValueFromTypeCode takes a typecode (a llvm.Value of type
 | 
					 | 
				
			||||||
// runtime.typecodeID), looks at the name, and extracts the typecode class and
 | 
					 | 
				
			||||||
// value from it. For example, for a typecode with the following name:
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//	reflect/types.type:pointer:named:reflect.ValueError
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// It extracts:
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//	class = "pointer"
 | 
					 | 
				
			||||||
//	value = "named:reflect.ValueError"
 | 
					 | 
				
			||||||
func getClassAndValueFromTypeCode(typecode llvm.Value) (class, value string) {
 | 
					 | 
				
			||||||
	typecodeName := typecode.Name()
 | 
					 | 
				
			||||||
	const prefix = "reflect/types.type:"
 | 
					 | 
				
			||||||
	if !strings.HasPrefix(typecodeName, prefix) {
 | 
					 | 
				
			||||||
		panic("unexpected typecode name: " + typecodeName)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	id := typecodeName[len(prefix):]
 | 
					 | 
				
			||||||
	class = id[:strings.IndexByte(id, ':')]
 | 
					 | 
				
			||||||
	value = id[len(class)+1:]
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getBasicNamedTypeNum returns an appropriate (unique) number for the given
 | 
					 | 
				
			||||||
// named type. If the name already has a number that number is returned, else a
 | 
					 | 
				
			||||||
// new number is returned. The number is always non-zero.
 | 
					 | 
				
			||||||
func (state *typeCodeAssignmentState) getBasicNamedTypeNum(name string) int {
 | 
					 | 
				
			||||||
	if num, ok := state.namedBasicTypes[name]; ok {
 | 
					 | 
				
			||||||
		return num
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	num := len(state.namedBasicTypes) + 1
 | 
					 | 
				
			||||||
	state.namedBasicTypes[name] = num
 | 
					 | 
				
			||||||
	return num
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getArrayTypeNum returns the array type number, which is an index into the
 | 
					 | 
				
			||||||
// reflect.arrayTypesSidetable or a unique number for this type if this table is
 | 
					 | 
				
			||||||
// not used.
 | 
					 | 
				
			||||||
func (state *typeCodeAssignmentState) getArrayTypeNum(typecode llvm.Value) int {
 | 
					 | 
				
			||||||
	name := typecode.Name()
 | 
					 | 
				
			||||||
	if num, ok := state.arrayTypes[name]; ok {
 | 
					 | 
				
			||||||
		// This array type already has an entry in the sidetable. Don't store
 | 
					 | 
				
			||||||
		// it twice.
 | 
					 | 
				
			||||||
		return num
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if !state.needsArrayTypesSidetable {
 | 
					 | 
				
			||||||
		// We don't need array sidetables, so we can just assign monotonically
 | 
					 | 
				
			||||||
		// increasing numbers to each array type.
 | 
					 | 
				
			||||||
		num := len(state.arrayTypes)
 | 
					 | 
				
			||||||
		state.arrayTypes[name] = num
 | 
					 | 
				
			||||||
		return num
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	elemTypeCode := state.builder.CreateExtractValue(typecode.Initializer(), 0, "")
 | 
					 | 
				
			||||||
	elemTypeNum := state.getTypeCodeNum(elemTypeCode)
 | 
					 | 
				
			||||||
	if elemTypeNum.BitLen() > state.uintptrLen || !elemTypeNum.IsUint64() {
 | 
					 | 
				
			||||||
		// TODO: make this a regular error
 | 
					 | 
				
			||||||
		panic("array element type has a type code that is too big")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The array side table is a sequence of {element type, array length}.
 | 
					 | 
				
			||||||
	arrayLength := state.builder.CreateExtractValue(typecode.Initializer(), 1, "").ZExtValue()
 | 
					 | 
				
			||||||
	buf := makeVarint(elemTypeNum.Uint64())
 | 
					 | 
				
			||||||
	buf = append(buf, makeVarint(arrayLength)...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	index := len(state.arrayTypesSidetable)
 | 
					 | 
				
			||||||
	state.arrayTypes[name] = index
 | 
					 | 
				
			||||||
	state.arrayTypesSidetable = append(state.arrayTypesSidetable, buf...)
 | 
					 | 
				
			||||||
	return index
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getStructTypeNum returns the struct type number, which is an index into
 | 
					 | 
				
			||||||
// reflect.structTypesSidetable or an unique number for every struct if this
 | 
					 | 
				
			||||||
// sidetable is not needed in the to-be-compiled program.
 | 
					 | 
				
			||||||
func (state *typeCodeAssignmentState) getStructTypeNum(typecode llvm.Value) int {
 | 
					 | 
				
			||||||
	name := typecode.Name()
 | 
					 | 
				
			||||||
	if num, ok := state.structTypes[name]; ok {
 | 
					 | 
				
			||||||
		// This struct already has an assigned type code.
 | 
					 | 
				
			||||||
		return num
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if !state.needsStructTypesSidetable {
 | 
					 | 
				
			||||||
		// We don't need struct sidetables, so we can just assign monotonically
 | 
					 | 
				
			||||||
		// increasing numbers to each struct type.
 | 
					 | 
				
			||||||
		num := len(state.structTypes)
 | 
					 | 
				
			||||||
		state.structTypes[name] = num
 | 
					 | 
				
			||||||
		return num
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get the fields this struct type contains.
 | 
					 | 
				
			||||||
	// The struct number will be the start index of
 | 
					 | 
				
			||||||
	structTypeGlobal := stripPointerCasts(state.builder.CreateExtractValue(typecode.Initializer(), 0, "")).Initializer()
 | 
					 | 
				
			||||||
	numFields := structTypeGlobal.Type().ArrayLength()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The first data that is stored in the struct sidetable is the number of
 | 
					 | 
				
			||||||
	// fields this struct contains. This is usually just a single byte because
 | 
					 | 
				
			||||||
	// most structs don't contain that many fields, but make it a varint just
 | 
					 | 
				
			||||||
	// to be sure.
 | 
					 | 
				
			||||||
	buf := makeVarint(uint64(numFields))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Iterate over every field in the struct.
 | 
					 | 
				
			||||||
	// Every field is stored sequentially in the struct sidetable. Fields can
 | 
					 | 
				
			||||||
	// be retrieved from this list of fields at runtime by iterating over all
 | 
					 | 
				
			||||||
	// of them until the right field has been found.
 | 
					 | 
				
			||||||
	// Perhaps adding some index would speed things up, but it would also make
 | 
					 | 
				
			||||||
	// the sidetable bigger.
 | 
					 | 
				
			||||||
	for i := 0; i < numFields; i++ {
 | 
					 | 
				
			||||||
		// Collect some information about this field.
 | 
					 | 
				
			||||||
		field := state.builder.CreateExtractValue(structTypeGlobal, i, "")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		nameGlobal := state.builder.CreateExtractValue(field, 1, "")
 | 
					 | 
				
			||||||
		if nameGlobal == llvm.ConstPointerNull(nameGlobal.Type()) {
 | 
					 | 
				
			||||||
			panic("compiler: no name for this struct field")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		fieldNameBytes := getGlobalBytes(stripPointerCasts(nameGlobal), state.builder)
 | 
					 | 
				
			||||||
		fieldNameNumber := state.getStructNameNumber(fieldNameBytes)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// See whether this struct field has an associated tag, and if so,
 | 
					 | 
				
			||||||
		// store that tag in the tags sidetable.
 | 
					 | 
				
			||||||
		tagGlobal := state.builder.CreateExtractValue(field, 2, "")
 | 
					 | 
				
			||||||
		hasTag := false
 | 
					 | 
				
			||||||
		tagNumber := 0
 | 
					 | 
				
			||||||
		if tagGlobal != llvm.ConstPointerNull(tagGlobal.Type()) {
 | 
					 | 
				
			||||||
			hasTag = true
 | 
					 | 
				
			||||||
			tagBytes := getGlobalBytes(stripPointerCasts(tagGlobal), state.builder)
 | 
					 | 
				
			||||||
			tagNumber = state.getStructNameNumber(tagBytes)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// The 'embedded' or 'anonymous' flag for this field.
 | 
					 | 
				
			||||||
		embedded := state.builder.CreateExtractValue(field, 3, "").ZExtValue() != 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// The first byte in the struct types sidetable is a flags byte with
 | 
					 | 
				
			||||||
		// two bits in it.
 | 
					 | 
				
			||||||
		flagsByte := byte(0)
 | 
					 | 
				
			||||||
		if embedded {
 | 
					 | 
				
			||||||
			flagsByte |= 1
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if hasTag {
 | 
					 | 
				
			||||||
			flagsByte |= 2
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if ast.IsExported(string(fieldNameBytes)) {
 | 
					 | 
				
			||||||
			flagsByte |= 4
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		buf = append(buf, flagsByte)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Get the type number and add it to the buffer.
 | 
					 | 
				
			||||||
		// All fields have a type, so include it directly here.
 | 
					 | 
				
			||||||
		typeNum := state.getTypeCodeNum(state.builder.CreateExtractValue(field, 0, ""))
 | 
					 | 
				
			||||||
		if typeNum.BitLen() > state.uintptrLen || !typeNum.IsUint64() {
 | 
					 | 
				
			||||||
			// TODO: make this a regular error
 | 
					 | 
				
			||||||
			panic("struct field has a type code that is too big")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		buf = append(buf, makeVarint(typeNum.Uint64())...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Add the name.
 | 
					 | 
				
			||||||
		buf = append(buf, makeVarint(uint64(fieldNameNumber))...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Add the tag, if there is one.
 | 
					 | 
				
			||||||
		if hasTag {
 | 
					 | 
				
			||||||
			buf = append(buf, makeVarint(uint64(tagNumber))...)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	num := len(state.structTypesSidetable)
 | 
					 | 
				
			||||||
	state.structTypes[name] = num
 | 
					 | 
				
			||||||
	state.structTypesSidetable = append(state.structTypesSidetable, buf...)
 | 
					 | 
				
			||||||
	return num
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getStructNameNumber stores this string (name or tag) onto the struct names
 | 
					 | 
				
			||||||
// sidetable. The format is a varint of the length of the struct, followed by
 | 
					 | 
				
			||||||
// the raw bytes of the name. Multiple identical strings are stored under the
 | 
					 | 
				
			||||||
// same name for space efficiency.
 | 
					 | 
				
			||||||
func (state *typeCodeAssignmentState) getStructNameNumber(nameBytes []byte) int {
 | 
					 | 
				
			||||||
	name := string(nameBytes)
 | 
					 | 
				
			||||||
	if n, ok := state.structNames[name]; ok {
 | 
					 | 
				
			||||||
		// This name was used before, re-use it now (for space efficiency).
 | 
					 | 
				
			||||||
		return n
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// This name is not yet in the names sidetable. Add it now.
 | 
					 | 
				
			||||||
	n := len(state.structNamesSidetable)
 | 
					 | 
				
			||||||
	state.structNames[name] = n
 | 
					 | 
				
			||||||
	state.structNamesSidetable = append(state.structNamesSidetable, makeVarint(uint64(len(nameBytes)))...)
 | 
					 | 
				
			||||||
	state.structNamesSidetable = append(state.structNamesSidetable, nameBytes...)
 | 
					 | 
				
			||||||
	return n
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// makeVarint is a small helper function that returns the bytes of the number in
 | 
					 | 
				
			||||||
// varint encoding.
 | 
					 | 
				
			||||||
func makeVarint(n uint64) []byte {
 | 
					 | 
				
			||||||
	buf := make([]byte, binary.MaxVarintLen64)
 | 
					 | 
				
			||||||
	return buf[:binary.PutUvarint(buf, n)]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,77 +0,0 @@
 | 
				
			||||||
package transform_test
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/tinygo-org/tinygo/transform"
 | 
					 | 
				
			||||||
	"tinygo.org/x/go-llvm"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type reflectAssert struct {
 | 
					 | 
				
			||||||
	call           llvm.Value
 | 
					 | 
				
			||||||
	name           string
 | 
					 | 
				
			||||||
	expectedNumber uint64
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Test reflect lowering. This code looks at IR like this:
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//	call void @main.assertType(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* inttoptr (i32 3 to i8*), i32 4, i8* undef, i8* undef)
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// and verifies that the ptrtoint constant (the first parameter of
 | 
					 | 
				
			||||||
// @main.assertType) is replaced with the correct type code.  The expected
 | 
					 | 
				
			||||||
// output is this:
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//	call void @main.assertType(i32 4, i8* inttoptr (i32 3 to i8*), i32 4, i8* undef, i8* undef)
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// The first and third parameter are compared and must match, the second
 | 
					 | 
				
			||||||
// parameter is ignored.
 | 
					 | 
				
			||||||
func TestReflect(t *testing.T) {
 | 
					 | 
				
			||||||
	t.Parallel()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mod := compileGoFileForTesting(t, "./testdata/reflect.go")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Run the instcombine pass, to clean up the IR a bit (especially
 | 
					 | 
				
			||||||
	// insertvalue/extractvalue instructions).
 | 
					 | 
				
			||||||
	pm := llvm.NewPassManager()
 | 
					 | 
				
			||||||
	defer pm.Dispose()
 | 
					 | 
				
			||||||
	pm.AddInstructionCombiningPass()
 | 
					 | 
				
			||||||
	pm.Run(mod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get a list of all the asserts in the source code.
 | 
					 | 
				
			||||||
	assertType := mod.NamedFunction("main.assertType")
 | 
					 | 
				
			||||||
	var asserts []reflectAssert
 | 
					 | 
				
			||||||
	for user := assertType.FirstUse(); !user.IsNil(); user = user.NextUse() {
 | 
					 | 
				
			||||||
		use := user.User()
 | 
					 | 
				
			||||||
		if use.IsACallInst().IsNil() {
 | 
					 | 
				
			||||||
			t.Fatal("expected call use of main.assertType")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		global := use.Operand(0).Operand(0)
 | 
					 | 
				
			||||||
		expectedNumber := use.Operand(2).ZExtValue()
 | 
					 | 
				
			||||||
		asserts = append(asserts, reflectAssert{
 | 
					 | 
				
			||||||
			call:           use,
 | 
					 | 
				
			||||||
			name:           global.Name(),
 | 
					 | 
				
			||||||
			expectedNumber: expectedNumber,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Sanity check to show that the test is actually testing anything.
 | 
					 | 
				
			||||||
	if len(asserts) < 3 {
 | 
					 | 
				
			||||||
		t.Errorf("expected at least 3 test cases, got %d", len(asserts))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Now lower the type codes.
 | 
					 | 
				
			||||||
	transform.LowerReflect(mod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check whether the values are as expected.
 | 
					 | 
				
			||||||
	for _, assert := range asserts {
 | 
					 | 
				
			||||||
		actualNumberValue := assert.call.Operand(0)
 | 
					 | 
				
			||||||
		if actualNumberValue.IsAConstantInt().IsNil() {
 | 
					 | 
				
			||||||
			t.Errorf("expected to see a constant for %s, got something else", assert.name)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		actualNumber := actualNumberValue.ZExtValue()
 | 
					 | 
				
			||||||
		if actualNumber != assert.expectedNumber {
 | 
					 | 
				
			||||||
			t.Errorf("%s: expected number 0b%b, got 0b%b", assert.name, assert.expectedNumber, actualNumber)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -113,11 +113,6 @@ func OptimizeReflectImplements(mod llvm.Module) {
 | 
				
			||||||
	builder := mod.Context().NewBuilder()
 | 
						builder := mod.Context().NewBuilder()
 | 
				
			||||||
	defer builder.Dispose()
 | 
						defer builder.Dispose()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get a few useful object for use later.
 | 
					 | 
				
			||||||
	targetData := llvm.NewTargetData(mod.DataLayout())
 | 
					 | 
				
			||||||
	defer targetData.Dispose()
 | 
					 | 
				
			||||||
	uintptrType := mod.Context().IntType(targetData.PointerSize() * 8)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Look up the (reflect.Value).Implements() method.
 | 
						// Look up the (reflect.Value).Implements() method.
 | 
				
			||||||
	var implementsFunc llvm.Value
 | 
						var implementsFunc llvm.Value
 | 
				
			||||||
	for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
 | 
						for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
 | 
				
			||||||
| 
						 | 
					@ -141,14 +136,13 @@ func OptimizeReflectImplements(mod llvm.Module) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		interfaceType := stripPointerCasts(call.Operand(2))
 | 
							interfaceType := stripPointerCasts(call.Operand(2))
 | 
				
			||||||
		if interfaceType.IsAGlobalVariable().IsNil() {
 | 
							if interfaceType.IsAGlobalVariable().IsNil() {
 | 
				
			||||||
			// The asserted interface is not constant, so can't optimize this
 | 
								// Interface is unknown at compile time. This can't be optimized.
 | 
				
			||||||
			// code.
 | 
					 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if strings.HasPrefix(interfaceType.Name(), "reflect/types.type:named:") {
 | 
							if strings.HasPrefix(interfaceType.Name(), "reflect/types.type:named:") {
 | 
				
			||||||
			// Get the underlying type.
 | 
								// Get the underlying type.
 | 
				
			||||||
			interfaceType = builder.CreateExtractValue(interfaceType.Initializer(), 0, "")
 | 
								interfaceType = stripPointerCasts(builder.CreateExtractValue(interfaceType.Initializer(), 2, ""))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !strings.HasPrefix(interfaceType.Name(), "reflect/types.type:interface:") {
 | 
							if !strings.HasPrefix(interfaceType.Name(), "reflect/types.type:interface:") {
 | 
				
			||||||
			// This is an error. The Type passed to Implements should be of
 | 
								// This is an error. The Type passed to Implements should be of
 | 
				
			||||||
| 
						 | 
					@ -156,16 +150,15 @@ func OptimizeReflectImplements(mod llvm.Module) {
 | 
				
			||||||
			// reported at runtime.
 | 
								// reported at runtime.
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if interfaceType.IsAGlobalVariable().IsNil() {
 | 
							typeAssertFunction := mod.NamedFunction(strings.TrimPrefix(interfaceType.Name(), "reflect/types.type:") + ".$typeassert")
 | 
				
			||||||
			// Interface is unknown at compile time. This can't be optimized.
 | 
							if typeAssertFunction.IsNil() {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		typeAssertFunction := builder.CreateExtractValue(interfaceType.Initializer(), 4, "").Operand(0)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Replace Implements call with the type assert call.
 | 
							// Replace Implements call with the type assert call.
 | 
				
			||||||
		builder.SetInsertPointBefore(call)
 | 
							builder.SetInsertPointBefore(call)
 | 
				
			||||||
		implements := builder.CreateCall(typeAssertFunction.GlobalValueType(), typeAssertFunction, []llvm.Value{
 | 
							implements := builder.CreateCall(typeAssertFunction.GlobalValueType(), typeAssertFunction, []llvm.Value{
 | 
				
			||||||
			builder.CreatePtrToInt(call.Operand(0), uintptrType, ""), // typecode to check
 | 
								call.Operand(0), // typecode to check
 | 
				
			||||||
		}, "")
 | 
							}, "")
 | 
				
			||||||
		call.ReplaceAllUsesWith(implements)
 | 
							call.ReplaceAllUsesWith(implements)
 | 
				
			||||||
		call.EraseFromParentAsInstruction()
 | 
							call.EraseFromParentAsInstruction()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										40
									
								
								transform/testdata/interface.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										40
									
								
								transform/testdata/interface.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -1,19 +1,19 @@
 | 
				
			||||||
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
 | 
					target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
 | 
				
			||||||
target triple = "armv7m-none-eabi"
 | 
					target triple = "armv7m-none-eabi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
 | 
					@"reflect/types.type:basic:uint8" = linkonce_odr constant { i8, i8* } { i8 8, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:basic:uint8", i32 0, i32 0) }, align 4
 | 
				
			||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
 | 
					@"reflect/types.type:pointer:basic:uint8" = linkonce_odr constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:uint8", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					 | 
				
			||||||
@"reflect/types.type:basic:uint8" = private constant %runtime.typecodeID zeroinitializer
 | 
					 | 
				
			||||||
@"reflect/types.typeid:basic:uint8" = external constant i8
 | 
					@"reflect/types.typeid:basic:uint8" = external constant i8
 | 
				
			||||||
@"reflect/types.typeid:basic:int16" = external constant i8
 | 
					@"reflect/types.typeid:basic:int16" = external constant i8
 | 
				
			||||||
@"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer
 | 
					@"reflect/types.type:basic:int" = linkonce_odr constant { i8, i8* } { i8 2, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:basic:int", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:int", i32 0, i32 0) }, align 4
 | 
				
			||||||
@"reflect/methods.NeverImplementedMethod()" = linkonce_odr constant i8 0
 | 
					@"reflect/methods.NeverImplementedMethod()" = linkonce_odr constant i8 0
 | 
				
			||||||
@"reflect/methods.Double() int" = linkonce_odr constant i8 0
 | 
					@"reflect/methods.Double() int" = linkonce_odr constant i8 0
 | 
				
			||||||
@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"reflect/methods.Double() int", i32 ptrtoint (i32 (i8*, i8*)* @"(Number).Double$invoke" to i32) }]
 | 
					@"Number$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x i8*], { i32 (i8*, i8*)* } } { i32 1, [1 x i8*] [i8* @"reflect/methods.Double() int"], { i32 (i8*, i8*)* } { i32 (i8*, i8*)* @"(Number).Double$invoke" } }
 | 
				
			||||||
@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([1 x %runtime.interfaceMethodInfo], [1 x %runtime.interfaceMethodInfo]* @"Number$methodset", i32 0, i32 0), %runtime.typecodeID* null, i32 0 }
 | 
					@"reflect/types.type:named:Number" = linkonce_odr constant { i8*, i8, i8*, i8* } { i8* bitcast ({ i32, [1 x i8*], { i32 (i8*, i8*)* } }* @"Number$methodset" to i8*), i8 34, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:named:Number", i32 0, i32 0), i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:int", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					@"reflect/types.type:pointer:named:Number" = linkonce_odr constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8*, i8, i8*, i8* }, { i8*, i8, i8*, i8* }* @"reflect/types.type:named:Number", i32 0, i32 1) }, align 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @runtime.typeAssert(i32, i8*)
 | 
					declare i1 @runtime.typeAssert(i8*, i8*)
 | 
				
			||||||
declare void @runtime.printuint8(i8)
 | 
					declare void @runtime.printuint8(i8)
 | 
				
			||||||
declare void @runtime.printint16(i16)
 | 
					declare void @runtime.printint16(i16)
 | 
				
			||||||
declare void @runtime.printint32(i32)
 | 
					declare void @runtime.printint32(i32)
 | 
				
			||||||
| 
						 | 
					@ -22,15 +22,15 @@ declare void @runtime.printnl()
 | 
				
			||||||
declare void @runtime.nilPanic(i8*)
 | 
					declare void @runtime.nilPanic(i8*)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define void @printInterfaces() {
 | 
					define void @printInterfaces() {
 | 
				
			||||||
  call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* inttoptr (i32 5 to i8*))
 | 
					  call void @printInterface(i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:int", i32 0, i32 0), i8* inttoptr (i32 5 to i8*))
 | 
				
			||||||
  call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:uint8" to i32), i8* inttoptr (i8 120 to i8*))
 | 
					  call void @printInterface(i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:uint8", i32 0, i32 0), i8* inttoptr (i8 120 to i8*))
 | 
				
			||||||
  call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32), i8* inttoptr (i32 3 to i8*))
 | 
					  call void @printInterface(i8* getelementptr inbounds ({ i8*, i8, i8*, i8* }, { i8*, i8, i8*, i8* }* @"reflect/types.type:named:Number", i32 0, i32 1), i8* inttoptr (i32 3 to i8*))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define void @printInterface(i32 %typecode, i8* %value) {
 | 
					define void @printInterface(i8* %typecode, i8* %value) {
 | 
				
			||||||
  %isUnmatched = call i1 @Unmatched$typeassert(i32 %typecode)
 | 
					  %isUnmatched = call i1 @Unmatched$typeassert(i8* %typecode)
 | 
				
			||||||
  br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
 | 
					  br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.Unmatched:
 | 
					typeswitch.Unmatched:
 | 
				
			||||||
| 
						 | 
					@ -40,16 +40,16 @@ typeswitch.Unmatched:
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.notUnmatched:
 | 
					typeswitch.notUnmatched:
 | 
				
			||||||
  %isDoubler = call i1 @Doubler$typeassert(i32 %typecode)
 | 
					  %isDoubler = call i1 @Doubler$typeassert(i8* %typecode)
 | 
				
			||||||
  br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
 | 
					  br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.Doubler:
 | 
					typeswitch.Doubler:
 | 
				
			||||||
  %doubler.result = call i32 @"Doubler.Double$invoke"(i8* %value, i32 %typecode, i8* undef)
 | 
					  %doubler.result = call i32 @"Doubler.Double$invoke"(i8* %value, i8* %typecode, i8* undef)
 | 
				
			||||||
  call void @runtime.printint32(i32 %doubler.result)
 | 
					  call void @runtime.printint32(i32 %doubler.result)
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.notDoubler:
 | 
					typeswitch.notDoubler:
 | 
				
			||||||
  %isByte = call i1 @runtime.typeAssert(i32 %typecode, i8* nonnull @"reflect/types.typeid:basic:uint8")
 | 
					  %isByte = call i1 @runtime.typeAssert(i8* %typecode, i8* nonnull @"reflect/types.typeid:basic:uint8")
 | 
				
			||||||
  br i1 %isByte, label %typeswitch.byte, label %typeswitch.notByte
 | 
					  br i1 %isByte, label %typeswitch.byte, label %typeswitch.notByte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.byte:
 | 
					typeswitch.byte:
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,7 @@ typeswitch.byte:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.notByte:
 | 
					typeswitch.notByte:
 | 
				
			||||||
  ; this is a type assert that always fails
 | 
					  ; this is a type assert that always fails
 | 
				
			||||||
  %isInt16 = call i1 @runtime.typeAssert(i32 %typecode, i8* nonnull @"reflect/types.typeid:basic:int16")
 | 
					  %isInt16 = call i1 @runtime.typeAssert(i8* %typecode, i8* nonnull @"reflect/types.typeid:basic:int16")
 | 
				
			||||||
  br i1 %isInt16, label %typeswitch.int16, label %typeswitch.notInt16
 | 
					  br i1 %isInt16, label %typeswitch.int16, label %typeswitch.notInt16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.int16:
 | 
					typeswitch.int16:
 | 
				
			||||||
| 
						 | 
					@ -84,11 +84,11 @@ define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %context) {
 | 
				
			||||||
  ret i32 %ret
 | 
					  ret i32 %ret
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i32 @"Doubler.Double$invoke"(i8* %receiver, i32 %typecode, i8* %context) #0
 | 
					declare i32 @"Doubler.Double$invoke"(i8* %receiver, i8* %typecode, i8* %context) #0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @Doubler$typeassert(i32 %typecode) #1
 | 
					declare i1 @Doubler$typeassert(i8* %typecode) #1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @Unmatched$typeassert(i32 %typecode) #2
 | 
					declare i1 @Unmatched$typeassert(i8* %typecode) #2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
attributes #0 = { "tinygo-invoke"="reflect/methods.Double() int" "tinygo-methods"="reflect/methods.Double() int" }
 | 
					attributes #0 = { "tinygo-invoke"="reflect/methods.Double() int" "tinygo-methods"="reflect/methods.Double() int" }
 | 
				
			||||||
attributes #1 = { "tinygo-methods"="reflect/methods.Double() int" }
 | 
					attributes #1 = { "tinygo-methods"="reflect/methods.Double() int" }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										38
									
								
								transform/testdata/interface.out.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										38
									
								
								transform/testdata/interface.out.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
 | 
					target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
 | 
				
			||||||
target triple = "armv7m-none-eabi"
 | 
					target triple = "armv7m-none-eabi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
 | 
					@"reflect/types.type:basic:uint8" = linkonce_odr constant { i8, i8* } { i8 8, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:basic:uint8", i32 0, i32 0) }, align 4
 | 
				
			||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
 | 
					@"reflect/types.type:pointer:basic:uint8" = linkonce_odr constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:uint8", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					@"reflect/types.type:basic:int" = linkonce_odr constant { i8, i8* } { i8 2, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:basic:int", i32 0, i32 0) }, align 4
 | 
				
			||||||
@"reflect/types.type:basic:uint8" = private constant %runtime.typecodeID zeroinitializer
 | 
					@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:int", i32 0, i32 0) }, align 4
 | 
				
			||||||
@"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer
 | 
					@"reflect/types.type:pointer:named:Number" = linkonce_odr constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:Number", i32 0, i32 0) }, align 4
 | 
				
			||||||
@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
 | 
					@"reflect/types.type:named:Number" = linkonce_odr constant { i8, i8*, i8* } { i8 34, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:named:Number", i32 0, i32 0), i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:int", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare void @runtime.printuint8(i8)
 | 
					declare void @runtime.printuint8(i8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,14 +21,14 @@ declare void @runtime.printnl()
 | 
				
			||||||
declare void @runtime.nilPanic(i8*)
 | 
					declare void @runtime.nilPanic(i8*)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define void @printInterfaces() {
 | 
					define void @printInterfaces() {
 | 
				
			||||||
  call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* inttoptr (i32 5 to i8*))
 | 
					  call void @printInterface(i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:int", i32 0, i32 0), i8* inttoptr (i32 5 to i8*))
 | 
				
			||||||
  call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:uint8" to i32), i8* inttoptr (i8 120 to i8*))
 | 
					  call void @printInterface(i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:uint8", i32 0, i32 0), i8* inttoptr (i8 120 to i8*))
 | 
				
			||||||
  call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32), i8* inttoptr (i32 3 to i8*))
 | 
					  call void @printInterface(i8* getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:Number", i32 0, i32 0), i8* inttoptr (i32 3 to i8*))
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define void @printInterface(i32 %typecode, i8* %value) {
 | 
					define void @printInterface(i8* %typecode, i8* %value) {
 | 
				
			||||||
  %isUnmatched = call i1 @"Unmatched$typeassert"(i32 %typecode)
 | 
					  %isUnmatched = call i1 @"Unmatched$typeassert"(i8* %typecode)
 | 
				
			||||||
  br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
 | 
					  br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.Unmatched:                             ; preds = %0
 | 
					typeswitch.Unmatched:                             ; preds = %0
 | 
				
			||||||
| 
						 | 
					@ -38,16 +38,16 @@ typeswitch.Unmatched:                             ; preds = %0
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.notUnmatched:                          ; preds = %0
 | 
					typeswitch.notUnmatched:                          ; preds = %0
 | 
				
			||||||
  %isDoubler = call i1 @"Doubler$typeassert"(i32 %typecode)
 | 
					  %isDoubler = call i1 @"Doubler$typeassert"(i8* %typecode)
 | 
				
			||||||
  br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
 | 
					  br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.Doubler:                               ; preds = %typeswitch.notUnmatched
 | 
					typeswitch.Doubler:                               ; preds = %typeswitch.notUnmatched
 | 
				
			||||||
  %doubler.result = call i32 @"Doubler.Double$invoke"(i8* %value, i32 %typecode, i8* undef)
 | 
					  %doubler.result = call i32 @"Doubler.Double$invoke"(i8* %value, i8* %typecode, i8* undef)
 | 
				
			||||||
  call void @runtime.printint32(i32 %doubler.result)
 | 
					  call void @runtime.printint32(i32 %doubler.result)
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.notDoubler:                            ; preds = %typeswitch.notUnmatched
 | 
					typeswitch.notDoubler:                            ; preds = %typeswitch.notUnmatched
 | 
				
			||||||
  %typeassert.ok = icmp eq i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:uint8" to i32), %typecode
 | 
					  %typeassert.ok = icmp eq i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:uint8", i32 0, i32 0), %typecode
 | 
				
			||||||
  br i1 %typeassert.ok, label %typeswitch.byte, label %typeswitch.notByte
 | 
					  br i1 %typeassert.ok, label %typeswitch.byte, label %typeswitch.notByte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typeswitch.byte:                                  ; preds = %typeswitch.notDoubler
 | 
					typeswitch.byte:                                  ; preds = %typeswitch.notDoubler
 | 
				
			||||||
| 
						 | 
					@ -80,9 +80,9 @@ define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %context) {
 | 
				
			||||||
  ret i32 %ret
 | 
					  ret i32 %ret
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define internal i32 @"Doubler.Double$invoke"(i8* %receiver, i32 %actualType, i8* %context) unnamed_addr #0 {
 | 
					define internal i32 @"Doubler.Double$invoke"(i8* %receiver, i8* %actualType, i8* %context) unnamed_addr #0 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %"named:Number.icmp" = icmp eq i32 %actualType, ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32)
 | 
					  %"named:Number.icmp" = icmp eq i8* %actualType, getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:Number", i32 0, i32 0)
 | 
				
			||||||
  br i1 %"named:Number.icmp", label %"named:Number", label %"named:Number.next"
 | 
					  br i1 %"named:Number.icmp", label %"named:Number", label %"named:Number.next"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"named:Number":                                   ; preds = %entry
 | 
					"named:Number":                                   ; preds = %entry
 | 
				
			||||||
| 
						 | 
					@ -94,9 +94,9 @@ entry:
 | 
				
			||||||
  unreachable
 | 
					  unreachable
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr #1 {
 | 
					define internal i1 @"Doubler$typeassert"(i8* %actualType) unnamed_addr #1 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %"named:Number.icmp" = icmp eq i32 %actualType, ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32)
 | 
					  %"named:Number.icmp" = icmp eq i8* %actualType, getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:Number", i32 0, i32 0)
 | 
				
			||||||
  br i1 %"named:Number.icmp", label %then, label %"named:Number.next"
 | 
					  br i1 %"named:Number.icmp", label %then, label %"named:Number.next"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
then:                                             ; preds = %entry
 | 
					then:                                             ; preds = %entry
 | 
				
			||||||
| 
						 | 
					@ -106,7 +106,7 @@ then:                                             ; preds = %entry
 | 
				
			||||||
  ret i1 false
 | 
					  ret i1 false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define internal i1 @"Unmatched$typeassert"(i32 %actualType) unnamed_addr #2 {
 | 
					define internal i1 @"Unmatched$typeassert"(i8* %actualType) unnamed_addr #2 {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  ret i1 false
 | 
					  ret i1 false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										31
									
								
								transform/testdata/reflect-implements.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										31
									
								
								transform/testdata/reflect-implements.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -1,19 +1,14 @@
 | 
				
			||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
 | 
					target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
 | 
				
			||||||
target triple = "i686--linux"
 | 
					target triple = "i686--linux"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
 | 
					%runtime._interface = type { i8*, i8* }
 | 
				
			||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 ptrtoint (i1 (i32)* @"error.$typeassert" to i32) }
 | 
					@"reflect/types.type:named:error" = internal constant { i8, i8*, i8* } { i8 52, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:named:error", i32 0, i32 0), i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, i32 0) }, align 4
 | 
				
			||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 ptrtoint (i1 (i32)* @"error.$typeassert" to i32) }
 | 
					@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = internal constant { i8, i8* } { i8 20, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", i32 0, i32 0) }, align 4
 | 
				
			||||||
@"reflect/methods.Error() string" = linkonce_odr constant i8 0
 | 
					@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = internal constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, i32 0) }, align 4
 | 
				
			||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
 | 
					@"reflect/types.type:pointer:named:error" = internal constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:error", i32 0, i32 0) }, align 4
 | 
				
			||||||
@"reflect/methods.Align() int" = linkonce_odr constant i8 0
 | 
					@"reflect/types.type:pointer:named:reflect.rawType" = internal constant { i8*, i8, i8* } { i8* null, i8 21, i8* null }, align 4
 | 
				
			||||||
@"reflect/methods.Implements(reflect.Type) bool" = linkonce_odr constant i8 0
 | 
					@"reflect/methods.Implements(reflect.Type) bool" = internal constant i8 0, align 1
 | 
				
			||||||
@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"reflect/methods.Align() int", i8* @"reflect/methods.Implements(reflect.Type) bool"]
 | 
					 | 
				
			||||||
@"reflect/types.type:named:reflect.rawType" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:uintptr", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([20 x %runtime.interfaceMethodInfo], [20 x %runtime.interfaceMethodInfo]* @"reflect.rawType$methodset", i32 0, i32 0), %runtime.typecodeID* null, i32 0 }
 | 
					 | 
				
			||||||
@"reflect.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer
 | 
					 | 
				
			||||||
@"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
; var errorType = reflect.TypeOf((*error)(nil)).Elem()
 | 
					; var errorType = reflect.TypeOf((*error)(nil)).Elem()
 | 
				
			||||||
; func isError(typ reflect.Type) bool {
 | 
					; func isError(typ reflect.Type) bool {
 | 
				
			||||||
| 
						 | 
					@ -22,9 +17,9 @@ target triple = "i686--linux"
 | 
				
			||||||
; The type itself is stored in %typ.value, %typ.typecode just refers to the
 | 
					; The type itself is stored in %typ.value, %typ.typecode just refers to the
 | 
				
			||||||
; type of reflect.Type. This function can be optimized because errorType is
 | 
					; type of reflect.Type. This function can be optimized because errorType is
 | 
				
			||||||
; known at compile time (after the interp pass has run).
 | 
					; known at compile time (after the interp pass has run).
 | 
				
			||||||
define i1 @main.isError(i32 %typ.typecode, i8* %typ.value, i8* %context) {
 | 
					define i1 @main.isError(i8* %typ.typecode, i8* %typ.value, i8* %context) {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:reflect.rawType" to i32), i8* bitcast (%runtime.typecodeID* @"reflect/types.type:named:error" to i8*), i32 %typ.typecode, i8* undef)
 | 
					  %result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i8* getelementptr inbounds ({ i8*, i8, i8* }, { i8*, i8, i8* }* @"reflect/types.type:pointer:named:reflect.rawType", i32 0, i32 1), i8* getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:error", i32 0, i32 0), i8* %typ.typecode, i8* undef)
 | 
				
			||||||
  ret i1 %result
 | 
					  ret i1 %result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,14 +28,14 @@ entry:
 | 
				
			||||||
; func isUnknown(typ, itf reflect.Type) bool {
 | 
					; func isUnknown(typ, itf reflect.Type) bool {
 | 
				
			||||||
;   return typ.Implements(itf)
 | 
					;   return typ.Implements(itf)
 | 
				
			||||||
; }
 | 
					; }
 | 
				
			||||||
define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context) {
 | 
					define i1 @main.isUnknown(i8* %typ.typecode, i8* %typ.value, i8* %itf.typecode, i8* %itf.value, i8* %context) {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i32 %typ.typecode, i8* undef)
 | 
					  %result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i8* %itf.typecode, i8* %itf.value, i8* %typ.typecode, i8* undef)
 | 
				
			||||||
  ret i1 %result
 | 
					  ret i1 %result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @"reflect.Type.Implements$invoke"(i8*, i32, i8*, i32, i8*) #0
 | 
					declare i1 @"reflect.Type.Implements$invoke"(i8*, i8*, i8*, i8*, i8*) #0
 | 
				
			||||||
declare i1 @"error.$typeassert"(i32) #1
 | 
					declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i8* %0) #1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
attributes #0 = { "tinygo-invoke"="reflect/methods.Implements(reflect.Type) bool" "tinygo-methods"="reflect/methods.Align() int; reflect/methods.Implements(reflect.Type) bool" }
 | 
					attributes #0 = { "tinygo-invoke"="reflect/methods.Implements(reflect.Type) bool" "tinygo-methods"="reflect/methods.Align() int; reflect/methods.Implements(reflect.Type) bool" }
 | 
				
			||||||
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
 | 
					attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								transform/testdata/reflect-implements.out.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										34
									
								
								transform/testdata/reflect-implements.out.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
					@ -1,36 +1,28 @@
 | 
				
			||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
 | 
					target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
 | 
				
			||||||
target triple = "i686--linux"
 | 
					target triple = "i686--linux"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
 | 
					@"reflect/types.type:named:error" = internal constant { i8, i8*, i8* } { i8 52, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:named:error", i32 0, i32 0), i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, i32 0) }, align 4
 | 
				
			||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
 | 
					@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = internal constant { i8, i8* } { i8 20, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = internal constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					@"reflect/types.type:pointer:named:error" = internal constant { i8, i8* } { i8 21, i8* getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:error", i32 0, i32 0) }, align 4
 | 
				
			||||||
 | 
					@"reflect/types.type:pointer:named:reflect.rawType" = internal constant { i8*, i8, i8* } { i8* null, i8 21, i8* null }, align 4
 | 
				
			||||||
 | 
					@"reflect/methods.Implements(reflect.Type) bool" = internal constant i8 0, align 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 ptrtoint (i1 (i32)* @"error.$typeassert" to i32) }
 | 
					define i1 @main.isError(i8* %typ.typecode, i8* %typ.value, i8* %context) {
 | 
				
			||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 ptrtoint (i1 (i32)* @"error.$typeassert" to i32) }
 | 
					 | 
				
			||||||
@"reflect/methods.Error() string" = linkonce_odr constant i8 0
 | 
					 | 
				
			||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
 | 
					 | 
				
			||||||
@"reflect/methods.Align() int" = linkonce_odr constant i8 0
 | 
					 | 
				
			||||||
@"reflect/methods.Implements(reflect.Type) bool" = linkonce_odr constant i8 0
 | 
					 | 
				
			||||||
@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"reflect/methods.Align() int", i8* @"reflect/methods.Implements(reflect.Type) bool"]
 | 
					 | 
				
			||||||
@"reflect/types.type:named:reflect.rawType" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:uintptr", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([20 x %runtime.interfaceMethodInfo], [20 x %runtime.interfaceMethodInfo]* @"reflect.rawType$methodset", i32 0, i32 0), %runtime.typecodeID* null, i32 0 }
 | 
					 | 
				
			||||||
@"reflect.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer
 | 
					 | 
				
			||||||
@"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
define i1 @main.isError(i32 %typ.typecode, i8* %typ.value, i8* %context) {
 | 
					 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %0 = ptrtoint i8* %typ.value to i32
 | 
					  %0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i8* %typ.value)
 | 
				
			||||||
  %1 = call i1 @"error.$typeassert"(i32 %0)
 | 
					  ret i1 %0
 | 
				
			||||||
  ret i1 %1
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context) {
 | 
					define i1 @main.isUnknown(i8* %typ.typecode, i8* %typ.value, i8* %itf.typecode, i8* %itf.value, i8* %context) {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  %result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i32 %typ.typecode, i8* undef)
 | 
					  %result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i8* %itf.typecode, i8* %itf.value, i8* %typ.typecode, i8* undef)
 | 
				
			||||||
  ret i1 %result
 | 
					  ret i1 %result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @"reflect.Type.Implements$invoke"(i8*, i32, i8*, i32, i8*) #0
 | 
					declare i1 @"reflect.Type.Implements$invoke"(i8*, i8*, i8*, i8*, i8*) #0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare i1 @"error.$typeassert"(i32) #1
 | 
					declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i8*) #1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
attributes #0 = { "tinygo-invoke"="reflect/methods.Implements(reflect.Type) bool" "tinygo-methods"="reflect/methods.Align() int; reflect/methods.Implements(reflect.Type) bool" }
 | 
					attributes #0 = { "tinygo-invoke"="reflect/methods.Implements(reflect.Type) bool" "tinygo-methods"="reflect/methods.Align() int; reflect/methods.Implements(reflect.Type) bool" }
 | 
				
			||||||
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
 | 
					attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче