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 }