ir_call.mx raw
1 package main
2
3 import (
4 "git.smesh.lol/moxie/pkg/mxutil"
5 . "git.smesh.lol/moxie/pkg/types"
6 )
7
8 func (e *irEmitter) emitUnOp(u *SSAUnOp) {
9 reg := e.regName(u)
10 if u.Op == OpMul {
11 loadType := e.llvmType(u.SSAType())
12 if loadType == "void" {
13 if at, ok := e.allocTypes[u.X]; ok {
14 loadType = at
15 } else if a, ok2 := u.X.(*SSAAlloc); ok2 {
16 loadType = e.inferAllocTypeFromStores(a)
17 } else {
18 loadType = "ptr"
19 }
20 e.allocTypes[u] = loadType
21 }
22 if loadType == "ptr" {
23 if ia, ok := u.X.(*SSAIndexAddr); ok {
24 baseU := SafeUnderlying(ia.X.SSAType())
25 if sl, ok3 := baseU.(*Slice); ok3 && sl.Elem != nil {
26 et := e.llvmType(sl.Elem)
27 if et != "void" && et != "ptr" {
28 loadType = et
29 }
30 } else if b, ok4 := baseU.(*Basic); ok4 && b.Info&IsString != 0 {
31 loadType = "i8"
32 } else {
33 et := e.llvmType(ia.SSAType())
34 if p2, ok5 := SafeUnderlying(ia.SSAType()).(*Pointer); ok5 && p2.Base != nil {
35 et = e.llvmType(p2.Base)
36 }
37 if et == "void" {
38 et = "i8"
39 }
40 if et != "ptr" {
41 loadType = et
42 }
43 }
44 }
45 if fa, ok := u.X.(*SSAFieldAddr); ok {
46 baseType := e.resolveFieldAddrBase(fa)
47 ft := extractTupleField(baseType, fa.Field)
48 if ft != "" && ft != "ptr" && ft != "void" {
49 loadType = ft
50 }
51 }
52 }
53 if _, isIA := u.X.(*SSAIndexAddr); isIA && len(loadType) > 0 && loadType[0] == '[' {
54 if at, ok2 := e.allocTypes[u.X]; ok2 && at != loadType {
55 loadType = at
56 e.allocTypes[u] = loadType
57 }
58 }
59 isGlobalLoad := false
60 if g, ok := u.X.(*SSAGlobal); ok {
61 loadType = e.resolveGlobalDeclType(g)
62 isGlobalLoad = true
63 }
64 if !isGlobalLoad {
65 if at, ok := e.allocTypes[u.X]; ok && at != "ptr" && at != "void" && at != loadType {
66 bothScalar := len(loadType) > 0 && loadType[0] == 'i' && len(at) > 0 && at[0] == 'i'
67 isArrayElem := len(at) > 0 && at[0] == '[' && len(loadType) > 0 && loadType[0] != '{' && loadType != at
68 bothAgg := len(loadType) > 0 && loadType[0] == '{' && len(at) > 0 && at[0] == '{'
69 isAggToScalar := len(loadType) > 0 && loadType[0] == '{' && len(at) > 0 && (at[0] == 'i' || at == "double" || at == "float")
70 if !bothScalar && !isArrayElem && !bothAgg && !isAggToScalar {
71 loadType = at
72 e.allocTypes[u] = loadType
73 }
74 }
75 }
76 addr := e.operand(u.X)
77 e.w(" ")
78 e.w(reg)
79 e.w(" = load ")
80 e.w(loadType)
81 e.w(", ptr ")
82 e.w(addr)
83 e.w("\n")
84 e.setRegType(u, reg, loadType)
85 return
86 }
87 valType := e.llvmType(u.X.SSAType())
88 resolved := e.resolvedType(u.X, valType)
89 if resolved != valType {
90 valType = resolved
91 }
92 if valType == "void" || valType == "" {
93 resType := e.llvmType(u.SSAType())
94 if resType != "void" && resType != "" {
95 valType = resType
96 } else {
97 valType = "i32"
98 }
99 }
100 val := e.operand(u.X)
101 if u.Op == OpSub {
102 isFloat := false
103 if b, ok := SafeUnderlying(u.X.SSAType()).(*Basic); ok {
104 isFloat = b.Info&IsFloat != 0
105 }
106 if !isFloat && (valType == "double" || valType == "float") {
107 isFloat = true
108 }
109 if !isFloat && isConstOperand(val) && looksLikeFloat(val) {
110 isFloat = true
111 resType := e.llvmType(u.SSAType())
112 if resType == "float" || resType == "double" {
113 valType = resType
114 } else {
115 valType = "double"
116 }
117 }
118 e.w(" ")
119 e.w(reg)
120 if isFloat {
121 e.w(" = fneg ")
122 e.w(valType)
123 e.w(" ")
124 e.w(val)
125 e.w("\n")
126 e.setRegType(u, reg, valType)
127 } else {
128 e.w(" = sub ")
129 e.w(valType)
130 e.w(" 0, ")
131 e.w(val)
132 e.w("\n")
133 }
134 return
135 }
136 if u.Op == OpNot || u.Op == OpXor {
137 e.w(" ")
138 e.w(reg)
139 e.w(" = xor ")
140 e.w(valType)
141 e.w(" ")
142 e.w(val)
143 e.w(", -1\n")
144 return
145 }
146 if u.Op == OpArrow {
147 e.emitChanRecv(u)
148 return
149 }
150 e.w(" ; unsupported unop op=")
151 e.w(u.Op.String())
152 e.w("\n")
153 }
154
155 func (e *irEmitter) callArgType(arg SSAValue, sig *Signature, i int32) (s string) {
156 if _, isFreeVar := arg.(*SSAFreeVar); isFreeVar {
157 return "ptr"
158 }
159 if _, isIA := arg.(*SSAIndexAddr); isIA {
160 return "ptr"
161 }
162 t := e.llvmType(arg.SSAType())
163 if _, isAlloc := arg.(*SSAAlloc); !isAlloc {
164 resolved := e.resolvedType(arg, t)
165 if resolved != t {
166 t = resolved
167 }
168 }
169 // Integer constants take the signature's param width: an untyped const
170 // defaults to i32, and passing "i32 -1" to an i64 param silently zeroes
171 // the high half at the ABI boundary.
172 if k, isConst := arg.(*SSAConst); isConst && sig != nil && sig.Params != nil {
173 if _, isInt := k.val.(*ConstInt); isInt {
174 sigIdx := i
175 if sig.Recv != nil {
176 sigIdx = i - 1
177 }
178 if sigIdx >= 0 && sigIdx < sig.Params.Len() {
179 sigT := e.llvmType(sig.Params.At(sigIdx).Typ)
180 if e.intBits(sigT) > 0 {
181 return sigT
182 }
183 }
184 }
185 }
186 if (t == "ptr" || t == "i1") && sig != nil && sig.Params != nil {
187 _, isCall := arg.(*SSACall)
188 _, isAlloc := arg.(*SSAAlloc)
189 if !isCall && !isAlloc {
190 sigIdx := i
191 if sig.Recv != nil {
192 sigIdx = i - 1
193 }
194 if sigIdx >= 0 && sigIdx < sig.Params.Len() {
195 sigT := e.llvmType(sig.Params.At(sigIdx).Typ)
196 if sigT != "ptr" && sigT != "void" && sigT != "i1" && sigT != "" {
197 return sigT
198 }
199 }
200 }
201 }
202 if t == "ptr" || t == "i1" {
203 if load, ok := arg.(*SSAUnOp); ok && load.Op == OpMul {
204 if g, ok2 := load.X.(*SSAGlobal); ok2 {
205 if gt, ok3 := e.globalTypes[e.globalName(g)]; ok3 {
206 return gt
207 }
208 }
209 }
210 }
211 if t != "void" {
212 return t
213 }
214 if sig != nil && sig.Params != nil {
215 sigIdx := i
216 if sig.Recv != nil {
217 sigIdx = i - 1
218 }
219 if sigIdx >= 0 && sigIdx < sig.Params.Len() {
220 return e.llvmType(sig.Params.At(sigIdx).Typ)
221 }
222 }
223 return "ptr"
224 }
225
226 func (e *irEmitter) callSig(c *SSACall) (s *Signature) {
227 if fn, ok := c.Call.Value.(*SSAFunction); ok && fn.Signature != nil {
228 return fn.Signature
229 }
230 if sig, ok := SafeUnderlying(c.Call.Value.SSAType()).(*Signature); ok {
231 return sig
232 }
233 return nil
234 }
235
236 func (e *irEmitter) emitCall(c *SSACall) {
237 if b, ok := c.Call.Value.(*SSABuiltin); ok {
238 e.emitBuiltinCall(c, b)
239 return
240 }
241 reg := e.regName(c)
242 retType := e.llvmType(c.SSAType())
243 isVoid := retType == "void"
244 sig := e.callSig(c)
245
246 if fn, ok := c.Call.Value.(*SSAFunction); ok {
247 if !e.isPkgFunc(fn) {
248 e.declareExternalFunc(fn)
249 }
250 useSret2 := needsSret(retType)
251 sretTmp2 := ""
252 if useSret2 {
253 e.nextReg++
254 sretTmp2 = "%sret." | irItoa(e.nextReg)
255 e.w(" ") ; e.w(sretTmp2) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n")
256 e.w(" call void ")
257 } else {
258 e.w(" ")
259 if !isVoid {
260 e.w(reg) ; e.w(" = ")
261 }
262 e.w("call ") ; e.w(retType) ; e.w(" ")
263 }
264 e.w(e.funcSymbol(fn))
265 e.w("(")
266 np := 0
267 if useSret2 {
268 e.w("ptr sret(") ; e.w(retType) ; e.w(") ") ; e.w(sretTmp2)
269 np++
270 }
271 sym := e.funcSymbol(fn)
272 isIntrinsic := mxutil.HasPrefix(sym, "@llvm.") || mxutil.HasPrefix(sym, "@\"llvm.")
273 isExport := fn.externalSymbol != ""
274 for i, arg := range c.Call.Args {
275 if np > 0 { e.w(", ") }
276 if arg == nil {
277 e.w("ptr null")
278 np++
279 continue
280 }
281 at := e.callArgType(arg, sig, int32(i))
282 av := e.operand(arg)
283 if av == "null" && at != "ptr" {
284 av = "zeroinitializer"
285 }
286 e.w(at) ; e.w(" ") ; e.w(av)
287 np++
288 }
289 skipCtx := isIntrinsic || isExport
290 if !skipCtx && len(cctx.wasmImportMap) > 0 {
291 wsym := sym
292 if len(wsym) > 0 && wsym[0] == '@' { wsym = wsym[1:] }
293 if len(wsym) >= 2 && wsym[0] == '"' && wsym[len(wsym)-1] == '"' { wsym = wsym[1 : len(wsym)-1] }
294 if _, wok := cctx.wasmImportMap[wsym]; wok {
295 skipCtx = true
296 }
297 }
298 if !skipCtx {
299 if np > 0 { e.w(", ") }
300 e.w("ptr null")
301 }
302 e.w(")\n")
303 if useSret2 {
304 e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(sretTmp2) ; e.w("\n")
305 }
306 return
307 }
308
309 funcVal := e.operand(c.Call.Value)
310 funcPtr := e.nextReg2("fp")
311 ctxPtr := e.nextReg2("fp")
312 e.w(" ") ; e.w(funcPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(funcVal) ; e.w(", 1\n")
313 e.w(" ") ; e.w(ctxPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(funcVal) ; e.w(", 0\n")
314 useSret := needsSret(retType)
315 sretTmp := ""
316 if useSret {
317 e.nextReg++
318 sretTmp = "%sret." | irItoa(e.nextReg)
319 e.w(" ") ; e.w(sretTmp) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n")
320 e.w(" call void ") ; e.w(funcPtr) ; e.w("(ptr sret(") ; e.w(retType) ; e.w(") ") ; e.w(sretTmp)
321 } else {
322 e.w(" ")
323 if !isVoid {
324 e.w(reg) ; e.w(" = ")
325 }
326 e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(funcPtr) ; e.w("(")
327 }
328 np2 := 0
329 if useSret { np2++ }
330 for i, arg := range c.Call.Args {
331 if np2 > 0 { e.w(", ") }
332 at := e.callArgType(arg, sig, int32(i))
333 av := e.operand(arg)
334 if av == "null" && at != "ptr" {
335 av = "zeroinitializer"
336 }
337 e.w(at) ; e.w(" ") ; e.w(av)
338 np2++
339 }
340 if np2 > 0 { e.w(", ") }
341 e.w("ptr ") ; e.w(ctxPtr)
342 e.w(")\n")
343 if useSret {
344 e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(sretTmp) ; e.w("\n")
345 }
346 }
347
348 func (e *irEmitter) emitBuiltinCall(c *SSACall, b *SSABuiltin) {
349 reg := e.regName(c)
350 name := b.SSAName()
351 if name == "recover" {
352 e.w(" ; unhandled builtin: recover\n")
353 recRT := e.ifaceType()
354 e.nextReg++
355 tmp := "%ub" | irItoa(e.nextReg)
356 e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(recRT) ; e.w("\n")
357 e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(recRT) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
358 e.allocTypes[c] = recRT
359 return
360 }
361 ipt := e.intptrType()
362 sty := e.sliceType()
363 if name == "len" {
364 if len(c.Call.Args) == 1 {
365 arg := e.operand(c.Call.Args[0])
366 u := SafeUnderlying(c.Call.Args[0].SSAType())
367 if u == nil { u = c.Call.Args[0].SSAType() }
368 if arr, ok := u.(*Array); ok {
369 rt1 := e.llvmType(c.SSAType())
370 e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt1) ; e.w(" ") ; e.w(irItoa(int32(arr.Len))) ; e.w(", 0\n")
371 _ = arg
372 return
373 }
374 if p, ok := u.(*Pointer); ok && p.Base != nil {
375 if arr, ok2 := SafeUnderlying(p.Base).(*Array); ok2 {
376 rt2 := e.llvmType(c.SSAType())
377 e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt2) ; e.w(" ") ; e.w(irItoa(int32(arr.Len))) ; e.w(", 0\n")
378 _ = arg
379 return
380 }
381 }
382 _, isSlice := u.(*Slice)
383 _, isMap := u.(*TCMap)
384 isStr := e.isStringLike(c.Call.Args[0].SSAType())
385 if !isSlice && !isMap && !isStr {
386 isSlice = true
387 }
388 if isMap {
389 rt3 := e.llvmType(c.SSAType())
390 e.nextReg++
391 tmp := "%bl" | irItoa(e.nextReg)
392 e.w(" ") ; e.w(tmp) ; e.w(" = call ") ; e.w(ipt) ; e.w(" @runtime.hashmapLen(ptr ") ; e.w(arg) ; e.w(", ptr null)\n")
393 if rt3 != ipt {
394 e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(rt3) ; e.w("\n")
395 } else {
396 e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(", 0\n")
397 }
398 e.declareRuntime("runtime.hashmapLen", ipt, "ptr")
399 return
400 }
401 if isSlice || isStr {
402 rt4 := e.llvmType(c.SSAType())
403 if rt4 != ipt {
404 e.nextReg++
405 tmp := "%bl" | irItoa(e.nextReg)
406 e.w(" ") ; e.w(tmp) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 1\n")
407 e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(rt4) ; e.w("\n")
408 } else {
409 e.w(" ") ; e.w(reg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 1\n")
410 }
411 return
412 }
413 }
414 } else if name == "cap" {
415 if len(c.Call.Args) == 1 {
416 arg := e.operand(c.Call.Args[0])
417 uc := SafeUnderlying(c.Call.Args[0].SSAType())
418 if uc == nil { uc = c.Call.Args[0].SSAType() }
419 if arr, ok := uc.(*Array); ok {
420 rt5 := e.llvmType(c.SSAType())
421 e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt5) ; e.w(" ") ; e.w(irItoa(int32(arr.Len))) ; e.w(", 0\n")
422 _ = arg
423 return
424 }
425 if p, ok := uc.(*Pointer); ok && p.Base != nil {
426 if arr, ok2 := SafeUnderlying(p.Base).(*Array); ok2 {
427 rt6 := e.llvmType(c.SSAType())
428 e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt6) ; e.w(" ") ; e.w(irItoa(int32(arr.Len))) ; e.w(", 0\n")
429 _ = arg
430 return
431 }
432 }
433 _, isSlice := uc.(*Slice)
434 isStr := e.isStringLike(c.Call.Args[0].SSAType())
435 if isSlice || isStr {
436 rt7 := e.llvmType(c.SSAType())
437 if rt7 != ipt {
438 e.nextReg++
439 tmp := "%bl" | irItoa(e.nextReg)
440 e.w(" ") ; e.w(tmp) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 2\n")
441 e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(rt7) ; e.w("\n")
442 } else {
443 e.w(" ") ; e.w(reg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 2\n")
444 }
445 return
446 }
447 }
448 } else if name == "append" {
449 if len(c.Call.Args) == 1 {
450 src := e.operand(c.Call.Args[0])
451 p := e.nextReg2("ap")
452 e.w(" ") ; e.w(p) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n")
453 l := e.nextReg2("ap")
454 e.w(" ") ; e.w(l) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n")
455 ca := e.nextReg2("ap")
456 e.w(" ") ; e.w(ca) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n")
457 s1 := e.nextReg2("ap")
458 e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(p) ; e.w(", 0\n")
459 s2 := e.nextReg2("ap")
460 e.w(" ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(l) ; e.w(", 1\n")
461 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(ca) ; e.w(", 2\n")
462 return
463 }
464 if len(c.Call.Args) > 2 {
465 src := e.operand(c.Call.Args[0])
466 elemType := ""
467 if sl, ok := SafeUnderlying(c.Call.Args[0].SSAType()).(*Slice); ok {
468 elemType = e.llvmType(sl.Elem)
469 }
470 if sl, ok := c.Call.Args[0].SSAType().(*Slice); ok && (elemType == "" || elemType == "void") {
471 elemType = e.llvmType(sl.Elem)
472 }
473 if elemType == "" || elemType == "void" {
474 et := e.llvmType(c.Call.Args[1].SSAType())
475 if et != "" && et != "void" {
476 elemType = et
477 } else {
478 elemType = "i8"
479 }
480 }
481 nElems := len(c.Call.Args) - 1
482 arrAlloca := e.nextReg2("ap")
483 arrTy := "[" | irItoa(nElems) | " x " | elemType | "]"
484 e.w(" ") ; e.w(arrAlloca) ; e.w(" = alloca ") ; e.w(arrTy) ; e.w("\n")
485 for j := 1; j < len(c.Call.Args); j++ {
486 elemVal := e.operand(c.Call.Args[j])
487 argLLT := e.llvmType(c.Call.Args[j].SSAType())
488 if len(argLLT) > 1 && argLLT[0] == 'i' && len(elemType) > 1 && elemType[0] == 'i' && argLLT != elemType {
489 aw := irParseIntWidth(argLLT)
490 ew := irParseIntWidth(elemType)
491 if ew > 0 && aw > ew {
492 tr := e.nextReg2("ap")
493 e.w(" ") ; e.w(tr) ; e.w(" = trunc ") ; e.w(argLLT) ; e.w(" ") ; e.w(elemVal) ; e.w(" to ") ; e.w(elemType) ; e.w("\n")
494 elemVal = tr
495 }
496 }
497 gep := e.nextReg2("ap")
498 e.w(" ") ; e.w(gep) ; e.w(" = getelementptr inbounds ") ; e.w(arrTy)
499 e.w(", ptr ") ; e.w(arrAlloca) ; e.w(", i32 0, i32 ") ; e.w(irItoa(j-1)) ; e.w("\n")
500 e.w(" store ") ; e.w(elemType) ; e.w(" ") ; e.w(elemVal) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n")
501 }
502 srcBuf := e.nextReg2("ap")
503 e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n")
504 srcLen := e.nextReg2("ap")
505 e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n")
506 srcCap := e.nextReg2("ap")
507 e.w(" ") ; e.w(srcCap) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n")
508 elemSz := e.nextReg2("ap")
509 e.w(" ") ; e.w(elemSz)
510 e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType)
511 e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n")
512 retTy := e.sliceType()
513 result := e.nextReg2("ap")
514 e.w(" ") ; e.w(result)
515 e.w(" = call ") ; e.w(retTy) ; e.w(" @runtime.sliceAppend(ptr ")
516 e.w(srcBuf) ; e.w(", ptr ") ; e.w(arrAlloca)
517 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen)
518 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcCap)
519 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(irItoa(nElems))
520 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz)
521 e.w(", ptr null)\n")
522 newPtr := e.nextReg2("ap")
523 e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n")
524 e.scopeTrackAlloc(newPtr)
525 newLen := e.nextReg2("ap")
526 e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n")
527 newCap := e.nextReg2("ap")
528 e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n")
529 s1 := e.nextReg2("ap")
530 e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n")
531 s2 := e.nextReg2("ap")
532 e.w(" ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newLen) ; e.w(", 1\n")
533 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newCap) ; e.w(", 2\n")
534 e.declareRuntime("runtime.sliceAppend", retTy, "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType())
535 return
536 }
537 if len(c.Call.Args) == 2 {
538 src := e.operand(c.Call.Args[0])
539 elems := e.operand(c.Call.Args[1])
540 elemType := ""
541 arg0t := c.Call.Args[0].SSAType()
542 if sl, ok := SafeUnderlying(arg0t).(*Slice); ok {
543 elemType = e.llvmType(sl.Elem)
544 }
545 if sl, ok := arg0t.(*Slice); ok && (elemType == "" || elemType == "void") {
546 elemType = e.llvmType(sl.Elem)
547 }
548 if elemType == "" || elemType == "void" {
549 et := e.llvmType(c.Call.Args[1].SSAType())
550 if et != "" && et != "void" {
551 elemType = et
552 } else {
553 elemType = "i8"
554 }
555 }
556 srcBuf := e.nextReg2("ap")
557 e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n")
558 srcLen := e.nextReg2("ap")
559 e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n")
560 srcCap := e.nextReg2("ap")
561 e.w(" ") ; e.w(srcCap) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n")
562 var elemsBuf, elemsLen string
563 arg1IsSlice := c.Call.HasDots
564 if !arg1IsSlice && c.Call.Args[1] != nil {
565 arg1t := c.Call.Args[1].SSAType()
566 if arg1t != nil {
567 arg1LT := e.llvmType(arg1t)
568 if arg1LT == e.sliceType() && elemType != e.sliceType() {
569 arg1IsSlice = true
570 }
571 }
572 if !arg1IsSlice {
573 arg0t2 := c.Call.Args[0].SSAType()
574 if arg0t2 != nil && arg1t != nil {
575 if Identical(arg0t2, arg1t) {
576 if _, ok := SafeUnderlying(arg0t2).(*Slice); ok {
577 arg1IsSlice = true
578 }
579 }
580 }
581 }
582 }
583 if arg1IsSlice {
584 elemsBuf = e.nextReg2("ap")
585 e.w(" ") ; e.w(elemsBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(elems) ; e.w(", 0\n")
586 elemsLen = e.nextReg2("ap")
587 e.w(" ") ; e.w(elemsLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(elems) ; e.w(", 1\n")
588 } else {
589 alloca := e.nextReg2("ap")
590 e.w(" ") ; e.w(alloca) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n")
591 e.w(" store ") ; e.w(elemType) ; e.w(" ") ; e.w(elems) ; e.w(", ptr ") ; e.w(alloca) ; e.w("\n")
592 elemsBuf = alloca
593 elemsLen = "1"
594 }
595 elemSz := e.nextReg2("ap")
596 e.w(" ") ; e.w(elemSz)
597 e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType)
598 e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n")
599 retTy := e.sliceType()
600 result := e.nextReg2("ap")
601 e.w(" ") ; e.w(result)
602 e.w(" = call ") ; e.w(retTy) ; e.w(" @runtime.sliceAppend(ptr ")
603 e.w(srcBuf) ; e.w(", ptr ") ; e.w(elemsBuf)
604 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen)
605 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcCap)
606 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemsLen)
607 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz)
608 e.w(", ptr null)\n")
609 newPtr := e.nextReg2("ap")
610 e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n")
611 e.scopeTrackAlloc(newPtr)
612 newLen := e.nextReg2("ap")
613 e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n")
614 newCap := e.nextReg2("ap")
615 e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n")
616 s1 := e.nextReg2("ap")
617 e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n")
618 s2 := e.nextReg2("ap")
619 e.w(" ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newLen) ; e.w(", 1\n")
620 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newCap) ; e.w(", 2\n")
621 e.declareRuntime("runtime.sliceAppend", retTy, "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType())
622 return
623 }
624 } else if name == "copy" {
625 if len(c.Call.Args) == 2 {
626 dst := e.operand(c.Call.Args[0])
627 src := e.operand(c.Call.Args[1])
628 elemType := "i8"
629 if sl, ok := SafeUnderlying(c.Call.Args[0].SSAType()).(*Slice); ok {
630 elemType = e.llvmType(sl.Elem)
631 }
632 dstBuf := e.nextReg2("cp")
633 e.w(" ") ; e.w(dstBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(dst) ; e.w(", 0\n")
634 dstLen := e.nextReg2("cp")
635 e.w(" ") ; e.w(dstLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(dst) ; e.w(", 1\n")
636 srcBuf := e.nextReg2("cp")
637 e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n")
638 srcLen := e.nextReg2("cp")
639 e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n")
640 elemSz := e.nextReg2("cp")
641 e.w(" ") ; e.w(elemSz)
642 e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType)
643 e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n")
644 callReg := e.nextReg2("cp")
645 e.w(" ") ; e.w(callReg)
646 e.w(" = call ") ; e.w(e.intptrType()) ; e.w(" @runtime.sliceCopy(ptr ")
647 e.w(dstBuf) ; e.w(", ptr ") ; e.w(srcBuf)
648 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(dstLen)
649 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen)
650 e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz)
651 e.w(", ptr null)\n")
652 rt8 := e.llvmType(c.SSAType())
653 if rt8 != e.intptrType() {
654 e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(callReg) ; e.w(" to ") ; e.w(rt8) ; e.w("\n")
655 } else {
656 e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(callReg) ; e.w(", 0\n")
657 }
658 e.declareRuntime("runtime.sliceCopy", e.intptrType(), "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType())
659 return
660 }
661 } else if name == "print" || name == "println" {
662 e.w(" call void @runtime.printlock(ptr null)\n")
663 for i, arg := range c.Call.Args {
664 if i > 0 && b.id == BuiltinPrintln {
665 e.w(" call void @runtime.printspace(ptr null)\n")
666 }
667 av := e.operand(arg)
668 at := arg.SSAType()
669 e.emitPrintArg(av, at)
670 }
671 if b.id == BuiltinPrintln {
672 e.w(" call void @runtime.printnl(ptr null)\n")
673 e.declareRuntime("runtime.printnl", "void", "")
674 }
675 e.w(" call void @runtime.printunlock(ptr null)\n")
676 e.declareRuntime("runtime.printlock", "void", "")
677 e.declareRuntime("runtime.printunlock", "void", "")
678 if b.id == BuiltinPrintln && len(c.Call.Args) > 1 {
679 e.declareRuntime("runtime.printspace", "void", "")
680 }
681 return
682 } else if name == "delete" {
683 if len(c.Call.Args) == 2 {
684 mapVal := e.operand(c.Call.Args[0])
685 keyVal := e.operand(c.Call.Args[1])
686 mt, _ := SafeUnderlying(c.Call.Args[0].SSAType()).(*TCMap)
687 keyType := "i32"
688 if mt != nil {
689 keyType = e.llvmType(mt.Key)
690 } else if c.Call.Args[1].SSAType() != nil {
691 keyType = e.llvmType(c.Call.Args[1].SSAType())
692 }
693 if mt != nil && e.isStringLike(mt.Key) {
694 kd := e.nextReg2("dl")
695 kl := e.nextReg2("dl")
696 kc := e.nextReg2("dl")
697 e.w(" ") ; e.w(kd) ; e.w(" = extractvalue " | e.sliceType() | " ") ; e.w(keyVal) ; e.w(", 0\n")
698 e.w(" ") ; e.w(kl) ; e.w(" = extractvalue " | e.sliceType() | " ") ; e.w(keyVal) ; e.w(", 1\n")
699 e.w(" ") ; e.w(kc) ; e.w(" = extractvalue " | e.sliceType() | " ") ; e.w(keyVal) ; e.w(", 2\n")
700 ipt2 := e.intptrType()
701 e.w(" call void @runtime.hashmapContentDelete(ptr ") ; e.w(mapVal)
702 e.w(", ptr ") ; e.w(kd)
703 e.w(", " | ipt2 | " ") ; e.w(kl)
704 e.w(", " | ipt2 | " ") ; e.w(kc)
705 e.w(", ptr null)\n")
706 e.declareRuntime("runtime.hashmapContentDelete", "void", "ptr, ptr, " | ipt | ", " | ipt)
707 } else {
708 keyAlloca := e.nextReg2("dl")
709 e.w(" ") ; e.w(keyAlloca) ; e.w(" = alloca ") ; e.w(keyType) ; e.w("\n")
710 e.w(" store ") ; e.w(keyType) ; e.w(" ") ; e.w(keyVal) ; e.w(", ptr ") ; e.w(keyAlloca) ; e.w("\n")
711 e.w(" call void @runtime.hashmapBinaryDelete(ptr ") ; e.w(mapVal)
712 e.w(", ptr ") ; e.w(keyAlloca) ; e.w(", ptr null)\n")
713 e.declareRuntime("runtime.hashmapBinaryDelete", "void", "ptr, ptr")
714 }
715 return
716 }
717 } else if name == "close" {
718 if len(c.Call.Args) == 1 {
719 e.w(" call void @runtime.chanClose(ptr ")
720 e.w(e.operand(c.Call.Args[0]))
721 e.w(", ptr null)\n")
722 e.declareRuntime("runtime.chanClose", "void", "ptr")
723 return
724 }
725 } else if name == "min" || name == "max" {
726 if len(c.Call.Args) >= 2 {
727 rt9 := e.llvmType(c.SSAType())
728 if rt9 == "" || rt9 == "void" {
729 rt9 = "i32"
730 }
731 a := e.operand(c.Call.Args[0])
732 b2 := e.operand(c.Call.Args[1])
733 cmpOp := "slt"
734 if b.id == BuiltinMax {
735 cmpOp = "sgt"
736 }
737 u := SafeUnderlying(c.SSAType())
738 if bb, ok := u.(*Basic); ok && bb.Info&IsUnsigned != 0 {
739 cmpOp = "ult"
740 if b.id == BuiltinMax {
741 cmpOp = "ugt"
742 }
743 }
744 e.nextReg++
745 cmpReg := "%mm" | irItoa(e.nextReg)
746 e.w(" ") ; e.w(cmpReg) ; e.w(" = icmp ") ; e.w(cmpOp) ; e.w(" ") ; e.w(rt9) ; e.w(" ") ; e.w(a) ; e.w(", ") ; e.w(b2) ; e.w("\n")
747 e.w(" ") ; e.w(reg) ; e.w(" = select i1 ") ; e.w(cmpReg) ; e.w(", ") ; e.w(rt9) ; e.w(" ") ; e.w(a) ; e.w(", ") ; e.w(rt9) ; e.w(" ") ; e.w(b2) ; e.w("\n")
748 return
749 }
750 } else if name == "spawn" {
751 ipt3 := e.intptrType()
752 if len(c.Call.Args) >= 1 {
753 // For spawn, we need the raw function pointer, not the fat pointer.
754 // operand() wraps SSAFunction in { ptr null, ptr @fn } but ptrtoint
755 // needs a plain pointer.
756 var rawFnSym string
757 if fn, ok := c.Call.Args[0].(*SSAFunction); ok {
758 if !e.isPkgFunc(fn) {
759 e.declareExternalFunc(fn)
760 }
761 rawFnSym = e.funcSymbol(fn)
762 } else {
763 // Closure or indirect - extract funcptr from fat pointer via alloca
764 fnVal := e.operand(c.Call.Args[0])
765 fpTmp := e.nextReg2("sp")
766 e.w(" ") ; e.w(fpTmp) ; e.w(" = alloca {ptr, ptr}\n")
767 e.w(" store {ptr, ptr} ") ; e.w(fnVal) ; e.w(", ptr ") ; e.w(fpTmp) ; e.w("\n")
768 fpGep := e.nextReg2("sp")
769 e.w(" ") ; e.w(fpGep) ; e.w(" = getelementptr inbounds {ptr, ptr}, ptr ") ; e.w(fpTmp) ; e.w(", i32 0, i32 1\n")
770 rawFnSym = e.nextReg2("sp")
771 e.w(" ") ; e.w(rawFnSym) ; e.w(" = load ptr, ptr ") ; e.w(fpGep) ; e.w("\n")
772 }
773 fnPtr := e.nextReg2("sp")
774 e.w(" ") ; e.w(fnPtr) ; e.w(" = ptrtoint ptr ") ; e.w(rawFnSym) ; e.w(" to ") ; e.w(ipt3) ; e.w("\n")
775 // Count channel args
776 nChans := int32(0)
777 for i := 1; i < len(c.Call.Args); i++ {
778 if _, ok := SafeUnderlying(c.Call.Args[i].SSAType()).(*TCChan); ok {
779 nChans++
780 }
781 }
782 // TODO: serialize args for Codec boundary, set up ring buffers per channel
783 e.w(" call void @runtime.spawnDomain(") ; e.w(ipt3) ; e.w(" ") ; e.w(fnPtr)
784 e.w(", ptr null, i32 ") ; e.w(irItoa(nChans)) ; e.w(", ptr null)\n")
785 // Get lifecycle channel - returns ptr to channel struct
786 e.w(" ") ; e.w(reg) ; e.w(" = call ptr @runtime.SpawnLifecycleChannel(ptr null)\n")
787 e.declareRuntime("runtime.spawnDomain", "void", ipt3 | ", ptr, i32")
788 e.declareRuntime("runtime.SpawnLifecycleChannel", "ptr", "")
789 }
790 return
791 }
792 e.w(" ; unhandled builtin: ")
793 e.w(name)
794 e.w("\n")
795 rt10 := e.llvmType(c.SSAType())
796 if rt10 != "void" && rt10 != "" {
797 if rt10 == "ptr" || e.intBits(rt10) > 0 || rt10 == "i1" {
798 e.emitZeroReg(reg, c.SSAType())
799 } else {
800 e.nextReg++
801 tmp := "%ub" | irItoa(e.nextReg)
802 e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(rt10) ; e.w("\n")
803 e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(rt10) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n")
804 e.allocTypes[c] = rt10
805 }
806 }
807 }
808
809 func (e *irEmitter) emitPrintArg(val string, t Type) {
810 if t == nil {
811 return
812 }
813 sty := e.sliceType()
814 switch u := SafeUnderlying(t).(type) {
815 case *Basic:
816 switch {
817 case u.Info&IsString != 0:
818 e.w(" call void @runtime.printstring(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
819 e.declareRuntime("runtime.printstring", "void", sty)
820 case u.Kind == Bool || u.Kind == UntypedBool:
821 e.w(" call void @runtime.printbool(i1 ") ; e.w(val) ; e.w(", ptr null)\n")
822 e.declareRuntime("runtime.printbool", "void", "i1")
823 case u.Kind == Float32:
824 e.w(" call void @runtime.printfloat32(float ") ; e.w(val) ; e.w(", ptr null)\n")
825 e.declareRuntime("runtime.printfloat32", "void", "float")
826 case u.Kind == Float64 || u.Kind == UntypedFloat:
827 e.w(" call void @runtime.printfloat64(double ") ; e.w(val) ; e.w(", ptr null)\n")
828 e.declareRuntime("runtime.printfloat64", "void", "double")
829 case u.Info&IsUnsigned != 0:
830 lt := e.llvmType(t)
831 fname := "runtime.printuint" | lt[1:]
832 e.w(" call void @") ; e.w(fname) ; e.w("(") ; e.w(lt) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
833 e.declareRuntime(fname, "void", lt)
834 case u.Info&IsInteger != 0:
835 lt := e.llvmType(t)
836 fname := "runtime.printint" | lt[1:]
837 e.w(" call void @") ; e.w(fname) ; e.w("(") ; e.w(lt) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
838 e.declareRuntime(fname, "void", lt)
839 }
840 case *Pointer:
841 ipt := e.intptrType()
842 e.nextReg++
843 tmp := "%pr" | irItoa(e.nextReg)
844 e.w(" ") ; e.w(tmp) ; e.w(" = ptrtoint ptr ") ; e.w(val) ; e.w(" to ") ; e.w(ipt) ; e.w("\n")
845 e.w(" call void @runtime.printptr(") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(", ptr null)\n")
846 e.declareRuntime("runtime.printptr", "void", ipt)
847 case *Slice:
848 if b, ok := u.Elem.(*Basic); ok && (b.Kind == Uint8 || b.Kind == Int8) {
849 e.w(" call void @runtime.printbytes(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
850 e.declareRuntime("runtime.printbytes", "void", sty)
851 } else {
852 e.w(" call void @runtime.printstring(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", ptr null)\n")
853 e.declareRuntime("runtime.printstring", "void", sty)
854 }
855 case *TCMap:
856 e.w(" call void @runtime.printmap(ptr ") ; e.w(val) ; e.w(", ptr null)\n")
857 e.declareRuntime("runtime.printmap", "void", "ptr")
858 }
859 }
860