ir_defer.mx raw
1 package main
2
3 import (
4 . "git.smesh.lol/moxie/pkg/types"
5 )
6
7 func (e *irEmitter) deferRetType(d *SSADefer) (s string) {
8 if fn, ok := d.Call.Value.(*SSAFunction); ok {
9 if fn.Signature == nil {
10 return "void"
11 }
12 r := fn.Signature.Results
13 if r == nil {
14 return "void"
15 }
16 return e.llvmType(r)
17 }
18 t := d.Call.Value.SSAType()
19 if t == nil {
20 return "void"
21 }
22 u := SafeUnderlying(t)
23 if sig, ok := u.(*Signature); ok {
24 r := sig.Results
25 if r == nil || r.Len() == 0 {
26 return "void"
27 }
28 return e.llvmType(r)
29 }
30 return "void"
31 }
32
33 func (e *irEmitter) deferArgType(arg SSAValue) (s string) {
34 t := e.llvmType(arg.SSAType())
35 if t == "void" {
36 return "ptr"
37 }
38 return t
39 }
40
41 func (e *irEmitter) deferIsDirectCall(d *SSADefer) (ok bool) {
42 if _, ok2 := d.Call.Value.(*SSAFunction); ok2 {
43 return true
44 }
45 if _, ok3 := d.Call.Value.(*SSABuiltin); ok3 {
46 return true
47 }
48 return false
49 }
50
51 func (e *irEmitter) deferStructType(d *SSADefer) (s string) {
52 ipt := e.intptrType()
53 s := "{" | ipt | ", ptr"
54 if !e.deferIsDirectCall(d) {
55 s = s | ", {ptr, ptr}"
56 }
57 for _, arg := range d.Call.Args {
58 s = s | ", " | e.deferArgType(arg)
59 }
60 return s | "}"
61 }
62
63 func (e *irEmitter) emitDefer(d *SSADefer) {
64 idx := -1
65 for i, dd := range e.deferList {
66 if dd == d {
67 idx = i
68 break
69 }
70 }
71 if idx < 0 {
72 e.w(" ; defer: not found in list\n")
73 return
74 }
75 ipt := e.intptrType()
76 id := e.deferID
77 e.deferID++
78 pfx := "%df" | irItoa(id) | "."
79 sty := e.deferStructType(d)
80
81 e.w(" ") ; e.w(pfx) ; e.w("a = alloca ") ; e.w(sty) ; e.w("\n")
82
83 cbGep := pfx | "cb"
84 e.w(" ") ; e.w(cbGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
85 e.w(", ptr ") ; e.w(pfx) ; e.w("a, i32 0, i32 0\n")
86 e.w(" store ") ; e.w(ipt) ; e.w(" ") ; e.w(irItoa(idx))
87 e.w(", ptr ") ; e.w(cbGep) ; e.w("\n")
88
89 nxGep := pfx | "nx"
90 e.w(" ") ; e.w(nxGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
91 e.w(", ptr ") ; e.w(pfx) ; e.w("a, i32 0, i32 1\n")
92 prev := pfx | "prev"
93 e.w(" ") ; e.w(prev) ; e.w(" = load ptr, ptr %deferPtr\n")
94 e.w(" store ptr ") ; e.w(prev) ; e.w(", ptr ") ; e.w(nxGep) ; e.w("\n")
95
96 fieldIdx := int32(2)
97 if !e.deferIsDirectCall(d) {
98 fvGep := pfx | "fv"
99 e.w(" ") ; e.w(fvGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
100 e.w(", ptr ") ; e.w(pfx) ; e.w("a, i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
101 fvOp := e.operand(d.Call.Value)
102 e.w(" store {ptr, ptr} ") ; e.w(fvOp)
103 e.w(", ptr ") ; e.w(fvGep) ; e.w("\n")
104 fieldIdx++
105 }
106
107 for i, arg := range d.Call.Args {
108 _ = i
109 aGep := pfx | "a" | irItoa(int32(fieldIdx))
110 e.w(" ") ; e.w(aGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
111 e.w(", ptr ") ; e.w(pfx) ; e.w("a, i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
112 at := e.deferArgType(arg)
113 av := e.operand(arg)
114 e.w(" store ") ; e.w(at) ; e.w(" ") ; e.w(av)
115 e.w(", ptr ") ; e.w(aGep) ; e.w("\n")
116 fieldIdx++
117 }
118
119 e.w(" store ptr ") ; e.w(pfx) ; e.w("a, ptr %deferPtr\n")
120 }
121
122 func (e *irEmitter) emitRunDefers() {
123 if len(e.deferList) == 0 {
124 return
125 }
126 ipt := e.intptrType()
127 id := e.deferID
128 e.deferID++
129 pfx := "rundefers" | irItoa(id)
130
131 e.w(" br label %") ; e.w(pfx) ; e.w(".head\n")
132 e.w(pfx) ; e.w(".head:\n")
133 e.w(" %") ; e.w(pfx) ; e.w(".cur = load ptr, ptr %deferPtr\n")
134 e.w(" %") ; e.w(pfx) ; e.w(".nil = icmp eq ptr %") ; e.w(pfx) ; e.w(".cur, null\n")
135 e.w(" br i1 %") ; e.w(pfx) ; e.w(".nil, label %") ; e.w(pfx) ; e.w(".end, label %")
136 e.w(pfx) ; e.w(".body\n")
137
138 e.w(pfx) ; e.w(".body:\n")
139 e.w(" %") ; e.w(pfx) ; e.w(".cb = load ") ; e.w(ipt) ; e.w(", ptr %") ; e.w(pfx) ; e.w(".cur\n")
140 nxGep := "%" | pfx | ".nxp"
141 e.w(" ") ; e.w(nxGep) ; e.w(" = getelementptr inbounds {") ; e.w(ipt) ; e.w(", ptr}, ptr %")
142 e.w(pfx) ; e.w(".cur, i32 0, i32 1\n")
143 e.w(" %") ; e.w(pfx) ; e.w(".nx = load ptr, ptr ") ; e.w(nxGep) ; e.w("\n")
144 e.w(" store ptr %") ; e.w(pfx) ; e.w(".nx, ptr %deferPtr\n")
145
146 for i, d := range e.deferList {
147 checkLabel := pfx | ".c" | irItoa(i)
148 callLabel := pfx | ".k" | irItoa(i)
149 var nextLabel string
150 if i+1 < len(e.deferList) {
151 nextLabel = pfx | ".c" | irItoa(i+1)
152 } else {
153 nextLabel = pfx | ".head"
154 }
155 if i == 0 {
156 e.w(" %") ; e.w(checkLabel) ; e.w(".eq = icmp eq ") ; e.w(ipt) ; e.w(" %")
157 e.w(pfx) ; e.w(".cb, 0\n")
158 e.w(" br i1 %") ; e.w(checkLabel) ; e.w(".eq, label %") ; e.w(callLabel)
159 e.w(", label %") ; e.w(nextLabel) ; e.w("\n")
160 } else {
161 e.w(checkLabel) ; e.w(":\n")
162 e.w(" %") ; e.w(checkLabel) ; e.w(".eq = icmp eq ") ; e.w(ipt) ; e.w(" %")
163 e.w(pfx) ; e.w(".cb, ") ; e.w(irItoa(i)) ; e.w("\n")
164 e.w(" br i1 %") ; e.w(checkLabel) ; e.w(".eq, label %") ; e.w(callLabel)
165 e.w(", label %") ; e.w(nextLabel) ; e.w("\n")
166 }
167
168 e.w(callLabel) ; e.w(":\n")
169 e.emitDeferDispatch(d, pfx, i)
170 e.w(" br label %") ; e.w(pfx) ; e.w(".head\n")
171 }
172
173 e.w(pfx) ; e.w(".end:\n")
174 }
175
176 func (e *irEmitter) emitDeferBuiltin(d *SSADefer, bi *SSABuiltin, pfx string, idx int32, sty string, dp string) {
177 fieldIdx := int32(2)
178 var args []string
179 var argTypes []string
180 for i, arg := range d.Call.Args {
181 aGep := "%" | pfx | ".da" | irItoa(idx) | "f" | irItoa(i)
182 e.w(" ") ; e.w(aGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
183 e.w(", ptr ") ; e.w(dp) ; e.w(", i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
184 at := e.deferArgType(arg)
185 aVal := "%" | pfx | ".dv" | irItoa(idx) | "f" | irItoa(i)
186 e.w(" ") ; e.w(aVal) ; e.w(" = load ") ; e.w(at) ; e.w(", ptr ") ; e.w(aGep) ; e.w("\n")
187 push(args, aVal)
188 push(argTypes, at)
189 fieldIdx++
190 }
191 name := bi.SSAName()
192 if name == "close" && len(args) == 1 {
193 e.declareRuntime("runtime.chanClose", "void", "ptr")
194 e.w(" call void @runtime.chanClose(") ; e.w(argTypes[0]) ; e.w(" ") ; e.w(args[0]) ; e.w(", ptr null)\n")
195 } else if name == "panic" && len(args) == 1 {
196 e.declareRuntime("runtime._panicstr", "void", argTypes[0])
197 e.w(" call void @runtime._panicstr(") ; e.w(argTypes[0]) ; e.w(" ") ; e.w(args[0]) ; e.w(", ptr null)\n")
198 } else if name == "println" || name == "print" {
199 e.declareRuntime("runtime.printlock", "void", "")
200 e.declareRuntime("runtime.printunlock", "void", "")
201 e.declareRuntime("runtime.printnl", "void", "")
202 e.w(" call void @runtime.printlock(ptr null)\n")
203 for ai, av := range args {
204 if ai > 0 {
205 e.declareRuntime("runtime.printspace", "void", "")
206 e.w(" call void @runtime.printspace(ptr null)\n")
207 }
208 at := argTypes[ai]
209 if at == e.sliceType() {
210 e.declareRuntime("runtime.printstring", "void", e.sliceType())
211 e.w(" call void @runtime.printstring(") ; e.w(at) ; e.w(" ") ; e.w(av) ; e.w(", ptr null)\n")
212 } else if at == "i1" {
213 e.declareRuntime("runtime.printbool", "void", "i1")
214 e.w(" call void @runtime.printbool(i1 ") ; e.w(av) ; e.w(", ptr null)\n")
215 } else if at == "i32" || at == "i64" || at == "i16" || at == "i8" {
216 fname := "runtime.printint" | at[1:]
217 e.declareRuntime(fname, "void", at)
218 e.w(" call void @") ; e.w(fname) ; e.w("(") ; e.w(at) ; e.w(" ") ; e.w(av) ; e.w(", ptr null)\n")
219 } else if at == "ptr" {
220 ipt := e.intptrType()
221 e.declareRuntime("runtime.printptr", "void", ipt)
222 tmp := "%" | pfx | ".ptmp" | irItoa(ai)
223 e.w(" ") ; e.w(tmp) ; e.w(" = ptrtoint ptr ") ; e.w(av) ; e.w(" to ") ; e.w(ipt) ; e.w("\n")
224 e.w(" call void @runtime.printptr(") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(", ptr null)\n")
225 }
226 }
227 if name == "println" {
228 e.w(" call void @runtime.printnl(ptr null)\n")
229 }
230 e.w(" call void @runtime.printunlock(ptr null)\n")
231 } else if name == "delete" && len(args) == 2 {
232 e.declareRuntime("runtime.hashmapBinaryDelete", "void", "ptr, ptr, i32")
233 e.w(" ; defer delete - not fully implemented\n")
234 } else if name == "recover" {
235 e.w(" ; defer recover() is a no-op\n")
236 } else {
237 e.w(" ; defer builtin ") ; e.w(name) ; e.w(" - not implemented\n")
238 }
239 }
240
241 func (e *irEmitter) emitDeferDispatch(d *SSADefer, pfx string, idx int32) {
242 sty := e.deferStructType(d)
243 dp := "%" | pfx | ".cur"
244
245 fieldIdx := int32(2)
246 if bi, isBi := d.Call.Value.(*SSABuiltin); isBi {
247 e.emitDeferBuiltin(d, bi, pfx, idx, sty, dp)
248 return
249 }
250 if fn, isFn := d.Call.Value.(*SSAFunction); isFn {
251 var args []string
252 var argTypes []string
253 for i, arg := range d.Call.Args {
254 aGep := "%" | pfx | ".da" | irItoa(idx) | "f" | irItoa(i)
255 e.w(" ") ; e.w(aGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
256 e.w(", ptr ") ; e.w(dp) ; e.w(", i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
257 at := e.deferArgType(arg)
258 aVal := "%" | pfx | ".dv" | irItoa(idx) | "f" | irItoa(i)
259 e.w(" ") ; e.w(aVal) ; e.w(" = load ") ; e.w(at) ; e.w(", ptr ") ; e.w(aGep) ; e.w("\n")
260 push(args, aVal)
261 push(argTypes, at)
262 fieldIdx++
263 }
264 if !e.isPkgFunc(fn) {
265 e.declareExternalFunc(fn)
266 }
267 rt := e.deferRetType(d)
268 e.w(" call ") ; e.w(rt) ; e.w(" ") ; e.w(e.funcSymbol(fn)) ; e.w("(")
269 for i, av := range args {
270 if i > 0 { e.w(", ") }
271 e.w(argTypes[i]) ; e.w(" ") ; e.w(av)
272 }
273 e.w(")\n")
274 } else {
275 fvGep := "%" | pfx | ".dfv" | irItoa(idx)
276 e.w(" ") ; e.w(fvGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
277 e.w(", ptr ") ; e.w(dp) ; e.w(", i32 0, i32 2\n")
278 fvVal := "%" | pfx | ".fvv" | irItoa(idx)
279 e.w(" ") ; e.w(fvVal) ; e.w(" = load {ptr, ptr}, ptr ") ; e.w(fvGep) ; e.w("\n")
280 fpReg := "%" | pfx | ".fp" | irItoa(idx)
281 ctxReg := "%" | pfx | ".ctx" | irItoa(idx)
282 e.w(" ") ; e.w(fpReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(fvVal) ; e.w(", 1\n")
283 e.w(" ") ; e.w(ctxReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(fvVal) ; e.w(", 0\n")
284 fieldIdx = 3
285 var args []string
286 var argTypes []string
287 for i, arg := range d.Call.Args {
288 aGep := "%" | pfx | ".da" | irItoa(idx) | "f" | irItoa(i)
289 e.w(" ") ; e.w(aGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
290 e.w(", ptr ") ; e.w(dp) ; e.w(", i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
291 at := e.deferArgType(arg)
292 aVal := "%" | pfx | ".dv" | irItoa(idx) | "f" | irItoa(i)
293 e.w(" ") ; e.w(aVal) ; e.w(" = load ") ; e.w(at) ; e.w(", ptr ") ; e.w(aGep) ; e.w("\n")
294 push(args, aVal)
295 push(argTypes, at)
296 fieldIdx++
297 }
298 rt := e.deferRetType(d)
299 e.w(" call ") ; e.w(rt) ; e.w(" ") ; e.w(fpReg) ; e.w("(")
300 for i, av := range args {
301 if i > 0 { e.w(", ") }
302 e.w(argTypes[i]) ; e.w(" ") ; e.w(av)
303 }
304 if len(args) > 0 { e.w(", ") }
305 e.w("ptr ") ; e.w(ctxReg)
306 e.w(")\n")
307 }
308 }
309