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