package main import ( . "git.smesh.lol/moxie/pkg/types" ) // typeNeedsCopy reports whether a value of type t may contain pointers to // arena-allocated data that would need relocation across an arena boundary. func typeNeedsCopy(t Type) (ok bool) { return typeNeedsCopyDepth(t, 0) } func typeNeedsCopyDepth(t Type, depth int32) (ok bool) { if t == nil || depth > 20 { return false } u := SafeUnderlying(t) if u == nil { return false } switch tt := u.(type) { case *Pointer: return true case *Slice: return true case *Basic: return tt.Kind == TCString || tt.Kind == UntypedString case *TCMap: return true case *TCChan: return true case *Signature: return true case *TCInterface: return true case *TCStruct: for i := int32(0); i < tt.NumFields(); i++ { if typeNeedsCopyDepth(tt.Field(i).Typ, depth+1) { return true } } case *Array: return typeNeedsCopyDepth(tt.Elem, depth+1) } return false } func (e *irEmitter) funcNeedsArenaCopy(f *SSAFunction) (ok bool) { if f == nil || f.Blocks == nil { return false } if f.Pkg != nil && f.Pkg.Pkg != nil { path := f.Pkg.Pkg.Path if path == "runtime" || path == "unsafe" { return false } } if f.Signature == nil || f.Signature.Results == nil || f.Signature.Results.Len() == 0 { return false } for i := int32(0); i < int32(f.Signature.Results.Len()); i++ { if typeNeedsCopy(f.Signature.Results.At(i).Typ) { return true } } return false } // --- Arena prologue --- func (e *irEmitter) emitArenaPrologue() (ok bool) { if !e.funcNeedsArenaCopy(e.curFunc) { return false } ipt := e.intptrType() e.w(" %fn_arena = call ptr @runtime.FnArenaPush(") e.w(ipt) e.w(" 65536, ptr null)\n") e.w(" %fn_mark = call ") e.w(ipt) e.w(" @runtime.ArenaMark(ptr null)\n") e.declareRuntime("runtime.FnArenaPush", "ptr", ipt) e.declareRuntime("runtime.ArenaMark", ipt, "") e.arenaActive = true return true } // --- Return with three-pass mark-compact --- func (e *irEmitter) emitRetWithArenaCopy(llvmType string, val string) { if !e.arenaActive { if llvmType == "void" { e.w(" ret void\n") } else { e.w(" ret ") ; e.w(llvmType) ; e.w(" ") ; e.w(val) ; e.w("\n") } return } needsCopy := false if val != "zeroinitializer" && llvmType != "void" { sig := e.curFunc.Signature if sig != nil && sig.Results != nil { for i := int32(0); i < int32(sig.Results.Len()); i++ { if typeNeedsCopy(sig.Results.At(i).Typ) { needsCopy = true break } } } } if !needsCopy { e.w(" call void @runtime.FnArenaPopFree(ptr null)\n") e.declareRuntime("runtime.FnArenaPopFree", "void", "") if llvmType == "void" { e.w(" ret void\n") } else { e.w(" ret ") ; e.w(llvmType) ; e.w(" ") ; e.w(val) ; e.w("\n") } return } ipt := e.intptrType() rsType := e.relocStateType() e.nextReg++ rs := "%rs" ; rs = rs | irItoa(e.nextReg) e.w(" ") ; e.w(rs) ; e.w(" = alloca ") ; e.w(rsType) ; e.w("\n") e.w(" store ") ; e.w(rsType) ; e.w(" zeroinitializer, ptr ") ; e.w(rs) ; e.w("\n") 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") e.declareRuntime("runtime.RelocInit", "void", "ptr, ptr, " | ipt) // ---- Pass 1: Mark ---- e.relocMarkVisited = map[string]bool{} results := e.curFunc.Signature.Results for i := int32(0); i < int32(results.Len()); i++ { rt := results.At(i).Typ if !typeNeedsCopy(rt) { continue } var rootReg string if results.Len() == 1 { rootReg = val } else { e.nextReg++ rootReg = "%p1ext" ; rootReg = rootReg | irItoa(e.nextReg) 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") } u := SafeUnderlying(rt) if _, isSt := u.(*TCStruct); isSt { slt := e.llvmType(rt) e.nextReg++ tmp := "%m1rt" ; tmp = tmp | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(slt) ; e.w("\n") e.w(" store ") ; e.w(slt) ; e.w(" ") ; e.w(rootReg) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") rootReg = tmp } e.emitRelocMark(rootReg, rt, rs) } // ---- Pass 2: Forward ---- e.nextReg++ popIgn := "%popign" ; popIgn = popIgn | irItoa(e.nextReg) e.w(" ") ; e.w(popIgn) ; e.w(" = call ptr @runtime.FnArenaPop(ptr null)\n") e.declareRuntime("runtime.FnArenaPop", "ptr", "") e.w(" call void @runtime.RelocForward(ptr ") ; e.w(rs) ; e.w(", ptr null)\n") e.declareRuntime("runtime.RelocForward", "void", "ptr") // ---- Pass 3: Compact + Fixup ---- e.w(" call void @runtime.RelocCompact(ptr ") ; e.w(rs) ; e.w(", ptr null)\n") e.declareRuntime("runtime.RelocCompact", "void", "ptr") e.w(" call void @runtime.RelocFixupAll(ptr ") ; e.w(rs) ; e.w(", ptr null)\n") e.declareRuntime("runtime.RelocFixupAll", "void", "ptr") e.relocFixupVisited = map[string]bool{} cur := val for i := int32(0); i < int32(results.Len()); i++ { rt := results.At(i).Typ if !typeNeedsCopy(rt) { continue } var fixReg string if results.Len() == 1 { fixReg = val } else { e.nextReg++ fixReg = "%p3ext" ; fixReg = fixReg | irItoa(e.nextReg) 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") } newReg := e.emitRelocFixup(fixReg, rt, rs) if results.Len() == 1 { cur = newReg } else if newReg != fixReg { elt := e.llvmType(rt) e.nextReg++ ins := "%p3ins" ; ins = ins | irItoa(e.nextReg) 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") cur = ins } } e.w(" call void @runtime.ArenaRelease(ptr %fn_arena, ptr null)\n") e.declareRuntime("runtime.ArenaRelease", "void", "ptr") e.w(" ret ") ; e.w(llvmType) ; e.w(" ") ; e.w(cur) ; e.w("\n") } func (e *irEmitter) relocStateType() (s string) { ipt := e.intptrType() return "{ptr, " | ipt | ", ptr, i32}" } // --- Pass 1 codegen: Mark --- func (e *irEmitter) emitRelocMark(reg string, t Type, rs string) { if t == nil || !typeNeedsCopy(t) { return } u := SafeUnderlying(t) if u == nil { return } ipt := e.intptrType() switch tt := u.(type) { case *Pointer: lt := e.llvmType(tt.Base) if lt == "void" || lt == "" { lt = "i8" } e.nextReg++ sz := "%m1sz" ; sz = sz | irItoa(e.nextReg) 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") fixupArg := ipt | " 0" if st, isSt := SafeUnderlying(tt.Base).(*TCStruct); isSt { fixupSym := e.getOrEmitRelocFixupFn(st) if fixupSym != "" { fixupArg = ipt | " ptrtoint (ptr " | fixupSym | " to " | ipt | ")" } } e.nextReg++ isNew := "%m1n" ; isNew = isNew | irItoa(e.nextReg) 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") e.declareRuntime("runtime.RelocMark", "i1", "ptr, ptr, " | ipt | ", " | ipt) if typeNeedsCopy(tt.Base) { e.nextReg++ lNew := "m1r" ; lNew = lNew | irItoa(e.nextReg) e.nextReg++ lSkip := "m1s" ; lSkip = lSkip | irItoa(e.nextReg) e.w(" br i1 ") ; e.w(isNew) ; e.w(", label %") ; e.w(lNew) ; e.w(", label %") ; e.w(lSkip) ; e.w("\n") e.w(lNew) ; e.w(":\n") e.emitRelocMarkFields(reg, tt.Base, rs) e.w(" br label %") ; e.w(lSkip) ; e.w("\n") e.w(lSkip) ; e.w(":\n") } case *Slice, *Basic: st := e.sliceType() e.nextReg++ dp := "%m1d" ; dp = dp | irItoa(e.nextReg) e.w(" ") ; e.w(dp) ; e.w(" = extractvalue ") ; e.w(st) ; e.w(" ") ; e.w(reg) ; e.w(", 0\n") e.nextReg++ ln := "%m1l" ; ln = ln | irItoa(e.nextReg) e.w(" ") ; e.w(ln) ; e.w(" = extractvalue ") ; e.w(st) ; e.w(" ") ; e.w(reg) ; e.w(", 1\n") elemType := "i8" if sl, ok := u.(*Slice); ok && sl.Elem != nil { elemType = e.llvmType(sl.Elem) } e.nextReg++ esz := "%m1e" ; esz = esz | irItoa(e.nextReg) 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") e.nextReg++ blen := "%m1b" ; blen = blen | irItoa(e.nextReg) e.w(" ") ; e.w(blen) ; e.w(" = mul ") ; e.w(ipt) ; e.w(" ") ; e.w(ln) ; e.w(", ") ; e.w(esz) ; e.w("\n") 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") e.declareRuntime("runtime.RelocMark", "i1", "ptr, ptr, " | ipt | ", " | ipt) case *TCMap: e.nextReg++ sz := "%m1m" ; sz = sz | irItoa(e.nextReg) e.w(" ") ; e.w(sz) ; e.w(" = ptrtoint ptr getelementptr ({ptr}, ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") 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") e.declareRuntime("runtime.RelocMark", "i1", "ptr, ptr, " | ipt | ", " | ipt) case *TCStruct: e.emitRelocMarkStructFields(reg, tt, rs) } } func (e *irEmitter) emitRelocMarkFields(ptrReg string, t Type, rs string) { u := SafeUnderlying(t) if u == nil { return } st, ok := u.(*TCStruct) if !ok { return } e.emitRelocMarkStructFields(ptrReg, st, rs) } func (e *irEmitter) emitRelocMarkStructFields(ptrReg string, st *TCStruct, rs string) { key := e.llvmStructType(st) if e.relocMarkVisited[key] { return } e.relocMarkVisited[key] = true lt := key for i := int32(0); i < st.NumFields(); i++ { ft := st.Field(i).Typ if !typeNeedsCopy(ft) { continue } e.nextReg++ gep := "%m1f" ; gep = gep | irItoa(e.nextReg) 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") u := SafeUnderlying(ft) if _, isSt := u.(*TCStruct); isSt { e.emitRelocMark(gep, ft, rs) } else { flt := e.llvmType(ft) e.nextReg++ fval := "%m1f" ; fval = fval | irItoa(e.nextReg) e.w(" ") ; e.w(fval) ; e.w(" = load ") ; e.w(flt) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n") e.emitRelocMark(fval, ft, rs) } } e.relocMarkVisited[key] = false } // --- Pass 3 codegen: Fixup --- func (e *irEmitter) emitRelocFixup(reg string, t Type, rs string) (newReg string) { if t == nil || !typeNeedsCopy(t) { return reg } u := SafeUnderlying(t) if u == nil { return reg } switch tt := u.(type) { case *Pointer: e.nextReg++ r := "%p3p" ; r = r | irItoa(e.nextReg) 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") e.declareRuntime("runtime.RelocLookup", "ptr", "ptr, ptr") return r case *Slice, *Basic: st := e.sliceType() e.nextReg++ dp := "%p3s" ; dp = dp | irItoa(e.nextReg) e.w(" ") ; e.w(dp) ; e.w(" = extractvalue ") ; e.w(st) ; e.w(" ") ; e.w(reg) ; e.w(", 0\n") e.nextReg++ ndp := "%p3s" ; ndp = ndp | irItoa(e.nextReg) 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") e.declareRuntime("runtime.RelocLookup", "ptr", "ptr, ptr") e.nextReg++ result := "%p3s" ; result = result | irItoa(e.nextReg) 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") return result case *TCMap: e.nextReg++ r := "%p3m" ; r = r | irItoa(e.nextReg) e.w(" ") ; e.w(r) ; e.w(" = call ptr @runtime.hashmapCopy(ptr ") ; e.w(reg) ; e.w(", ptr null)\n") e.declareRuntime("runtime.hashmapCopy", "ptr", "ptr") return r case *TCStruct: return e.emitRelocFixupStruct(reg, tt, rs) } return reg } func (e *irEmitter) emitRelocFixupFields(ptrReg string, t Type, rs string) { u := SafeUnderlying(t) st, ok := u.(*TCStruct) if !ok { return } key := e.llvmStructType(st) if e.relocFixupVisited[key] { return } e.relocFixupVisited[key] = true lt := key for i := int32(0); i < st.NumFields(); i++ { ft := st.Field(i).Typ if !typeNeedsCopy(ft) { continue } e.nextReg++ gep := "%p3ff" ; gep = gep | irItoa(e.nextReg) 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") fu := SafeUnderlying(ft) switch fu.(type) { case *Pointer: e.w(" call void @runtime.RelocFixupPtr(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(gep) ; e.w(", ptr null)\n") e.declareRuntime("runtime.RelocFixupPtr", "void", "ptr, ptr") case *Slice, *Basic: e.w(" call void @runtime.RelocFixupSlice(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(gep) ; e.w(", ptr null)\n") e.declareRuntime("runtime.RelocFixupSlice", "void", "ptr, ptr") case *TCMap: e.nextReg++ old := "%p3ff" ; old = old | irItoa(e.nextReg) e.w(" ") ; e.w(old) ; e.w(" = load ptr, ptr ") ; e.w(gep) ; e.w("\n") e.nextReg++ nw := "%p3ff" ; nw = nw | irItoa(e.nextReg) e.w(" ") ; e.w(nw) ; e.w(" = call ptr @runtime.hashmapCopy(ptr ") ; e.w(old) ; e.w(", ptr null)\n") e.declareRuntime("runtime.hashmapCopy", "ptr", "ptr") e.w(" store ptr ") ; e.w(nw) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n") case *TCStruct: e.emitRelocFixupFields(gep, ft, rs) } } e.relocFixupVisited[key] = false } func (e *irEmitter) emitRelocFixupStruct(reg string, st *TCStruct, rs string) (newReg string) { key := e.llvmStructType(st) if e.relocFixupVisited[key] { return reg } e.relocFixupVisited[key] = true lt := key e.nextReg++ tmp := "%p3st" ; tmp = tmp | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(lt) ; e.w("\n") e.w(" store ") ; e.w(lt) ; e.w(" ") ; e.w(reg) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") for i := int32(0); i < st.NumFields(); i++ { ft := st.Field(i).Typ if !typeNeedsCopy(ft) { continue } e.nextReg++ gep := "%p3st" ; gep = gep | irItoa(e.nextReg) 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") fu := SafeUnderlying(ft) switch fu.(type) { case *Pointer: e.w(" call void @runtime.RelocFixupPtr(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(gep) ; e.w(", ptr null)\n") e.declareRuntime("runtime.RelocFixupPtr", "void", "ptr, ptr") case *Slice, *Basic: e.w(" call void @runtime.RelocFixupSlice(ptr ") ; e.w(rs) ; e.w(", ptr ") ; e.w(gep) ; e.w(", ptr null)\n") e.declareRuntime("runtime.RelocFixupSlice", "void", "ptr, ptr") case *TCMap: e.nextReg++ old := "%p3st" ; old = old | irItoa(e.nextReg) e.w(" ") ; e.w(old) ; e.w(" = load ptr, ptr ") ; e.w(gep) ; e.w("\n") e.nextReg++ nw := "%p3st" ; nw = nw | irItoa(e.nextReg) e.w(" ") ; e.w(nw) ; e.w(" = call ptr @runtime.hashmapCopy(ptr ") ; e.w(old) ; e.w(", ptr null)\n") e.declareRuntime("runtime.hashmapCopy", "ptr", "ptr") e.w(" store ptr ") ; e.w(nw) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n") case *TCStruct: e.emitRelocFixupFields(gep, ft, rs) } } e.nextReg++ result := "%p3st" ; result = result | irItoa(e.nextReg) e.w(" ") ; e.w(result) ; e.w(" = load ") ; e.w(lt) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") e.relocFixupVisited[key] = false return result } // --- Per-type fixup functions --- func relocFixupSymbol(lt string) (sym string) { h := uint32(0x811c9dc5) for i := int32(0); i < len(lt); i++ { h = h ^ uint32(lt[i]) h = h * 0x01000193 } return "@__reloc_fixup_" | irItoa(int32(h & 0x7fffffff)) } // getOrEmitRelocFixupFn returns the symbol for a per-type fixup function. func (e *irEmitter) getOrEmitRelocFixupFn(st *TCStruct) (sym string) { lt := e.llvmStructType(st) if lt == "" { return "" } sym = relocFixupSymbol(st.String()) bareSym := sym if len(bareSym) > 0 && bareSym[0] == '@' { bareSym = bareSym[1:] } for di := int32(0); di < len(e.definedSyms); di++ { ds := e.definedSyms[di] if len(ds) == len(bareSym) { match := true for bi := int32(0); bi < len(ds); bi++ { if ds[bi] != bareSym[bi] { match = false break } } if match { return sym } } } for pi := int32(0); pi < len(e.pendRelocFixupDefs); pi++ { ps := e.pendRelocFixupDefs[pi] if len(ps) == len(bareSym) { match := true for bi := int32(0); bi < len(ps); bi++ { if ps[bi] != bareSym[bi] { match = false break } } if match { return sym } } } push(e.pendRelocFixupDefs, bareSym) e.declareRuntime("runtime.RelocFixupPtr", "void", "ptr, ptr") e.declareRuntime("runtime.RelocFixupSlice", "void", "ptr, ptr") e.declareRuntime("runtime.hashmapCopy", "ptr", "ptr") // Write fixup function directly to output file using per-segment writes. if e.outFile != nil { e.outFile.Write([]byte("\ndefine linkonce_odr void ")) e.outFile.Write([]byte(sym)) e.outFile.Write([]byte("(ptr %obj, ptr %rs, ptr %ctx) {\nentry:\n")) regN := int32(0) for i := int32(0); i < st.NumFields(); i++ { ft := st.Field(i).Typ if !typeNeedsCopy(ft) { continue } regN++ gep := "%fg" | irItoa(regN) e.outFile.Write([]byte(" ")) e.outFile.Write([]byte(gep)) e.outFile.Write([]byte(" = getelementptr ")) e.outFile.Write([]byte(lt)) e.outFile.Write([]byte(", ptr %obj, i32 0, i32 ")) e.outFile.Write([]byte(irItoa(i))) e.outFile.Write([]byte("\n")) fu := SafeUnderlying(ft) switch fu.(type) { case *Pointer: e.outFile.Write([]byte(" call void @runtime.RelocFixupPtr(ptr %rs, ptr ")) e.outFile.Write([]byte(gep)) e.outFile.Write([]byte(", ptr null)\n")) case *Slice, *Basic: e.outFile.Write([]byte(" call void @runtime.RelocFixupSlice(ptr %rs, ptr ")) e.outFile.Write([]byte(gep)) e.outFile.Write([]byte(", ptr null)\n")) case *TCMap: regN++ old := "%fg" | irItoa(regN) e.outFile.Write([]byte(" ")) e.outFile.Write([]byte(old)) e.outFile.Write([]byte(" = load ptr, ptr ")) e.outFile.Write([]byte(gep)) e.outFile.Write([]byte("\n")) regN++ chk := "%fg" | irItoa(regN) regN++ ldo := "fgdo" | irItoa(regN) regN++ lsk := "fgsk" | irItoa(regN) e.outFile.Write([]byte(" ")) e.outFile.Write([]byte(chk)) e.outFile.Write([]byte(" = icmp ne ptr ")) e.outFile.Write([]byte(old)) e.outFile.Write([]byte(", null\n")) e.outFile.Write([]byte(" br i1 ")) e.outFile.Write([]byte(chk)) e.outFile.Write([]byte(", label %")) e.outFile.Write([]byte(ldo)) e.outFile.Write([]byte(", label %")) e.outFile.Write([]byte(lsk)) e.outFile.Write([]byte("\n")) e.outFile.Write([]byte(ldo)) e.outFile.Write([]byte(":\n")) regN++ nw := "%fg" | irItoa(regN) e.outFile.Write([]byte(" ")) e.outFile.Write([]byte(nw)) e.outFile.Write([]byte(" = call ptr @runtime.hashmapCopy(ptr ")) e.outFile.Write([]byte(old)) e.outFile.Write([]byte(", ptr null)\n")) e.outFile.Write([]byte(" store ptr ")) e.outFile.Write([]byte(nw)) e.outFile.Write([]byte(", ptr ")) e.outFile.Write([]byte(gep)) e.outFile.Write([]byte("\n")) e.outFile.Write([]byte(" br label %")) e.outFile.Write([]byte(lsk)) e.outFile.Write([]byte("\n")) e.outFile.Write([]byte(lsk)) e.outFile.Write([]byte(":\n")) case *TCStruct: if innerSt, ok2 := fu.(*TCStruct); ok2 { innerLt := e.llvmStructType(innerSt) for j := int32(0); j < innerSt.NumFields(); j++ { jft := innerSt.Field(j).Typ if !typeNeedsCopy(jft) { continue } regN++ igep := "%fg" | irItoa(regN) e.outFile.Write([]byte(" ")) e.outFile.Write([]byte(igep)) e.outFile.Write([]byte(" = getelementptr ")) e.outFile.Write([]byte(innerLt)) e.outFile.Write([]byte(", ptr ")) e.outFile.Write([]byte(gep)) e.outFile.Write([]byte(", i32 0, i32 ")) e.outFile.Write([]byte(irItoa(j))) e.outFile.Write([]byte("\n")) jfu := SafeUnderlying(jft) switch jfu.(type) { case *Pointer: e.outFile.Write([]byte(" call void @runtime.RelocFixupPtr(ptr %rs, ptr ")) e.outFile.Write([]byte(igep)) e.outFile.Write([]byte(", ptr null)\n")) case *Slice, *Basic: e.outFile.Write([]byte(" call void @runtime.RelocFixupSlice(ptr %rs, ptr ")) e.outFile.Write([]byte(igep)) e.outFile.Write([]byte(", ptr null)\n")) case *TCMap: regN++ io := "%fg" | irItoa(regN) e.outFile.Write([]byte(" ")) e.outFile.Write([]byte(io)) e.outFile.Write([]byte(" = load ptr, ptr ")) e.outFile.Write([]byte(igep)) e.outFile.Write([]byte("\n")) regN++ ichk := "%fg" | irItoa(regN) regN++ ildo := "fgdo" | irItoa(regN) regN++ ilsk := "fgsk" | irItoa(regN) e.outFile.Write([]byte(" ")) e.outFile.Write([]byte(ichk)) e.outFile.Write([]byte(" = icmp ne ptr ")) e.outFile.Write([]byte(io)) e.outFile.Write([]byte(", null\n")) e.outFile.Write([]byte(" br i1 ")) e.outFile.Write([]byte(ichk)) e.outFile.Write([]byte(", label %")) e.outFile.Write([]byte(ildo)) e.outFile.Write([]byte(", label %")) e.outFile.Write([]byte(ilsk)) e.outFile.Write([]byte("\n")) e.outFile.Write([]byte(ildo)) e.outFile.Write([]byte(":\n")) regN++ inw := "%fg" | irItoa(regN) e.outFile.Write([]byte(" ")) e.outFile.Write([]byte(inw)) e.outFile.Write([]byte(" = call ptr @runtime.hashmapCopy(ptr ")) e.outFile.Write([]byte(io)) e.outFile.Write([]byte(", ptr null)\n")) e.outFile.Write([]byte(" store ptr ")) e.outFile.Write([]byte(inw)) e.outFile.Write([]byte(", ptr ")) e.outFile.Write([]byte(igep)) e.outFile.Write([]byte("\n")) e.outFile.Write([]byte(" br label %")) e.outFile.Write([]byte(ilsk)) e.outFile.Write([]byte("\n")) e.outFile.Write([]byte(ilsk)) e.outFile.Write([]byte(":\n")) } } } } } e.outFile.Write([]byte(" ret void\n}\n")) } return sym } // --- Legacy compat (emit loop calls these) --- type deepCopyEntry struct { sym string typ Type } func (e *irEmitter) getOrCreateDeepCopyFn(t Type) (sym string) { return "@__dcopy_opaque" } func typeKeyForDeepCopy(t Type) (s string) { if t == nil { return "" } return t.String() } func (e *irEmitter) emitDeepCopyFunctions() { // Mark-compact handles relocation inline. No per-type functions needed. } func (e *irEmitter) visitedType() (s string) { return "{i32, [256 x [2 x ptr]]}" } func (e *irEmitter) emitDeepCopyTupleValue(reg string, llvmType string, results *Tuple, visited string) (newReg string) { return reg }