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