ir_deepcopy.mx raw

   1  package main
   2  
   3  import (
   4  	. "git.smesh.lol/moxie/pkg/types"
   5  )
   6  
   7  // typeNeedsCopy reports whether a value of type t may contain pointers to
   8  // arena-allocated data that would need relocation across an arena boundary.
   9  func typeNeedsCopy(t Type) (ok bool) {
  10  	return typeNeedsCopyDepth(t, 0)
  11  }
  12  
  13  func typeNeedsCopyDepth(t Type, depth int32) (ok bool) {
  14  	if t == nil || depth > 20 {
  15  		return false
  16  	}
  17  	u := SafeUnderlying(t)
  18  	if u == nil {
  19  		return false
  20  	}
  21  	switch tt := u.(type) {
  22  	case *Pointer:
  23  		return true
  24  	case *Slice:
  25  		return true
  26  	case *Basic:
  27  		return tt.Kind == TCString || tt.Kind == UntypedString
  28  	case *TCMap:
  29  		return true
  30  	case *TCChan:
  31  		return true
  32  	case *Signature:
  33  		return true
  34  	case *TCInterface:
  35  		return true
  36  	case *TCStruct:
  37  		for i := int32(0); i < tt.NumFields(); i++ {
  38  			if typeNeedsCopyDepth(tt.Field(i).Typ, depth+1) {
  39  				return true
  40  			}
  41  		}
  42  	case *Array:
  43  		return typeNeedsCopyDepth(tt.Elem, depth+1)
  44  	}
  45  	return false
  46  }
  47  
  48  func (e *irEmitter) funcNeedsArenaCopy(f *SSAFunction) (ok bool) {
  49  	if f == nil || f.Blocks == nil {
  50  		return false
  51  	}
  52  	if f.Pkg != nil && f.Pkg.Pkg != nil {
  53  		path := f.Pkg.Pkg.Path
  54  		if path == "runtime" || path == "unsafe" {
  55  			return false
  56  		}
  57  	}
  58  	if f.Signature == nil || f.Signature.Results == nil || f.Signature.Results.Len() == 0 {
  59  		return false
  60  	}
  61  	for i := int32(0); i < int32(f.Signature.Results.Len()); i++ {
  62  		if typeNeedsCopy(f.Signature.Results.At(i).Typ) {
  63  			return true
  64  		}
  65  	}
  66  	return false
  67  }
  68  
  69  // --- Arena prologue ---
  70  
  71  func (e *irEmitter) emitArenaPrologue() (ok bool) {
  72  	if !e.funcNeedsArenaCopy(e.curFunc) {
  73  		return false
  74  	}
  75  	ipt := e.intptrType()
  76  	e.w("  %fn_arena = call ptr @runtime.FnArenaPush(")
  77  	e.w(ipt)
  78  	e.w(" 65536, ptr null)\n")
  79  	e.w("  %fn_mark = call ")
  80  	e.w(ipt)
  81  	e.w(" @runtime.ArenaMark(ptr null)\n")
  82  	e.declareRuntime("runtime.FnArenaPush", "ptr", ipt)
  83  	e.declareRuntime("runtime.ArenaMark", ipt, "")
  84  	e.arenaActive = true
  85  	return true
  86  }
  87  
  88  // --- Return with three-pass mark-compact ---
  89  
  90  func (e *irEmitter) emitRetWithArenaCopy(llvmType string, val string) {
  91  	if !e.arenaActive {
  92  		if llvmType == "void" {
  93  			e.w("  ret void\n")
  94  		} else {
  95  			e.w("  ret ") ; e.w(llvmType) ; e.w(" ") ; e.w(val) ; e.w("\n")
  96  		}
  97  		return
  98  	}
  99  
 100  	needsCopy := false
 101  	if val != "zeroinitializer" && llvmType != "void" {
 102  		sig := e.curFunc.Signature
 103  		if sig != nil && sig.Results != nil {
 104  			for i := int32(0); i < int32(sig.Results.Len()); i++ {
 105  				if typeNeedsCopy(sig.Results.At(i).Typ) {
 106  					needsCopy = true
 107  					break
 108  				}
 109  			}
 110  		}
 111  	}
 112  	if !needsCopy {
 113  		e.w("  call void @runtime.FnArenaPopFree(ptr null)\n")
 114  		e.declareRuntime("runtime.FnArenaPopFree", "void", "")
 115  		if llvmType == "void" {
 116  			e.w("  ret void\n")
 117  		} else {
 118  			e.w("  ret ") ; e.w(llvmType) ; e.w(" ") ; e.w(val) ; e.w("\n")
 119  		}
 120  		return
 121  	}
 122  
 123  	ipt := e.intptrType()
 124  	rsType := e.relocStateType()
 125  
 126  	e.nextReg++
 127  	rs := "%rs" ; rs = rs | irItoa(e.nextReg)
 128  	e.w("  ") ; e.w(rs) ; e.w(" = alloca ") ; e.w(rsType) ; e.w("\n")
 129  	e.w("  store ") ; e.w(rsType) ; e.w(" zeroinitializer, ptr ") ; e.w(rs) ; e.w("\n")
 130  
 131  	e.w("  call void @runtime.RelocInit(ptr ") ; e.w(rs) ; e.w(", ptr %fn_arena, ") ; e.w(ipt) ; e.w(" %fn_mark, ptr null)\n")
 132  	e.declareRuntime("runtime.RelocInit", "void", "ptr, ptr, " | ipt)
 133  
 134  	// ---- Pass 1: Mark ----
 135  	e.relocMarkVisited = map[string]bool{}
 136  	results := e.curFunc.Signature.Results
 137  	for i := int32(0); i < int32(results.Len()); i++ {
 138  		rt := results.At(i).Typ
 139  		if !typeNeedsCopy(rt) {
 140  			continue
 141  		}
 142  		var rootReg string
 143  		if results.Len() == 1 {
 144  			rootReg = val
 145  		} else {
 146  			e.nextReg++
 147  			rootReg = "%p1ext" ; rootReg = rootReg | irItoa(e.nextReg)
 148  			e.w("  ") ; e.w(rootReg) ; e.w(" = extractvalue ") ; e.w(llvmType) ; e.w(" ") ; e.w(val) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n")
 149  		}
 150  		u := SafeUnderlying(rt)
 151  		if _, isSt := u.(*TCStruct); isSt {
 152  			slt := e.llvmType(rt)
 153  			e.nextReg++
 154  			tmp := "%m1rt" ; tmp = tmp | irItoa(e.nextReg)
 155  			e.w("  ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(slt) ; e.w("\n")
 156  			e.w("  store ") ; e.w(slt) ; e.w(" ") ; e.w(rootReg) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
 157  			rootReg = tmp
 158  		}
 159  		e.emitRelocMark(rootReg, rt, rs)
 160  	}
 161  
 162  	// ---- Pass 2: Forward ----
 163  	e.nextReg++
 164  	popIgn := "%popign" ; popIgn = popIgn | irItoa(e.nextReg)
 165  	e.w("  ") ; e.w(popIgn) ; e.w(" = call ptr @runtime.FnArenaPop(ptr null)\n")
 166  	e.declareRuntime("runtime.FnArenaPop", "ptr", "")
 167  	e.w("  call void @runtime.RelocForward(ptr ") ; e.w(rs) ; e.w(", ptr null)\n")
 168  	e.declareRuntime("runtime.RelocForward", "void", "ptr")
 169  
 170  	// ---- Pass 3: Compact + Fixup ----
 171  	e.w("  call void @runtime.RelocCompact(ptr ") ; e.w(rs) ; e.w(", ptr null)\n")
 172  	e.declareRuntime("runtime.RelocCompact", "void", "ptr")
 173  	e.w("  call void @runtime.RelocFixupAll(ptr ") ; e.w(rs) ; e.w(", ptr null)\n")
 174  	e.declareRuntime("runtime.RelocFixupAll", "void", "ptr")
 175  
 176  	e.relocFixupVisited = map[string]bool{}
 177  	cur := val
 178  	for i := int32(0); i < int32(results.Len()); i++ {
 179  		rt := results.At(i).Typ
 180  		if !typeNeedsCopy(rt) {
 181  			continue
 182  		}
 183  		var fixReg string
 184  		if results.Len() == 1 {
 185  			fixReg = val
 186  		} else {
 187  			e.nextReg++
 188  			fixReg = "%p3ext" ; fixReg = fixReg | irItoa(e.nextReg)
 189  			e.w("  ") ; e.w(fixReg) ; e.w(" = extractvalue ") ; e.w(llvmType) ; e.w(" ") ; e.w(cur) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n")
 190  		}
 191  		newReg := e.emitRelocFixup(fixReg, rt, rs)
 192  		if results.Len() == 1 {
 193  			cur = newReg
 194  		} else if newReg != fixReg {
 195  			elt := e.llvmType(rt)
 196  			e.nextReg++
 197  			ins := "%p3ins" ; ins = ins | irItoa(e.nextReg)
 198  			e.w("  ") ; e.w(ins) ; e.w(" = insertvalue ") ; e.w(llvmType) ; e.w(" ") ; e.w(cur) ; e.w(", ") ; e.w(elt) ; e.w(" ") ; e.w(newReg) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n")
 199  			cur = ins
 200  		}
 201  	}
 202  
 203  	e.w("  call void @runtime.ArenaRelease(ptr %fn_arena, ptr null)\n")
 204  	e.declareRuntime("runtime.ArenaRelease", "void", "ptr")
 205  	e.w("  ret ") ; e.w(llvmType) ; e.w(" ") ; e.w(cur) ; e.w("\n")
 206  }
 207  
 208  func (e *irEmitter) relocStateType() (s string) {
 209  	ipt := e.intptrType()
 210  	return "{ptr, " | ipt | ", ptr, i32}"
 211  }
 212  
 213  // --- Pass 1 codegen: Mark ---
 214  
 215  func (e *irEmitter) emitRelocMark(reg string, t Type, rs string) {
 216  	if t == nil || !typeNeedsCopy(t) {
 217  		return
 218  	}
 219  	u := SafeUnderlying(t)
 220  	if u == nil {
 221  		return
 222  	}
 223  	ipt := e.intptrType()
 224  	switch tt := u.(type) {
 225  	case *Pointer:
 226  		lt := e.llvmType(tt.Base)
 227  		if lt == "void" || lt == "" {
 228  			lt = "i8"
 229  		}
 230  		e.nextReg++
 231  		sz := "%m1sz" ; sz = sz | irItoa(e.nextReg)
 232  		e.w("  ") ; e.w(sz) ; e.w(" = ptrtoint ptr getelementptr (") ; e.w(lt) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n")
 233  		fixupArg := ipt | " 0"
 234  		if st, isSt := SafeUnderlying(tt.Base).(*TCStruct); isSt {
 235  			fixupSym := e.getOrEmitRelocFixupFn(st)
 236  			if fixupSym != "" {
 237  				fixupArg = ipt | " ptrtoint (ptr " | fixupSym | " to " | ipt | ")"
 238  			}
 239  		}
 240  		e.nextReg++
 241  		isNew := "%m1n" ; isNew = isNew | irItoa(e.nextReg)
 242  		e.w("  ") ; e.w(isNew) ; e.w(" = call i1 @runtime.RelocMark(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(reg) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(sz) ; e.w(", ") ; e.w(fixupArg) ; e.w(", ptr null)\n")
 243  		e.declareRuntime("runtime.RelocMark", "i1", "ptr, ptr, " | ipt | ", " | ipt)
 244  		if typeNeedsCopy(tt.Base) {
 245  			e.nextReg++
 246  			lNew := "m1r" ; lNew = lNew | irItoa(e.nextReg)
 247  			e.nextReg++
 248  			lSkip := "m1s" ; lSkip = lSkip | irItoa(e.nextReg)
 249  			e.w("  br i1 ") ; e.w(isNew) ; e.w(", label %") ; e.w(lNew) ; e.w(", label %") ; e.w(lSkip) ; e.w("\n")
 250  			e.w(lNew) ; e.w(":\n")
 251  			e.emitRelocMarkFields(reg, tt.Base, rs)
 252  			e.w("  br label %") ; e.w(lSkip) ; e.w("\n")
 253  			e.w(lSkip) ; e.w(":\n")
 254  		}
 255  	case *Slice, *Basic:
 256  		st := e.sliceType()
 257  		e.nextReg++
 258  		dp := "%m1d" ; dp = dp | irItoa(e.nextReg)
 259  		e.w("  ") ; e.w(dp) ; e.w(" = extractvalue ") ; e.w(st) ; e.w(" ") ; e.w(reg) ; e.w(", 0\n")
 260  		e.nextReg++
 261  		ln := "%m1l" ; ln = ln | irItoa(e.nextReg)
 262  		e.w("  ") ; e.w(ln) ; e.w(" = extractvalue ") ; e.w(st) ; e.w(" ") ; e.w(reg) ; e.w(", 1\n")
 263  		elemType := "i8"
 264  		if sl, ok := u.(*Slice); ok && sl.Elem != nil {
 265  			elemType = e.llvmType(sl.Elem)
 266  		}
 267  		e.nextReg++
 268  		esz := "%m1e" ; esz = esz | irItoa(e.nextReg)
 269  		e.w("  ") ; e.w(esz) ; e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n")
 270  		e.nextReg++
 271  		blen := "%m1b" ; blen = blen | irItoa(e.nextReg)
 272  		e.w("  ") ; e.w(blen) ; e.w(" = mul ") ; e.w(ipt) ; e.w(" ") ; e.w(ln) ; e.w(", ") ; e.w(esz) ; e.w("\n")
 273  		e.w("  call i1 @runtime.RelocMark(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(dp) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(blen) ; e.w(", ") ; e.w(ipt) ; e.w(" 0, ptr null)\n")
 274  		e.declareRuntime("runtime.RelocMark", "i1", "ptr, ptr, " | ipt | ", " | ipt)
 275  	case *TCMap:
 276  		e.nextReg++
 277  		sz := "%m1m" ; sz = sz | irItoa(e.nextReg)
 278  		e.w("  ") ; e.w(sz) ; e.w(" = ptrtoint ptr getelementptr ({ptr}, ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n")
 279  		e.w("  call i1 @runtime.RelocMark(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(reg) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(sz) ; e.w(", ") ; e.w(ipt) ; e.w(" 0, ptr null)\n")
 280  		e.declareRuntime("runtime.RelocMark", "i1", "ptr, ptr, " | ipt | ", " | ipt)
 281  	case *TCStruct:
 282  		e.emitRelocMarkStructFields(reg, tt, rs)
 283  	}
 284  }
 285  
 286  func (e *irEmitter) emitRelocMarkFields(ptrReg string, t Type, rs string) {
 287  	u := SafeUnderlying(t)
 288  	if u == nil {
 289  		return
 290  	}
 291  	st, ok := u.(*TCStruct)
 292  	if !ok {
 293  		return
 294  	}
 295  	e.emitRelocMarkStructFields(ptrReg, st, rs)
 296  }
 297  
 298  func (e *irEmitter) emitRelocMarkStructFields(ptrReg string, st *TCStruct, rs string) {
 299  	key := e.llvmStructType(st)
 300  	if e.relocMarkVisited[key] {
 301  		return
 302  	}
 303  	e.relocMarkVisited[key] = true
 304  	lt := key
 305  	for i := int32(0); i < st.NumFields(); i++ {
 306  		ft := st.Field(i).Typ
 307  		if !typeNeedsCopy(ft) {
 308  			continue
 309  		}
 310  		e.nextReg++
 311  		gep := "%m1f" ; gep = gep | irItoa(e.nextReg)
 312  		e.w("  ") ; e.w(gep) ; e.w(" = getelementptr ") ; e.w(lt) ; e.w(", ptr ") ; e.w(ptrReg) ; e.w(", i32 0, i32 ") ; e.w(irItoa(i)) ; e.w("\n")
 313  		u := SafeUnderlying(ft)
 314  		if _, isSt := u.(*TCStruct); isSt {
 315  			e.emitRelocMark(gep, ft, rs)
 316  		} else {
 317  			flt := e.llvmType(ft)
 318  			e.nextReg++
 319  			fval := "%m1f" ; fval = fval | irItoa(e.nextReg)
 320  			e.w("  ") ; e.w(fval) ; e.w(" = load ") ; e.w(flt) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n")
 321  			e.emitRelocMark(fval, ft, rs)
 322  		}
 323  	}
 324  	e.relocMarkVisited[key] = false
 325  }
 326  
 327  // --- Pass 3 codegen: Fixup ---
 328  
 329  func (e *irEmitter) emitRelocFixup(reg string, t Type, rs string) (newReg string) {
 330  	if t == nil || !typeNeedsCopy(t) {
 331  		return reg
 332  	}
 333  	u := SafeUnderlying(t)
 334  	if u == nil {
 335  		return reg
 336  	}
 337  	switch tt := u.(type) {
 338  	case *Pointer:
 339  		e.nextReg++
 340  		r := "%p3p" ; r = r | irItoa(e.nextReg)
 341  		e.w("  ") ; e.w(r) ; e.w(" = call ptr @runtime.RelocLookup(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(reg) ; e.w(", ptr null)\n")
 342  		e.declareRuntime("runtime.RelocLookup", "ptr", "ptr, ptr")
 343  		return r
 344  	case *Slice, *Basic:
 345  		st := e.sliceType()
 346  		e.nextReg++
 347  		dp := "%p3s" ; dp = dp | irItoa(e.nextReg)
 348  		e.w("  ") ; e.w(dp) ; e.w(" = extractvalue ") ; e.w(st) ; e.w(" ") ; e.w(reg) ; e.w(", 0\n")
 349  		e.nextReg++
 350  		ndp := "%p3s" ; ndp = ndp | irItoa(e.nextReg)
 351  		e.w("  ") ; e.w(ndp) ; e.w(" = call ptr @runtime.RelocLookup(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(dp) ; e.w(", ptr null)\n")
 352  		e.declareRuntime("runtime.RelocLookup", "ptr", "ptr, ptr")
 353  		e.nextReg++
 354  		result := "%p3s" ; result = result | irItoa(e.nextReg)
 355  		e.w("  ") ; e.w(result) ; e.w(" = insertvalue ") ; e.w(st) ; e.w(" ") ; e.w(reg) ; e.w(", ptr ") ; e.w(ndp) ; e.w(", 0\n")
 356  		return result
 357  	case *TCMap:
 358  		e.nextReg++
 359  		r := "%p3m" ; r = r | irItoa(e.nextReg)
 360  		e.w("  ") ; e.w(r) ; e.w(" = call ptr @runtime.hashmapCopy(ptr ") ; e.w(reg) ; e.w(", ptr null)\n")
 361  		e.declareRuntime("runtime.hashmapCopy", "ptr", "ptr")
 362  		return r
 363  	case *TCStruct:
 364  		return e.emitRelocFixupStruct(reg, tt, rs)
 365  	}
 366  	return reg
 367  }
 368  
 369  func (e *irEmitter) emitRelocFixupFields(ptrReg string, t Type, rs string) {
 370  	u := SafeUnderlying(t)
 371  	st, ok := u.(*TCStruct)
 372  	if !ok {
 373  		return
 374  	}
 375  	key := e.llvmStructType(st)
 376  	if e.relocFixupVisited[key] {
 377  		return
 378  	}
 379  	e.relocFixupVisited[key] = true
 380  	lt := key
 381  	for i := int32(0); i < st.NumFields(); i++ {
 382  		ft := st.Field(i).Typ
 383  		if !typeNeedsCopy(ft) {
 384  			continue
 385  		}
 386  		e.nextReg++
 387  		gep := "%p3ff" ; gep = gep | irItoa(e.nextReg)
 388  		e.w("  ") ; e.w(gep) ; e.w(" = getelementptr ") ; e.w(lt) ; e.w(", ptr ") ; e.w(ptrReg) ; e.w(", i32 0, i32 ") ; e.w(irItoa(i)) ; e.w("\n")
 389  		fu := SafeUnderlying(ft)
 390  		switch fu.(type) {
 391  		case *Pointer:
 392  			e.w("  call void @runtime.RelocFixupPtr(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(gep) ; e.w(", ptr null)\n")
 393  			e.declareRuntime("runtime.RelocFixupPtr", "void", "ptr, ptr")
 394  		case *Slice, *Basic:
 395  			e.w("  call void @runtime.RelocFixupSlice(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(gep) ; e.w(", ptr null)\n")
 396  			e.declareRuntime("runtime.RelocFixupSlice", "void", "ptr, ptr")
 397  		case *TCMap:
 398  			e.nextReg++
 399  			old := "%p3ff" ; old = old | irItoa(e.nextReg)
 400  			e.w("  ") ; e.w(old) ; e.w(" = load ptr, ptr ") ; e.w(gep) ; e.w("\n")
 401  			e.nextReg++
 402  			nw := "%p3ff" ; nw = nw | irItoa(e.nextReg)
 403  			e.w("  ") ; e.w(nw) ; e.w(" = call ptr @runtime.hashmapCopy(ptr ") ; e.w(old) ; e.w(", ptr null)\n")
 404  			e.declareRuntime("runtime.hashmapCopy", "ptr", "ptr")
 405  			e.w("  store ptr ") ; e.w(nw) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n")
 406  		case *TCStruct:
 407  			e.emitRelocFixupFields(gep, ft, rs)
 408  		}
 409  	}
 410  	e.relocFixupVisited[key] = false
 411  }
 412  
 413  func (e *irEmitter) emitRelocFixupStruct(reg string, st *TCStruct, rs string) (newReg string) {
 414  	key := e.llvmStructType(st)
 415  	if e.relocFixupVisited[key] {
 416  		return reg
 417  	}
 418  	e.relocFixupVisited[key] = true
 419  	lt := key
 420  	e.nextReg++
 421  	tmp := "%p3st" ; tmp = tmp | irItoa(e.nextReg)
 422  	e.w("  ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(lt) ; e.w("\n")
 423  	e.w("  store ") ; e.w(lt) ; e.w(" ") ; e.w(reg) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
 424  
 425  	for i := int32(0); i < st.NumFields(); i++ {
 426  		ft := st.Field(i).Typ
 427  		if !typeNeedsCopy(ft) {
 428  			continue
 429  		}
 430  		e.nextReg++
 431  		gep := "%p3st" ; gep = gep | irItoa(e.nextReg)
 432  		e.w("  ") ; e.w(gep) ; e.w(" = getelementptr ") ; e.w(lt) ; e.w(", ptr ") ; e.w(tmp) ; e.w(", i32 0, i32 ") ; e.w(irItoa(i)) ; e.w("\n")
 433  		fu := SafeUnderlying(ft)
 434  		switch fu.(type) {
 435  		case *Pointer:
 436  			e.w("  call void @runtime.RelocFixupPtr(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(gep) ; e.w(", ptr null)\n")
 437  			e.declareRuntime("runtime.RelocFixupPtr", "void", "ptr, ptr")
 438  		case *Slice, *Basic:
 439  			e.w("  call void @runtime.RelocFixupSlice(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(gep) ; e.w(", ptr null)\n")
 440  			e.declareRuntime("runtime.RelocFixupSlice", "void", "ptr, ptr")
 441  		case *TCMap:
 442  			e.nextReg++
 443  			old := "%p3st" ; old = old | irItoa(e.nextReg)
 444  			e.w("  ") ; e.w(old) ; e.w(" = load ptr, ptr ") ; e.w(gep) ; e.w("\n")
 445  			e.nextReg++
 446  			nw := "%p3st" ; nw = nw | irItoa(e.nextReg)
 447  			e.w("  ") ; e.w(nw) ; e.w(" = call ptr @runtime.hashmapCopy(ptr ") ; e.w(old) ; e.w(", ptr null)\n")
 448  			e.declareRuntime("runtime.hashmapCopy", "ptr", "ptr")
 449  			e.w("  store ptr ") ; e.w(nw) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n")
 450  		case *TCStruct:
 451  			e.emitRelocFixupFields(gep, ft, rs)
 452  		}
 453  	}
 454  	e.nextReg++
 455  	result := "%p3st" ; result = result | irItoa(e.nextReg)
 456  	e.w("  ") ; e.w(result) ; e.w(" = load ") ; e.w(lt) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
 457  	e.relocFixupVisited[key] = false
 458  	return result
 459  }
 460  
 461  // --- Per-type fixup functions ---
 462  
 463  func relocFixupSymbol(lt string) (sym string) {
 464  	h := uint32(0x811c9dc5)
 465  	for i := int32(0); i < len(lt); i++ {
 466  		h = h ^ uint32(lt[i])
 467  		h = h * 0x01000193
 468  	}
 469  	return "@__reloc_fixup_" | irItoa(int32(h & 0x7fffffff))
 470  }
 471  
 472  // getOrEmitRelocFixupFn returns the symbol for a per-type fixup function.
 473  func (e *irEmitter) getOrEmitRelocFixupFn(st *TCStruct) (sym string) {
 474  	lt := e.llvmStructType(st)
 475  	if lt == "" {
 476  		return ""
 477  	}
 478  	sym = relocFixupSymbol(st.String())
 479  	bareSym := sym
 480  	if len(bareSym) > 0 && bareSym[0] == '@' {
 481  		bareSym = bareSym[1:]
 482  	}
 483  	for di := int32(0); di < len(e.definedSyms); di++ {
 484  		ds := e.definedSyms[di]
 485  		if len(ds) == len(bareSym) {
 486  			match := true
 487  			for bi := int32(0); bi < len(ds); bi++ {
 488  				if ds[bi] != bareSym[bi] {
 489  					match = false
 490  					break
 491  				}
 492  			}
 493  			if match {
 494  				return sym
 495  			}
 496  		}
 497  	}
 498  	for pi := int32(0); pi < len(e.pendRelocFixupDefs); pi++ {
 499  		ps := e.pendRelocFixupDefs[pi]
 500  		if len(ps) == len(bareSym) {
 501  			match := true
 502  			for bi := int32(0); bi < len(ps); bi++ {
 503  				if ps[bi] != bareSym[bi] {
 504  					match = false
 505  					break
 506  				}
 507  			}
 508  			if match {
 509  				return sym
 510  			}
 511  		}
 512  	}
 513  	push(e.pendRelocFixupDefs, bareSym)
 514  	e.declareRuntime("runtime.RelocFixupPtr", "void", "ptr, ptr")
 515  	e.declareRuntime("runtime.RelocFixupSlice", "void", "ptr, ptr")
 516  	e.declareRuntime("runtime.hashmapCopy", "ptr", "ptr")
 517  	// Write fixup function directly to output file using per-segment writes.
 518  	if e.outFile != nil {
 519  		e.outFile.Write([]byte("\ndefine linkonce_odr void "))
 520  		e.outFile.Write([]byte(sym))
 521  		e.outFile.Write([]byte("(ptr %obj, ptr %rs, ptr %ctx) {\nentry:\n"))
 522  		regN := int32(0)
 523  		for i := int32(0); i < st.NumFields(); i++ {
 524  			ft := st.Field(i).Typ
 525  			if !typeNeedsCopy(ft) {
 526  				continue
 527  			}
 528  			regN++
 529  			gep := "%fg" | irItoa(regN)
 530  			e.outFile.Write([]byte("  "))
 531  			e.outFile.Write([]byte(gep))
 532  			e.outFile.Write([]byte(" = getelementptr "))
 533  			e.outFile.Write([]byte(lt))
 534  			e.outFile.Write([]byte(", ptr %obj, i32 0, i32 "))
 535  			e.outFile.Write([]byte(irItoa(i)))
 536  			e.outFile.Write([]byte("\n"))
 537  			fu := SafeUnderlying(ft)
 538  			switch fu.(type) {
 539  			case *Pointer:
 540  				e.outFile.Write([]byte("  call void @runtime.RelocFixupPtr(ptr %rs, ptr "))
 541  				e.outFile.Write([]byte(gep))
 542  				e.outFile.Write([]byte(", ptr null)\n"))
 543  			case *Slice, *Basic:
 544  				e.outFile.Write([]byte("  call void @runtime.RelocFixupSlice(ptr %rs, ptr "))
 545  				e.outFile.Write([]byte(gep))
 546  				e.outFile.Write([]byte(", ptr null)\n"))
 547  			case *TCMap:
 548  				regN++
 549  				old := "%fg" | irItoa(regN)
 550  				e.outFile.Write([]byte("  "))
 551  				e.outFile.Write([]byte(old))
 552  				e.outFile.Write([]byte(" = load ptr, ptr "))
 553  				e.outFile.Write([]byte(gep))
 554  				e.outFile.Write([]byte("\n"))
 555  				regN++
 556  				chk := "%fg" | irItoa(regN)
 557  				regN++
 558  				ldo := "fgdo" | irItoa(regN)
 559  				regN++
 560  				lsk := "fgsk" | irItoa(regN)
 561  				e.outFile.Write([]byte("  "))
 562  				e.outFile.Write([]byte(chk))
 563  				e.outFile.Write([]byte(" = icmp ne ptr "))
 564  				e.outFile.Write([]byte(old))
 565  				e.outFile.Write([]byte(", null\n"))
 566  				e.outFile.Write([]byte("  br i1 "))
 567  				e.outFile.Write([]byte(chk))
 568  				e.outFile.Write([]byte(", label %"))
 569  				e.outFile.Write([]byte(ldo))
 570  				e.outFile.Write([]byte(", label %"))
 571  				e.outFile.Write([]byte(lsk))
 572  				e.outFile.Write([]byte("\n"))
 573  				e.outFile.Write([]byte(ldo))
 574  				e.outFile.Write([]byte(":\n"))
 575  				regN++
 576  				nw := "%fg" | irItoa(regN)
 577  				e.outFile.Write([]byte("  "))
 578  				e.outFile.Write([]byte(nw))
 579  				e.outFile.Write([]byte(" = call ptr @runtime.hashmapCopy(ptr "))
 580  				e.outFile.Write([]byte(old))
 581  				e.outFile.Write([]byte(", ptr null)\n"))
 582  				e.outFile.Write([]byte("  store ptr "))
 583  				e.outFile.Write([]byte(nw))
 584  				e.outFile.Write([]byte(", ptr "))
 585  				e.outFile.Write([]byte(gep))
 586  				e.outFile.Write([]byte("\n"))
 587  				e.outFile.Write([]byte("  br label %"))
 588  				e.outFile.Write([]byte(lsk))
 589  				e.outFile.Write([]byte("\n"))
 590  				e.outFile.Write([]byte(lsk))
 591  				e.outFile.Write([]byte(":\n"))
 592  			case *TCStruct:
 593  				if innerSt, ok2 := fu.(*TCStruct); ok2 {
 594  					innerLt := e.llvmStructType(innerSt)
 595  					for j := int32(0); j < innerSt.NumFields(); j++ {
 596  						jft := innerSt.Field(j).Typ
 597  						if !typeNeedsCopy(jft) {
 598  							continue
 599  						}
 600  						regN++
 601  						igep := "%fg" | irItoa(regN)
 602  						e.outFile.Write([]byte("  "))
 603  						e.outFile.Write([]byte(igep))
 604  						e.outFile.Write([]byte(" = getelementptr "))
 605  						e.outFile.Write([]byte(innerLt))
 606  						e.outFile.Write([]byte(", ptr "))
 607  						e.outFile.Write([]byte(gep))
 608  						e.outFile.Write([]byte(", i32 0, i32 "))
 609  						e.outFile.Write([]byte(irItoa(j)))
 610  						e.outFile.Write([]byte("\n"))
 611  						jfu := SafeUnderlying(jft)
 612  						switch jfu.(type) {
 613  						case *Pointer:
 614  							e.outFile.Write([]byte("  call void @runtime.RelocFixupPtr(ptr %rs, ptr "))
 615  							e.outFile.Write([]byte(igep))
 616  							e.outFile.Write([]byte(", ptr null)\n"))
 617  						case *Slice, *Basic:
 618  							e.outFile.Write([]byte("  call void @runtime.RelocFixupSlice(ptr %rs, ptr "))
 619  							e.outFile.Write([]byte(igep))
 620  							e.outFile.Write([]byte(", ptr null)\n"))
 621  						case *TCMap:
 622  							regN++
 623  							io := "%fg" | irItoa(regN)
 624  							e.outFile.Write([]byte("  "))
 625  							e.outFile.Write([]byte(io))
 626  							e.outFile.Write([]byte(" = load ptr, ptr "))
 627  							e.outFile.Write([]byte(igep))
 628  							e.outFile.Write([]byte("\n"))
 629  							regN++
 630  							ichk := "%fg" | irItoa(regN)
 631  							regN++
 632  							ildo := "fgdo" | irItoa(regN)
 633  							regN++
 634  							ilsk := "fgsk" | irItoa(regN)
 635  							e.outFile.Write([]byte("  "))
 636  							e.outFile.Write([]byte(ichk))
 637  							e.outFile.Write([]byte(" = icmp ne ptr "))
 638  							e.outFile.Write([]byte(io))
 639  							e.outFile.Write([]byte(", null\n"))
 640  							e.outFile.Write([]byte("  br i1 "))
 641  							e.outFile.Write([]byte(ichk))
 642  							e.outFile.Write([]byte(", label %"))
 643  							e.outFile.Write([]byte(ildo))
 644  							e.outFile.Write([]byte(", label %"))
 645  							e.outFile.Write([]byte(ilsk))
 646  							e.outFile.Write([]byte("\n"))
 647  							e.outFile.Write([]byte(ildo))
 648  							e.outFile.Write([]byte(":\n"))
 649  							regN++
 650  							inw := "%fg" | irItoa(regN)
 651  							e.outFile.Write([]byte("  "))
 652  							e.outFile.Write([]byte(inw))
 653  							e.outFile.Write([]byte(" = call ptr @runtime.hashmapCopy(ptr "))
 654  							e.outFile.Write([]byte(io))
 655  							e.outFile.Write([]byte(", ptr null)\n"))
 656  							e.outFile.Write([]byte("  store ptr "))
 657  							e.outFile.Write([]byte(inw))
 658  							e.outFile.Write([]byte(", ptr "))
 659  							e.outFile.Write([]byte(igep))
 660  							e.outFile.Write([]byte("\n"))
 661  							e.outFile.Write([]byte("  br label %"))
 662  							e.outFile.Write([]byte(ilsk))
 663  							e.outFile.Write([]byte("\n"))
 664  							e.outFile.Write([]byte(ilsk))
 665  							e.outFile.Write([]byte(":\n"))
 666  						}
 667  					}
 668  				}
 669  			}
 670  		}
 671  		e.outFile.Write([]byte("  ret void\n}\n"))
 672  	}
 673  	return sym
 674  }
 675  
 676  // --- Legacy compat (emit loop calls these) ---
 677  
 678  type deepCopyEntry struct {
 679  	sym string
 680  	typ Type
 681  }
 682  
 683  func (e *irEmitter) getOrCreateDeepCopyFn(t Type) (sym string) {
 684  	return "@__dcopy_opaque"
 685  }
 686  
 687  func typeKeyForDeepCopy(t Type) (s string) {
 688  	if t == nil {
 689  		return "<nil>"
 690  	}
 691  	return t.String()
 692  }
 693  
 694  func (e *irEmitter) emitDeepCopyFunctions() {
 695  	// Mark-compact handles relocation inline. No per-type functions needed.
 696  }
 697  
 698  func (e *irEmitter) visitedType() (s string) {
 699  	return "{i32, [256 x [2 x ptr]]}"
 700  }
 701  
 702  func (e *irEmitter) emitDeepCopyTupleValue(reg string, llvmType string, results *Tuple, visited string) (newReg string) {
 703  	return reg
 704  }
 705