github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/interface.go (about)

     1  package compiler
     2  
     3  // This file transforms interface-related instructions (*ssa.MakeInterface,
     4  // *ssa.TypeAssert, calls on interface types) to an intermediate IR form, to be
     5  // lowered to the final form by the interface lowering pass. See
     6  // interface-lowering.go for more details.
     7  
     8  import (
     9  	"encoding/binary"
    10  	"fmt"
    11  	"go/token"
    12  	"go/types"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"golang.org/x/tools/go/ssa"
    17  	"tinygo.org/x/go-llvm"
    18  )
    19  
    20  // Type kinds for basic types.
    21  // They must match the constants for the Kind type in src/reflect/type.go.
    22  var basicTypes = [...]uint8{
    23  	types.Bool:          1,
    24  	types.Int:           2,
    25  	types.Int8:          3,
    26  	types.Int16:         4,
    27  	types.Int32:         5,
    28  	types.Int64:         6,
    29  	types.Uint:          7,
    30  	types.Uint8:         8,
    31  	types.Uint16:        9,
    32  	types.Uint32:        10,
    33  	types.Uint64:        11,
    34  	types.Uintptr:       12,
    35  	types.Float32:       13,
    36  	types.Float64:       14,
    37  	types.Complex64:     15,
    38  	types.Complex128:    16,
    39  	types.String:        17,
    40  	types.UnsafePointer: 18,
    41  }
    42  
    43  // These must also match the constants for the Kind type in src/reflect/type.go.
    44  const (
    45  	typeKindChan      = 19
    46  	typeKindInterface = 20
    47  	typeKindPointer   = 21
    48  	typeKindSlice     = 22
    49  	typeKindArray     = 23
    50  	typeKindSignature = 24
    51  	typeKindMap       = 25
    52  	typeKindStruct    = 26
    53  )
    54  
    55  // Flags stored in the first byte of the struct field byte array. Must be kept
    56  // up to date with src/reflect/type.go.
    57  const (
    58  	structFieldFlagAnonymous = 1 << iota
    59  	structFieldFlagHasTag
    60  	structFieldFlagIsExported
    61  	structFieldFlagIsEmbedded
    62  )
    63  
    64  type reflectChanDir int
    65  
    66  const (
    67  	refRecvDir reflectChanDir            = 1 << iota // <-chan
    68  	refSendDir                                       // chan<-
    69  	refBothDir = refRecvDir | refSendDir             // chan
    70  )
    71  
    72  // createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
    73  // It tries to put the type in the interface value, but if that's not possible,
    74  // it will do an allocation of the right size and put that in the interface
    75  // value field.
    76  //
    77  // An interface value is a {typecode, value} tuple named runtime._interface.
    78  func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
    79  	itfValue := b.emitPointerPack([]llvm.Value{val})
    80  	itfType := b.getTypeCode(typ)
    81  	itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
    82  	itf = b.CreateInsertValue(itf, itfType, 0, "")
    83  	itf = b.CreateInsertValue(itf, itfValue, 1, "")
    84  	return itf
    85  }
    86  
    87  // extractValueFromInterface extract the value from an interface value
    88  // (runtime._interface) under the assumption that it is of the type given in
    89  // llvmType. The behavior is undefied if the interface is nil or llvmType
    90  // doesn't match the underlying type of the interface.
    91  func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type) llvm.Value {
    92  	valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr")
    93  	return b.emitPointerUnpack(valuePtr, []llvm.Type{llvmType})[0]
    94  }
    95  
    96  func (c *compilerContext) pkgPathPtr(pkgpath string) llvm.Value {
    97  	pkgpathName := "reflect/types.type.pkgpath.empty"
    98  	if pkgpath != "" {
    99  		pkgpathName = "reflect/types.type.pkgpath:" + pkgpath
   100  	}
   101  
   102  	pkgpathGlobal := c.mod.NamedGlobal(pkgpathName)
   103  	if pkgpathGlobal.IsNil() {
   104  		pkgpathInitializer := c.ctx.ConstString(pkgpath+"\x00", false)
   105  		pkgpathGlobal = llvm.AddGlobal(c.mod, pkgpathInitializer.Type(), pkgpathName)
   106  		pkgpathGlobal.SetInitializer(pkgpathInitializer)
   107  		pkgpathGlobal.SetAlignment(1)
   108  		pkgpathGlobal.SetUnnamedAddr(true)
   109  		pkgpathGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
   110  		pkgpathGlobal.SetGlobalConstant(true)
   111  	}
   112  	pkgPathPtr := llvm.ConstGEP(pkgpathGlobal.GlobalValueType(), pkgpathGlobal, []llvm.Value{
   113  		llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   114  		llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   115  	})
   116  
   117  	return pkgPathPtr
   118  }
   119  
   120  // getTypeCode returns a reference to a type code.
   121  // A type code is a pointer to a constant global that describes the type.
   122  // This function returns a pointer to the 'kind' field (which might not be the
   123  // first field in the struct).
   124  func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
   125  	ms := c.program.MethodSets.MethodSet(typ)
   126  	hasMethodSet := ms.Len() != 0
   127  	if _, ok := typ.Underlying().(*types.Interface); ok {
   128  		hasMethodSet = false
   129  	}
   130  
   131  	var numMethods int
   132  	if hasMethodSet {
   133  		for i := 0; i < ms.Len(); i++ {
   134  			if ms.At(i).Obj().Exported() {
   135  				numMethods++
   136  			}
   137  		}
   138  	}
   139  
   140  	// Short-circuit all the global pointer logic here for pointers to pointers.
   141  	if typ, ok := typ.(*types.Pointer); ok {
   142  		if _, ok := typ.Elem().(*types.Pointer); ok {
   143  			// For a pointer to a pointer, we just increase the pointer by 1
   144  			ptr := c.getTypeCode(typ.Elem())
   145  			// if the type is already *****T or higher, we can't make it.
   146  			if typstr := typ.String(); strings.HasPrefix(typstr, "*****") {
   147  				c.addError(token.NoPos, fmt.Sprintf("too many levels of pointers for typecode: %s", typstr))
   148  			}
   149  			return llvm.ConstGEP(c.ctx.Int8Type(), ptr, []llvm.Value{
   150  				llvm.ConstInt(c.ctx.Int32Type(), 1, false),
   151  			})
   152  		}
   153  	}
   154  
   155  	typeCodeName, isLocal := getTypeCodeName(typ)
   156  	globalName := "reflect/types.type:" + typeCodeName
   157  	var global llvm.Value
   158  	if isLocal {
   159  		// This type is a named type inside a function, like this:
   160  		//
   161  		//     func foo() any {
   162  		//         type named int
   163  		//         return named(0)
   164  		//     }
   165  		if obj := c.interfaceTypes.At(typ); obj != nil {
   166  			global = obj.(llvm.Value)
   167  		}
   168  	} else {
   169  		// Regular type (named or otherwise).
   170  		global = c.mod.NamedGlobal(globalName)
   171  	}
   172  	if global.IsNil() {
   173  		var typeFields []llvm.Value
   174  		// Define the type fields. These must match the structs in
   175  		// src/reflect/type.go (ptrType, arrayType, etc). See the comment at the
   176  		// top of src/reflect/type.go for more information on the layout of these structs.
   177  		typeFieldTypes := []*types.Var{
   178  			types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
   179  		}
   180  		switch typ := typ.(type) {
   181  		case *types.Basic:
   182  			typeFieldTypes = append(typeFieldTypes,
   183  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   184  			)
   185  		case *types.Named:
   186  			name := typ.Obj().Name()
   187  			var pkgname string
   188  			if pkg := typ.Obj().Pkg(); pkg != nil {
   189  				pkgname = pkg.Name()
   190  			}
   191  			typeFieldTypes = append(typeFieldTypes,
   192  				types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
   193  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   194  				types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
   195  				types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
   196  				types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(pkgname)+1+len(name)+1))),
   197  			)
   198  		case *types.Chan:
   199  			typeFieldTypes = append(typeFieldTypes,
   200  				types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]), // reuse for select chan direction
   201  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   202  				types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
   203  			)
   204  		case *types.Slice:
   205  			typeFieldTypes = append(typeFieldTypes,
   206  				types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
   207  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   208  				types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
   209  			)
   210  		case *types.Pointer:
   211  			typeFieldTypes = append(typeFieldTypes,
   212  				types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
   213  				types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
   214  			)
   215  		case *types.Array:
   216  			typeFieldTypes = append(typeFieldTypes,
   217  				types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
   218  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   219  				types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
   220  				types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
   221  				types.NewVar(token.NoPos, nil, "sliceOf", types.Typ[types.UnsafePointer]),
   222  			)
   223  		case *types.Map:
   224  			typeFieldTypes = append(typeFieldTypes,
   225  				types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
   226  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   227  				types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
   228  				types.NewVar(token.NoPos, nil, "keyType", types.Typ[types.UnsafePointer]),
   229  			)
   230  		case *types.Struct:
   231  			typeFieldTypes = append(typeFieldTypes,
   232  				types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
   233  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   234  				types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
   235  				types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uint32]),
   236  				types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
   237  				types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
   238  			)
   239  		case *types.Interface:
   240  			typeFieldTypes = append(typeFieldTypes,
   241  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   242  			)
   243  			// TODO: methods
   244  		case *types.Signature:
   245  			typeFieldTypes = append(typeFieldTypes,
   246  				types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
   247  			)
   248  			// TODO: signature params and return values
   249  		}
   250  		if hasMethodSet {
   251  			// This method set is appended at the start of the struct. It is
   252  			// removed in the interface lowering pass.
   253  			// TODO: don't remove these and instead do what upstream Go is doing
   254  			// instead. See: https://research.swtch.com/interfaces. This can
   255  			// likely be optimized in LLVM using
   256  			// https://llvm.org/docs/TypeMetadata.html.
   257  			typeFieldTypes = append([]*types.Var{
   258  				types.NewVar(token.NoPos, nil, "methodSet", types.Typ[types.UnsafePointer]),
   259  			}, typeFieldTypes...)
   260  		}
   261  		globalType := types.NewStruct(typeFieldTypes, nil)
   262  		global = llvm.AddGlobal(c.mod, c.getLLVMType(globalType), globalName)
   263  		if isLocal {
   264  			c.interfaceTypes.Set(typ, global)
   265  		}
   266  		metabyte := getTypeKind(typ)
   267  
   268  		// Precompute these so we don't have to calculate them at runtime.
   269  		if types.Comparable(typ) {
   270  			metabyte |= 1 << 6
   271  		}
   272  
   273  		if hashmapIsBinaryKey(typ) {
   274  			metabyte |= 1 << 7
   275  		}
   276  
   277  		switch typ := typ.(type) {
   278  		case *types.Basic:
   279  			typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
   280  		case *types.Named:
   281  			name := typ.Obj().Name()
   282  			var pkgpath string
   283  			var pkgname string
   284  			if pkg := typ.Obj().Pkg(); pkg != nil {
   285  				pkgpath = pkg.Path()
   286  				pkgname = pkg.Name()
   287  			}
   288  			pkgPathPtr := c.pkgPathPtr(pkgpath)
   289  			typeFields = []llvm.Value{
   290  				llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
   291  				c.getTypeCode(types.NewPointer(typ)),                        // ptrTo
   292  				c.getTypeCode(typ.Underlying()),                             // underlying
   293  				pkgPathPtr,                                                  // pkgpath pointer
   294  				c.ctx.ConstString(pkgname+"."+name+"\x00", false),           // name
   295  			}
   296  			metabyte |= 1 << 5 // "named" flag
   297  		case *types.Chan:
   298  			var dir reflectChanDir
   299  			switch typ.Dir() {
   300  			case types.SendRecv:
   301  				dir = refBothDir
   302  			case types.RecvOnly:
   303  				dir = refRecvDir
   304  			case types.SendOnly:
   305  				dir = refSendDir
   306  			}
   307  
   308  			typeFields = []llvm.Value{
   309  				llvm.ConstInt(c.ctx.Int16Type(), uint64(dir), false), // actually channel direction
   310  				c.getTypeCode(types.NewPointer(typ)),                 // ptrTo
   311  				c.getTypeCode(typ.Elem()),                            // elementType
   312  			}
   313  		case *types.Slice:
   314  			typeFields = []llvm.Value{
   315  				llvm.ConstInt(c.ctx.Int16Type(), 0, false), // numMethods
   316  				c.getTypeCode(types.NewPointer(typ)),       // ptrTo
   317  				c.getTypeCode(typ.Elem()),                  // elementType
   318  			}
   319  		case *types.Pointer:
   320  			typeFields = []llvm.Value{
   321  				llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
   322  				c.getTypeCode(typ.Elem()),
   323  			}
   324  		case *types.Array:
   325  			typeFields = []llvm.Value{
   326  				llvm.ConstInt(c.ctx.Int16Type(), 0, false),             // numMethods
   327  				c.getTypeCode(types.NewPointer(typ)),                   // ptrTo
   328  				c.getTypeCode(typ.Elem()),                              // elementType
   329  				llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
   330  				c.getTypeCode(types.NewSlice(typ.Elem())),              // slicePtr
   331  			}
   332  		case *types.Map:
   333  			typeFields = []llvm.Value{
   334  				llvm.ConstInt(c.ctx.Int16Type(), 0, false), // numMethods
   335  				c.getTypeCode(types.NewPointer(typ)),       // ptrTo
   336  				c.getTypeCode(typ.Elem()),                  // elem
   337  				c.getTypeCode(typ.Key()),                   // key
   338  			}
   339  		case *types.Struct:
   340  			var pkgpath string
   341  			if typ.NumFields() > 0 {
   342  				if pkg := typ.Field(0).Pkg(); pkg != nil {
   343  					pkgpath = pkg.Path()
   344  				}
   345  			}
   346  			pkgPathPtr := c.pkgPathPtr(pkgpath)
   347  
   348  			llvmStructType := c.getLLVMType(typ)
   349  			size := c.targetData.TypeStoreSize(llvmStructType)
   350  			typeFields = []llvm.Value{
   351  				llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
   352  				c.getTypeCode(types.NewPointer(typ)),                        // ptrTo
   353  				pkgPathPtr,
   354  				llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false),            // size
   355  				llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
   356  			}
   357  			structFieldType := c.getLLVMRuntimeType("structField")
   358  
   359  			var fields []llvm.Value
   360  			for i := 0; i < typ.NumFields(); i++ {
   361  				field := typ.Field(i)
   362  				offset := c.targetData.ElementOffset(llvmStructType, i)
   363  				var flags uint8
   364  				if field.Anonymous() {
   365  					flags |= structFieldFlagAnonymous
   366  				}
   367  				if typ.Tag(i) != "" {
   368  					flags |= structFieldFlagHasTag
   369  				}
   370  				if token.IsExported(field.Name()) {
   371  					flags |= structFieldFlagIsExported
   372  				}
   373  				if field.Embedded() {
   374  					flags |= structFieldFlagIsEmbedded
   375  				}
   376  
   377  				var offsBytes [binary.MaxVarintLen32]byte
   378  				offLen := binary.PutUvarint(offsBytes[:], offset)
   379  
   380  				data := string(flags) + string(offsBytes[:offLen]) + field.Name() + "\x00"
   381  				if typ.Tag(i) != "" {
   382  					if len(typ.Tag(i)) > 0xff {
   383  						c.addError(field.Pos(), fmt.Sprintf("struct tag is %d bytes which is too long, max is 255", len(typ.Tag(i))))
   384  					}
   385  					data += string([]byte{byte(len(typ.Tag(i)))}) + typ.Tag(i)
   386  				}
   387  				dataInitializer := c.ctx.ConstString(data, false)
   388  				dataGlobal := llvm.AddGlobal(c.mod, dataInitializer.Type(), globalName+"."+field.Name())
   389  				dataGlobal.SetInitializer(dataInitializer)
   390  				dataGlobal.SetAlignment(1)
   391  				dataGlobal.SetUnnamedAddr(true)
   392  				dataGlobal.SetLinkage(llvm.InternalLinkage)
   393  				dataGlobal.SetGlobalConstant(true)
   394  				fieldType := c.getTypeCode(field.Type())
   395  				fields = append(fields, llvm.ConstNamedStruct(structFieldType, []llvm.Value{
   396  					fieldType,
   397  					llvm.ConstGEP(dataGlobal.GlobalValueType(), dataGlobal, []llvm.Value{
   398  						llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   399  						llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   400  					}),
   401  				}))
   402  			}
   403  			typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
   404  		case *types.Interface:
   405  			typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
   406  			// TODO: methods
   407  		case *types.Signature:
   408  			typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
   409  			// TODO: params, return values, etc
   410  		}
   411  		// Prepend metadata byte.
   412  		typeFields = append([]llvm.Value{
   413  			llvm.ConstInt(c.ctx.Int8Type(), uint64(metabyte), false),
   414  		}, typeFields...)
   415  		if hasMethodSet {
   416  			typeFields = append([]llvm.Value{
   417  				c.getTypeMethodSet(typ),
   418  			}, typeFields...)
   419  		}
   420  		alignment := c.targetData.TypeAllocSize(c.dataPtrType)
   421  		if alignment < 4 {
   422  			alignment = 4
   423  		}
   424  		globalValue := c.ctx.ConstStruct(typeFields, false)
   425  		global.SetInitializer(globalValue)
   426  		if isLocal {
   427  			global.SetLinkage(llvm.InternalLinkage)
   428  		} else {
   429  			global.SetLinkage(llvm.LinkOnceODRLinkage)
   430  		}
   431  		global.SetGlobalConstant(true)
   432  		global.SetAlignment(int(alignment))
   433  		if c.Debug {
   434  			file := c.getDIFile("<Go type>")
   435  			diglobal := c.dibuilder.CreateGlobalVariableExpression(file, llvm.DIGlobalVariableExpression{
   436  				Name:        "type " + typ.String(),
   437  				File:        file,
   438  				Line:        1,
   439  				Type:        c.getDIType(globalType),
   440  				LocalToUnit: false,
   441  				Expr:        c.dibuilder.CreateExpression(nil),
   442  				AlignInBits: uint32(alignment * 8),
   443  			})
   444  			global.AddMetadata(0, diglobal)
   445  		}
   446  	}
   447  	offset := uint64(0)
   448  	if hasMethodSet {
   449  		// The pointer to the method set is always the first element of the
   450  		// global (if there is a method set). However, the pointer we return
   451  		// should point to the 'kind' field not the method set.
   452  		offset = 1
   453  	}
   454  	return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
   455  		llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   456  		llvm.ConstInt(c.ctx.Int32Type(), offset, false),
   457  	})
   458  }
   459  
   460  // getTypeKind returns the type kind for the given type, as defined by
   461  // reflect.Kind.
   462  func getTypeKind(t types.Type) uint8 {
   463  	switch t := t.Underlying().(type) {
   464  	case *types.Basic:
   465  		return basicTypes[t.Kind()]
   466  	case *types.Chan:
   467  		return typeKindChan
   468  	case *types.Interface:
   469  		return typeKindInterface
   470  	case *types.Pointer:
   471  		return typeKindPointer
   472  	case *types.Slice:
   473  		return typeKindSlice
   474  	case *types.Array:
   475  		return typeKindArray
   476  	case *types.Signature:
   477  		return typeKindSignature
   478  	case *types.Map:
   479  		return typeKindMap
   480  	case *types.Struct:
   481  		return typeKindStruct
   482  	default:
   483  		panic("unknown type")
   484  	}
   485  }
   486  
   487  var basicTypeNames = [...]string{
   488  	types.Bool:          "bool",
   489  	types.Int:           "int",
   490  	types.Int8:          "int8",
   491  	types.Int16:         "int16",
   492  	types.Int32:         "int32",
   493  	types.Int64:         "int64",
   494  	types.Uint:          "uint",
   495  	types.Uint8:         "uint8",
   496  	types.Uint16:        "uint16",
   497  	types.Uint32:        "uint32",
   498  	types.Uint64:        "uint64",
   499  	types.Uintptr:       "uintptr",
   500  	types.Float32:       "float32",
   501  	types.Float64:       "float64",
   502  	types.Complex64:     "complex64",
   503  	types.Complex128:    "complex128",
   504  	types.String:        "string",
   505  	types.UnsafePointer: "unsafe.Pointer",
   506  }
   507  
   508  // getTypeCodeName returns a name for this type that can be used in the
   509  // interface lowering pass to assign type codes as expected by the reflect
   510  // package. See getTypeCodeNum.
   511  func getTypeCodeName(t types.Type) (string, bool) {
   512  	switch t := t.(type) {
   513  	case *types.Named:
   514  		// Note: check for `t.Obj().Pkg() != nil` for Go 1.18 only.
   515  		if t.Obj().Pkg() != nil && t.Obj().Parent() != t.Obj().Pkg().Scope() {
   516  			return "named:" + t.String() + "$local", true
   517  		}
   518  		return "named:" + t.String(), false
   519  	case *types.Array:
   520  		s, isLocal := getTypeCodeName(t.Elem())
   521  		return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + s, isLocal
   522  	case *types.Basic:
   523  		return "basic:" + basicTypeNames[t.Kind()], false
   524  	case *types.Chan:
   525  		s, isLocal := getTypeCodeName(t.Elem())
   526  		var dir string
   527  		switch t.Dir() {
   528  		case types.SendOnly:
   529  			dir = "s:"
   530  		case types.RecvOnly:
   531  			dir = "r:"
   532  		case types.SendRecv:
   533  			dir = "sr:"
   534  		}
   535  
   536  		return "chan:" + dir + s, isLocal
   537  	case *types.Interface:
   538  		isLocal := false
   539  		methods := make([]string, t.NumMethods())
   540  		for i := 0; i < t.NumMethods(); i++ {
   541  			name := t.Method(i).Name()
   542  			if !token.IsExported(name) {
   543  				name = t.Method(i).Pkg().Path() + "." + name
   544  			}
   545  			s, local := getTypeCodeName(t.Method(i).Type())
   546  			if local {
   547  				isLocal = true
   548  			}
   549  			methods[i] = name + ":" + s
   550  		}
   551  		return "interface:" + "{" + strings.Join(methods, ",") + "}", isLocal
   552  	case *types.Map:
   553  		keyType, keyLocal := getTypeCodeName(t.Key())
   554  		elemType, elemLocal := getTypeCodeName(t.Elem())
   555  		return "map:" + "{" + keyType + "," + elemType + "}", keyLocal || elemLocal
   556  	case *types.Pointer:
   557  		s, isLocal := getTypeCodeName(t.Elem())
   558  		return "pointer:" + s, isLocal
   559  	case *types.Signature:
   560  		isLocal := false
   561  		params := make([]string, t.Params().Len())
   562  		for i := 0; i < t.Params().Len(); i++ {
   563  			s, local := getTypeCodeName(t.Params().At(i).Type())
   564  			if local {
   565  				isLocal = true
   566  			}
   567  			params[i] = s
   568  		}
   569  		results := make([]string, t.Results().Len())
   570  		for i := 0; i < t.Results().Len(); i++ {
   571  			s, local := getTypeCodeName(t.Results().At(i).Type())
   572  			if local {
   573  				isLocal = true
   574  			}
   575  			results[i] = s
   576  		}
   577  		return "func:" + "{" + strings.Join(params, ",") + "}{" + strings.Join(results, ",") + "}", isLocal
   578  	case *types.Slice:
   579  		s, isLocal := getTypeCodeName(t.Elem())
   580  		return "slice:" + s, isLocal
   581  	case *types.Struct:
   582  		elems := make([]string, t.NumFields())
   583  		isLocal := false
   584  		for i := 0; i < t.NumFields(); i++ {
   585  			embedded := ""
   586  			if t.Field(i).Embedded() {
   587  				embedded = "#"
   588  			}
   589  			s, local := getTypeCodeName(t.Field(i).Type())
   590  			if local {
   591  				isLocal = true
   592  			}
   593  			elems[i] = embedded + t.Field(i).Name() + ":" + s
   594  			if t.Tag(i) != "" {
   595  				elems[i] += "`" + t.Tag(i) + "`"
   596  			}
   597  		}
   598  		return "struct:" + "{" + strings.Join(elems, ",") + "}", isLocal
   599  	default:
   600  		panic("unknown type: " + t.String())
   601  	}
   602  }
   603  
   604  // getTypeMethodSet returns a reference (GEP) to a global method set. This
   605  // method set should be unreferenced after the interface lowering pass.
   606  func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
   607  	globalName := typ.String() + "$methodset"
   608  	global := c.mod.NamedGlobal(globalName)
   609  	if global.IsNil() {
   610  		ms := c.program.MethodSets.MethodSet(typ)
   611  
   612  		// Create method set.
   613  		var signatures, wrappers []llvm.Value
   614  		for i := 0; i < ms.Len(); i++ {
   615  			method := ms.At(i)
   616  			signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
   617  			signatures = append(signatures, signatureGlobal)
   618  			fn := c.program.MethodValue(method)
   619  			llvmFnType, llvmFn := c.getFunction(fn)
   620  			if llvmFn.IsNil() {
   621  				// compiler error, so panic
   622  				panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
   623  			}
   624  			wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn)
   625  			wrappers = append(wrappers, wrapper)
   626  		}
   627  
   628  		// Construct global value.
   629  		globalValue := c.ctx.ConstStruct([]llvm.Value{
   630  			llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
   631  			llvm.ConstArray(c.dataPtrType, signatures),
   632  			c.ctx.ConstStruct(wrappers, false),
   633  		}, false)
   634  		global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
   635  		global.SetInitializer(globalValue)
   636  		global.SetGlobalConstant(true)
   637  		global.SetUnnamedAddr(true)
   638  		global.SetLinkage(llvm.LinkOnceODRLinkage)
   639  	}
   640  	return global
   641  }
   642  
   643  // getMethodSignatureName returns a unique name (that can be used as the name of
   644  // a global) for the given method.
   645  func (c *compilerContext) getMethodSignatureName(method *types.Func) string {
   646  	signature := methodSignature(method)
   647  	var globalName string
   648  	if token.IsExported(method.Name()) {
   649  		globalName = "reflect/methods." + signature
   650  	} else {
   651  		globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature
   652  	}
   653  	return globalName
   654  }
   655  
   656  // getMethodSignature returns a global variable which is a reference to an
   657  // external *i8 indicating the indicating the signature of this method. It is
   658  // used during the interface lowering pass.
   659  func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
   660  	globalName := c.getMethodSignatureName(method)
   661  	signatureGlobal := c.mod.NamedGlobal(globalName)
   662  	if signatureGlobal.IsNil() {
   663  		// TODO: put something useful in these globals, such as the method
   664  		// signature. Useful to one day implement reflect.Value.Method(n).
   665  		signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
   666  		signatureGlobal.SetInitializer(llvm.ConstInt(c.ctx.Int8Type(), 0, false))
   667  		signatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
   668  		signatureGlobal.SetGlobalConstant(true)
   669  		signatureGlobal.SetAlignment(1)
   670  	}
   671  	return signatureGlobal
   672  }
   673  
   674  // createTypeAssert will emit the code for a typeassert, used in if statements
   675  // and in type switches (Go SSA does not have type switches, only if/else
   676  // chains). Note that even though the Go SSA does not contain type switches,
   677  // LLVM will recognize the pattern and make it a real switch in many cases.
   678  //
   679  // Type asserts on concrete types are trivial: just compare type numbers. Type
   680  // asserts on interfaces are more difficult, see the comments in the function.
   681  func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
   682  	itf := b.getValue(expr.X, getPos(expr))
   683  	assertedType := b.getLLVMType(expr.AssertedType)
   684  
   685  	actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
   686  	commaOk := llvm.Value{}
   687  
   688  	if intf, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
   689  		if intf.Empty() {
   690  			// intf is the empty interface => no methods
   691  			// This type assertion always succeeds, so we can just set commaOk to true.
   692  			commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true)
   693  		} else {
   694  			// Type assert on interface type with methods.
   695  			// This is a call to an interface type assert function.
   696  			// The interface lowering pass will define this function by filling it
   697  			// with a type switch over all concrete types that implement this
   698  			// interface, and returning whether it's one of the matched types.
   699  			// This is very different from how interface asserts are implemented in
   700  			// the main Go compiler, where the runtime checks whether the type
   701  			// implements each method of the interface. See:
   702  			// https://research.swtch.com/interfaces
   703  			fn := b.getInterfaceImplementsFunc(expr.AssertedType)
   704  			commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
   705  		}
   706  	} else {
   707  		name, _ := getTypeCodeName(expr.AssertedType)
   708  		globalName := "reflect/types.typeid:" + name
   709  		assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
   710  		if assertedTypeCodeGlobal.IsNil() {
   711  			// Create a new typecode global.
   712  			assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
   713  			assertedTypeCodeGlobal.SetGlobalConstant(true)
   714  		}
   715  		// Type assert on concrete type.
   716  		// Call runtime.typeAssert, which will be lowered to a simple icmp or
   717  		// const false in the interface lowering pass.
   718  		commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
   719  	}
   720  
   721  	// Add 2 new basic blocks (that should get optimized away): one for the
   722  	// 'ok' case and one for all instructions following this type assert.
   723  	// This is necessary because we need to insert the casted value or the
   724  	// nil value based on whether the assert was successful. Casting before
   725  	// this check tells LLVM that it can use this value and may
   726  	// speculatively dereference pointers before the check. This can lead to
   727  	// a miscompilation resulting in a segfault at runtime.
   728  	// Additionally, this is even required by the Go spec: a failed
   729  	// typeassert should return a zero value, not an incorrectly casted
   730  	// value.
   731  
   732  	prevBlock := b.GetInsertBlock()
   733  	okBlock := b.insertBasicBlock("typeassert.ok")
   734  	nextBlock := b.insertBasicBlock("typeassert.next")
   735  	b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
   736  	b.CreateCondBr(commaOk, okBlock, nextBlock)
   737  
   738  	// Retrieve the value from the interface if the type assert was
   739  	// successful.
   740  	b.SetInsertPointAtEnd(okBlock)
   741  	var valueOk llvm.Value
   742  	if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
   743  		// Type assert on interface type. Easy: just return the same
   744  		// interface value.
   745  		valueOk = itf
   746  	} else {
   747  		// Type assert on concrete type. Extract the underlying type from
   748  		// the interface (but only after checking it matches).
   749  		valueOk = b.extractValueFromInterface(itf, assertedType)
   750  	}
   751  	b.CreateBr(nextBlock)
   752  
   753  	// Continue after the if statement.
   754  	b.SetInsertPointAtEnd(nextBlock)
   755  	phi := b.CreatePHI(assertedType, "typeassert.value")
   756  	phi.AddIncoming([]llvm.Value{llvm.ConstNull(assertedType), valueOk}, []llvm.BasicBlock{prevBlock, okBlock})
   757  
   758  	if expr.CommaOk {
   759  		tuple := b.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(b.ctx.Int1Type())}, false) // create empty tuple
   760  		tuple = b.CreateInsertValue(tuple, phi, 0, "")                                                          // insert value
   761  		tuple = b.CreateInsertValue(tuple, commaOk, 1, "")                                                      // insert 'comma ok' boolean
   762  		return tuple
   763  	} else {
   764  		// This is kind of dirty as the branch above becomes mostly useless,
   765  		// but hopefully this gets optimized away.
   766  		b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "")
   767  		return phi
   768  	}
   769  }
   770  
   771  // getMethodsString returns a string to be used in the "tinygo-methods" string
   772  // attribute for interface functions.
   773  func (c *compilerContext) getMethodsString(itf *types.Interface) string {
   774  	methods := make([]string, itf.NumMethods())
   775  	for i := range methods {
   776  		methods[i] = c.getMethodSignatureName(itf.Method(i))
   777  	}
   778  	return strings.Join(methods, "; ")
   779  }
   780  
   781  // getInterfaceImplementsFunc returns a declared function that works as a type
   782  // switch. The interface lowering pass will define this function.
   783  func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
   784  	s, _ := getTypeCodeName(assertedType.Underlying())
   785  	fnName := s + ".$typeassert"
   786  	llvmFn := c.mod.NamedFunction(fnName)
   787  	if llvmFn.IsNil() {
   788  		llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false)
   789  		llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
   790  		c.addStandardDeclaredAttributes(llvmFn)
   791  		methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
   792  		llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
   793  	}
   794  	return llvmFn
   795  }
   796  
   797  // getInvokeFunction returns the thunk to call the given interface method. The
   798  // thunk is declared, not defined: it will be defined by the interface lowering
   799  // pass.
   800  func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
   801  	s, _ := getTypeCodeName(instr.Value.Type().Underlying())
   802  	fnName := s + "." + instr.Method.Name() + "$invoke"
   803  	llvmFn := c.mod.NamedFunction(fnName)
   804  	if llvmFn.IsNil() {
   805  		sig := instr.Method.Type().(*types.Signature)
   806  		var paramTuple []*types.Var
   807  		for i := 0; i < sig.Params().Len(); i++ {
   808  			paramTuple = append(paramTuple, sig.Params().At(i))
   809  		}
   810  		paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
   811  		llvmFnType := c.getLLVMFunctionType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
   812  		llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
   813  		c.addStandardDeclaredAttributes(llvmFn)
   814  		llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
   815  		methods := c.getMethodsString(instr.Value.Type().Underlying().(*types.Interface))
   816  		llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
   817  	}
   818  	return llvmFn
   819  }
   820  
   821  // getInterfaceInvokeWrapper returns a wrapper for the given method so it can be
   822  // invoked from an interface. The wrapper takes in a pointer to the underlying
   823  // value, dereferences or unpacks it if necessary, and calls the real method.
   824  // If the method to wrap has a pointer receiver, no wrapping is necessary and
   825  // the function is returned directly.
   826  func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType llvm.Type, llvmFn llvm.Value) llvm.Value {
   827  	wrapperName := llvmFn.Name() + "$invoke"
   828  	wrapper := c.mod.NamedFunction(wrapperName)
   829  	if !wrapper.IsNil() {
   830  		// Wrapper already created. Return it directly.
   831  		return wrapper
   832  	}
   833  
   834  	// Get the expanded receiver type.
   835  	receiverType := c.getLLVMType(fn.Signature.Recv().Type())
   836  	var expandedReceiverType []llvm.Type
   837  	for _, info := range c.expandFormalParamType(receiverType, "", nil) {
   838  		expandedReceiverType = append(expandedReceiverType, info.llvmType)
   839  	}
   840  
   841  	// Does this method even need any wrapping?
   842  	if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {
   843  		// Nothing to wrap.
   844  		// Casting a function signature to a different signature and calling it
   845  		// with a receiver pointer bitcasted to *i8 (as done in calls on an
   846  		// interface) is hopefully a safe (defined) operation.
   847  		return llvmFn
   848  	}
   849  
   850  	// create wrapper function
   851  	paramTypes := append([]llvm.Type{c.dataPtrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
   852  	wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
   853  	wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
   854  	c.addStandardAttributes(wrapper)
   855  
   856  	wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
   857  	wrapper.SetUnnamedAddr(true)
   858  
   859  	// Create a new builder just to create this wrapper.
   860  	b := builder{
   861  		compilerContext: c,
   862  		Builder:         c.ctx.NewBuilder(),
   863  	}
   864  	defer b.Builder.Dispose()
   865  
   866  	// add debug info if needed
   867  	if c.Debug {
   868  		pos := c.program.Fset.Position(fn.Pos())
   869  		difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
   870  		b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
   871  	}
   872  
   873  	// set up IR builder
   874  	block := b.ctx.AddBasicBlock(wrapper, "entry")
   875  	b.SetInsertPointAtEnd(block)
   876  
   877  	receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
   878  	params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
   879  	if llvmFnType.ReturnType().TypeKind() == llvm.VoidTypeKind {
   880  		b.CreateCall(llvmFnType, llvmFn, params, "")
   881  		b.CreateRetVoid()
   882  	} else {
   883  		ret := b.CreateCall(llvmFnType, llvmFn, params, "ret")
   884  		b.CreateRet(ret)
   885  	}
   886  
   887  	return wrapper
   888  }
   889  
   890  // methodSignature creates a readable version of a method signature (including
   891  // the function name, excluding the receiver name). This string is used
   892  // internally to match interfaces and to call the correct method on an
   893  // interface. Examples:
   894  //
   895  //	String() string
   896  //	Read([]byte) (int, error)
   897  func methodSignature(method *types.Func) string {
   898  	return method.Name() + signature(method.Type().(*types.Signature))
   899  }
   900  
   901  // Make a readable version of a function (pointer) signature.
   902  // Examples:
   903  //
   904  //	() string
   905  //	(string, int) (int, error)
   906  func signature(sig *types.Signature) string {
   907  	s := ""
   908  	if sig.Params().Len() == 0 {
   909  		s += "()"
   910  	} else {
   911  		s += "("
   912  		for i := 0; i < sig.Params().Len(); i++ {
   913  			if i > 0 {
   914  				s += ", "
   915  			}
   916  			s += typestring(sig.Params().At(i).Type())
   917  		}
   918  		s += ")"
   919  	}
   920  	if sig.Results().Len() == 0 {
   921  		// keep as-is
   922  	} else if sig.Results().Len() == 1 {
   923  		s += " " + typestring(sig.Results().At(0).Type())
   924  	} else {
   925  		s += " ("
   926  		for i := 0; i < sig.Results().Len(); i++ {
   927  			if i > 0 {
   928  				s += ", "
   929  			}
   930  			s += typestring(sig.Results().At(i).Type())
   931  		}
   932  		s += ")"
   933  	}
   934  	return s
   935  }
   936  
   937  // typestring returns a stable (human-readable) type string for the given type
   938  // that can be used for interface equality checks. It is almost (but not
   939  // exactly) the same as calling t.String(). The main difference is some
   940  // normalization around `byte` vs `uint8` for example.
   941  func typestring(t types.Type) string {
   942  	// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
   943  	switch t := t.(type) {
   944  	case *types.Array:
   945  		return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
   946  	case *types.Basic:
   947  		return basicTypeNames[t.Kind()]
   948  	case *types.Chan:
   949  		switch t.Dir() {
   950  		case types.SendRecv:
   951  			return "chan (" + typestring(t.Elem()) + ")"
   952  		case types.SendOnly:
   953  			return "chan<- (" + typestring(t.Elem()) + ")"
   954  		case types.RecvOnly:
   955  			return "<-chan (" + typestring(t.Elem()) + ")"
   956  		default:
   957  			panic("unknown channel direction")
   958  		}
   959  	case *types.Interface:
   960  		methods := make([]string, t.NumMethods())
   961  		for i := range methods {
   962  			method := t.Method(i)
   963  			methods[i] = method.Name() + signature(method.Type().(*types.Signature))
   964  		}
   965  		return "interface{" + strings.Join(methods, ";") + "}"
   966  	case *types.Map:
   967  		return "map[" + typestring(t.Key()) + "]" + typestring(t.Elem())
   968  	case *types.Named:
   969  		return t.String()
   970  	case *types.Pointer:
   971  		return "*" + typestring(t.Elem())
   972  	case *types.Signature:
   973  		return "func" + signature(t)
   974  	case *types.Slice:
   975  		return "[]" + typestring(t.Elem())
   976  	case *types.Struct:
   977  		fields := make([]string, t.NumFields())
   978  		for i := range fields {
   979  			field := t.Field(i)
   980  			fields[i] = field.Name() + " " + typestring(field.Type())
   981  			if tag := t.Tag(i); tag != "" {
   982  				fields[i] += " " + strconv.Quote(tag)
   983  			}
   984  		}
   985  		return "struct{" + strings.Join(fields, ";") + "}"
   986  	default:
   987  		panic("unknown type: " + t.String())
   988  	}
   989  }