ir_descriptors.mx raw

   1  package main
   2  
   3  import (
   4  	"runtime"
   5  	"unsafe"
   6  	. "git.smesh.lol/moxie/pkg/types"
   7  	"git.smesh.lol/moxie/pkg/mxutil"
   8  	"git.smesh.lol/moxie/pkg/token"
   9  )
  10  
  11  // Runtime Kind values - must match src/runtime/typekind.mx exactly.
  12  const (
  13  	rtKindBool          = 1
  14  	rtKindInt           = 2
  15  	rtKindInt8          = 3
  16  	rtKindInt16         = 4
  17  	rtKindInt32         = 5
  18  	rtKindInt64         = 6
  19  	rtKindUint          = 7
  20  	rtKindUint8         = 8
  21  	rtKindUint16        = 9
  22  	rtKindUint32        = 10
  23  	rtKindUint64        = 11
  24  	rtKindUintptr       = 12
  25  	rtKindFloat32       = 13
  26  	rtKindFloat64       = 14
  27  	rtKindComplex64     = 15
  28  	rtKindComplex128    = 16
  29  	rtKindBytes         = 17
  30  	rtKindUnsafePointer = 18
  31  	rtKindChan          = 19
  32  	rtKindInterface     = 20
  33  	rtKindPointer       = 21
  34  	rtKindSlice         = 22
  35  	rtKindArray         = 23
  36  	rtKindFunc          = 24
  37  	rtKindMap           = 25
  38  	rtKindStruct        = 26
  39  )
  40  
  41  const (
  42  	rtFlagNamed      = 32
  43  	rtFlagComparable = 64
  44  	rtFlagIsBinary   = 128
  45  )
  46  
  47  const rtKindInvalid = 0
  48  
  49  // basicTypeDescName maps a Basic to its descriptor name tail, mirroring the
  50  // legacy compiler's basicTypeNames table.
  51  func basicTypeDescName(k BasicKind) (s string) {
  52  	switch k {
  53  	case Bool, UntypedBool:
  54  		return "bool"
  55  	case Int8:
  56  		return "int8"
  57  	case Int16:
  58  		return "int16"
  59  	case Int32, UntypedInt, UntypedRune:
  60  		return "int32"
  61  	case Int64:
  62  		return "int64"
  63  	case Uint8:
  64  		return "uint8"
  65  	case Uint16:
  66  		return "uint16"
  67  	case Uint32:
  68  		return "uint32"
  69  	case Uint64:
  70  		return "uint64"
  71  	case Uintptr:
  72  		return "uintptr"
  73  	case Float32:
  74  		return "float32"
  75  	case Float64, UntypedFloat:
  76  		return "float64"
  77  	case TCString, UntypedString:
  78  		return "bytes"
  79  	case UnsafePointer:
  80  		return "unsafe.Pointer"
  81  	}
  82  	return "invalid"
  83  }
  84  
  85  // typeDescName returns the descriptor name tail (without the
  86  // "reflect/types.type:" prefix), matching the legacy getTypeCodeName.
  87  func (e *irEmitter) typeDescName(t Type) (s string) {
  88  	return e.typeDescNameInner(t)
  89  }
  90  
  91  func (e *irEmitter) typeDescNameInner(t Type) (s string) {
  92  	if t == nil {
  93  		return "basic:invalid"
  94  	}
  95  	if b, ok := t.(*Basic); ok {
  96  		if b == nil {
  97  			mxutil.WriteStr(2, "DBG DESC typed-nil Basic\n")
  98  			return "basic:invalid"
  99  		}
 100  		return "basic:" | basicTypeDescName(b.Kind)
 101  	}
 102  	if named, ok := t.(*Named); ok {
 103  		if named == nil {
 104  			mxutil.WriteStr(2, "DBG DESC typed-nil Named\n")
 105  			return "named:unknown"
 106  		}
 107  		if named.Obj == nil {
 108  			return "named:unknown"
 109  		}
 110  		if named.Obj.Pkg == nil {
 111  			return "named:" | named.Obj.Name
 112  		}
 113  		pkgPath := normalizePkgPath(named.Obj.Pkg.Path)
 114  		return "named:" | pkgPath | "." | named.Obj.Name
 115  	}
 116  	if p, ok := t.(*Pointer); ok {
 117  		if p == nil {
 118  			mxutil.WriteStr(2, "DBG DESC typed-nil Pointer\n")
 119  			return "pointer:basic:invalid"
 120  		}
 121  		return "pointer:" | e.typeDescName(p.Base)
 122  	}
 123  	if sl, ok := t.(*Slice); ok {
 124  		if sl == nil {
 125  			mxutil.WriteStr(2, "DBG DESC typed-nil Slice\n")
 126  			return "slice:basic:invalid"
 127  		}
 128  		if b, ok2 := SafeUnderlying(sl.Elem).(*Basic); ok2 && b != nil && b.Kind == Uint8 {
 129  			return "basic:bytes"
 130  		}
 131  		return "slice:" | e.typeDescName(sl.Elem)
 132  	}
 133  	if arr, ok := t.(*Array); ok {
 134  		if arr == nil {
 135  			mxutil.WriteStr(2, "DBG DESC typed-nil Array\n")
 136  			return "array:0:basic:invalid"
 137  		}
 138  		al := arr.Len
 139  		if al < 0 {
 140  			al = 0
 141  		}
 142  		return "array:" | token.Itoa64(al) | ":" | e.typeDescName(arr.Elem)
 143  	}
 144  	if m, ok := t.(*TCMap); ok {
 145  		if m == nil {
 146  			mxutil.WriteStr(2, "DBG DESC typed-nil TCMap\n")
 147  			return "map:{basic:invalid,basic:invalid}"
 148  		}
 149  		return "map:{" | e.typeDescName(m.Key) | "," | e.typeDescName(m.Elem) | "}"
 150  	}
 151  	if ch, ok := t.(*TCChan); ok {
 152  		if ch == nil {
 153  			mxutil.WriteStr(2, "DBG DESC typed-nil TCChan\n")
 154  			return "chan:sr:basic:invalid"
 155  		}
 156  		dir := "sr:"
 157  		if ch.Dir == TCSendOnly {
 158  			dir = "s:"
 159  		} else if ch.Dir == TCRecvOnly {
 160  			dir = "r:"
 161  		}
 162  		return "chan:" | dir | e.typeDescName(ch.Elem)
 163  	}
 164  	if iface, ok := t.(*TCInterface); ok {
 165  		if iface == nil {
 166  			mxutil.WriteStr(2, "DBG DESC typed-nil TCInterface\n")
 167  			return "interface:{}"
 168  		}
 169  		out := "interface:{"
 170  		for i := int32(0); i < iface.NumMethods(); i++ {
 171  			if i > 0 {
 172  				out = out | ","
 173  			}
 174  			m := iface.Method(i)
 175  			if m == nil {
 176  				continue
 177  			}
 178  			nm := m.Name
 179  			if !(len(nm) > 0 && nm[0] >= 'A' && nm[0] <= 'Z') {
 180  				nm = "?" | "." | nm
 181  			}
 182  			out = out | nm | ":" | e.typeDescName(m.Sig)
 183  		}
 184  		return out | "}"
 185  	}
 186  	if st, ok := t.(*TCStruct); ok {
 187  		if st == nil {
 188  			mxutil.WriteStr(2, "DBG DESC typed-nil TCStruct\n")
 189  			return "struct:{}"
 190  		}
 191  		out := "struct:{"
 192  		for i := int32(0); i < st.NumFields(); i++ {
 193  			if i > 0 {
 194  				out = out | ","
 195  			}
 196  			f := st.Field(i)
 197  			if f == nil {
 198  				mxutil.WriteStr(2, "DBG DESC nil struct field\n")
 199  				continue
 200  			}
 201  			if f.Anonymous {
 202  				out = out | "#"
 203  			}
 204  			out = out | f.Name | ":" | e.typeDescName(f.Typ)
 205  			if tag := st.Tag(i); tag != "" {
 206  				out = out | "`" | tag | "`"
 207  			}
 208  		}
 209  		return out | "}"
 210  	}
 211  	if sig, ok := t.(*Signature); ok {
 212  		if sig == nil {
 213  			mxutil.WriteStr(2, "DBG DESC typed-nil Signature\n")
 214  			return "func:{}{}"
 215  		}
 216  		out := "func:{"
 217  		if sig.Params != nil {
 218  			for i := int32(0); i < sig.Params.Len(); i++ {
 219  				if i > 0 {
 220  					out = out | ","
 221  				}
 222  				pt := sig.Params.At(i)
 223  				if pt == nil || pt.Typ == nil {
 224  					continue
 225  				}
 226  				out = out | e.typeDescName(pt.Typ)
 227  			}
 228  		}
 229  		out = out | "}{"
 230  		if sig.Results != nil {
 231  			for i := int32(0); i < sig.Results.Len(); i++ {
 232  				if i > 0 {
 233  					out = out | ","
 234  				}
 235  				rt := sig.Results.At(i)
 236  				if rt == nil || rt.Typ == nil {
 237  					continue
 238  				}
 239  				out = out | e.typeDescName(rt.Typ)
 240  			}
 241  		}
 242  		return out | "}"
 243  	}
 244  	return "basic:invalid"
 245  }
 246  
 247  // runtimeKindOf returns the runtime Kind value for the type.
 248  func runtimeKindOf(t Type) (k int32) {
 249  	u := SafeUnderlying(t)
 250  	switch tt := u.(type) {
 251  	case *Basic:
 252  		switch tt.Kind {
 253  		case Bool, UntypedBool:
 254  			return rtKindBool
 255  		case Int8:
 256  			return rtKindInt8
 257  		case Int16:
 258  			return rtKindInt16
 259  		case Int32, UntypedInt, UntypedRune:
 260  			return rtKindInt32
 261  		case Int64:
 262  			return rtKindInt64
 263  		case Uint8:
 264  			return rtKindUint8
 265  		case Uint16:
 266  			return rtKindUint16
 267  		case Uint32:
 268  			return rtKindUint32
 269  		case Uint64:
 270  			return rtKindUint64
 271  		case Uintptr:
 272  			return rtKindUintptr
 273  		case Float32:
 274  			return rtKindFloat32
 275  		case Float64, UntypedFloat:
 276  			return rtKindFloat64
 277  		case TCString, UntypedString:
 278  			return rtKindBytes
 279  		case UnsafePointer:
 280  			return rtKindUnsafePointer
 281  		}
 282  	case *Pointer:
 283  		return rtKindPointer
 284  	case *Slice:
 285  		return rtKindSlice
 286  	case *Array:
 287  		return rtKindArray
 288  	case *TCMap:
 289  		return rtKindMap
 290  	case *TCChan:
 291  		return rtKindChan
 292  	case *TCInterface:
 293  		return rtKindInterface
 294  	case *Signature:
 295  		return rtKindFunc
 296  	case *TCStruct:
 297  		return rtKindStruct
 298  	}
 299  	return rtKindInvalid
 300  }
 301  
 302  // typeDescComparable mirrors the legacy types.Comparable for the moxie type
 303  // system.
 304  func typeDescComparable(t Type) (ok bool) {
 305  	if t == nil {
 306  		return false
 307  	}
 308  	u := SafeUnderlying(t)
 309  	switch tt := u.(type) {
 310  	case *Basic:
 311  		return tt.Kind != UnsafePointer
 312  	case *Pointer, *TCChan, *TCInterface:
 313  		return true
 314  	case *Slice, *TCMap, *Signature:
 315  		return false
 316  	case *Array:
 317  		return typeDescComparable(tt.Elem)
 318  	case *TCStruct:
 319  		for i := int32(0); i < tt.NumFields(); i++ {
 320  			if !typeDescComparable(tt.Field(i).Typ) {
 321  				return false
 322  			}
 323  		}
 324  		return true
 325  	}
 326  	return false
 327  }
 328  
 329  // descPtrSize returns the target pointer size in bytes. Type descriptors
 330  // describe the target's memory layout, not the host's: on wasm32 a pointer is
 331  // 4 bytes, so a string is {ptr, i32, i32} (12) and the compiler's own struct
 332  // offsets differ from the 64-bit host layout. Legacy reads the pointer size
 333  // from LLVM's TargetData (legacy/compiler/sizes.go), so a descriptor built
 334  // here with 64-bit offsets makes the runtime codec read and write the wrong
 335  // slots on wasm - a cross-package struct return then loses every field after
 336  // the first.
 337  func descPtrSize() (n int32) {
 338  	if mxutil.TargetArch() == "wasm" {
 339  		return 4
 340  	}
 341  	return 8
 342  }
 343  
 344  // typeDescSize returns the in-memory size of the type, matching the LLVM
 345  // struct layout (natural alignment).
 346  func typeDescSize(t Type) (sz int32) {
 347  	u := SafeUnderlying(t)
 348  	switch tt := u.(type) {
 349  	case *Basic:
 350  		switch tt.Kind {
 351  		case Bool, UntypedBool, Int8, Uint8:
 352  			return 1
 353  		case Int16, Uint16:
 354  			return 2
 355  		case Int32, Uint32, Float32, UntypedInt, UntypedRune:
 356  			return 4
 357  		case Int64, Uint64, Float64, UntypedFloat:
 358  			return 8
 359  		case TCString, UntypedString:
 360  			return descPtrSize() * 3
 361  		case UnsafePointer:
 362  			return descPtrSize()
 363  		}
 364  	case *Pointer, *TCMap, *TCChan:
 365  		return descPtrSize()
 366  	case *Signature:
 367  		// Func values are two words: the LLVM type is {ptr, ptr} and
 368  		// legacy's stdSizes.Sizeof returns PtrSize*2. Reporting 8 here
 369  		// shifted every field after a func field by 8 bytes in the type
 370  		// descriptor, so the codec read the wrong slot.
 371  		return descPtrSize() * 2
 372  	case *Slice:
 373  		return descPtrSize() * 3
 374  	case *TCInterface:
 375  		return descPtrSize() * 2
 376  	case *Array:
 377  		es := typeDescSize(tt.Elem)
 378  		ea := typeDescAlign(tt.Elem)
 379  		n := int32(tt.Len)
 380  		if n <= 0 {
 381  			return 0
 382  		}
 383  		return (n-1)*ea + es
 384  	case *TCStruct:
 385  		off := int32(0)
 386  		maxal := int32(1)
 387  		for i := int32(0); i < tt.NumFields(); i++ {
 388  			fa := typeDescAlign(tt.Field(i).Typ)
 389  			if fa > maxal {
 390  				maxal = fa
 391  			}
 392  			off = (off + fa - 1) &^ (fa - 1)
 393  			off += typeDescSize(tt.Field(i).Typ)
 394  		}
 395  		return (off + maxal - 1) &^ (maxal - 1)
 396  	}
 397  	return 0
 398  }
 399  
 400  // typeDescAlign returns the natural alignment of the type.
 401  func typeDescAlign(t Type) (al int32) {
 402  	u := SafeUnderlying(t)
 403  	switch tt := u.(type) {
 404  	case *Basic:
 405  		if tt.Kind == TCString || tt.Kind == UntypedString {
 406  			// Strings are {ptr, len, cap}: pointer-aligned, not 8-aligned,
 407  			// so on wasm32 (12-byte string) the alignment is 4.
 408  			return descPtrSize()
 409  		}
 410  		s := typeDescSize(tt)
 411  		if s > 4 {
 412  			return 8
 413  		}
 414  		if s == 0 {
 415  			return 1
 416  		}
 417  		return s
 418  	case *Pointer, *TCMap, *TCChan, *Signature, *Slice, *TCInterface:
 419  		return descPtrSize()
 420  	case *Array:
 421  		return typeDescAlign(tt.Elem)
 422  	case *TCStruct:
 423  		maxal := int32(1)
 424  		for i := int32(0); i < tt.NumFields(); i++ {
 425  			fa := typeDescAlign(tt.Field(i).Typ)
 426  			if fa > maxal {
 427  				maxal = fa
 428  			}
 429  		}
 430  		return maxal
 431  	}
 432  	return 8
 433  }
 434  
 435  // structFieldOffset returns the byte offset of field i in the struct.
 436  func structFieldOffset(t *TCStruct, i int32) (off int32) {
 437  	off = 0
 438  	for j := int32(0); j < i; j++ {
 439  		align := typeDescAlign(t.Field(j).Typ)
 440  		off = (off + align - 1) &^ (align - 1)
 441  		off += typeDescSize(t.Field(j).Typ)
 442  	}
 443  	fa := typeDescAlign(t.Field(i).Typ)
 444  	return (off + fa - 1) &^ (fa - 1)
 445  }
 446  
 447  // uvarintBytes encodes n as an unsigned LEB128 varint (binary.PutUvarint).
 448  func uvarintBytes(n uint32) (b []byte) {
 449  	for n >= 0x80 {
 450  		b = mxutil.Ensure(b, 1)
 451  		push(b, byte(n)|0x80)
 452  		n >>= 7
 453  	}
 454  	b = mxutil.Ensure(b, 1)
 455  	push(b, byte(n))
 456  	return b
 457  }
 458  
 459  // descGlobalName returns the quoted global name for a descriptor tail.
 460  // The tail carries struct tags verbatim, so the name must be escaped for
 461  // LLVM exactly as go-llvm escapes it on the legacy side.
 462  func descGlobalName(tail string) (s string) {
 463  	return "\"" | "reflect/types.type:" | irEscapeString(tail) | "\""
 464  }
 465  
 466  // emitPkgpath emits the pkgpath string global once per path.
 467  func (e *irEmitter) emitPkgpath(pkgpath string) (name string) {
 468  	ppName := "reflect/types.type.pkgpath.empty"
 469  	if pkgpath != "" {
 470  		ppName = "reflect/types.type.pkgpath:" | pkgpath
 471  	}
 472  	if e.emittedPkgpaths == nil {
 473  		e.emittedPkgpaths = map[string]bool{}
 474  	}
 475  	if e.emittedPkgpaths[ppName] {
 476  		return ppName
 477  	}
 478  	e.emittedPkgpaths[ppName] = true
 479  	e.w(irQuoteName(ppName) | " = private constant [")
 480  	e.w(irItoa64(int64(len(pkgpath) + 1)))
 481  	e.w(" x i8] c\"")
 482  	e.w(irEscapeString(pkgpath))
 483  	e.w("\\00\", align 1\n")
 484  	return ppName
 485  }
 486  
 487  // emittedPkgpaths is declared on irEmitter in ir_emit.mx.
 488  
 489  // arenaString copies s into the compile arena so it survives per-function
 490  // fn-arena release.
 491  func (e *irEmitter) arenaString(s string) (out string) {
 492  	if len(s) == 0 {
 493  		return ""
 494  	}
 495  	ptr := runtime.ArenaAlloc(e.compileArena, uintptr(len(s)))
 496  	buf := unsafe.Slice((*byte)(ptr), len(s))
 497  	n := int32(len(s))
 498  	for i := int32(0); i < n; i++ {
 499  		buf[i] = s[i]
 500  	}
 501  		return buf
 502  }
 503  
 504  // collectTypeDesc records a type whose descriptor is referenced, so the
 505  // emitter can define the globals at the end of the module. Runs inside the
 506  // per-function fn arena; the map and its keys must survive into emit()'s
 507  // trailing section, so allocate them in the compile arena: the map root and
 508  // buckets pin to the arena current at creation, and the tail string is
 509  // copied because the original bytes sit in the caller's fn arena.
 510  func (e *irEmitter) collectTypeDesc(t Type) {
 511  	if t == nil {
 512  		return
 513  	}
 514  	saved := runtime.CurrentArena()
 515  	runtime.SetCurrentArena(e.compileArena)
 516  	if e.pendTypeDescs == nil {
 517  		e.pendTypeDescs = map[string]Type{}
 518  	}
 519  	tail := e.arenaString(e.typeDescName(t))
 520  	if _, ok := e.pendTypeDescs[tail]; !ok {
 521  		e.pendTypeDescs[tail] = t
 522  	}
 523  	runtime.SetCurrentArena(saved)
 524  }
 525  
 526  
 527  
 528  // emitBasicIdentities defines the synthetic String() identity wrappers for
 529  // the basic types. A compiler without the moxie.stringer tag (the wasm
 530  // target) carries no runtime String() bodies, so the interface path calls
 531  // these instead; only the []byte wrapper is meaningful (a byte slice
 532  // String()s to itself), the rest give every boxed builtin an arm.
 533  func (e *irEmitter) emitBasicIdentities() {
 534  	st := e.sliceType()
 535  	basicNames := []string{"bool", "int8", "int16", "int32", "int64",
 536  		"uint8", "uint16", "uint32", "uint64", "uintptr", "float32", "float64", "bytes"}
 537  	for _, bn := range basicNames {
 538  		sym := "basic:" | bn | ".String$identity"
 539  		e.w("define internal " | st | " " | irQuoteName(sym) | "(ptr %idp, ptr %idc) {\n")
 540  		e.w("entry:\n")
 541  		e.w("  %idr = load " | st | ", ptr %idp\n")
 542  		e.w("  ret " | st | " %idr\n")
 543  		e.w("}\n")
 544  	}
 545  }
 546  
 547  // emitTypeDescs defines all pending descriptor globals.
 548  // collectTypeDescFromTail records a descriptor referenced by name tail,
 549  // without having a Type object. Used by the iface dispatch codegen when
 550  // implTypeID resolves an extTypeID string directly.
 551  func (e *irEmitter) collectTypeDescFromTail(tail string) {
 552  	if tail == "" || e.pendTypeDescs == nil {
 553  		return
 554  	}
 555  	saved := runtime.CurrentArena()
 556  	runtime.SetCurrentArena(e.compileArena)
 557  	if _, ok := e.pendTypeDescs[tail]; !ok {
 558  		e.pendTypeDescs[e.arenaString(tail)] = Typ[Invalid]
 559  	}
 560  	runtime.SetCurrentArena(saved)
 561  }
 562  
 563  func (e *irEmitter) emitTypeDescs() {
 564  	if e.pendTypeDescs == nil {
 565  		return
 566  	}
 567  	e.w("%runtime.structField = type { ptr, ptr }\n")
 568  	var tails []string
 569  	for t := range e.pendTypeDescs {
 570  		tails = mxutil.Ensure(tails, 1)
 571  		push(tails, t)
 572  	}
 573  	for i := 1; i < len(tails); i++ {
 574  		for j := i; j > 0 && tails[j] < tails[j-1]; j-- {
 575  			tails[j], tails[j-1] = tails[j-1], tails[j]
 576  		}
 577  	}
 578  	for _, tail := range tails {
 579  		e.emitTypeDesc(tail)
 580  	}
 581  }
 582  
 583  // emitTypeDesc emits the descriptor global for the given name tail,
 584  // recursively emitting referenced descriptors. The Type objects collected
 585  // in pendTypeDescs can point into transient fn-arena/stack memory, so the
 586  // emission reconstructs types from the tail text instead of dereferencing
 587  // them.
 588  func (e *irEmitter) emitTypeDesc(tail string) {
 589  	for _, t := range e.emittedTails {
 590  		if t == tail {
 591  			return
 592  		}
 593  	}
 594  	e.emittedTails = mxutil.Ensure(e.emittedTails, 1)
 595  	e.emittedTails = push(e.emittedTails, tail)
 596  	tt := e.typeFromTail(tail)
 597  	if tt == nil || tt == Typ[Invalid] {
 598  		e.w(irQuoteName("reflect/types.type:" | tail) | " = linkonce_odr constant { i8, ptr } { i8 0, ptr null }, align 8\n")
 599  		return
 600  	}
 601  	e.emitTypeDescValue(tt)
 602  }
 603  
 604  // tailAtoi64 parses a decimal integer from the tail text.
 605  func tailAtoi64(s string) (n int64) {
 606  	for i := 0; i < len(s); i++ {
 607  		c := s[i]
 608  		if c < '0' || c > '9' {
 609  			break
 610  		}
 611  		n = n*10 + int64(c-'0')
 612  	}
 613  	return n
 614  }
 615  
 616  // tailSplitTop splits s at commas at brace depth 0.
 617  func tailSplitTop(s string) (parts []string) {
 618  	depth := int32(0)
 619  	start := int32(0)
 620  	inTag := false
 621  	for i := int32(0); i < int32(len(s)); i++ {
 622  		c := s[i]
 623  		if c == '`' {
 624  			// Struct tags sit verbatim between backticks and may contain
 625  			// ',', '{' or '}' (asn1:"optional,default:0"). Treating their
 626  			// contents as structure split one field into several, so the
 627  			// descriptor rebuilt from the tail named a type that no
 628  			// definition matched.
 629  			inTag = !inTag
 630  			continue
 631  		}
 632  		if inTag {
 633  			continue
 634  		}
 635  		if c == '{' {
 636  			depth++
 637  		} else if c == '}' {
 638  			depth--
 639  		} else if c == ',' && depth == 0 {
 640  			parts = mxutil.Ensure(parts, 1)
 641  			push(parts, s[start:i])
 642  			start = i + 1
 643  		}
 644  	}
 645  	parts = mxutil.Ensure(parts, 1)
 646  	push(parts, s[start:])
 647  	return parts
 648  }
 649  
 650  // takeBraceGroup extracts the brace-delimited group starting at s[from]
 651  // (which must be '{'), scanning nested braces, returning the inner content
 652  // and the index just past the closing brace.
 653  func takeBraceGroup(s string, from int32) (content string, next int32, ok bool) {
 654  	depth := int32(0)
 655  	start := from + 1
 656  	for i := from; i < int32(len(s)); i++ {
 657  		c := s[i]
 658  		if c == '{' {
 659  			depth++
 660  		} else if c == '}' {
 661  			depth--
 662  			if depth == 0 {
 663  				return s[start:i], i + 1, true
 664  			}
 665  		}
 666  	}
 667  	return "", from, false
 668  }
 669  
 670  // basicFromTail maps a basic type name tail to its Basic type.
 671  func basicFromTail(bn string) (t Type) {
 672  	switch bn {
 673  	case "bool":
 674  		return Typ[Bool]
 675  	case "int8":
 676  		return Typ[Int8]
 677  	case "int16":
 678  		return Typ[Int16]
 679  	case "int32":
 680  		return Typ[Int32]
 681  	case "int64":
 682  		return Typ[Int64]
 683  	case "uint8":
 684  		return Typ[Uint8]
 685  	case "uint16":
 686  		return Typ[Uint16]
 687  	case "uint32":
 688  		return Typ[Uint32]
 689  	case "uint64":
 690  		return Typ[Uint64]
 691  	case "uintptr":
 692  		return Typ[Uintptr]
 693  	case "float32":
 694  		return Typ[Float32]
 695  	case "float64":
 696  		return Typ[Float64]
 697  	case "bytes":
 698  		return Typ[TCString]
 699  	case "unsafe.Pointer":
 700  		return Typ[UnsafePointer]
 701  	}
 702  	return Typ[Invalid]
 703  }
 704  
 705  var fromTailDepth int32
 706  
 707  // typeFromTail reconstructs a Type from its descriptor name tail.
 708  func (e *irEmitter) typeFromTail(tail string) (t Type) {
 709  	fromTailDepth++
 710  	if fromTailDepth > 40 {
 711  		fromTailDepth--
 712  		return Typ[Invalid]
 713  	}
 714  	rv := e.typeFromTailInner(tail)
 715  	fromTailDepth--
 716  	return rv
 717  }
 718  
 719  func (e *irEmitter) typeFromTailInner(tail string) (t Type) {
 720  	if mxutil.HasPrefix(tail, "basic:") {
 721  		return basicFromTail(tail[len("basic:"):])
 722  	}
 723  	if mxutil.HasPrefix(tail, "pointer:") {
 724  		return NewPointer(e.typeFromTail(tail[len("pointer:"):]))
 725  	}
 726  	if mxutil.HasPrefix(tail, "slice:") {
 727  		return NewSlice(e.typeFromTail(tail[len("slice:"):]))
 728  	}
 729  	if mxutil.HasPrefix(tail, "array:") {
 730  		rest := tail[len("array:"):]
 731  		ci := -1
 732  		for i := 0; i < len(rest); i++ {
 733  			if rest[i] == ':' {
 734  				ci = i
 735  				break
 736  			}
 737  		}
 738  		if ci < 0 {
 739  			return Typ[Invalid]
 740  		}
 741  		return NewArray(e.typeFromTail(rest[ci+1:]), tailAtoi64(rest[:ci]))
 742  	}
 743  	if mxutil.HasPrefix(tail, "map:{") {
 744  		inner := tail[len("map:{"):len(tail)-1]
 745  		parts := tailSplitTop(inner)
 746  		if len(parts) != 2 {
 747  			return Typ[Invalid]
 748  		}
 749  		return NewTCMap(e.typeFromTail(parts[0]), e.typeFromTail(parts[1]))
 750  	}
 751  	if mxutil.HasPrefix(tail, "chan:") {
 752  		rest := tail[len("chan:"):]
 753  		dir := TCSendRecv
 754  		if mxutil.HasPrefix(rest, "s:") {
 755  			dir = TCSendOnly
 756  			rest = rest[2:]
 757  		} else if mxutil.HasPrefix(rest, "r:") {
 758  			dir = TCRecvOnly
 759  			rest = rest[2:]
 760  		} else if mxutil.HasPrefix(rest, "sr:") {
 761  			rest = rest[3:]
 762  		}
 763  		return NewTCChan(dir, e.typeFromTail(rest))
 764  	}
 765  	if mxutil.HasPrefix(tail, "interface:{") {
 766  		inner := tail[len("interface:{"):len(tail)-1]
 767  		var methods []*IfaceMethod
 768  		if inner != "" {
 769  			ms := tailSplitTop(inner)
 770  			for i := 0; i < len(ms); i++ {
 771  				m := ms[i]
 772  				if m == "" {
 773  					continue
 774  				}
 775  				ci := -1
 776  				for j := 0; j < len(m); j++ {
 777  					if m[j] == ':' {
 778  						ci = j
 779  						break
 780  					}
 781  				}
 782  				if ci < 0 {
 783  					continue
 784  				}
 785  				mname := m[:ci]
 786  				if mxutil.HasPrefix(mname, "?.") {
 787  					mname = mname[2:]
 788  				}
 789  				msig := e.typeFromTail(m[ci+1:])
 790  				ss, isSig := msig.(*Signature)
 791  				if isSig {
 792  					methods = mxutil.Ensure(methods, 1)
 793  					push(methods, NewTCIfaceMethod(mname, ss))
 794  				}
 795  			}
 796  		}
 797  		if len(methods) == 0 {
 798  			return NewTCInterface(nil, nil)
 799  		}
 800  		ifc := NewTCInterface(methods, nil)
 801  		ifc.AllMethods = methods
 802  		ifc.Completed = true
 803  		return ifc
 804  	}
 805  	if mxutil.HasPrefix(tail, "func:{") {
 806  		rest := tail[len("func:"):]
 807  		pContent, next, ok := takeBraceGroup(rest, 0)
 808  		if !ok {
 809  			return Typ[Invalid]
 810  		}
 811  		var params []*TCVar
 812  		if pContent != "" {
 813  			ps := tailSplitTop(pContent)
 814  			for i := 0; i < len(ps); i++ {
 815  				if ps[i] == "" {
 816  					continue
 817  				}
 818  				params = mxutil.Ensure(params, 1)
 819  				push(params, NewTCVar(nil, "", e.typeFromTail(ps[i])))
 820  			}
 821  		}
 822  		var results []*TCVar
 823  		if next < int32(len(rest)) && rest[next] == '{' {
 824  			rContent, _, ok2 := takeBraceGroup(rest, next)
 825  			if ok2 && rContent != "" {
 826  				rs := tailSplitTop(rContent)
 827  				for i := 0; i < len(rs); i++ {
 828  					if rs[i] == "" {
 829  						continue
 830  					}
 831  					results = mxutil.Ensure(results, 1)
 832  					push(results, NewTCVar(nil, "", e.typeFromTail(rs[i])))
 833  				}
 834  			}
 835  		}
 836  		return NewSignature(nil, NewTuple(params...), NewTuple(results...), false)
 837  	}
 838  	if mxutil.HasPrefix(tail, "struct:{") {
 839  		inner := tail[len("struct:{"):len(tail)-1]
 840  		fields := tailSplitTop(inner)
 841  		var flds []*TCVar
 842  		var tags []string
 843  		for i := 0; i < len(fields); i++ {
 844  			f := fields[i]
 845  			if f == "" {
 846  				continue
 847  			}
 848  			ci := -1
 849  			for j := 0; j < len(f); j++ {
 850  				if f[j] == ':' {
 851  					ci = j
 852  					break
 853  				}
 854  			}
 855  			if ci < 0 {
 856  				continue
 857  			}
 858  			fname := f[:ci]
 859  			ftail := f[ci+1:]
 860  			anon := false
 861  			if len(fname) > 0 && fname[0] == '#' {
 862  				anon = true
 863  				fname = fname[1:]
 864  			}
 865  			// Strip the optional `tag` suffix. The tag is the trailing
 866  			// backtick run, but the type before it may contain nested
 867  			// tags, so walk the whole field tracking tag state rather
 868  			// than taking the first backtick.
 869  			tag := ""
 870  			inTag := false
 871  			lastOpen := -1
 872  			for j := 0; j < len(ftail); j++ {
 873  				if ftail[j] == '`' {
 874  					if inTag {
 875  						inTag = false
 876  					} else {
 877  						inTag = true
 878  						lastOpen = j
 879  					}
 880  				}
 881  			}
 882  			if !inTag && lastOpen >= 0 && len(ftail) > 0 && ftail[len(ftail)-1] == '`' {
 883  				tag = ftail[lastOpen+1 : len(ftail)-1]
 884  				ftail = ftail[:lastOpen]
 885  			}
 886  			ft := e.typeFromTail(ftail)
 887  			flds = mxutil.Ensure(flds, 1)
 888  			push(flds, NewTCField(nil, fname, ft, anon))
 889  			tags = mxutil.Ensure(tags, 1)
 890  			push(tags, tag)
 891  		}
 892  		return NewTCStruct(flds, tags)
 893  	}
 894  	if mxutil.HasPrefix(tail, "named:") {
 895  		rest := tail[len("named:"):]
 896  		pkgPath := ""
 897  		name := rest
 898  		dot := -1
 899  		for i := len(rest) - 1; i >= 0; i-- {
 900  			if rest[i] == '.' {
 901  				dot = i
 902  				break
 903  			}
 904  		}
 905  		if dot >= 0 {
 906  			pkgPath = rest[:dot]
 907  			name = rest[dot+1:]
 908  		}
 909  		lookupScopeParent := func(sc *Scope, n string) Object {
 910  			found, obj := sc.LookupParent(n)
 911  			_ = found
 912  			return obj
 913  		}
 914  		if e.pkg != nil && e.pkg.Pkg != nil && e.pkg.Pkg.Scope != nil {
 915  			if pkgPath == "" || normalizePkgPath(e.pkg.Pkg.Path) == pkgPath {
 916  				if obj := lookupScopeParent(e.pkg.Pkg.Scope, name); obj != nil {
 917  					if tn, ok := obj.(*TypeName); ok && tn.Typ != nil {
 918  						return tn.Typ
 919  					}
 920  				}
 921  			}
 922  		}
 923  		if ImportRegistry != nil {
 924  			for _, p := range ImportRegistry {
 925  				if p != nil && p.Scope != nil {
 926  					if pkgPath == "" || normalizePkgPath(p.Path) == pkgPath {
 927  						if obj := lookupScopeParent(p.Scope, name); obj != nil {
 928  							if tn, ok := obj.(*TypeName); ok && tn.Typ != nil {
 929  								return tn.Typ
 930  							}
 931  						}
 932  					}
 933  				}
 934  			}
 935  		}
 936  		if pkgPath == "" && Universe != nil {
 937  			if obj := lookupScopeParent(Universe, name); obj != nil {
 938  				if tn, ok := obj.(*TypeName); ok && tn.Typ != nil {
 939  					return tn.Typ
 940  				}
 941  			}
 942  		}
 943  	
 944  		return Typ[Invalid]
 945  	}
 946  	return Typ[Invalid]
 947  }
 948  
 949  // needTypeDesc recursively emits the descriptor for t. The type must
 950  // already be pending (collectTypeDesc ran at the reference site); the
 951  // pendTypeDescs map doubles as the emitted-set, so re-collecting here
 952  // would re-arm an already-emitted tail.
 953  func (e *irEmitter) needTypeDesc(t Type) {
 954  	if t == nil {
 955  		return
 956  	}
 957  	e.emitTypeDesc(e.typeDescName(t))
 958  }
 959  
 960  // emitTypeDescValue writes the descriptor global and its dependencies.
 961  func (e *irEmitter) emitTypeDescValue(t Type) {
 962  	if named, isNamed := t.(*Named); isNamed {
 963  		e.emitNamedDesc(t, named)
 964  		return
 965  	}
 966  	u := SafeUnderlying(t)
 967  	if u == nil {
 968  		return
 969  	}
 970  	if e.typeDescName(t) == "basic:bytes" {
 971  		// []byte and string unify to the bytes basic descriptor.
 972  		pt := NewPointer(t)
 973  		ptTail := e.typeDescName(pt)
 974  		e.needTypeDesc(pt)
 975  		e.w("@\"" | "reflect/types.type:basic:bytes" | "\" = linkonce_odr constant { i8, ptr } { i8 " | irItoa64(int64(rtKindBytes|rtFlagComparable)) | ", ptr @" | descGlobalName(ptTail) | " }, align 8\n")
 976  		return
 977  	}
 978  	gname := descGlobalName(e.typeDescName(t))
 979  	meta := runtimeKindOf(t)
 980  	if typeDescComparable(t) {
 981  		meta |= rtFlagComparable
 982  	}
 983  
 984  	switch tt := u.(type) {
 985  	case *Basic:
 986  		pt := NewPointer(t)
 987  		ptTail := e.typeDescName(pt)
 988  		e.needTypeDesc(pt)
 989  		e.w("@" | gname | " = linkonce_odr constant { i8, ptr } { i8 " | irItoa64(int64(meta)) | ", ptr @" | descGlobalName(ptTail) | " }, align 8\n")
 990  	case *Pointer:
 991  		elem := tt.Base
 992  		e.needTypeDesc(elem)
 993  		e.w("@" | gname | " = linkonce_odr constant { i8, i16, ptr } { i8 " | irItoa64(int64(meta)) | ", i16 0, ptr @" | descGlobalName(e.typeDescName(elem)) | " }, align 8\n")
 994  	case *Slice:
 995  		elem := tt.Elem
 996  		e.needTypeDesc(elem)
 997  		pt := NewPointer(t)
 998  		ptTail := e.typeDescName(pt)
 999  		e.needTypeDesc(pt)
1000  		e.w("@" | gname | " = linkonce_odr constant { i8, i16, ptr, ptr } { i8 " | irItoa64(int64(meta)) | ", i16 0, ptr @" | descGlobalName(ptTail) | ", ptr @" | descGlobalName(e.typeDescName(elem)) | " }, align 8\n")
1001  	case *Array:
1002  		elem := tt.Elem
1003  		e.needTypeDesc(elem)
1004  		pt := NewPointer(t)
1005  		ptTail := e.typeDescName(pt)
1006  		e.needTypeDesc(pt)
1007  		sliceOf := NewSlice(elem)
1008  		e.needTypeDesc(sliceOf)
1009  		// The length is a uintptr, so it must be the target's word width.
1010  		// Emitting a fixed i64 aligns it to 8 on wasm32 and lands it at
1011  		// offset 16, while the runtime's rtArrayType.arrayLen is a 4-byte
1012  		// word at offset 12: the runtime then reads padding and sees 0,
1013  		// which makes every array-typed field decode as a zero-length
1014  		// array of size 0.
1015  		ipt := e.intptrType()
1016  		e.w("@" | gname | " = linkonce_odr constant { i8, i16, ptr, ptr, " | ipt | ", ptr } { i8 " | irItoa64(int64(meta)) | ", i16 0, ptr @" | descGlobalName(ptTail) | ", ptr @" | descGlobalName(e.typeDescName(elem)) | ", " | ipt | " " | token.Itoa64(tt.Len) | ", ptr @" | descGlobalName(e.typeDescName(sliceOf)) | " }, align 8\n")
1017  	case *TCMap:
1018  		e.needTypeDesc(tt.Key)
1019  		e.needTypeDesc(tt.Elem)
1020  		pt := NewPointer(t)
1021  		ptTail := e.typeDescName(pt)
1022  		e.needTypeDesc(pt)
1023  		e.w("@" | gname | " = linkonce_odr constant { i8, i16, ptr, ptr, ptr } { i8 " | irItoa64(int64(meta)) | ", i16 0, ptr @" | descGlobalName(ptTail) | ", ptr @" | descGlobalName(e.typeDescName(tt.Elem)) | ", ptr @" | descGlobalName(e.typeDescName(tt.Key)) | " }, align 8\n")
1024  	case *TCChan:
1025  		e.needTypeDesc(tt.Elem)
1026  		pt := NewPointer(t)
1027  		ptTail := e.typeDescName(pt)
1028  		e.needTypeDesc(pt)
1029  		dir := int64(0)
1030  		if tt.Dir == TCSendOnly {
1031  			dir = 1
1032  		} else if tt.Dir == TCRecvOnly {
1033  			dir = 2
1034  		}
1035  		e.w("@" | gname | " = linkonce_odr constant { i8, i16, ptr, ptr } { i8 " | irItoa64(int64(meta)) | ", i16 " | token.Itoa64(dir) | ", ptr @" | descGlobalName(ptTail) | ", ptr @" | descGlobalName(e.typeDescName(tt.Elem)) | " }, align 8\n")
1036  	case *TCInterface:
1037  		pt := NewPointer(t)
1038  		ptTail := e.typeDescName(pt)
1039  		e.needTypeDesc(pt)
1040  		e.w("@" | gname | " = linkonce_odr constant { i8, ptr } { i8 " | irItoa64(int64(meta)) | ", ptr @" | descGlobalName(ptTail) | " }, align 8\n")
1041  	case *Signature:
1042  		pt := NewPointer(t)
1043  		ptTail := e.typeDescName(pt)
1044  		e.needTypeDesc(pt)
1045  		e.w("@" | gname | " = linkonce_odr constant { i8, ptr } { i8 " | irItoa64(int64(meta)) | ", ptr @" | descGlobalName(ptTail) | " }, align 8\n")
1046  	case *TCStruct:
1047  		pt := NewPointer(t)
1048  		ptTail := e.typeDescName(pt)
1049  		e.needTypeDesc(pt)
1050  		e.emitStructDesc(t, tt, gname, meta, descGlobalName(ptTail))
1051  	}
1052  }
1053  
1054  // emitNamedDesc emits a named type descriptor:
1055  // {meta, numMethod, ptrTo, underlying, pkgpath, name}.
1056  func (e *irEmitter) emitNamedDesc(t Type, named *Named) {
1057  	gname := descGlobalName(e.typeDescName(t))
1058  	meta := rtKindOfNamed(named) | rtFlagNamed
1059  	if typeDescComparable(t) {
1060  		meta |= rtFlagComparable
1061  	}
1062  	pkgpath := ""
1063  	pkgname := ""
1064  	name := "unknown"
1065  	if named.Obj != nil {
1066  		name = named.Obj.Name
1067  		if named.Obj.Pkg != nil {
1068  			pkgpath = normalizePkgPath(named.Obj.Pkg.Path)
1069  			pkgname = named.Obj.Pkg.Name
1070  		}
1071  	}
1072  	ppName := e.emitPkgpath(pkgpath)
1073  
1074  	ptTail := e.typeDescName(NewPointer(t))
1075  	e.needTypeDesc(NewPointer(t))
1076  	e.needTypeDesc(named.Under)
1077  
1078  	nameStr := pkgname | "." | name
1079  	gnameRaw := "reflect/types.type:" | e.typeDescName(t)
1080  	e.w(irQuoteName(gnameRaw) | " = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [")
1081  	e.w(irItoa64(int64(len(nameStr) + 1)))
1082  	e.w(" x i8] } { i8 ")
1083  	e.w(irItoa64(int64(meta)))
1084  	e.w(", i16 0, ptr @")
1085  	e.w(descGlobalName(ptTail))
1086  	e.w(", ptr @")
1087  	e.w(descGlobalName(e.typeDescName(named.Under)))
1088  	e.w(", ptr ")
1089  	e.w(irQuoteName(ppName))
1090  	e.w(", [")
1091  	e.w(irItoa64(int64(len(nameStr) + 1)))
1092  	e.w(" x i8] c\"")
1093  	e.w(irEscapeString(nameStr))
1094  	e.w("\\00\" }, align 8\n")
1095  }
1096  
1097  // rtKindOfNamed returns the underlying runtime kind of a named type.
1098  func rtKindOfNamed(named *Named) (k int32) {
1099  	if named == nil || named.Under == nil {
1100  		return rtKindInvalid
1101  	}
1102  	return runtimeKindOf(named.Under)
1103  }
1104  
1105  // emitStructDesc emits a struct descriptor: the struct global plus field
1106  // data globals and the pkgpath string.
1107  func (e *irEmitter) emitStructDesc(t Type, st *TCStruct, gname string, meta int32, ptGlobal string) {
1108  	tail := e.typeDescName(t)
1109  	nf := st.NumFields()
1110  	size := typeDescSize(t)
1111  
1112  	pkgpath := ""
1113  	if nf > 0 {
1114  		if st.Field(0).Pkg != nil {
1115  			pkgpath = normalizePkgPath(st.Field(0).Pkg.Path)
1116  		}
1117  	}
1118  	ppName := e.emitPkgpath(pkgpath)
1119  
1120  	var fieldRefs []string
1121  	for i := int32(0); i < nf; i++ {
1122  		f := st.Field(i)
1123  		ft := f.Typ
1124  		e.needTypeDesc(ft)
1125  		ftGlobal := descGlobalName(e.typeDescName(ft))
1126  		off := structFieldOffset(st, i)
1127  		var flags uint8
1128  		if f.Anonymous {
1129  			flags |= 1
1130  		}
1131  		if st.Tag(i) != "" {
1132  			flags |= 2
1133  		}
1134  		if f.Exported() {
1135  			flags |= 4
1136  		}
1137  		var data []byte
1138  		data = mxutil.Ensure(data, 1)
1139  		push(data, flags)
1140  		vv := uvarintBytes(uint32(off))
1141  		for _, vb := range vv {
1142  			data = mxutil.Ensure(data, 1)
1143  			push(data, vb)
1144  		}
1145  		for j := int32(0); j < int32(len(f.Name)); j++ {
1146  			data = mxutil.Ensure(data, 1)
1147  			push(data, f.Name[j])
1148  		}
1149  		data = mxutil.Ensure(data, 1)
1150  		push(data, 0)
1151  		tag := st.Tag(i)
1152  		if tag != "" {
1153  			data = mxutil.Ensure(data, 1)
1154  			push(data, byte(len(tag)))
1155  			for j := int32(0); j < int32(len(tag)); j++ {
1156  				data = mxutil.Ensure(data, 1)
1157  				push(data, tag[j])
1158  			}
1159  		}
1160  		dgName := "reflect/types.type:" | tail | "." | f.Name
1161  		e.w(irQuoteName(dgName) | " = private constant [")
1162  		e.w(irItoa64(int64(len(data))))
1163  		e.w(" x i8] c\"")
1164  		e.w(irEscapeString(string(data)))
1165  		e.w("\", align 1\n")
1166  		fieldRefs = mxutil.Ensure(fieldRefs, 1)
1167  		push(fieldRefs, "%runtime.structField { ptr @" | ftGlobal | ", ptr " | irQuoteName(dgName) | " }")
1168  	}
1169  
1170  	e.w("@" | gname | " = linkonce_odr constant { i8, i16, ptr, ptr, i32, i16, [")
1171  	e.w(irItoa64(int64(nf)))
1172  	e.w(" x %runtime.structField] } { i8 ")
1173  	e.w(irItoa64(int64(meta)))
1174  	e.w(", i16 0, ptr @")
1175  	e.w(ptGlobal)
1176  	e.w(", ptr ")
1177  	e.w(irQuoteName(ppName))
1178  	e.w(", i32 ")
1179  	e.w(irItoa64(int64(size)))
1180  	e.w(", i16 ")
1181  	e.w(irItoa64(int64(nf)))
1182  	e.w(", [")
1183  	e.w(irItoa64(int64(nf)))
1184  	e.w(" x %runtime.structField] [")
1185  	for i, fr := range fieldRefs {
1186  		if i > 0 {
1187  			e.w(", ")
1188  		}
1189  		e.w(fr)
1190  	}
1191  	e.w("] }, align 8\n")
1192  }
1193