ir_call.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) emitUnOp(u *SSAUnOp) {
   9  	reg := e.regName(u)
  10  	if u.Op == OpMul {
  11  		loadType := e.llvmType(u.SSAType())
  12  		if loadType == "void" {
  13  			if at, ok := e.allocTypes[u.X]; ok {
  14  				loadType = at
  15  			} else if a, ok2 := u.X.(*SSAAlloc); ok2 {
  16  				loadType = e.inferAllocTypeFromStores(a)
  17  			} else {
  18  				loadType = "ptr"
  19  			}
  20  			e.allocTypes[u] = loadType
  21  		}
  22  		if loadType == "ptr" {
  23  			if ia, ok := u.X.(*SSAIndexAddr); ok {
  24  				baseU := SafeUnderlying(ia.X.SSAType())
  25  				if sl, ok3 := baseU.(*Slice); ok3 && sl.Elem != nil {
  26  					et := e.llvmType(sl.Elem)
  27  					if et != "void" && et != "ptr" {
  28  						loadType = et
  29  					}
  30  				} else if b, ok4 := baseU.(*Basic); ok4 && b.Info&IsString != 0 {
  31  					loadType = "i8"
  32  				} else {
  33  					et := e.llvmType(ia.SSAType())
  34  					if p2, ok5 := SafeUnderlying(ia.SSAType()).(*Pointer); ok5 && p2.Base != nil {
  35  						et = e.llvmType(p2.Base)
  36  					}
  37  					if et == "void" {
  38  						et = "i8"
  39  					}
  40  					if et != "ptr" {
  41  						loadType = et
  42  					}
  43  				}
  44  			}
  45  			if fa, ok := u.X.(*SSAFieldAddr); ok {
  46  				baseType := e.resolveFieldAddrBase(fa)
  47  				ft := extractTupleField(baseType, fa.Field)
  48  				if ft != "" && ft != "ptr" && ft != "void" {
  49  					loadType = ft
  50  				}
  51  			}
  52  		}
  53  		if _, isIA := u.X.(*SSAIndexAddr); isIA && len(loadType) > 0 && loadType[0] == '[' {
  54  			if at, ok2 := e.allocTypes[u.X]; ok2 && at != loadType {
  55  				loadType = at
  56  				e.allocTypes[u] = loadType
  57  			}
  58  		}
  59  		isGlobalLoad := false
  60  		if g, ok := u.X.(*SSAGlobal); ok {
  61  			loadType = e.resolveGlobalDeclType(g)
  62  			isGlobalLoad = true
  63  		}
  64  		if !isGlobalLoad {
  65  			if at, ok := e.allocTypes[u.X]; ok && at != "ptr" && at != "void" && at != loadType {
  66  				bothScalar := len(loadType) > 0 && loadType[0] == 'i' && len(at) > 0 && at[0] == 'i'
  67  				isArrayElem := len(at) > 0 && at[0] == '[' && len(loadType) > 0 && loadType[0] != '{' && loadType != at
  68  				bothAgg := len(loadType) > 0 && loadType[0] == '{' && len(at) > 0 && at[0] == '{'
  69  				isAggToScalar := len(loadType) > 0 && loadType[0] == '{' && len(at) > 0 && (at[0] == 'i' || at == "double" || at == "float")
  70  				if !bothScalar && !isArrayElem && !bothAgg && !isAggToScalar {
  71  					loadType = at
  72  					e.allocTypes[u] = loadType
  73  				}
  74  			}
  75  		}
  76  		addr := e.operand(u.X)
  77  		e.w("  ")
  78  		e.w(reg)
  79  		e.w(" = load ")
  80  		e.w(loadType)
  81  		e.w(", ptr ")
  82  		e.w(addr)
  83  		e.w("\n")
  84  		e.setRegType(u, reg, loadType)
  85  		return
  86  	}
  87  	valType := e.llvmType(u.X.SSAType())
  88  	resolved := e.resolvedType(u.X, valType)
  89  	if resolved != valType {
  90  		valType = resolved
  91  	}
  92  	if valType == "void" || valType == "" {
  93  		resType := e.llvmType(u.SSAType())
  94  		if resType != "void" && resType != "" {
  95  			valType = resType
  96  		} else {
  97  			valType = "i32"
  98  		}
  99  	}
 100  	val := e.operand(u.X)
 101  	if u.Op == OpSub {
 102  		isFloat := false
 103  		if b, ok := SafeUnderlying(u.X.SSAType()).(*Basic); ok {
 104  			isFloat = b.Info&IsFloat != 0
 105  		}
 106  		if !isFloat && (valType == "double" || valType == "float") {
 107  			isFloat = true
 108  		}
 109  		if !isFloat && isConstOperand(val) && looksLikeFloat(val) {
 110  			isFloat = true
 111  			resType := e.llvmType(u.SSAType())
 112  			if resType == "float" || resType == "double" {
 113  				valType = resType
 114  			} else {
 115  				valType = "double"
 116  			}
 117  		}
 118  		e.w("  ")
 119  		e.w(reg)
 120  		if isFloat {
 121  			e.w(" = fneg ")
 122  			e.w(valType)
 123  			e.w(" ")
 124  			e.w(val)
 125  			e.w("\n")
 126  			e.setRegType(u, reg, valType)
 127  		} else {
 128  			e.w(" = sub ")
 129  			e.w(valType)
 130  			e.w(" 0, ")
 131  			e.w(val)
 132  			e.w("\n")
 133  		}
 134  		return
 135  	}
 136  	if u.Op == OpNot || u.Op == OpXor {
 137  		e.w("  ")
 138  		e.w(reg)
 139  		e.w(" = xor ")
 140  		e.w(valType)
 141  		e.w(" ")
 142  		e.w(val)
 143  		e.w(", -1\n")
 144  		return
 145  	}
 146  	if u.Op == OpArrow {
 147  		e.emitChanRecv(u)
 148  		return
 149  	}
 150  	e.w("  ; unsupported unop op=")
 151  	e.w(u.Op.String())
 152  	e.w("\n")
 153  }
 154  
 155  func (e *irEmitter) callArgType(arg SSAValue, sig *Signature, i int32) (s string) {
 156  	if _, isFreeVar := arg.(*SSAFreeVar); isFreeVar {
 157  		return "ptr"
 158  	}
 159  	if _, isIA := arg.(*SSAIndexAddr); isIA {
 160  		return "ptr"
 161  	}
 162  	t := e.llvmType(arg.SSAType())
 163  	if _, isAlloc := arg.(*SSAAlloc); !isAlloc {
 164  		resolved := e.resolvedType(arg, t)
 165  		if resolved != t {
 166  			t = resolved
 167  		}
 168  	}
 169  	// Integer constants take the signature's param width: an untyped const
 170  	// defaults to i32, and passing "i32 -1" to an i64 param silently zeroes
 171  	// the high half at the ABI boundary.
 172  	if k, isConst := arg.(*SSAConst); isConst && sig != nil && sig.Params != nil {
 173  		if _, isInt := k.val.(*ConstInt); isInt {
 174  			sigIdx := i
 175  			if sig.Recv != nil {
 176  				sigIdx = i - 1
 177  			}
 178  			if sigIdx >= 0 && sigIdx < sig.Params.Len() {
 179  				sigT := e.llvmType(sig.Params.At(sigIdx).Typ)
 180  				if e.intBits(sigT) > 0 {
 181  					return sigT
 182  				}
 183  			}
 184  		}
 185  	}
 186  	if (t == "ptr" || t == "i1") && sig != nil && sig.Params != nil {
 187  		_, isCall := arg.(*SSACall)
 188  		_, isAlloc := arg.(*SSAAlloc)
 189  		if !isCall && !isAlloc {
 190  			sigIdx := i
 191  			if sig.Recv != nil {
 192  				sigIdx = i - 1
 193  			}
 194  			if sigIdx >= 0 && sigIdx < sig.Params.Len() {
 195  				sigT := e.llvmType(sig.Params.At(sigIdx).Typ)
 196  				if sigT != "ptr" && sigT != "void" && sigT != "i1" && sigT != "" {
 197  					return sigT
 198  				}
 199  			}
 200  		}
 201  	}
 202  	if t == "ptr" || t == "i1" {
 203  		if load, ok := arg.(*SSAUnOp); ok && load.Op == OpMul {
 204  			if g, ok2 := load.X.(*SSAGlobal); ok2 {
 205  				if gt, ok3 := e.globalTypes[e.globalName(g)]; ok3 {
 206  					return gt
 207  				}
 208  			}
 209  		}
 210  	}
 211  	if t != "void" {
 212  		return t
 213  	}
 214  	if sig != nil && sig.Params != nil {
 215  		sigIdx := i
 216  		if sig.Recv != nil {
 217  			sigIdx = i - 1
 218  		}
 219  		if sigIdx >= 0 && sigIdx < sig.Params.Len() {
 220  			return e.llvmType(sig.Params.At(sigIdx).Typ)
 221  		}
 222  	}
 223  	return "ptr"
 224  }
 225  
 226  func (e *irEmitter) callSig(c *SSACall) (s *Signature) {
 227  	if fn, ok := c.Call.Value.(*SSAFunction); ok && fn.Signature != nil {
 228  		return fn.Signature
 229  	}
 230  	if sig, ok := SafeUnderlying(c.Call.Value.SSAType()).(*Signature); ok {
 231  		return sig
 232  	}
 233  	return nil
 234  }
 235  
 236  func (e *irEmitter) emitCall(c *SSACall) {
 237  	if b, ok := c.Call.Value.(*SSABuiltin); ok {
 238  		e.emitBuiltinCall(c, b)
 239  		return
 240  	}
 241  	reg := e.regName(c)
 242  	retType := e.llvmType(c.SSAType())
 243  	isVoid := retType == "void"
 244  	sig := e.callSig(c)
 245  
 246  	if fn, ok := c.Call.Value.(*SSAFunction); ok {
 247  		if !e.isPkgFunc(fn) {
 248  			e.declareExternalFunc(fn)
 249  		}
 250  		useSret2 := needsSret(retType)
 251  		sretTmp2 := ""
 252  		if useSret2 {
 253  			e.nextReg++
 254  			sretTmp2 = "%sret." | irItoa(e.nextReg)
 255  			e.w("  ") ; e.w(sretTmp2) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n")
 256  			e.w("  call void ")
 257  		} else {
 258  			e.w("  ")
 259  			if !isVoid {
 260  				e.w(reg) ; e.w(" = ")
 261  			}
 262  			e.w("call ") ; e.w(retType) ; e.w(" ")
 263  		}
 264  		e.w(e.funcSymbol(fn))
 265  		e.w("(")
 266  		np := 0
 267  		if useSret2 {
 268  			e.w("ptr sret(") ; e.w(retType) ; e.w(") ") ; e.w(sretTmp2)
 269  			np++
 270  		}
 271  		sym := e.funcSymbol(fn)
 272  		isIntrinsic := mxutil.HasPrefix(sym, "@llvm.") || mxutil.HasPrefix(sym, "@\"llvm.")
 273  		isExport := fn.externalSymbol != ""
 274  		for i, arg := range c.Call.Args {
 275  			if np > 0 { e.w(", ") }
 276  			if arg == nil {
 277  				e.w("ptr null")
 278  				np++
 279  				continue
 280  			}
 281  			at := e.callArgType(arg, sig, int32(i))
 282  			av := e.operand(arg)
 283  			if av == "null" && at != "ptr" {
 284  				av = "zeroinitializer"
 285  			}
 286  			e.w(at) ; e.w(" ") ; e.w(av)
 287  			np++
 288  		}
 289  		skipCtx := isIntrinsic || isExport
 290  		if !skipCtx && len(cctx.wasmImportMap) > 0 {
 291  			wsym := sym
 292  			if len(wsym) > 0 && wsym[0] == '@' { wsym = wsym[1:] }
 293  			if len(wsym) >= 2 && wsym[0] == '"' && wsym[len(wsym)-1] == '"' { wsym = wsym[1 : len(wsym)-1] }
 294  			if _, wok := cctx.wasmImportMap[wsym]; wok {
 295  				skipCtx = true
 296  			}
 297  		}
 298  		if !skipCtx {
 299  			if np > 0 { e.w(", ") }
 300  			e.w("ptr null")
 301  		}
 302  		e.w(")\n")
 303  		if useSret2 {
 304  			e.w("  ") ; e.w(reg) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(sretTmp2) ; e.w("\n")
 305  		}
 306  		return
 307  	}
 308  
 309  	funcVal := e.operand(c.Call.Value)
 310  	funcPtr := e.nextReg2("fp")
 311  	ctxPtr := e.nextReg2("fp")
 312  	e.w("  ") ; e.w(funcPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(funcVal) ; e.w(", 1\n")
 313  	e.w("  ") ; e.w(ctxPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(funcVal) ; e.w(", 0\n")
 314  	useSret := needsSret(retType)
 315  	sretTmp := ""
 316  	if useSret {
 317  		e.nextReg++
 318  		sretTmp = "%sret." | irItoa(e.nextReg)
 319  		e.w("  ") ; e.w(sretTmp) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n")
 320  		e.w("  call void ") ; e.w(funcPtr) ; e.w("(ptr sret(") ; e.w(retType) ; e.w(") ") ; e.w(sretTmp)
 321  	} else {
 322  		e.w("  ")
 323  		if !isVoid {
 324  			e.w(reg) ; e.w(" = ")
 325  		}
 326  		e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(funcPtr) ; e.w("(")
 327  	}
 328  	np2 := 0
 329  	if useSret { np2++ }
 330  	for i, arg := range c.Call.Args {
 331  		if np2 > 0 { e.w(", ") }
 332  		at := e.callArgType(arg, sig, int32(i))
 333  		av := e.operand(arg)
 334  		if av == "null" && at != "ptr" {
 335  			av = "zeroinitializer"
 336  		}
 337  		e.w(at) ; e.w(" ") ; e.w(av)
 338  		np2++
 339  	}
 340  	if np2 > 0 { e.w(", ") }
 341  	e.w("ptr ") ; e.w(ctxPtr)
 342  	e.w(")\n")
 343  	if useSret {
 344  		e.w("  ") ; e.w(reg) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(sretTmp) ; e.w("\n")
 345  	}
 346  }
 347  
 348  func (e *irEmitter) emitBuiltinCall(c *SSACall, b *SSABuiltin) {
 349  	reg := e.regName(c)
 350  	name := b.SSAName()
 351  	if name == "recover" {
 352  		e.w("  ; unhandled builtin: recover\n")
 353  		recRT := e.ifaceType()
 354  		e.nextReg++
 355  		tmp := "%ub" | irItoa(e.nextReg)
 356  		e.w("  ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(recRT) ; e.w("\n")
 357  		e.w("  ") ; e.w(reg) ; e.w(" = load ") ; e.w(recRT) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
 358  		e.allocTypes[c] = recRT
 359  		return
 360  	}
 361  	ipt := e.intptrType()
 362  	sty := e.sliceType()
 363  	if name == "len" {
 364  		if len(c.Call.Args) == 1 {
 365  			arg := e.operand(c.Call.Args[0])
 366  			u := SafeUnderlying(c.Call.Args[0].SSAType())
 367  			if u == nil { u = c.Call.Args[0].SSAType() }
 368  			if arr, ok := u.(*Array); ok {
 369  				rt1 := e.llvmType(c.SSAType())
 370  				e.w("  ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt1) ; e.w(" ") ; e.w(irItoa(int32(arr.Len))) ; e.w(", 0\n")
 371  				_ = arg
 372  				return
 373  			}
 374  			if p, ok := u.(*Pointer); ok && p.Base != nil {
 375  				if arr, ok2 := SafeUnderlying(p.Base).(*Array); ok2 {
 376  					rt2 := e.llvmType(c.SSAType())
 377  					e.w("  ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt2) ; e.w(" ") ; e.w(irItoa(int32(arr.Len))) ; e.w(", 0\n")
 378  					_ = arg
 379  					return
 380  				}
 381  			}
 382  			_, isSlice := u.(*Slice)
 383  			_, isMap := u.(*TCMap)
 384  			isStr := e.isStringLike(c.Call.Args[0].SSAType())
 385  			if !isSlice && !isMap && !isStr {
 386  				isSlice = true
 387  			}
 388  			if isMap {
 389  				rt3 := e.llvmType(c.SSAType())
 390  				e.nextReg++
 391  				tmp := "%bl" | irItoa(e.nextReg)
 392  				e.w("  ") ; e.w(tmp) ; e.w(" = call ") ; e.w(ipt) ; e.w(" @runtime.hashmapLen(ptr ") ; e.w(arg) ; e.w(", ptr null)\n")
 393  				if rt3 != ipt {
 394  					e.w("  ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(rt3) ; e.w("\n")
 395  				} else {
 396  					e.w("  ") ; e.w(reg) ; e.w(" = add ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(", 0\n")
 397  				}
 398  				e.declareRuntime("runtime.hashmapLen", ipt, "ptr")
 399  				return
 400  			}
 401  			if isSlice || isStr {
 402  				rt4 := e.llvmType(c.SSAType())
 403  				if rt4 != ipt {
 404  					e.nextReg++
 405  					tmp := "%bl" | irItoa(e.nextReg)
 406  					e.w("  ") ; e.w(tmp) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 1\n")
 407  					e.w("  ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(rt4) ; e.w("\n")
 408  				} else {
 409  					e.w("  ") ; e.w(reg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 1\n")
 410  				}
 411  				return
 412  			}
 413  		}
 414  	} else if name == "cap" {
 415  		if len(c.Call.Args) == 1 {
 416  			arg := e.operand(c.Call.Args[0])
 417  			uc := SafeUnderlying(c.Call.Args[0].SSAType())
 418  			if uc == nil { uc = c.Call.Args[0].SSAType() }
 419  			if arr, ok := uc.(*Array); ok {
 420  				rt5 := e.llvmType(c.SSAType())
 421  				e.w("  ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt5) ; e.w(" ") ; e.w(irItoa(int32(arr.Len))) ; e.w(", 0\n")
 422  				_ = arg
 423  				return
 424  			}
 425  			if p, ok := uc.(*Pointer); ok && p.Base != nil {
 426  				if arr, ok2 := SafeUnderlying(p.Base).(*Array); ok2 {
 427  					rt6 := e.llvmType(c.SSAType())
 428  					e.w("  ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt6) ; e.w(" ") ; e.w(irItoa(int32(arr.Len))) ; e.w(", 0\n")
 429  					_ = arg
 430  					return
 431  				}
 432  			}
 433  			_, isSlice := uc.(*Slice)
 434  			isStr := e.isStringLike(c.Call.Args[0].SSAType())
 435  			if isSlice || isStr {
 436  				rt7 := e.llvmType(c.SSAType())
 437  				if rt7 != ipt {
 438  					e.nextReg++
 439  					tmp := "%bl" | irItoa(e.nextReg)
 440  					e.w("  ") ; e.w(tmp) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 2\n")
 441  					e.w("  ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(rt7) ; e.w("\n")
 442  				} else {
 443  					e.w("  ") ; e.w(reg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 2\n")
 444  				}
 445  				return
 446  			}
 447  		}
 448  	} else if name == "append" {
 449  		if len(c.Call.Args) == 1 {
 450  			src := e.operand(c.Call.Args[0])
 451  			p := e.nextReg2("ap")
 452  			e.w("  ") ; e.w(p) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n")
 453  			l := e.nextReg2("ap")
 454  			e.w("  ") ; e.w(l) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n")
 455  			ca := e.nextReg2("ap")
 456  			e.w("  ") ; e.w(ca) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n")
 457  			s1 := e.nextReg2("ap")
 458  			e.w("  ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(p) ; e.w(", 0\n")
 459  			s2 := e.nextReg2("ap")
 460  			e.w("  ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(l) ; e.w(", 1\n")
 461  			e.w("  ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(ca) ; e.w(", 2\n")
 462  			return
 463  		}
 464  		if len(c.Call.Args) > 2 {
 465  			src := e.operand(c.Call.Args[0])
 466  			elemType := ""
 467  			if sl, ok := SafeUnderlying(c.Call.Args[0].SSAType()).(*Slice); ok {
 468  				elemType = e.llvmType(sl.Elem)
 469  			}
 470  			if sl, ok := c.Call.Args[0].SSAType().(*Slice); ok && (elemType == "" || elemType == "void") {
 471  				elemType = e.llvmType(sl.Elem)
 472  			}
 473  			if elemType == "" || elemType == "void" {
 474  				et := e.llvmType(c.Call.Args[1].SSAType())
 475  				if et != "" && et != "void" {
 476  					elemType = et
 477  				} else {
 478  					elemType = "i8"
 479  				}
 480  			}
 481  			nElems := len(c.Call.Args) - 1
 482  			arrAlloca := e.nextReg2("ap")
 483  			arrTy := "[" | irItoa(nElems) | " x " | elemType | "]"
 484  			e.w("  ") ; e.w(arrAlloca) ; e.w(" = alloca ") ; e.w(arrTy) ; e.w("\n")
 485  			for j := 1; j < len(c.Call.Args); j++ {
 486  				elemVal := e.operand(c.Call.Args[j])
 487  				argLLT := e.llvmType(c.Call.Args[j].SSAType())
 488  				if len(argLLT) > 1 && argLLT[0] == 'i' && len(elemType) > 1 && elemType[0] == 'i' && argLLT != elemType {
 489  					aw := irParseIntWidth(argLLT)
 490  					ew := irParseIntWidth(elemType)
 491  					if ew > 0 && aw > ew {
 492  						tr := e.nextReg2("ap")
 493  						e.w("  ") ; e.w(tr) ; e.w(" = trunc ") ; e.w(argLLT) ; e.w(" ") ; e.w(elemVal) ; e.w(" to ") ; e.w(elemType) ; e.w("\n")
 494  						elemVal = tr
 495  					}
 496  				}
 497  				gep := e.nextReg2("ap")
 498  				e.w("  ") ; e.w(gep) ; e.w(" = getelementptr inbounds ") ; e.w(arrTy)
 499  				e.w(", ptr ") ; e.w(arrAlloca) ; e.w(", i32 0, i32 ") ; e.w(irItoa(j-1)) ; e.w("\n")
 500  				e.w("  store ") ; e.w(elemType) ; e.w(" ") ; e.w(elemVal) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n")
 501  			}
 502  			srcBuf := e.nextReg2("ap")
 503  			e.w("  ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n")
 504  			srcLen := e.nextReg2("ap")
 505  			e.w("  ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n")
 506  			srcCap := e.nextReg2("ap")
 507  			e.w("  ") ; e.w(srcCap) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n")
 508  			elemSz := e.nextReg2("ap")
 509  			e.w("  ") ; e.w(elemSz)
 510  			e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType)
 511  			e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n")
 512  			retTy := e.sliceType()
 513  			result := e.nextReg2("ap")
 514  			e.w("  ") ; e.w(result)
 515  			e.w(" = call ") ; e.w(retTy) ; e.w(" @runtime.sliceAppend(ptr ")
 516  			e.w(srcBuf) ; e.w(", ptr ") ; e.w(arrAlloca)
 517  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen)
 518  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcCap)
 519  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(irItoa(nElems))
 520  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz)
 521  			e.w(", ptr null)\n")
 522  			newPtr := e.nextReg2("ap")
 523  			e.w("  ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n")
 524  			e.scopeTrackAlloc(newPtr)
 525  			newLen := e.nextReg2("ap")
 526  			e.w("  ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n")
 527  			newCap := e.nextReg2("ap")
 528  			e.w("  ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n")
 529  			s1 := e.nextReg2("ap")
 530  			e.w("  ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n")
 531  			s2 := e.nextReg2("ap")
 532  			e.w("  ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newLen) ; e.w(", 1\n")
 533  			e.w("  ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newCap) ; e.w(", 2\n")
 534  			e.declareRuntime("runtime.sliceAppend", retTy, "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType())
 535  			return
 536  		}
 537  		if len(c.Call.Args) == 2 {
 538  			src := e.operand(c.Call.Args[0])
 539  			elems := e.operand(c.Call.Args[1])
 540  			elemType := ""
 541  			arg0t := c.Call.Args[0].SSAType()
 542  			if sl, ok := SafeUnderlying(arg0t).(*Slice); ok {
 543  				elemType = e.llvmType(sl.Elem)
 544  			}
 545  			if sl, ok := arg0t.(*Slice); ok && (elemType == "" || elemType == "void") {
 546  				elemType = e.llvmType(sl.Elem)
 547  			}
 548  			if elemType == "" || elemType == "void" {
 549  				et := e.llvmType(c.Call.Args[1].SSAType())
 550  				if et != "" && et != "void" {
 551  					elemType = et
 552  				} else {
 553  					elemType = "i8"
 554  				}
 555  			}
 556  			srcBuf := e.nextReg2("ap")
 557  			e.w("  ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n")
 558  			srcLen := e.nextReg2("ap")
 559  			e.w("  ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n")
 560  			srcCap := e.nextReg2("ap")
 561  			e.w("  ") ; e.w(srcCap) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n")
 562  			var elemsBuf, elemsLen string
 563  			arg1IsSlice := c.Call.HasDots
 564  			if !arg1IsSlice && c.Call.Args[1] != nil {
 565  				arg1t := c.Call.Args[1].SSAType()
 566  				if arg1t != nil {
 567  					arg1LT := e.llvmType(arg1t)
 568  					if arg1LT == e.sliceType() && elemType != e.sliceType() {
 569  						arg1IsSlice = true
 570  					}
 571  				}
 572  				if !arg1IsSlice {
 573  					arg0t2 := c.Call.Args[0].SSAType()
 574  					if arg0t2 != nil && arg1t != nil {
 575  						if Identical(arg0t2, arg1t) {
 576  							if _, ok := SafeUnderlying(arg0t2).(*Slice); ok {
 577  								arg1IsSlice = true
 578  							}
 579  						}
 580  					}
 581  				}
 582  			}
 583  			if arg1IsSlice {
 584  				elemsBuf = e.nextReg2("ap")
 585  				e.w("  ") ; e.w(elemsBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(elems) ; e.w(", 0\n")
 586  				elemsLen = e.nextReg2("ap")
 587  				e.w("  ") ; e.w(elemsLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(elems) ; e.w(", 1\n")
 588  			} else {
 589  				alloca := e.nextReg2("ap")
 590  				e.w("  ") ; e.w(alloca) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n")
 591  				e.w("  store ") ; e.w(elemType) ; e.w(" ") ; e.w(elems) ; e.w(", ptr ") ; e.w(alloca) ; e.w("\n")
 592  				elemsBuf = alloca
 593  				elemsLen = "1"
 594  			}
 595  			elemSz := e.nextReg2("ap")
 596  			e.w("  ") ; e.w(elemSz)
 597  			e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType)
 598  			e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n")
 599  			retTy := e.sliceType()
 600  			result := e.nextReg2("ap")
 601  			e.w("  ") ; e.w(result)
 602  			e.w(" = call ") ; e.w(retTy) ; e.w(" @runtime.sliceAppend(ptr ")
 603  			e.w(srcBuf) ; e.w(", ptr ") ; e.w(elemsBuf)
 604  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen)
 605  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcCap)
 606  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemsLen)
 607  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz)
 608  			e.w(", ptr null)\n")
 609  			newPtr := e.nextReg2("ap")
 610  			e.w("  ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n")
 611  			e.scopeTrackAlloc(newPtr)
 612  			newLen := e.nextReg2("ap")
 613  			e.w("  ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n")
 614  			newCap := e.nextReg2("ap")
 615  			e.w("  ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n")
 616  			s1 := e.nextReg2("ap")
 617  			e.w("  ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n")
 618  			s2 := e.nextReg2("ap")
 619  			e.w("  ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newLen) ; e.w(", 1\n")
 620  			e.w("  ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newCap) ; e.w(", 2\n")
 621  			e.declareRuntime("runtime.sliceAppend", retTy, "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType())
 622  			return
 623  		}
 624  	} else if name == "copy" {
 625  		if len(c.Call.Args) == 2 {
 626  			dst := e.operand(c.Call.Args[0])
 627  			src := e.operand(c.Call.Args[1])
 628  			elemType := "i8"
 629  			if sl, ok := SafeUnderlying(c.Call.Args[0].SSAType()).(*Slice); ok {
 630  				elemType = e.llvmType(sl.Elem)
 631  			}
 632  			dstBuf := e.nextReg2("cp")
 633  			e.w("  ") ; e.w(dstBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(dst) ; e.w(", 0\n")
 634  			dstLen := e.nextReg2("cp")
 635  			e.w("  ") ; e.w(dstLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(dst) ; e.w(", 1\n")
 636  			srcBuf := e.nextReg2("cp")
 637  			e.w("  ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n")
 638  			srcLen := e.nextReg2("cp")
 639  			e.w("  ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n")
 640  			elemSz := e.nextReg2("cp")
 641  			e.w("  ") ; e.w(elemSz)
 642  			e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType)
 643  			e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n")
 644  			callReg := e.nextReg2("cp")
 645  			e.w("  ") ; e.w(callReg)
 646  			e.w(" = call ") ; e.w(e.intptrType()) ; e.w(" @runtime.sliceCopy(ptr ")
 647  			e.w(dstBuf) ; e.w(", ptr ") ; e.w(srcBuf)
 648  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(dstLen)
 649  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen)
 650  			e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz)
 651  			e.w(", ptr null)\n")
 652  			rt8 := e.llvmType(c.SSAType())
 653  			if rt8 != e.intptrType() {
 654  				e.w("  ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(callReg) ; e.w(" to ") ; e.w(rt8) ; e.w("\n")
 655  			} else {
 656  				e.w("  ") ; e.w(reg) ; e.w(" = add ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(callReg) ; e.w(", 0\n")
 657  			}
 658  			e.declareRuntime("runtime.sliceCopy", e.intptrType(), "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType())
 659  			return
 660  		}
 661  	} else if name == "print" || name == "println" {
 662  		e.w("  call void @runtime.printlock(ptr null)\n")
 663  		for i, arg := range c.Call.Args {
 664  			if i > 0 && b.id == BuiltinPrintln {
 665  				e.w("  call void @runtime.printspace(ptr null)\n")
 666  			}
 667  			av := e.operand(arg)
 668  			at := arg.SSAType()
 669  			e.emitPrintArg(av, at)
 670  		}
 671  		if b.id == BuiltinPrintln {
 672  			e.w("  call void @runtime.printnl(ptr null)\n")
 673  			e.declareRuntime("runtime.printnl", "void", "")
 674  		}
 675  		e.w("  call void @runtime.printunlock(ptr null)\n")
 676  		e.declareRuntime("runtime.printlock", "void", "")
 677  		e.declareRuntime("runtime.printunlock", "void", "")
 678  		if b.id == BuiltinPrintln && len(c.Call.Args) > 1 {
 679  			e.declareRuntime("runtime.printspace", "void", "")
 680  		}
 681  		return
 682  	} else if name == "delete" {
 683  		if len(c.Call.Args) == 2 {
 684  			mapVal := e.operand(c.Call.Args[0])
 685  			keyVal := e.operand(c.Call.Args[1])
 686  			mt, _ := SafeUnderlying(c.Call.Args[0].SSAType()).(*TCMap)
 687  			keyType := "i32"
 688  			if mt != nil {
 689  				keyType = e.llvmType(mt.Key)
 690  			} else if c.Call.Args[1].SSAType() != nil {
 691  				keyType = e.llvmType(c.Call.Args[1].SSAType())
 692  			}
 693  			if mt != nil && e.isStringLike(mt.Key) {
 694  				kd := e.nextReg2("dl")
 695  				kl := e.nextReg2("dl")
 696  				kc := e.nextReg2("dl")
 697  				e.w("  ") ; e.w(kd) ; e.w(" = extractvalue " | e.sliceType() | " ") ; e.w(keyVal) ; e.w(", 0\n")
 698  				e.w("  ") ; e.w(kl) ; e.w(" = extractvalue " | e.sliceType() | " ") ; e.w(keyVal) ; e.w(", 1\n")
 699  				e.w("  ") ; e.w(kc) ; e.w(" = extractvalue " | e.sliceType() | " ") ; e.w(keyVal) ; e.w(", 2\n")
 700  				ipt2 := e.intptrType()
 701  				e.w("  call void @runtime.hashmapContentDelete(ptr ") ; e.w(mapVal)
 702  				e.w(", ptr ") ; e.w(kd)
 703  				e.w(", " | ipt2 | " ") ; e.w(kl)
 704  				e.w(", " | ipt2 | " ") ; e.w(kc)
 705  				e.w(", ptr null)\n")
 706  				e.declareRuntime("runtime.hashmapContentDelete", "void", "ptr, ptr, " | ipt | ", " | ipt)
 707  			} else {
 708  				keyAlloca := e.nextReg2("dl")
 709  				e.w("  ") ; e.w(keyAlloca) ; e.w(" = alloca ") ; e.w(keyType) ; e.w("\n")
 710  				e.w("  store ") ; e.w(keyType) ; e.w(" ") ; e.w(keyVal) ; e.w(", ptr ") ; e.w(keyAlloca) ; e.w("\n")
 711  				e.w("  call void @runtime.hashmapBinaryDelete(ptr ") ; e.w(mapVal)
 712  				e.w(", ptr ") ; e.w(keyAlloca) ; e.w(", ptr null)\n")
 713  				e.declareRuntime("runtime.hashmapBinaryDelete", "void", "ptr, ptr")
 714  			}
 715  			return
 716  		}
 717  	} else if name == "close" {
 718  		if len(c.Call.Args) == 1 {
 719  			e.w("  call void @runtime.chanClose(ptr ")
 720  			e.w(e.operand(c.Call.Args[0]))
 721  			e.w(", ptr null)\n")
 722  			e.declareRuntime("runtime.chanClose", "void", "ptr")
 723  			return
 724  		}
 725  	} else if name == "min" || name == "max" {
 726  		if len(c.Call.Args) >= 2 {
 727  			rt9 := e.llvmType(c.SSAType())
 728  			if rt9 == "" || rt9 == "void" {
 729  				rt9 = "i32"
 730  			}
 731  			a := e.operand(c.Call.Args[0])
 732  			b2 := e.operand(c.Call.Args[1])
 733  			cmpOp := "slt"
 734  			if b.id == BuiltinMax {
 735  				cmpOp = "sgt"
 736  			}
 737  			u := SafeUnderlying(c.SSAType())
 738  			if bb, ok := u.(*Basic); ok && bb.Info&IsUnsigned != 0 {
 739  				cmpOp = "ult"
 740  				if b.id == BuiltinMax {
 741  					cmpOp = "ugt"
 742  				}
 743  			}
 744  			e.nextReg++
 745  			cmpReg := "%mm" | irItoa(e.nextReg)
 746  			e.w("  ") ; e.w(cmpReg) ; e.w(" = icmp ") ; e.w(cmpOp) ; e.w(" ") ; e.w(rt9) ; e.w(" ") ; e.w(a) ; e.w(", ") ; e.w(b2) ; e.w("\n")
 747  			e.w("  ") ; e.w(reg) ; e.w(" = select i1 ") ; e.w(cmpReg) ; e.w(", ") ; e.w(rt9) ; e.w(" ") ; e.w(a) ; e.w(", ") ; e.w(rt9) ; e.w(" ") ; e.w(b2) ; e.w("\n")
 748  			return
 749  		}
 750  	} else if name == "spawn" {
 751  		ipt3 := e.intptrType()
 752  		if len(c.Call.Args) >= 1 {
 753  			// For spawn, we need the raw function pointer, not the fat pointer.
 754  			// operand() wraps SSAFunction in { ptr null, ptr @fn } but ptrtoint
 755  			// needs a plain pointer.
 756  			var rawFnSym string
 757  			if fn, ok := c.Call.Args[0].(*SSAFunction); ok {
 758  				if !e.isPkgFunc(fn) {
 759  					e.declareExternalFunc(fn)
 760  				}
 761  				rawFnSym = e.funcSymbol(fn)
 762  			} else {
 763  				// Closure or indirect - extract funcptr from fat pointer via alloca
 764  				fnVal := e.operand(c.Call.Args[0])
 765  				fpTmp := e.nextReg2("sp")
 766  				e.w("  ") ; e.w(fpTmp) ; e.w(" = alloca {ptr, ptr}\n")
 767  				e.w("  store {ptr, ptr} ") ; e.w(fnVal) ; e.w(", ptr ") ; e.w(fpTmp) ; e.w("\n")
 768  				fpGep := e.nextReg2("sp")
 769  				e.w("  ") ; e.w(fpGep) ; e.w(" = getelementptr inbounds {ptr, ptr}, ptr ") ; e.w(fpTmp) ; e.w(", i32 0, i32 1\n")
 770  				rawFnSym = e.nextReg2("sp")
 771  				e.w("  ") ; e.w(rawFnSym) ; e.w(" = load ptr, ptr ") ; e.w(fpGep) ; e.w("\n")
 772  			}
 773  			fnPtr := e.nextReg2("sp")
 774  			e.w("  ") ; e.w(fnPtr) ; e.w(" = ptrtoint ptr ") ; e.w(rawFnSym) ; e.w(" to ") ; e.w(ipt3) ; e.w("\n")
 775  			// Count channel args
 776  			nChans := int32(0)
 777  			for i := 1; i < len(c.Call.Args); i++ {
 778  				if _, ok := SafeUnderlying(c.Call.Args[i].SSAType()).(*TCChan); ok {
 779  					nChans++
 780  				}
 781  			}
 782  			// TODO: serialize args for Codec boundary, set up ring buffers per channel
 783  			e.w("  call void @runtime.spawnDomain(") ; e.w(ipt3) ; e.w(" ") ; e.w(fnPtr)
 784  			e.w(", ptr null, i32 ") ; e.w(irItoa(nChans)) ; e.w(", ptr null)\n")
 785  			// Get lifecycle channel - returns ptr to channel struct
 786  			e.w("  ") ; e.w(reg) ; e.w(" = call ptr @runtime.SpawnLifecycleChannel(ptr null)\n")
 787  			e.declareRuntime("runtime.spawnDomain", "void", ipt3 | ", ptr, i32")
 788  			e.declareRuntime("runtime.SpawnLifecycleChannel", "ptr", "")
 789  		}
 790  		return
 791  	}
 792  	e.w("  ; unhandled builtin: ")
 793  	e.w(name)
 794  	e.w("\n")
 795  	rt10 := e.llvmType(c.SSAType())
 796  	if rt10 != "void" && rt10 != "" {
 797  		if rt10 == "ptr" || e.intBits(rt10) > 0 || rt10 == "i1" {
 798  			e.emitZeroReg(reg, c.SSAType())
 799  		} else {
 800  			e.nextReg++
 801  			tmp := "%ub" | irItoa(e.nextReg)
 802  			e.w("  ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(rt10) ; e.w("\n")
 803  			e.w("  ") ; e.w(reg) ; e.w(" = load ") ; e.w(rt10) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
 804  			e.allocTypes[c] = rt10
 805  		}
 806  	}
 807  }
 808  
 809  func (e *irEmitter) emitPrintArg(val string, t Type) {
 810  	if t == nil {
 811  		return
 812  	}
 813  	sty := e.sliceType()
 814  	switch u := SafeUnderlying(t).(type) {
 815  	case *Basic:
 816  		switch {
 817  		case u.Info&IsString != 0:
 818  			e.w("  call void @runtime.printstring(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
 819  			e.declareRuntime("runtime.printstring", "void", sty)
 820  		case u.Kind == Bool || u.Kind == UntypedBool:
 821  			e.w("  call void @runtime.printbool(i1 ") ; e.w(val) ; e.w(", ptr null)\n")
 822  			e.declareRuntime("runtime.printbool", "void", "i1")
 823  		case u.Kind == Float32:
 824  			e.w("  call void @runtime.printfloat32(float ") ; e.w(val) ; e.w(", ptr null)\n")
 825  			e.declareRuntime("runtime.printfloat32", "void", "float")
 826  		case u.Kind == Float64 || u.Kind == UntypedFloat:
 827  			e.w("  call void @runtime.printfloat64(double ") ; e.w(val) ; e.w(", ptr null)\n")
 828  			e.declareRuntime("runtime.printfloat64", "void", "double")
 829  		case u.Info&IsUnsigned != 0:
 830  			lt := e.llvmType(t)
 831  			fname := "runtime.printuint" | lt[1:]
 832  			e.w("  call void @") ; e.w(fname) ; e.w("(") ; e.w(lt) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
 833  			e.declareRuntime(fname, "void", lt)
 834  		case u.Info&IsInteger != 0:
 835  			lt := e.llvmType(t)
 836  			fname := "runtime.printint" | lt[1:]
 837  			e.w("  call void @") ; e.w(fname) ; e.w("(") ; e.w(lt) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
 838  			e.declareRuntime(fname, "void", lt)
 839  		}
 840  	case *Pointer:
 841  		ipt := e.intptrType()
 842  		e.nextReg++
 843  		tmp := "%pr" | irItoa(e.nextReg)
 844  		e.w("  ") ; e.w(tmp) ; e.w(" = ptrtoint ptr ") ; e.w(val) ; e.w(" to ") ; e.w(ipt) ; e.w("\n")
 845  		e.w("  call void @runtime.printptr(") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(", ptr null)\n")
 846  		e.declareRuntime("runtime.printptr", "void", ipt)
 847  	case *Slice:
 848  		if b, ok := u.Elem.(*Basic); ok && (b.Kind == Uint8 || b.Kind == Int8) {
 849  			e.w("  call void @runtime.printbytes(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
 850  			e.declareRuntime("runtime.printbytes", "void", sty)
 851  		} else {
 852  			e.w("  call void @runtime.printstring(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
 853  			e.declareRuntime("runtime.printstring", "void", sty)
 854  		}
 855  	case *TCMap:
 856  		e.w("  call void @runtime.printmap(ptr ") ; e.w(val) ; e.w(", ptr null)\n")
 857  		e.declareRuntime("runtime.printmap", "void", "ptr")
 858  	}
 859  }
 860