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