function.mx raw
1 package emit
2
3 import (
4 "git.smesh.lol/moxie/pkg/syntax"
5 "git.smesh.lol/moxie/pkg/ssa"
6 "git.smesh.lol/moxie/pkg/types"
7 )
8
9 func (e *irEmitter) emitFunction(f *ssa.SSAFunction) {
10 e.w("; [emit] " | f.SSAName() | "\n")
11 if len(f.Blocks) == 0 {
12 linkName := f.SSAName()
13 if f.Pkg != nil && f.Pkg.Pkg != nil {
14 linkName = f.Pkg.Pkg.Path() | "." | f.SSAName()
15 }
16 alias := e.asmAlias(linkName)
17 if alias != "" {
18 e.emitAsmAlias(f, alias)
19 return
20 }
21 e.emitFuncDecl(f)
22 return
23 }
24 e.curFunc = f
25 e.nextReg = 0
26 e.deferList = nil
27 e.deferID = 0
28 e.deallocActive = false
29 e.deallocAllocs = nil
30 e.deallocCount = 0
31 e.scopeActive = false
32 e.scopeAllocs = map[int32][]string{}
33 e.instrScope = map[ssa.SSAInstruction]int32{}
34 e.valName = map[ssa.SSAValue]string{}
35 e.nameUsed = map[string]bool{}
36 e.allocTypes = map[ssa.SSAValue]string{}
37 e.regTypes = map[string]string{}
38 e.blockExitLabel = map[int32]string{}
39
40 usedNames := map[string]int32{}
41 for i, p := range f.Params {
42 pname := p.SSAName()
43 if pname == "" {
44 pname = "p" | irItoa(i)
45 }
46 if cnt, ok := usedNames[pname]; ok {
47 pname = pname | "." | irItoa(cnt)
48 }
49 usedNames[p.SSAName()]++
50 e.valName[p] = "%" | pname
51 e.nameUsed["%" | pname] = true
52 }
53
54 e.w("\ndefine hidden ")
55 e.w(e.funcRetType(f))
56 e.w(" ")
57 e.w(e.funcSymbol(f))
58 e.w("(")
59 for i, p := range f.Params {
60 if i > 0 {
61 e.w(", ")
62 }
63 pt := e.llvmType(p.SSAType())
64 if pt == "void" {
65 pt = "ptr"
66 }
67 e.w(pt)
68 e.w(" ")
69 e.w(e.regName(p))
70 }
71 if !f.IsExternC() {
72 if len(f.Params) > 0 {
73 e.w(", ")
74 }
75 ctxName := "context"
76 for _, p := range f.Params {
77 if p.SSAName() == "context" {
78 ctxName = "context.1"
79 break
80 }
81 }
82 e.w("ptr %")
83 e.w(ctxName)
84 }
85 e.w(") {\n")
86
87 // Pre-scan: set allocTypes, detect cross-block alloca references
88 e.allocBlock = map[ssa.SSAValue]int32{}
89 for _, b := range f.Blocks {
90 for _, instr := range b.Instrs {
91 if n, ok := instr.(*ssa.SSANext); ok {
92 if ri, ok2 := n.Iter.(*ssa.SSARange); ok2 {
93 rangeSSAType := ri.X.SSAType()
94 rangeUnderlying := types.SafeUnderlying(rangeSSAType)
95 if mt, ok3 := rangeUnderlying.(*types.TCMap); ok3 {
96 e.allocTypes[n] = "{i1, " | e.llvmType(mt.Key()) | ", " | e.llvmType(mt.Elem()) | "}"
97 } else if arr, ok3 := rangeUnderlying.(*types.Array); ok3 {
98 elemType := e.llvmType(arr.Elem())
99 e.allocTypes[n] = "{i1, i32, " | elemType | "}"
100 } else if sl, ok3 := rangeUnderlying.(*types.Slice); ok3 {
101 elemType := e.llvmType(sl.Elem())
102 e.allocTypes[n] = "{i1, i32, " | elemType | "}"
103 } else if p, ok3 := rangeUnderlying.(*types.Pointer); ok3 && p.Elem() != nil {
104 if arr2, ok4 := types.SafeUnderlying(p.Elem()).(*types.Array); ok4 {
105 elemType := e.llvmType(arr2.Elem())
106 e.allocTypes[n] = "{i1, i32, " | elemType | "}"
107 }
108 } else if n.IsString {
109 e.allocTypes[n] = "{i1, i32, i8}"
110 } else {
111 et := "i32"
112 if tup, ok3 := n.SSAType().(*types.Tuple); ok3 && tup.Len() >= 3 {
113 vt := tup.At(2).Type()
114 if vt != nil {
115 vlt := e.llvmType(vt)
116 if vlt != "void" && vlt != "i32" {
117 et = vlt
118 }
119 }
120 }
121 if et != "i32" {
122 e.allocTypes[n] = "{i1, i32, " | et | "}"
123 }
124 }
125 }
126 }
127 if c, ok := instr.(*ssa.SSACall); ok {
128 if b2, ok2 := c.Call.Value.(*ssa.SSABuiltin); ok2 && b2.SSAName() == "recover" {
129 e.allocTypes[c] = e.ifaceType()
130 }
131 }
132 if a, ok := instr.(*ssa.SSAAlloc); ok {
133 e.allocBlock[a] = b.Index
134 }
135 }
136 }
137 hoistAllocs := map[ssa.SSAValue]bool{}
138 for _, b := range f.Blocks {
139 for _, instr := range b.Instrs {
140 refs := e.instrOperands(instr)
141 for _, ref := range refs {
142 if ab, ok := e.allocBlock[ref]; ok && ab != 0 && ab != b.Index {
143 hoistAllocs[ref] = true
144 }
145 }
146 }
147 }
148 e.hoisted = hoistAllocs
149 e.missingStores = nil
150 e.storedTo = map[string]bool{}
151 for _, b := range f.Blocks {
152 for _, instr := range b.Instrs {
153 if s, ok := instr.(*ssa.SSAStore); ok && s.Addr != nil {
154 e.storedTo[s.Addr.SSAName()] = true
155 }
156 }
157 }
158 e.usedAs = map[string]bool{}
159 for _, b := range f.Blocks {
160 for _, instr := range b.Instrs {
161 refs := e.instrOperands(instr)
162 for _, ref := range refs {
163 if ref != nil {
164 e.usedAs[ref.SSAName()] = true
165 }
166 }
167 }
168 }
169 for _, b := range f.Blocks {
170 for i := 0; i+1 < len(b.Instrs); i++ {
171 load, isLoad := b.Instrs[i].(*ssa.SSAUnOp)
172 if !isLoad || load.Op != ssa.OpMul {
173 continue
174 }
175 alloc, isAlloc := b.Instrs[i+1].(*ssa.SSAAlloc)
176 if !isAlloc {
177 continue
178 }
179 if !e.usedAs[load.SSAName()] && !e.storedTo[alloc.SSAName()] && hoistAllocs[alloc] {
180 srcAlloc, isSrcAlloc := load.X.(*ssa.SSAAlloc)
181 if !isSrcAlloc {
182 continue
183 }
184 srcType := e.llvmType(srcAlloc.SSAType())
185 if p, ok := types.SafeUnderlying(srcAlloc.SSAType()).(*types.Pointer); ok && p.Elem() != nil {
186 srcType = e.llvmType(p.Elem())
187 }
188 if len(srcType) > 0 && srcType[0] == '[' {
189 if e.missingStores == nil {
190 e.missingStores = map[ssa.SSAValue]ssa.SSAValue{}
191 }
192 e.missingStores[load] = alloc
193 e.allocTypes[alloc] = srcType
194 }
195 }
196 }
197 }
198 for _, b := range f.Blocks {
199 for _, instr := range b.Instrs {
200 e.instrScope[instr] = b.ScopeID
201 }
202 }
203 var hoistList []*ssa.SSAAlloc
204 for v := range hoistAllocs {
205 if a, ok := v.(*ssa.SSAAlloc); ok {
206 hoistList = append(hoistList, a)
207 }
208 }
209 for i := 1; i < len(hoistList); i++ {
210 for j := i; j > 0 && hoistList[j].SSAName() < hoistList[j-1].SSAName(); j-- {
211 hoistList[j], hoistList[j-1] = hoistList[j-1], hoistList[j]
212 }
213 }
214 for _, b := range f.Blocks {
215 for _, instr := range b.Instrs {
216 if d, ok := instr.(*ssa.SSADefer); ok {
217 e.deferList = append(e.deferList, d)
218 }
219 }
220 }
221 scopeIDs := map[int32]bool{}
222 hasHeapAllocs := false
223 for _, b := range f.Blocks {
224 if b.ScopeID > 0 {
225 scopeIDs[b.ScopeID] = true
226 }
227 for _, instr := range b.Instrs {
228 switch instr.(type) {
229 case *ssa.SSAAlloc:
230 if instr.(*ssa.SSAAlloc).Heap {
231 hasHeapAllocs = true
232 }
233 case *ssa.SSAMakeSlice, *ssa.SSAMakeMap, *ssa.SSAMakeChan, *ssa.SSAMakeClosure, *ssa.SSASlice:
234 hasHeapAllocs = true
235 }
236 }
237 }
238 for _, b := range f.Blocks {
239 if b.Index == 0 {
240 e.w("bb.entry:\n")
241 e.blockExitLabel[0] = "%bb.entry"
242 for _, a := range hoistList {
243 e.emitAlloc(a)
244 }
245 if len(e.deferList) > 0 {
246 e.w(" %deferPtr = alloca ptr\n")
247 e.w(" store ptr null, ptr %deferPtr\n")
248 }
249 if len(scopeIDs) > 0 || hasHeapAllocs {
250 e.scopeActive = true
251 e.w(" %scope.head = alloca ptr\n")
252 e.w(" store ptr null, ptr %scope.head\n")
253 for sid := range scopeIDs {
254 e.w(" %scope.save.")
255 e.w(irItoa(sid))
256 e.w(" = alloca ptr\n")
257 e.w(" store ptr null, ptr %scope.save.")
258 e.w(irItoa(sid))
259 e.w("\n")
260 }
261 e.declareRuntime("runtime.scopePush", "void", "ptr, ptr")
262 e.declareRuntime("runtime.scopeFreeTo", "ptr", "ptr, ptr")
263 e.declareRuntime("runtime.scopeRelocate", "void", "ptr, ptr, ptr")
264 }
265 e.deallocSetup(f)
266 if len(e.curFunc.FreeVars) > 0 {
267 e.emitFreeVarUnpack(e.curFunc)
268 }
269 for _, instr := range b.Instrs {
270 e.emitInstr(instr)
271 }
272 } else {
273 e.emitBlock(b)
274 }
275 }
276 e.hoisted = nil
277
278 e.w("}\n")
279 }
280
281 func (e *irEmitter) emitAsmAlias(f *ssa.SSAFunction, targetName string) {
282 sym := e.funcSymbol(f)
283 retType := e.funcRetType(f)
284 e.w("\ndefine hidden ")
285 e.w(retType)
286 e.w(" ")
287 e.w(sym)
288 e.w("(")
289 var paramTypes []string
290 var paramNames []string
291 n := 0
292 if f.Signature != nil && f.Signature.Params() != nil {
293 for i := 0; i < f.Signature.Params().Len(); i++ {
294 if n > 0 {
295 e.w(", ")
296 }
297 pt := e.llvmType(f.Signature.Params().At(i).Type())
298 pn := "%p" | irItoa(i)
299 e.w(pt)
300 e.w(" ")
301 e.w(pn)
302 paramTypes = append(paramTypes, pt)
303 paramNames = append(paramNames, pn)
304 n++
305 }
306 }
307 if n > 0 {
308 e.w(", ")
309 }
310 e.w("ptr %context) {\nentry:\n")
311 target := "@\"" | targetName | "\""
312 if retType == "void" {
313 e.w(" call void ")
314 e.w(target)
315 e.w("(")
316 for i, pt := range paramTypes {
317 if i > 0 {
318 e.w(", ")
319 }
320 e.w(pt)
321 e.w(" ")
322 e.w(paramNames[i])
323 }
324 e.w(", ptr null)\n ret void\n}\n")
325 } else {
326 e.w(" %r = call ")
327 e.w(retType)
328 e.w(" ")
329 e.w(target)
330 e.w("(")
331 for i, pt := range paramTypes {
332 if i > 0 {
333 e.w(", ")
334 }
335 e.w(pt)
336 e.w(" ")
337 e.w(paramNames[i])
338 }
339 e.w(", ptr null)\n ret ")
340 e.w(retType)
341 e.w(" %r\n}\n")
342 }
343 }
344
345 func (e *irEmitter) emitFuncDecl(f *ssa.SSAFunction) {
346 sym := e.funcSymbol(f)
347 if _, ok := e.extDecls[sym]; ok {
348 return
349 }
350 e.w("\ndeclare ")
351 e.w(e.funcRetType(f))
352 e.w(" ")
353 e.w(sym)
354 e.w("(")
355 n := 0
356 hasRecv := f.Signature != nil && f.Signature.Recv() != nil
357 if hasRecv {
358 e.w("ptr")
359 n++
360 }
361 if f.Signature != nil && f.Signature.Params() != nil {
362 for i := 0; i < f.Signature.Params().Len(); i++ {
363 if n > 0 {
364 e.w(", ")
365 }
366 e.w(e.llvmType(f.Signature.Params().At(i).Type()))
367 n++
368 }
369 }
370 if !f.IsExternC() {
371 if n > 0 {
372 e.w(", ")
373 }
374 e.w("ptr")
375 }
376 e.w(")\n")
377 e.extDecls[sym] = ""
378 }
379
380 func (e *irEmitter) resolveResultType(t syntax.Type) string {
381 rt := e.llvmType(t)
382 if rt != "void" { return rt }
383 if t == nil { return "ptr" }
384 u := types.SafeUnderlying(t)
385 if u == nil { return "ptr" }
386 switch u.(type) {
387 case *types.TCInterface:
388 return e.ifaceType()
389 case *types.Signature:
390 return "{ptr, ptr}"
391 case *types.TCStruct:
392 return e.llvmStructType(u.(*types.TCStruct))
393 case *types.Slice:
394 return e.sliceType()
395 }
396 return "ptr"
397 }
398
399 func (e *irEmitter) funcRetType(f *ssa.SSAFunction) string {
400 if f.Signature == nil || f.Signature.Results() == nil || f.Signature.Results().Len() == 0 {
401 return "void"
402 }
403 if f.Signature.Results().Len() == 1 {
404 return e.resolveResultType(f.Signature.Results().At(0).Type())
405 }
406 s := "{"
407 for i := 0; i < f.Signature.Results().Len(); i++ {
408 if i > 0 {
409 s = s | ", "
410 }
411 s = s | e.resolveResultType(f.Signature.Results().At(i).Type())
412 }
413 return s | "}"
414 }
415
416 func (e *irEmitter) emitBlock(b *ssa.SSABasicBlock) {
417 label := "b" | irItoa(b.Index)
418 if b.Index == 0 {
419 label = "bb.entry"
420 }
421 e.w(label)
422 e.w(":\n")
423 e.blockExitLabel[b.Index] = "%" | label
424
425 if b.Index == 0 && len(e.curFunc.FreeVars) > 0 {
426 e.emitFreeVarUnpack(e.curFunc)
427 }
428
429 scopeSaveEmitted := false
430 for _, instr := range b.Instrs {
431 if !scopeSaveEmitted {
432 if _, isPhi := instr.(*ssa.SSAPhi); !isPhi {
433 scopeSaveEmitted = true
434 e.emitScopeSave(b)
435 }
436 }
437 e.emitInstr(instr)
438 if e.missingStores != nil {
439 if v, ok2 := instr.(ssa.SSAValue); ok2 {
440 if dst, ok3 := e.missingStores[v]; ok3 {
441 loadReg := e.regName(v)
442 dstReg := e.regName(dst)
443 arrType := e.allocTypes[dst]
444 e.w(" store ") ; e.w(arrType) ; e.w(" ") ; e.w(loadReg) ; e.w(", ptr ") ; e.w(dstReg) ; e.w("\n")
445 }
446 }
447 }
448 }
449
450 hasTerminator := false
451 if n := len(b.Instrs); n > 0 {
452 switch b.Instrs[n-1].(type) {
453 case *ssa.SSAJump, *ssa.SSAIf, *ssa.SSAReturn:
454 hasTerminator = true
455 }
456 }
457 if !hasTerminator {
458 e.w(" unreachable\n")
459 }
460 }
461
462 func (e *irEmitter) blockLabel(b *ssa.SSABasicBlock) string {
463 if b.Index == 0 {
464 return "%entry"
465 }
466 return "%b" | irItoa(b.Index)
467 }
468