ir_iface.mx raw
1 package main
2
3 import (
4 "git.smesh.lol/moxie/pkg/mxutil"
5 . "git.smesh.lol/moxie/pkg/types"
6 )
7
8 func (e *irEmitter) emitMakeInterface(m *SSAMakeInterface) {
9 reg := e.regName(m)
10 val := e.operand(m.X)
11 valType := e.llvmType(m.X.SSAType())
12 ity := e.ifaceType()
13 if valType == "void" {
14 ipt := e.intptrType()
15 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(ity) ; e.w(" zeroinitializer, " | ipt | " 0, 0\n")
16 return
17 }
18 if _, isAlloc := m.X.(*SSAAlloc); !isAlloc {
19 if _, isIA := m.X.(*SSAIndexAddr); !isIA {
20 if at, ok := e.allocTypes[m.X]; ok && at != "ptr" && at != "void" {
21 valType = at
22 }
23 }
24 }
25 if valType == ity {
26 tp := e.nextReg2("mi")
27 e.w(" ") ; e.w(tp) ; e.w(" = extractvalue ") ; e.w(ity) ; e.w(" ") ; e.w(val) ; e.w(", 0\n")
28 dp := e.nextReg2("mi")
29 e.w(" ") ; e.w(dp) ; e.w(" = extractvalue ") ; e.w(ity) ; e.w(" ") ; e.w(val) ; e.w(", 1\n")
30 t1a := e.nextReg2("mi")
31 ipt := e.intptrType()
32 e.w(" ") ; e.w(t1a) ; e.w(" = insertvalue ") ; e.w(ity) ; e.w(" undef, " | ipt | " ") ; e.w(tp) ; e.w(", 0\n")
33 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(ity) ; e.w(" ") ; e.w(t1a) ; e.w(", ptr ") ; e.w(dp) ; e.w(", 1\n")
34 return
35 }
36 var valPtr string
37 if valType == "ptr" {
38 valPtr = val
39 } else if e.isScalarType(valType) {
40 // Always heap-allocate scalar values for interface boxing.
41 // The invoke dispatch passes the value pointer to methods that
42 // dereference it; inlining via inttoptr produces an invalid address.
43 ipt := e.intptrType()
44 sz := e.nextReg2("mi")
45 e.w(" ") ; e.w(sz) ; e.w(" = ptrtoint ptr getelementptr (") ; e.w(valType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n")
46 valPtr = e.nextReg2("mi")
47 e.w(" ") ; e.w(valPtr) ; e.w(" = call ptr @runtime.alloc(") ; e.w(ipt) ; e.w(" ") ; e.w(sz) ; e.w(", ptr null, ptr null)\n")
48 e.w(" store ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n")
49 e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr")
50 e.scopeTrackAlloc(valPtr)
51 } else {
52 ipt := e.intptrType()
53 sz := e.nextReg2("ha")
54 e.w(" ") ; e.w(sz) ; e.w(" = ptrtoint ptr getelementptr (") ; e.w(valType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n")
55 valPtr = e.nextReg2("mi")
56 e.w(" ") ; e.w(valPtr) ; e.w(" = call ptr @runtime.alloc(") ; e.w(ipt) ; e.w(" ") ; e.w(sz) ; e.w(", ptr null, ptr null)\n")
57 e.w(" store ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n")
58 e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr")
59 e.scopeTrackAlloc(valPtr)
60 }
61 typeid := e.typeIDHash(m.X.SSAType())
62 t1 := e.nextReg2("mi")
63 e.w(" ") ; e.w(t1) ; e.w(" = insertvalue " | e.ifaceType() | " undef, " | e.intptrType() | " ") ; e.w(typeid) ; e.w(", 0\n")
64 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue " | e.ifaceType() | " ") ; e.w(t1) ; e.w(", ptr ") ; e.w(valPtr) ; e.w(", 1\n")
65 }
66
67 // hashToIntptr folds a 64-bit FNV-1a hash to intptr width (32-bit on wasm32).
68 func (e *irEmitter) hashToIntptr(h uint64) (s string) {
69 if e.intptrType() == "i32" {
70 return irUitoa64((h >> 32) ^ (h & 0xFFFFFFFF))
71 }
72 return irUitoa64(h)
73 }
74
75 func (e *irEmitter) typeIDHash(t Type) (s string) {
76 name := e.reflectTypeName(t)
77 if _, ok := e.typeIDs[name]; !ok {
78 found := false
79 for _, p := range e.pendTypeIDs {
80 if p.k == name { found = true; break }
81 }
82 if !found {
83 e.typeIDNext++
84 push(e.pendTypeIDs, emitKVI{name, e.typeIDNext})
85 }
86 }
87 return e.hashToIntptr(fnv1a64(name))
88 }
89
90 func (e *irEmitter) typeIDHashI64(t Type) (s string) {
91 name := e.reflectTypeName(t)
92 return e.intptrType() | " " | e.hashToIntptr(fnv1a64(name))
93 }
94
95 func (e *irEmitter) reflectTypeName(t Type) (s string) {
96 if b, ok := t.(*Basic); ok {
97 switch b.Kind {
98 case Bool, UntypedBool:
99 return "reflect/types.type:basic:bool"
100 case Int8:
101 return "reflect/types.type:basic:int8"
102 case Int16:
103 return "reflect/types.type:basic:int16"
104 case Int32, UntypedInt, UntypedRune:
105 return "reflect/types.type:basic:int32"
106 case Int64:
107 return "reflect/types.type:basic:int64"
108 case Uint8:
109 return "reflect/types.type:basic:uint8"
110 case Uint16:
111 return "reflect/types.type:basic:uint16"
112 case Uint32:
113 return "reflect/types.type:basic:uint32"
114 case Uint64:
115 return "reflect/types.type:basic:uint64"
116 case Float32:
117 return "reflect/types.type:basic:float32"
118 case Float64, UntypedFloat:
119 return "reflect/types.type:basic:float64"
120 case TCString, UntypedString:
121 return "reflect/types.type:basic:bytes"
122 case UnsafePointer:
123 return "reflect/types.type:basic:uintptr"
124 }
125 }
126 if named, ok := t.(*Named); ok && named.Obj != nil {
127 pkgPath := ""
128 if named.Obj.Pkg != nil {
129 pkgPath = named.Obj.Pkg.Path
130 }
131 if pkgPath == "" {
132 pkgPath = e.pkg.Pkg.Path
133 }
134 result := "reflect/types.type:named:" | pkgPath | "." | named.Obj.Name
135 if pkgPath == e.pkg.Pkg.Path {
136 push(e.pendLocalTypeIDs, "\"" | result | "\"")
137 }
138 return result
139 }
140 if p, ok := t.(*Pointer); ok {
141 inner := e.reflectTypeName(p.Base)
142 if mxutil.HasPrefix(inner, "reflect/types.type:") {
143 result := "reflect/types.type:pointer:" | inner[len("reflect/types.type:"):]
144 quoted := "\"" | result | "\""
145 innerQ := "\"" | inner | "\""
146 inLocal := e.localTypeIDs != nil && e.localTypeIDs[innerQ]
147 if !inLocal {
148 for _, pk := range e.pendLocalTypeIDs {
149 if pk == innerQ { inLocal = true; break }
150 }
151 }
152 if inLocal {
153 push(e.pendLocalTypeIDs, quoted)
154 }
155 return result
156 }
157 return inner | ".ptr"
158 }
159 if _, ok := t.(*TCInterface); ok {
160 result := "reflect/types.type:interface:{}"
161 push(e.pendLocalTypeIDs, "\"" | result | "\"")
162 return result
163 }
164 if sl, ok := t.(*Slice); ok {
165 elem := sl.Elem
166 if b, ok2 := SafeUnderlying(elem).(*Basic); ok2 && b.Kind == Uint8 {
167 return "reflect/types.type:basic:bytes"
168 }
169 inner := e.reflectTypeName(elem)
170 if mxutil.HasPrefix(inner, "reflect/types.type:") {
171 result := "reflect/types.type:slice:" | inner[len("reflect/types.type:"):]
172 push(e.pendLocalTypeIDs, "\"" | result | "\"")
173 return result
174 }
175 return inner | ".slice"
176 }
177 pkg := e.pkg.Pkg.Path
178 return pkg | ".typeid.unknown"
179 }
180
181 func (e *irEmitter) findIfaceImpls(methodName string) (ss []ifaceImpl) {
182 var impls []ifaceImpl
183 hasType := map[string]bool{}
184 var mkeys []string
185 for k := range e.pkg.Members {
186 push(mkeys, k)
187 }
188 for i := 1; i < len(mkeys); i++ {
189 for j := i; j > 0 && mkeys[j] < mkeys[j-1]; j-- {
190 mkeys[j], mkeys[j-1] = mkeys[j-1], mkeys[j]
191 }
192 }
193 for mki := 0; mki < len(mkeys); mki++ {
194 mname := mkeys[mki]
195 m := e.pkg.Members[mname]
196 fn, ok := m.(*SSAFunction)
197 if !ok {
198 continue
199 }
200 dotIdx := -1
201 for i := 0; i < len(mname); i++ {
202 if mname[i] == '.' {
203 dotIdx = i
204 break
205 }
206 }
207 if dotIdx < 0 {
208 continue
209 }
210 mpart := mname[dotIdx+1:]
211 if mpart != methodName {
212 continue
213 }
214 tname := mname[:dotIdx]
215 looked := e.pkg.Pkg.Scope.Lookup(tname)
216 if looked == nil {
217 continue
218 }
219 tn, ok2 := looked.(*TypeName)
220 if !ok2 || tn.Typ == nil {
221 continue
222 }
223 isPtrRecv := fn.object != nil && fn.object.PtrRecv
224 recvT := tn.Typ
225 if isPtrRecv {
226 recvT = NewPointer(recvT)
227 }
228 push(impls, ifaceImpl{fn: fn, recvType: recvT, ptrRecv: isPtrRecv})
229 hasType[tname] = true
230 }
231 scopeNames := e.pkg.Pkg.Scope.Names()
232 for sni := 0; sni < len(scopeNames); sni++ {
233 sname := scopeNames[sni]
234 tn2, ok4 := e.pkg.Pkg.Scope.Lookup(sname).(*TypeName)
235 if !ok4 || tn2.Typ == nil {
236 continue
237 }
238 if hasType[sname] {
239 continue
240 }
241 chain, fn, embedT := e.findEmbedMethod(tn2.Typ, methodName, 0)
242 if fn != nil {
243 isPtrRecv := fn.object != nil && fn.object.PtrRecv
244 push(impls, ifaceImpl{
245 fn: fn,
246 recvType: NewPointer(tn2.Typ),
247 ptrRecv: isPtrRecv,
248 embedField: chain[0],
249 embedType: embedT,
250 embedChain: chain,
251 })
252 hasType[sname] = true
253 }
254 }
255 selfPath := e.pkg.Pkg.Path
256 var importedPkgs []*TCPackage
257 seenPkg := map[string]bool{}
258 // Explicit import list first: scope scanning misses imports that lost
259 // a local-name collision (imports are file-scoped).
260 for _, ip := range e.pkg.Pkg.Imports {
261 if seenPkg[ip.Path] {
262 continue
263 }
264 push(importedPkgs, ip)
265 seenPkg[ip.Path] = true
266 }
267 scopeNames2 := e.pkg.Pkg.Scope.Names()
268 for sni2 := 0; sni2 < len(scopeNames2); sni2++ {
269 obj := e.pkg.Pkg.Scope.Lookup(scopeNames2[sni2])
270 pn, ok := obj.(*PkgName)
271 if !ok || pn.Imported == nil || seenPkg[pn.Imported.Path] {
272 continue
273 }
274 push(importedPkgs, pn.Imported)
275 seenPkg[pn.Imported.Path] = true
276 }
277 // The build is two-phase: every package in the link set is type-checked
278 // (TypeCheckOnly/loadMxh) before any package is emitted, so the registry
279 // holds the whole link set here. Scan it all: an interface parameter can
280 // receive values of any type in the link, including types from packages
281 // downstream of this one (io.ReadFull dispatching Read on a
282 // crypto/rand.reader). Cross-package impl symbols are declared
283 // extern_weak (see declareExtInvoke): a link reusing this package's
284 // cached .bc without some impl's package resolves the weak ref to null,
285 // and that dispatch arm is dead there because no make-interface site can
286 // produce the matching type-ID without the defining package linked.
287 // moxie/runtime/unsafe are runtime-provided (legacy symbol mangling,
288 // local linkage) and never linkable.
289 var regPaths []string
290 for rPath := range cctx.universe.Registry {
291 push(regPaths, rPath)
292 }
293 for i := 1; i < len(regPaths); i++ {
294 for j := i; j > 0 && regPaths[j] < regPaths[j-1]; j-- {
295 regPaths[j], regPaths[j-1] = regPaths[j-1], regPaths[j]
296 }
297 }
298 for rpi := 0; rpi < len(regPaths); rpi++ {
299 rPath := regPaths[rpi]
300 rPkg := cctx.universe.Registry[rPath]
301 if rPkg == nil || seenPkg[rPath] {
302 continue
303 }
304 if rPath == "moxie" || rPath == "runtime" || rPath == "unsafe" {
305 continue
306 }
307 push(importedPkgs, rPkg)
308 }
309 for ipi := 0; ipi < len(importedPkgs); ipi++ {
310 ipkg := importedPkgs[ipi]
311 pkgPath := ipkg.Path
312 if pkgPath == selfPath {
313 continue
314 }
315 if ipkg.Scope == nil {
316 continue
317 }
318 names := ipkg.Scope.Names()
319 for ni := 0; ni < len(names); ni++ {
320 tname := names[ni]
321 tn3, ok7 := ipkg.Scope.Lookup(tname).(*TypeName)
322 if !ok7 || tn3.Typ == nil {
323 continue
324 }
325 named3, ok8 := tn3.Typ.(*Named)
326 if !ok8 {
327 continue
328 }
329 // Generic types' methods are never emitted as standalone symbols
330 // (monomorphization only); a direct extern call would be undefined.
331 if len(named3.TParams) > 0 {
332 continue
333 }
334 found := false
335 for mi := 0; mi < named3.NumMethods(); mi++ {
336 m := named3.Method(mi)
337 if m.Name != methodName {
338 continue
339 }
340 isPR := m.PtrRecv
341 sym := pkgPath | "." | tname | "." | methodName
342 tid := ""
343 if isPR {
344 tid = "reflect/types.type:pointer:named:" | pkgPath | "." | tname
345 } else {
346 tid = "reflect/types.type:named:" | pkgPath | "." | tname
347 }
348 msig := m.Signature()
349 push(impls, ifaceImpl{
350 recvType: tn3.Typ,
351 ptrRecv: isPR,
352 extSymbol: sym,
353 extTypeID: tid,
354 extSig: msig,
355 })
356 found = true
357 }
358 if !found {
359 chain, embedSym, embedSig, embedT := findExtEmbedMethod(named3, pkgPath, tname, methodName, 0)
360 if embedSym != "" {
361 tid := "reflect/types.type:pointer:named:" | pkgPath | "." | tname
362 push(impls, ifaceImpl{
363 recvType: NewPointer(tn3.Typ),
364 ptrRecv: true,
365 extSymbol: embedSym,
366 extTypeID: tid,
367 extSig: embedSig,
368 embedField: chain[0],
369 embedType: embedT,
370 embedChain: chain,
371 })
372 }
373 }
374 }
375 }
376 for i := 1; i < len(impls); i++ {
377 for j := i; j > 0 && impls[j].recvType.String() < impls[j-1].recvType.String(); j-- {
378 impls[j], impls[j-1] = impls[j-1], impls[j]
379 }
380 }
381 return impls
382 }
383
384 func (e *irEmitter) findEmbedMethod(t Type, methodName string, depth int32) (chain []int32, fn *SSAFunction, embedT Type) {
385 if depth > 5 {
386 return nil, nil, nil
387 }
388 st, ok := SafeUnderlying(t).(*TCStruct)
389 if !ok {
390 return nil, nil, nil
391 }
392 for fi := 0; fi < st.NumFields(); fi++ {
393 f := st.Field(fi)
394 if !f.Anonymous {
395 continue
396 }
397 embedType := f.Typ
398 embedName := ""
399 if en, ok2 := embedType.(*Named); ok2 && en.Obj != nil {
400 embedName = en.Obj.Name
401 }
402 if embedName == "" {
403 continue
404 }
405 embedMName := embedName | "." | methodName
406 if fn2, ok2 := e.pkg.Members[embedMName].(*SSAFunction); ok2 {
407 return []int32{fi}, fn2, embedType
408 }
409 sub, fn3, embedT2 := e.findEmbedMethod(embedType, methodName, depth+1)
410 if fn3 != nil {
411 return []int32{fi} | sub, fn3, embedT2
412 }
413 }
414 return nil, nil, nil
415 }
416
417 // findExtEmbedMethod searches embedded fields of a cross-package Named type
418 // for a method with the given name. Returns the field index chain, the
419 // external symbol for the method, the method signature, and the embed type.
420 func namedObjName(n *Named) (s string) {
421 if n != nil && n.Obj != nil {
422 return n.Obj.Name
423 }
424 return ""
425 }
426
427 func findExtEmbedMethod(named *Named, pkgPath string, outerName string, methodName string, depth int32) (chain []int32, sym string, sig *Signature, embedT Type) {
428 if depth > 5 {
429 return nil, "", nil, nil
430 }
431 st, ok := SafeUnderlying(named).(*TCStruct)
432 if !ok {
433 return nil, "", nil, nil
434 }
435 for fi := 0; fi < st.NumFields(); fi++ {
436 f := st.Field(fi)
437 if !f.Anonymous {
438 continue
439 }
440 embedType := f.Typ
441 var embedNamed *Named
442 if en, ok2 := embedType.(*Named); ok2 {
443 embedNamed = en
444 } else if p, ok3 := embedType.(*Pointer); ok3 {
445 if en2, ok4 := p.Base.(*Named); ok4 {
446 embedNamed = en2
447 }
448 }
449 if embedNamed == nil || embedNamed.Obj == nil {
450 continue
451 }
452 embedPkg := pkgPath
453 embedName := namedObjName(embedNamed)
454 if embedNamed.Obj.Pkg != nil {
455 embedPkg = embedNamed.Obj.Pkg.Path
456 }
457 for mi := 0; mi < embedNamed.NumMethods(); mi++ {
458 m := embedNamed.Method(mi)
459 if m.Name != methodName {
460 continue
461 }
462 sym2 := embedPkg | "." | embedName | "." | methodName
463 return []int32{fi}, sym2, m.Signature(), embedType
464 }
465 sub, sym3, sig2, eT := findExtEmbedMethod(embedNamed, embedPkg, embedName, methodName, depth+1)
466 if sym3 != "" {
467 return []int32{fi} | sub, sym3, sig2, eT
468 }
469 }
470 return nil, "", nil, nil
471 }
472
473 type ifaceImpl struct {
474 fn *SSAFunction
475 recvType Type
476 ptrRecv bool
477 embedField int32
478 embedType Type
479 embedChain []int32
480 extSymbol string
481 extTypeID string
482 extSig *Signature
483 }
484
485 func (e *irEmitter) implFuncSym(impl ifaceImpl) (s string) {
486 if impl.extSymbol != "" {
487 if irNeedsQuote(impl.extSymbol) {
488 return "@\"" | impl.extSymbol | "\""
489 }
490 return "@" | impl.extSymbol
491 }
492 return e.funcSymbol(impl.fn)
493 }
494
495 func (e *irEmitter) declareExtInvoke(impl ifaceImpl, inv *SSAInvoke) {
496 if impl.extSymbol == "" {
497 return
498 }
499 sym := e.implFuncSym(impl)
500 if _, ok := e.extDecls[sym]; ok {
501 return
502 }
503 for _, p := range e.pendExtDecls {
504 if p.k == sym { return }
505 }
506 // Use the impl's actual return type (not the caller's expected type)
507 // so the declaration matches the definition in the defining package.
508 retType := e.llvmType(inv.SSAType())
509 if impl.fn != nil && impl.fn.Signature != nil && impl.fn.Signature.Results != nil {
510 implRet := e.llvmType(impl.fn.Signature.Results)
511 if implRet != "" && implRet != "void" {
512 retType = implRet
513 }
514 } else if impl.extSig != nil && impl.extSig.Results != nil {
515 implRet := e.llvmType(impl.extSig.Results)
516 if implRet != "" && implRet != "void" {
517 retType = implRet
518 }
519 }
520 useSret := needsSret(retType)
521 // All receivers are ptr in Moxie (no value receivers).
522 recvLLVM := "ptr"
523 params := ""
524 if useSret {
525 params = "ptr, "
526 }
527 var psegs []string
528 if params != "" {
529 push(psegs, params)
530 }
531 push(psegs, recvLLVM)
532 var sig *Signature
533 if impl.fn != nil && impl.fn.Signature != nil {
534 sig = impl.fn.Signature
535 } else if impl.extSig != nil {
536 sig = impl.extSig
537 }
538 if sig != nil && sig.Params != nil {
539 for i := 0; i < sig.Params.Len(); i++ {
540 pt := e.llvmType(sig.Params.At(i).Typ)
541 if pt == "void" {
542 pt = "ptr"
543 }
544 push(psegs, pt)
545 }
546 } else {
547 for _, arg := range inv.Args {
548 argT := e.llvmType(arg.SSAType())
549 if argT == "void" {
550 argT = "ptr"
551 }
552 push(psegs, argT)
553 }
554 }
555 push(psegs, "ptr")
556 params = joinStrs(commaSep(psegs))
557 // extern_weak: dispatch lists enumerate impls from the whole link set;
558 // a link reusing this module's cached .bc without an impl's package
559 // resolves the ref to null instead of failing, and the guarded call arm
560 // is dead there (no make-interface site can produce the type-ID).
561 declRet := retType
562 if useSret {
563 declRet = "void"
564 }
565 push(e.pendExtDecls, emitKV{sym, "extern_weak " | declRet | " " | sym | "(" | params | ")"})
566 }
567
568 func (e *irEmitter) emitExtInvokeCall(reg, retType, funcSym, recvLLVM, recv string, inv *SSAInvoke, isVoid bool, impl ifaceImpl) {
569 var casegs []string
570 push(casegs, recvLLVM | " " | recv)
571 var sig *Signature
572 if impl.fn != nil {
573 sig = impl.fn.Signature
574 } else if impl.extSig != nil {
575 sig = impl.extSig
576 }
577 for i, arg := range inv.Args {
578 argT := e.llvmType(arg.SSAType())
579 if argT == "void" {
580 argT = "ptr"
581 }
582 argVal := e.operand(arg)
583 if sig != nil && sig.Params != nil && i < sig.Params.Len() {
584 pt := e.llvmType(sig.Params.At(i).Typ)
585 if pt == "void" {
586 pt = "ptr"
587 }
588 if pt != argT && pt != "" {
589 e.nextReg++
590 tmp := "%eicast" | irItoa(e.nextReg)
591 e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(pt) ; e.w("\n")
592 e.w(" store ") ; e.w(pt) ; e.w(" zeroinitializer, ptr ") ; e.w(tmp) ; e.w("\n")
593 e.w(" store ") ; e.w(argT) ; e.w(" ") ; e.w(argVal) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
594 e.nextReg++
595 loaded := "%eild" | irItoa(e.nextReg)
596 e.w(" ") ; e.w(loaded) ; e.w(" = load ") ; e.w(pt) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
597 argT = pt
598 argVal = loaded
599 }
600 }
601 push(casegs, argT | " " | argVal)
602 }
603 push(casegs, "ptr null")
604 callArgs := joinStrs(commaSep(casegs))
605 e.w(" ")
606 if !isVoid {
607 e.w(reg) ; e.w(" = ")
608 }
609 e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(funcSym) ; e.w("(")
610 e.w(callArgs)
611 e.w(")\n")
612 }
613
614 func (e *irEmitter) implTypeID(impl ifaceImpl) (s string) {
615 if impl.extTypeID != "" {
616 return e.hashToIntptr(fnv1a64(impl.extTypeID))
617 }
618 return e.typeIDHash(impl.recvType)
619 }
620
621 // emitIfaceAssertOK emits an i1 that is true when the dynamic type-ID in
622 // typeID belongs to a type whose method set covers every method of the
623 // asserted interface. Candidates come from the same whole-link-set scan
624 // invoke dispatch uses (findIfaceImpls), so assert and invoke agree on
625 // what implements what. Pointer types carry the full method set; value
626 // types only value-receiver methods (Go method-set rules).
627 func (e *irEmitter) emitIfaceAssertOK(ifaceT *TCInterface, typeID string) (s string) {
628 mnames, msigs := collectIfaceMethodInfo(ifaceT, 0)
629 // The universe error interface carries an implicit String() string so fmt
630 // can print errors; concrete error types are not required to declare it.
631 // Drop it from the required set whenever Error is present, or no type
632 // would ever cover the full set.
633 hasError := false
634 for mi := 0; mi < len(mnames); mi++ {
635 if mnames[mi] == "Error" {
636 hasError = true
637 }
638 }
639 if hasError {
640 var keepN []string
641 var keepS []*Signature
642 for mi := 0; mi < len(mnames); mi++ {
643 if mnames[mi] != "String" {
644 push(keepN, mnames[mi])
645 push(keepS, msigs[mi])
646 }
647 }
648 mnames = keepN
649 msigs = keepS
650 }
651 n := int32(len(mnames))
652 allCount := map[string]int32{}
653 valCount := map[string]int32{}
654 var order []string
655 for mi := int32(0); mi < n; mi++ {
656 impls := e.findIfaceImpls(mnames[mi])
657 seen := map[string]bool{}
658 for ii := 0; ii < len(impls); ii++ {
659 // Check return type and parameter count compatibility.
660 if msigs[mi] != nil && impls[ii].fn != nil && impls[ii].fn.Signature != nil {
661 ifaceRet := e.llvmType(msigs[mi].Results)
662 implRet := e.llvmType(impls[ii].fn.Signature.Results)
663 if ifaceRet != implRet {
664 continue
665 }
666 ifaceParams := int32(0)
667 if msigs[mi].Params != nil {
668 ifaceParams = int32(msigs[mi].Params.Len())
669 }
670 implParams := int32(0)
671 if impls[ii].fn.Signature.Params != nil {
672 implParams = int32(impls[ii].fn.Signature.Params.Len())
673 }
674 if ifaceParams != implParams {
675 continue
676 }
677 }
678 base := e.implBaseTypeName(impls[ii])
679 if base == "" || seen[base] {
680 continue
681 }
682 seen[base] = true
683 if _, have := allCount[base]; !have {
684 push(order, base)
685 }
686 allCount[base] = allCount[base] + 1
687 if !impls[ii].ptrRecv {
688 valCount[base] = valCount[base] + 1
689 }
690 }
691 }
692 pfx := "reflect/types.type:"
693 var tids []string
694 for oi := 0; oi < len(order); oi++ {
695 base := order[oi]
696 if allCount[base] == n {
697 push(tids, e.hashToIntptr(fnv1a64(pfx|"pointer:"|base[len(pfx):])))
698 }
699 if valCount[base] == n {
700 push(tids, e.hashToIntptr(fnv1a64(base)))
701 }
702 }
703 ok := ""
704 for ti := 0; ti < len(tids); ti++ {
705 cmp := e.nextReg2("ta")
706 e.w(" ") ; e.w(cmp) ; e.w(" = icmp eq " | e.intptrType() | " ") ; e.w(typeID) ; e.w(", ") ; e.w(tids[ti]) ; e.w("\n")
707 if ok == "" {
708 ok = cmp
709 } else {
710 acc := e.nextReg2("ta")
711 e.w(" ") ; e.w(acc) ; e.w(" = or i1 ") ; e.w(ok) ; e.w(", ") ; e.w(cmp) ; e.w("\n")
712 ok = acc
713 }
714 }
715 if ok == "" {
716 // no implementer in the link set: the assertion can never succeed
717 ok = e.nextReg2("ta")
718 e.w(" ") ; e.w(ok) ; e.w(" = icmp eq " | e.intptrType() | " ") ; e.w(typeID) ; e.w(", -1\n")
719 }
720 return ok
721 }
722
723 // collectIfaceMethodNames returns the deduplicated method names of an
724 // interface, walking embedded interfaces when the completed allMethods
725 // list is unavailable.
726 func collectIfaceMethodNames(ifaceT *TCInterface, depth int32) (ss []string) {
727 names, _ := collectIfaceMethodInfo(ifaceT, depth)
728 return names
729 }
730
731 func collectIfaceMethodInfo(ifaceT *TCInterface, depth int32) (ss []string, sigs []*Signature) {
732 if ifaceT == nil || depth > 4 {
733 return nil, nil
734 }
735 var names []string
736 var sigList []*Signature
737 if ifaceT.NumMethods() > 0 {
738 for i := int32(0); i < ifaceT.NumMethods(); i++ {
739 push(names, ifaceT.Method(i).Name)
740 push(sigList, ifaceT.Method(i).Sig)
741 }
742 } else {
743 for i := int32(0); i < ifaceT.NumExplicitMethods(); i++ {
744 push(names, ifaceT.ExplicitMethod(i).Name)
745 push(sigList, ifaceT.ExplicitMethod(i).Sig)
746 }
747 for i := int32(0); i < ifaceT.NumEmbeddeds(); i++ {
748 if em, ok := SafeUnderlying(ifaceT.EmbeddedType(i)).(*TCInterface); ok {
749 en, es := collectIfaceMethodInfo(em, depth+1)
750 names = names | en
751 sigList = sigList | es
752 }
753 }
754 }
755 seen := map[string]bool{}
756 var outN []string
757 var outS []*Signature
758 for i := 0; i < len(names); i++ {
759 if seen[names[i]] {
760 continue
761 }
762 seen[names[i]] = true
763 push(outN, names[i])
764 if i < len(sigList) {
765 push(outS, sigList[i])
766 } else {
767 push(outS, nil)
768 }
769 }
770 return outN, outS
771 }
772
773 // implBaseTypeName returns the reflect name of the impl's receiver base
774 // (non-pointer) type, used to intersect method coverage across impls.
775 func (e *irEmitter) implBaseTypeName(impl ifaceImpl) (s string) {
776 if impl.extTypeID != "" {
777 name := impl.extTypeID
778 if mxutil.HasPrefix(name, "reflect/types.type:pointer:") {
779 name = "reflect/types.type:" | name[len("reflect/types.type:pointer:"):]
780 }
781 return name
782 }
783 rt := impl.recvType
784 if pt, isP := rt.(*Pointer); isP {
785 rt = pt.Base
786 }
787 if rt == nil {
788 return ""
789 }
790 return e.reflectTypeName(rt)
791 }
792
793 func (e *irEmitter) emitEmbedChainGEP(impl ifaceImpl, valPtr string) (s string) {
794 chain := impl.embedChain
795 if len(chain) == 0 {
796 chain = []int32{impl.embedField}
797 }
798 outerType := impl.recvType
799 if pt, ok := outerType.(*Pointer); ok {
800 outerType = pt.Base
801 }
802 cur := valPtr
803 curType := outerType
804 for _, idx := range chain {
805 outerLLVM := e.llvmType(curType)
806 gep := e.nextReg2("eg")
807 e.w(" ") ; e.w(gep) ; e.w(" = getelementptr inbounds ") ; e.w(outerLLVM)
808 e.w(", ptr ") ; e.w(cur) ; e.w(", i32 0, i32 ") ; e.w(irItoa(idx)) ; e.w("\n")
809 cur = gep
810 st, ok := SafeUnderlying(curType).(*TCStruct)
811 if ok && idx < st.NumFields() {
812 curType = st.Field(idx).Typ
813 }
814 }
815 return cur
816 }
817
818 type invokeArg struct {
819 typ string
820 val string
821 }
822
823 func (e *irEmitter) prepareInvokeArgs(inv *SSAInvoke, impl ifaceImpl) (ss []invokeArg) {
824 var sig *Signature
825 if impl.fn != nil {
826 sig = impl.fn.Signature
827 } else if impl.extSig != nil {
828 sig = impl.extSig
829 }
830 var result []invokeArg
831 for i, arg := range inv.Args {
832 argT := e.llvmType(arg.SSAType())
833 if at, ok := e.allocTypes[arg]; ok && at != "ptr" && at != "void" {
834 argT = at
835 }
836 if argT == "void" {
837 argT = "ptr"
838 }
839 argV := e.operand(arg)
840 if sig != nil && sig.Params != nil && i < sig.Params.Len() {
841 pt := e.llvmType(sig.Params.At(i).Typ)
842 if pt != "void" && pt != "ptr" && pt != "" && pt != argT && len(pt) > len(argT) {
843 e.nextReg++
844 tmp := "%icast" | irItoa(e.nextReg)
845 e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(pt) ; e.w("\n")
846 e.w(" store ") ; e.w(pt) ; e.w(" zeroinitializer, ptr ") ; e.w(tmp) ; e.w("\n")
847 e.w(" store ") ; e.w(argT) ; e.w(" ") ; e.w(argV) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
848 e.nextReg++
849 loaded := "%icld" | irItoa(e.nextReg)
850 e.w(" ") ; e.w(loaded) ; e.w(" = load ") ; e.w(pt) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
851 argT = pt
852 argV = loaded
853 }
854 }
855 if argT == "void" {
856 argT = "ptr"
857 }
858 push(result, invokeArg{typ: argT, val: argV})
859 }
860 return result
861 }
862
863 func (e *irEmitter) emitInvokeArgs(args []invokeArg) {
864 for _, a := range args {
865 e.w(", ") ; e.w(a.typ) ; e.w(" ") ; e.w(a.val)
866 }
867 }
868
869 func (e *irEmitter) emitInvoke(inv *SSAInvoke) {
870 reg := e.regName(inv)
871 ifaceVal := e.operand(inv.X)
872 retSSAType := inv.SSAType()
873 if retSSAType == nil && inv.IfaceType != nil {
874 for i := 0; i < inv.IfaceType.NumMethods(); i++ {
875 m := inv.IfaceType.Method(i)
876 if m != nil && m.Name == inv.MethodName && m.Sig != nil {
877 if m.Sig.Results != nil && m.Sig.Results.Len() == 1 {
878 retSSAType = m.Sig.Results.At(0).Typ
879 } else if m.Sig.Results != nil && m.Sig.Results.Len() > 1 {
880 retSSAType = m.Sig.Results
881 }
882 break
883 }
884 }
885 }
886 retType := e.llvmType(retSSAType)
887 isVoid := retType == "void"
888
889 xtype := inv.X.SSAType()
890 concretePtr := false
891 if xtype != nil {
892 u := SafeUnderlying(xtype)
893 if u != nil {
894 _, isP := u.(*Pointer)
895 _, isI := u.(*TCInterface)
896 if isP && !isI {
897 concretePtr = true
898 }
899 }
900 }
901
902 tidPtr := e.nextReg2("tid")
903 valPtr := e.nextReg2("vp")
904 if concretePtr {
905 e.w(" ") ; e.w(valPtr) ; e.w(" = bitcast ptr ") ; e.w(ifaceVal) ; e.w(" to ptr\n")
906 } else {
907 e.w(" ") ; e.w(tidPtr) ; e.w(" = extractvalue " | e.ifaceType() | " ") ; e.w(ifaceVal) ; e.w(", 0\n")
908 e.w(" ") ; e.w(valPtr) ; e.w(" = extractvalue " | e.ifaceType() | " ") ; e.w(ifaceVal) ; e.w(", 1\n")
909 }
910
911 allImpls := e.findIfaceImpls(inv.MethodName)
912 // Filter impls by return type and parameter count compatibility.
913 nInvokeArgs := int32(len(inv.Args))
914 var impls []ifaceImpl
915 for _, impl := range allImpls {
916 if impl.fn != nil && impl.fn.Signature != nil {
917 implRet := e.llvmType(impl.fn.Signature.Results)
918 if retType != implRet && retType != "void" && implRet != "void" {
919 continue
920 }
921 // Parameter count must match the invoke arg count.
922 implParams := int32(0)
923 if impl.fn.Signature.Params != nil {
924 implParams = int32(impl.fn.Signature.Params.Len())
925 }
926 if implParams != nInvokeArgs {
927 continue
928 }
929 } else if impl.extSig != nil {
930 implParams := int32(0)
931 if impl.extSig.Params != nil {
932 implParams = int32(impl.extSig.Params.Len())
933 }
934 if implParams != nInvokeArgs {
935 continue
936 }
937 }
938 push(impls, impl)
939 }
940 for _, impl := range impls {
941 e.declareExtInvoke(impl, inv)
942 }
943
944 if len(impls) == 0 {
945 e.w(" ; invoke: no implementations for ") ; e.w(inv.MethodName) ; e.w("\n")
946 if !isVoid {
947 e.nextReg++
948 zp := "%zp" | irItoa(e.nextReg)
949 e.w(" ") ; e.w(zp) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n")
950 e.w(" store ") ; e.w(retType) ; e.w(" zeroinitializer, ptr ") ; e.w(zp) ; e.w("\n")
951 e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(zp) ; e.w("\n")
952 }
953 return
954 }
955
956 if len(impls) == 1 {
957 impl := impls[0]
958 callRecv := valPtr
959 if impl.embedType != nil {
960 callRecv = e.emitEmbedChainGEP(impl, valPtr)
961 }
962 // All receivers are ptr in Moxie.
963 recvLLVM := "ptr"
964 recv := callRecv
965 if impl.extSymbol != "" {
966 e.emitExtInvokeCall(reg, retType, e.implFuncSym(impl), recvLLVM, recv, inv, isVoid, impl)
967 } else {
968 e.w(" ")
969 prepArgs := e.prepareInvokeArgs(inv, impl)
970 if !isVoid {
971 e.w(reg) ; e.w(" = ")
972 }
973 e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(e.implFuncSym(impl)) ; e.w("(")
974 e.w(recvLLVM) ; e.w(" ") ; e.w(recv)
975 e.emitInvokeArgs(prepArgs)
976 e.w(", ptr null)\n")
977 }
978 return
979 }
980
981 baseID := e.nextReg
982 mergeLabel := "invoke.merge" | irItoa(baseID)
983 var checkLabels []string
984 var caseLabels []string
985 var callRegs []string
986 for i := range impls {
987 push(checkLabels, "invoke.check" | irItoa(baseID) | "." | irItoa(i))
988 push(caseLabels, "invoke.case" | irItoa(baseID) | "." | irItoa(i))
989 if !isVoid {
990 push(callRegs, e.nextReg2("cr"))
991 }
992 }
993 defaultLabel := "invoke.default" | irItoa(baseID)
994
995 e.w(" br label %") ; e.w(checkLabels[0]) ; e.w("\n")
996
997 for i, impl := range impls {
998 nextCheck := defaultLabel
999 if i < len(impls)-1 {
1000 nextCheck = checkLabels[i+1]
1001 }
1002 e.w(checkLabels[i]) ; e.w(":\n")
1003 tidGlobal := e.implTypeID(impl)
1004 cmpReg := e.nextReg2("cmp")
1005 e.w(" ") ; e.w(cmpReg) ; e.w(" = icmp eq " | e.intptrType() | " ") ; e.w(tidPtr) ; e.w(", ") ; e.w(tidGlobal) ; e.w("\n")
1006 e.w(" br i1 ") ; e.w(cmpReg) ; e.w(", label %") ; e.w(caseLabels[i]) ; e.w(", label %") ; e.w(nextCheck) ; e.w("\n")
1007
1008 e.w(caseLabels[i]) ; e.w(":\n")
1009 // All receivers are ptr in Moxie.
1010 callRecv := valPtr
1011 if impl.embedType != nil {
1012 callRecv = e.emitEmbedChainGEP(impl, valPtr)
1013 }
1014 recvLLVM := "ptr"
1015 recv := callRecv
1016 if impl.extSymbol != "" {
1017 creg := ""
1018 if !isVoid {
1019 creg = callRegs[i]
1020 }
1021 e.emitExtInvokeCall(creg, retType, e.implFuncSym(impl), recvLLVM, recv, inv, isVoid, impl)
1022 } else {
1023 prepArgs := e.prepareInvokeArgs(inv, impl)
1024 e.w(" ")
1025 if !isVoid {
1026 e.w(callRegs[i]) ; e.w(" = ")
1027 }
1028 e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(e.implFuncSym(impl)) ; e.w("(")
1029 e.w(recvLLVM) ; e.w(" ") ; e.w(recv)
1030 e.emitInvokeArgs(prepArgs)
1031 e.w(", ptr null)\n")
1032 }
1033 e.w(" br label %") ; e.w(mergeLabel) ; e.w("\n")
1034 }
1035
1036 e.w(defaultLabel) ; e.w(":\n")
1037 // A typeID that matches no impl means the dispatch list is incomplete -
1038 // fail loud instead of executing unreachable-UB with a garbage method.
1039 {
1040 sty := e.sliceType()
1041 msg := "interface dispatch: no impl for " | inv.MethodName
1042 idx := e.addStringConst(msg)
1043 ipt := e.intptrType()
1044 slen := irItoa64(int64(len(msg)))
1045 e.w(" call void @runtime._panicstr(") ; e.w(sty) ; e.w(" { ptr ") ; e.w(e.strConstGlobal(idx)) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(slen) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(slen) ; e.w(" }, ptr null)\n")
1046 e.declareRuntime("runtime._panicstr", "void", sty)
1047 }
1048 e.w(" unreachable\n")
1049
1050 e.w(mergeLabel) ; e.w(":\n")
1051 if blk := inv.InstrBlock(); blk != nil {
1052 e.blockExitLabel[blk.Index] = "%" | mergeLabel
1053 }
1054 if !isVoid {
1055 e.w(" ") ; e.w(reg) ; e.w(" = phi ") ; e.w(retType) ; e.w(" ")
1056 for i := range impls {
1057 if i > 0 { e.w(", ") }
1058 e.w("[ ") ; e.w(callRegs[i]) ; e.w(", %") ; e.w(caseLabels[i]) ; e.w(" ]")
1059 }
1060 e.w("\n")
1061 }
1062 }
1063
1064 func (e *irEmitter) emitTypeAssert(t *SSATypeAssert) {
1065 reg := e.regName(t)
1066 val := e.operand(t.X)
1067 if t.AssertedType == nil {
1068 e.w(" ; skipped nil type assertion\n")
1069 return
1070 }
1071 assertedType := e.llvmType(t.AssertedType)
1072 voidAssert := assertedType == "void"
1073 if voidAssert {
1074 assertedType = "ptr"
1075 }
1076 // Check if input is already a concrete ptr (not an interface {ptr, ptr})
1077 var xType Type
1078 if t.X != nil {
1079 xType = t.X.SSAType()
1080 }
1081 if xType == nil {
1082 e.w(" ; skipped nil X type\n")
1083 return
1084 }
1085 inputType := e.llvmType(xType)
1086 if at, ok := e.allocTypes[t.X]; ok {
1087 inputType = at
1088 }
1089 var valPtr, typePtr string
1090 if inputType == "ptr" {
1091 valPtr = val
1092 typePtr = "null"
1093 } else {
1094 valPtr = e.nextReg2("ta")
1095 e.w(" ") ; e.w(valPtr) ; e.w(" = extractvalue " | e.ifaceType() | " ") ; e.w(val) ; e.w(", 1\n")
1096 typePtr = e.nextReg2("ta")
1097 e.w(" ") ; e.w(typePtr) ; e.w(" = extractvalue " | e.ifaceType() | " ") ; e.w(val) ; e.w(", 0\n")
1098 }
1099 // Asserting to an interface type: no concrete value ever carries the
1100 // interface type's own hash, so the check must instead admit the
1101 // type-ID of any type in the link set whose method set covers the
1102 // interface (same scan invoke dispatch uses).
1103 var ifaceT *TCInterface
1104 if it, okI := SafeUnderlying(t.AssertedType).(*TCInterface); okI && assertedType == e.ifaceType() {
1105 ifaceT = it
1106 }
1107 isIfaceAssert := ifaceT != nil
1108 if t.CommaOk {
1109 var ok string
1110 if isIfaceAssert && typePtr != "null" {
1111 ok = e.emitIfaceAssertOK(ifaceT, typePtr)
1112 } else if isIfaceAssert {
1113 // degenerate concrete-ptr input: statically typed, non-nil check
1114 ok = e.nextReg2("ta")
1115 e.w(" ") ; e.w(ok) ; e.w(" = icmp ne ptr ") ; e.w(valPtr) ; e.w(", null\n")
1116 } else {
1117 tidGlobal := e.typeIDHash(t.AssertedType)
1118 ok = e.nextReg2("ta")
1119 tp := typePtr
1120 if tp == "null" { tp = "0" }
1121 e.w(" ") ; e.w(ok) ; e.w(" = icmp eq " | e.intptrType() | " ") ; e.w(tp) ; e.w(", ") ; e.w(tidGlobal) ; e.w("\n")
1122 }
1123 // A failed comma-ok assertion must yield the zero value, never the
1124 // raw interface data reinterpreted as the asserted type.
1125 var loaded string
1126 if isIfaceAssert {
1127 // result keeps the {i64, ptr} interface representation
1128 tidOut := typePtr
1129 if typePtr == "null" {
1130 tidOut = "0"
1131 if t.X.SSAType() != nil {
1132 tidOut = e.typeIDHash(t.X.SSAType())
1133 }
1134 }
1135 selTid := e.nextReg2("ta")
1136 ipt := e.intptrType()
1137 e.w(" ") ; e.w(selTid) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", " | ipt | " ") ; e.w(tidOut) ; e.w(", " | ipt | " 0\n")
1138 selPtr := e.nextReg2("ta")
1139 e.w(" ") ; e.w(selPtr) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ptr ") ; e.w(valPtr) ; e.w(", ptr null\n")
1140 agg0 := e.nextReg2("ta")
1141 e.w(" ") ; e.w(agg0) ; e.w(" = insertvalue " | e.ifaceType() | " undef, " | e.intptrType() | " ") ; e.w(selTid) ; e.w(", 0\n")
1142 loaded = e.nextReg2("ta")
1143 e.w(" ") ; e.w(loaded) ; e.w(" = insertvalue " | e.ifaceType() | " ") ; e.w(agg0) ; e.w(", ptr ") ; e.w(selPtr) ; e.w(", 1\n")
1144 } else if assertedType == "ptr" {
1145 loaded = e.nextReg2("ta")
1146 e.w(" ") ; e.w(loaded) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ptr ") ; e.w(valPtr) ; e.w(", ptr null\n")
1147 } else if e.isScalarType(assertedType) {
1148 extracted := e.extractScalarFromIface(valPtr, assertedType)
1149 zero := "0"
1150 if assertedType == "float" || assertedType == "double" {
1151 zero = "0.000000e+00"
1152 }
1153 loaded = e.nextReg2("ta")
1154 e.w(" ") ; e.w(loaded) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ") ; e.w(assertedType) ; e.w(" ") ; e.w(extracted) ; e.w(", ") ; e.w(assertedType) ; e.w(" ") ; e.w(zero) ; e.w("\n")
1155 } else {
1156 nonnull0 := e.nextReg2("ta")
1157 e.w(" ") ; e.w(nonnull0) ; e.w(" = icmp ne ptr ") ; e.w(valPtr) ; e.w(", null\n")
1158 nonnull := e.nextReg2("ta")
1159 e.w(" ") ; e.w(nonnull) ; e.w(" = and i1 ") ; e.w(ok) ; e.w(", ") ; e.w(nonnull0) ; e.w("\n")
1160 e.nextReg++
1161 safeLabel := "ta.safe" | irItoa(e.nextReg)
1162 e.nextReg++
1163 zeroLabel := "ta.zero" | irItoa(e.nextReg)
1164 e.nextReg++
1165 mergeLabel := "ta.merge" | irItoa(e.nextReg)
1166 e.w(" br i1 ") ; e.w(nonnull) ; e.w(", label %") ; e.w(safeLabel) ; e.w(", label %") ; e.w(zeroLabel) ; e.w("\n")
1167 e.w(safeLabel) ; e.w(":\n")
1168 realLoad := e.nextReg2("ta")
1169 e.w(" ") ; e.w(realLoad) ; e.w(" = load ") ; e.w(assertedType) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n")
1170 e.w(" br label %") ; e.w(mergeLabel) ; e.w("\n")
1171 e.w(zeroLabel) ; e.w(":\n")
1172 e.w(" br label %") ; e.w(mergeLabel) ; e.w("\n")
1173 e.w(mergeLabel) ; e.w(":\n")
1174 loaded = e.nextReg2("ta")
1175 e.w(" ") ; e.w(loaded) ; e.w(" = phi ") ; e.w(assertedType) ; e.w(" [ ") ; e.w(realLoad) ; e.w(", %") ; e.w(safeLabel) ; e.w(" ], [ zeroinitializer, %") ; e.w(zeroLabel) ; e.w(" ]\n")
1176 if blk := t.InstrBlock(); blk != nil {
1177 e.blockExitLabel[blk.Index] = "%" | mergeLabel
1178 }
1179 }
1180 tupType := "{" | assertedType | ", i1}"
1181 t1 := e.nextReg2("ta")
1182 e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, ") ; e.w(assertedType) ; e.w(" ") ; e.w(loaded) ; e.w(", 0\n")
1183 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t1) ; e.w(", i1 ") ; e.w(ok) ; e.w(", 1\n")
1184 if voidAssert {
1185 e.allocTypes[t] = tupType
1186 }
1187 } else {
1188 // Single-result assertion: panic on type-ID mismatch (Go semantics).
1189 // typePtr == "null" means the input was already a concrete pointer,
1190 // so there is no runtime type information to check.
1191 if typePtr != "null" {
1192 var ok string
1193 if isIfaceAssert {
1194 ok = e.emitIfaceAssertOK(ifaceT, typePtr)
1195 } else {
1196 tidGlobal := e.typeIDHash(t.AssertedType)
1197 ok = e.nextReg2("ta")
1198 e.w(" ") ; e.w(ok) ; e.w(" = icmp eq " | e.intptrType() | " ") ; e.w(typePtr) ; e.w(", ") ; e.w(tidGlobal) ; e.w("\n")
1199 }
1200 e.nextReg++
1201 goodLabel := "ta.good" | irItoa(e.nextReg)
1202 e.nextReg++
1203 badLabel := "ta.bad" | irItoa(e.nextReg)
1204 e.w(" br i1 ") ; e.w(ok) ; e.w(", label %") ; e.w(goodLabel) ; e.w(", label %") ; e.w(badLabel) ; e.w("\n")
1205 e.w(badLabel) ; e.w(":\n")
1206 sty := e.sliceType()
1207 msg := "type assertion failed"
1208 idx := e.addStringConst(msg)
1209 ipt := e.intptrType()
1210 slen := irItoa64(int64(len(msg)))
1211 e.w(" call void @runtime._panicstr(") ; e.w(sty) ; e.w(" { ptr ") ; e.w(e.strConstGlobal(idx)) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(slen) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(slen) ; e.w(" }, ptr null)\n")
1212 e.declareRuntime("runtime._panicstr", "void", sty)
1213 e.w(" unreachable\n")
1214 e.w(goodLabel) ; e.w(":\n")
1215 if blk := t.InstrBlock(); blk != nil {
1216 e.blockExitLabel[blk.Index] = "%" | goodLabel
1217 }
1218 }
1219 if isIfaceAssert {
1220 tidOut := typePtr
1221 if typePtr == "null" {
1222 tidOut = "0"
1223 if t.X.SSAType() != nil {
1224 tidOut = e.typeIDHash(t.X.SSAType())
1225 }
1226 }
1227 agg0 := e.nextReg2("ta")
1228 e.w(" ") ; e.w(agg0) ; e.w(" = insertvalue " | e.ifaceType() | " undef, " | e.intptrType() | " ") ; e.w(tidOut) ; e.w(", 0\n")
1229 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue " | e.ifaceType() | " ") ; e.w(agg0) ; e.w(", ptr ") ; e.w(valPtr) ; e.w(", 1\n")
1230 } else if assertedType == "ptr" {
1231 e.w(" ") ; e.w(reg) ; e.w(" = select i1 true, ptr ") ; e.w(valPtr) ; e.w(", ptr null\n")
1232 } else if e.isScalarType(assertedType) {
1233 extracted := e.extractScalarFromIface(valPtr, assertedType)
1234 e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(assertedType) ; e.w(" ") ; e.w(extracted) ; e.w(", 0\n")
1235 } else {
1236 e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(assertedType) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n")
1237 }
1238 if voidAssert {
1239 e.allocTypes[t] = assertedType
1240 }
1241 }
1242 }
1243
1244 func (e *irEmitter) extractScalarFromIface(valPtr string, assertedType string) (s string) {
1245 ipt := e.intptrType()
1246 srcBits := e.intBits(ipt)
1247 dstBits := e.intBits(assertedType)
1248 // On wasm32 (ipt=i32), values wider than intptr are heap-allocated;
1249 // the interface data field holds a pointer to the value.
1250 if dstBits > srcBits && dstBits > 0 {
1251 loaded := e.nextReg2("ta")
1252 e.w(" ") ; e.w(loaded) ; e.w(" = load ") ; e.w(assertedType) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n")
1253 return loaded
1254 }
1255 raw := e.nextReg2("ta")
1256 e.w(" ") ; e.w(raw) ; e.w(" = ptrtoint ptr ") ; e.w(valPtr) ; e.w(" to ") ; e.w(ipt) ; e.w("\n")
1257 if assertedType == ipt {
1258 return raw
1259 }
1260 if assertedType == "i1" || assertedType == "i8" || assertedType == "i16" || assertedType == "i32" {
1261 tr := e.nextReg2("ta")
1262 e.w(" ") ; e.w(tr) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(raw) ; e.w(" to ") ; e.w(assertedType) ; e.w("\n")
1263 return tr
1264 }
1265 if assertedType == "float" {
1266 tr := e.nextReg2("ta")
1267 e.truncToI32(tr, raw)
1268 bc := e.nextReg2("ta")
1269 e.w(" ") ; e.w(bc) ; e.w(" = bitcast i32 ") ; e.w(tr) ; e.w(" to float\n")
1270 return bc
1271 }
1272 if assertedType == "double" {
1273 loaded := e.nextReg2("ta")
1274 e.w(" ") ; e.w(loaded) ; e.w(" = load double, ptr ") ; e.w(valPtr) ; e.w("\n")
1275 return loaded
1276 }
1277 return raw
1278 }
1279