dealloc.mx raw
1 package emit
2
3 import (
4 "git.smesh.lol/moxie/pkg/ssa"
5 )
6
7 func (e *irEmitter) instrOperands(instr ssa.SSAInstruction) []ssa.SSAValue {
8 switch i := instr.(type) {
9 case *ssa.SSAStore:
10 return []ssa.SSAValue{i.Addr, i.Val}
11 case *ssa.SSAUnOp:
12 return []ssa.SSAValue{i.X}
13 case *ssa.SSABinOp:
14 return []ssa.SSAValue{i.X, i.Y}
15 case *ssa.SSACall:
16 out := []ssa.SSAValue{i.Call.Value}
17 for _, a := range i.Call.Args {
18 out = append(out, a)
19 }
20 return out
21 case *ssa.SSAFieldAddr:
22 return []ssa.SSAValue{i.X}
23 case *ssa.SSAIndexAddr:
24 return []ssa.SSAValue{i.X, i.Index}
25 case *ssa.SSAExtract:
26 return []ssa.SSAValue{i.Tuple}
27 case *ssa.SSAPhi:
28 return i.Edges
29 case *ssa.SSAReturn:
30 var out []ssa.SSAValue
31 for _, r := range i.Results {
32 out = append(out, r)
33 }
34 return out
35 case *ssa.SSAIf:
36 return []ssa.SSAValue{i.Cond}
37 case *ssa.SSAConvert:
38 return []ssa.SSAValue{i.X}
39 case *ssa.SSAChangeType:
40 return []ssa.SSAValue{i.X}
41 case *ssa.SSAMakeInterface:
42 return []ssa.SSAValue{i.X}
43 case *ssa.SSATypeAssert:
44 return []ssa.SSAValue{i.X}
45 case *ssa.SSASlice:
46 out := []ssa.SSAValue{i.X}
47 if i.Low != nil { out = append(out, i.Low) }
48 if i.High != nil { out = append(out, i.High) }
49 if i.Max != nil { out = append(out, i.Max) }
50 return out
51 case *ssa.SSAMapUpdate:
52 return []ssa.SSAValue{i.Map, i.Key, i.Value}
53 case *ssa.SSALookup:
54 return []ssa.SSAValue{i.X, i.Index}
55 case *ssa.SSARange:
56 return []ssa.SSAValue{i.X}
57 case *ssa.SSANext:
58 return []ssa.SSAValue{i.Iter}
59 case *ssa.SSASend:
60 return []ssa.SSAValue{i.Chan, i.X}
61 case *ssa.SSAMakeSlice:
62 out := []ssa.SSAValue{i.Len}
63 if i.Cap != nil { out = append(out, i.Cap) }
64 if i.Data != nil { out = append(out, i.Data) }
65 return out
66 }
67 return nil
68 }
69
70 func simpleItoa(n int32) string {
71 if n == 0 {
72 return "0"
73 }
74 neg := n < 0
75 if neg {
76 n = -n
77 }
78 buf := []byte{:0:20}
79 for n > 0 {
80 buf = append(buf, byte('0'+n%10))
81 n /= 10
82 }
83 if neg {
84 buf = append(buf, '-')
85 }
86 for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
87 buf[i], buf[j] = buf[j], buf[i]
88 }
89 return string(buf)
90 }
91
92 func (e *irEmitter) deallocSetup(f *ssa.SSAFunction) {
93 heapCount := int32(0)
94 for _, b := range f.Blocks {
95 for _, instr := range b.Instrs {
96 if a, ok := instr.(*ssa.SSAAlloc); ok && a.Heap {
97 heapCount++
98 }
99 }
100 }
101 if heapCount == 0 {
102 return
103 }
104 e.deallocActive = true
105 e.deallocAllocs = map[ssa.SSAValue]int32{}
106 e.w(" %dealloc.head = alloca ptr\n")
107 e.w(" store ptr null, ptr %dealloc.head\n")
108 idx := int32(0)
109 for _, b := range f.Blocks {
110 for _, instr := range b.Instrs {
111 if a, ok := instr.(*ssa.SSAAlloc); ok && a.Heap {
112 e.deallocAllocs[a] = idx
113 e.w(" %dealloc.entry." | irItoa(idx) | " = alloca {ptr, ptr}\n")
114 e.w(" store {ptr, ptr} zeroinitializer, ptr %dealloc.entry." | irItoa(idx) | "\n")
115 idx++
116 }
117 }
118 }
119 e.deallocCount = idx
120 e.declareRuntime("runtime.deallocPush", "void", "ptr, ptr, ptr")
121 e.declareRuntime("runtime.deallocFree", "void", "ptr")
122 e.declareRuntime("runtime.deallocEscape", "void", "ptr, ptr")
123 }
124
125 func (e *irEmitter) deallocAfterAlloc(a *ssa.SSAAlloc, reg string) {
126 if !e.deallocActive {
127 return
128 }
129 idx, ok := e.deallocAllocs[a]
130 if !ok {
131 return
132 }
133 entry := "%dealloc.entry." | irItoa(idx)
134 e.w(" call void @runtime.deallocPush(ptr " | reg | ", ptr %dealloc.head, ptr " | entry | ")\n")
135 }
136
137 func (e *irEmitter) deallocBeforeReturn(r *ssa.SSAReturn) {
138 if !e.deallocActive {
139 return
140 }
141 for _, res := range r.Results {
142 e.deallocEscapeResult(res)
143 }
144 e.nextReg++
145 tmp := "%dealloc.list" | irItoa(e.nextReg)
146 e.w(" " | tmp | " = load ptr, ptr %dealloc.head\n")
147 e.w(" call void @runtime.deallocFree(ptr " | tmp | ")\n")
148 }
149
150 func (e *irEmitter) deallocEscapeResult(v ssa.SSAValue) {
151 if v == nil {
152 return
153 }
154 if a, ok := v.(*ssa.SSAAlloc); ok {
155 if _, tracked := e.deallocAllocs[a]; tracked {
156 e.nextReg++
157 tmp := "%dealloc.esc.list" | irItoa(e.nextReg)
158 e.w(" " | tmp | " = load ptr, ptr %dealloc.head\n")
159 e.w(" call void @runtime.deallocEscape(ptr " | e.operand(v) | ", ptr " | tmp | ")\n")
160 }
161 }
162 }
163
164 func (e *irEmitter) scopeTrackAlloc(ptrReg string) {
165 if !e.scopeActive {
166 return
167 }
168 e.w(" call void @runtime.scopePush(ptr ")
169 e.w(ptrReg)
170 e.w(", ptr %scope.head)\n")
171 }
172
173 func (e *irEmitter) scopeEscapeReturnResults(r *ssa.SSAReturn) {
174 if !e.scopeActive {
175 return
176 }
177 for _, res := range r.Results {
178 if res == nil {
179 continue
180 }
181 typ := e.llvmType(res.SSAType())
182 val := e.operand(res)
183 if val == "null" || val == "zeroinitializer" || val == "0" {
184 continue
185 }
186 ptrReg := ""
187 sty := e.sliceType()
188 if typ == "ptr" {
189 ptrReg = val
190 } else if typ == sty {
191 e.nextReg++
192 ptrReg = "%esc.p" | irItoa(e.nextReg)
193 e.w(" ") ; e.w(ptrReg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", 0\n")
194 } else if typ == "{ptr, ptr}" {
195 e.nextReg++
196 ptrReg = "%esc.p" | irItoa(e.nextReg)
197 e.w(" ") ; e.w(ptrReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 0\n")
198 }
199 if ptrReg == "" {
200 continue
201 }
202 e.w(" call void @runtime.scopeEscapeReturn(ptr ")
203 e.w(ptrReg)
204 e.w(", ptr %scope.head)\n")
205 e.declareRuntime("runtime.scopeEscapeReturn", "void", "ptr, ptr")
206 }
207 if len(e.curFunc.NamedResults) > 0 {
208 for _, nr := range e.curFunc.NamedResults {
209 nrt := e.namedResultType(nr)
210 if nrt != "ptr" && nrt != e.sliceType() && nrt != "{ptr, ptr}" {
211 continue
212 }
213 e.nextReg++
214 tmp := "%esc.nr" | irItoa(e.nextReg)
215 e.w(" ") ; e.w(tmp) ; e.w(" = load ") ; e.w(nrt) ; e.w(", ptr ") ; e.w(e.regName(nr)) ; e.w("\n")
216 ptrReg := ""
217 sty := e.sliceType()
218 if nrt == "ptr" {
219 ptrReg = tmp
220 } else if nrt == sty {
221 e.nextReg++
222 ptrReg = "%esc.nrp" | irItoa(e.nextReg)
223 e.w(" ") ; e.w(ptrReg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(tmp) ; e.w(", 0\n")
224 } else if nrt == "{ptr, ptr}" {
225 e.nextReg++
226 ptrReg = "%esc.nrp" | irItoa(e.nextReg)
227 e.w(" ") ; e.w(ptrReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(tmp) ; e.w(", 0\n")
228 }
229 if ptrReg == "" {
230 continue
231 }
232 e.w(" call void @runtime.scopeEscapeReturn(ptr ")
233 e.w(ptrReg)
234 e.w(", ptr %scope.head)\n")
235 e.declareRuntime("runtime.scopeEscapeReturn", "void", "ptr, ptr")
236 }
237 }
238 }
239
240 func (e *irEmitter) emitScopeFreeAll() {
241 if !e.scopeActive {
242 return
243 }
244 e.nextReg++
245 cur := "%sf.all" | irItoa(e.nextReg)
246 e.w(" ")
247 e.w(cur)
248 e.w(" = load ptr, ptr %scope.head\n")
249 e.nextReg++
250 fn := "%sf.fn" | irItoa(e.nextReg)
251 e.w(" ")
252 e.w(fn)
253 e.w(" = call ptr @runtime.scopeFreeTo(ptr ")
254 e.w(cur)
255 e.w(", ptr null)\n")
256 e.w(" store ptr ")
257 e.w(fn)
258 e.w(", ptr %scope.head\n")
259 }
260
261 func (e *irEmitter) emitScopeFreesTo(scopeID int32) {
262 e.nextReg++
263 saved := "%sf.sv" | irItoa(e.nextReg)
264 e.w(" ")
265 e.w(saved)
266 e.w(" = load ptr, ptr %scope.save.")
267 e.w(irItoa(scopeID))
268 e.w("\n")
269 e.nextReg++
270 cur := "%sf.cur" | irItoa(e.nextReg)
271 e.w(" ")
272 e.w(cur)
273 e.w(" = load ptr, ptr %scope.head\n")
274 e.nextReg++
275 fn := "%sf.fn" | irItoa(e.nextReg)
276 e.w(" ")
277 e.w(fn)
278 e.w(" = call ptr @runtime.scopeFreeTo(ptr ")
279 e.w(cur)
280 e.w(", ptr ")
281 e.w(saved)
282 e.w(")\n")
283 e.w(" store ptr ")
284 e.w(fn)
285 e.w(", ptr %scope.head\n")
286 }
287
288 func (e *irEmitter) emitScopeSave(b *ssa.SSABasicBlock) {
289 if b.ScopeID <= 0 {
290 return
291 }
292 enteringScope := false
293 for _, pred := range b.Preds {
294 if pred.ScopeID != b.ScopeID {
295 enteringScope = true
296 break
297 }
298 }
299 if enteringScope || len(b.Preds) == 0 {
300 e.nextReg++
301 tmp := "%ss" | irItoa(e.nextReg)
302 e.w(" ")
303 e.w(tmp)
304 e.w(" = load ptr, ptr %scope.head\n")
305 e.w(" store ptr ")
306 e.w(tmp)
307 e.w(", ptr %scope.save.")
308 e.w(irItoa(b.ScopeID))
309 e.w("\n")
310 }
311 }
312
313 func (e *irEmitter) emitScopeRelocateOnStore(s *ssa.SSAStore, val, valType string) {
314 if !e.scopeActive {
315 return
316 }
317 blk := s.InstrBlock()
318 if blk == nil || blk.ScopeID == 0 {
319 return
320 }
321 addrScope := int32(0)
322 if aid, ok := e.instrScope[s.Addr]; ok {
323 addrScope = aid
324 }
325 if addrScope >= blk.ScopeID {
326 return
327 }
328 if val == "null" || val == "zeroinitializer" || val == "0" {
329 return
330 }
331 ptrReg := ""
332 sty := e.sliceType()
333 if valType == "ptr" {
334 ptrReg = val
335 } else if valType == sty {
336 e.nextReg++
337 ptrReg = "%sr.p" | irItoa(e.nextReg)
338 e.w(" ") ; e.w(ptrReg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", 0\n")
339 } else if valType == "{ptr, ptr}" {
340 e.nextReg++
341 ptrReg = "%sr.p" | irItoa(e.nextReg)
342 e.w(" ") ; e.w(ptrReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 0\n")
343 }
344 if ptrReg == "" {
345 return
346 }
347 e.w(" call void @runtime.scopeRelocate(ptr ")
348 e.w(ptrReg)
349 e.w(", ptr %scope.head, ptr %scope.save.")
350 e.w(irItoa(blk.ScopeID))
351 e.w(")\n")
352 e.declareRuntime("runtime.scopeRelocate", "void", "ptr, ptr, ptr")
353 }
354