ir_chan.mx raw
1 package main
2
3 import (
4 . "git.smesh.lol/moxie/pkg/types"
5 )
6
7 func (e *irEmitter) chanPanicStr() (s string) {
8 msg := "channels not implemented"
9 idx := e.addStringConst(msg)
10 ipt := e.intptrType()
11 slen := irItoa64(int64(len(msg)))
12 return "{ ptr " | e.strConstGlobal(idx) | ", " | ipt | " " | slen | ", " | ipt | " " | slen | " }"
13 }
14
15 func (e *irEmitter) emitChanPanic() {
16 sty := e.sliceType()
17 e.w(" call void @runtime._panicstr(") ; e.w(sty) ; e.w(" ")
18 e.w(e.chanPanicStr()) ; e.w(", ptr null)\n")
19 e.declareRuntime("runtime._panicstr", "void", sty)
20 e.w(" unreachable\n")
21 e.blockDead = true
22 }
23
24 func (e *irEmitter) chanElemType(chanType Type) (elemT string) {
25 if ch, ok := SafeUnderlying(chanType).(*TCChan); ok && ch.Elem != nil {
26 et := e.llvmType(ch.Elem)
27 if et != "void" && et != "" {
28 return et
29 }
30 }
31 return "i8"
32 }
33
34 func (e *irEmitter) emitChanRecv(u *SSAUnOp) {
35 reg := e.regName(u)
36 elemType := e.chanElemType(u.X.SSAType())
37 ch := e.operand(u.X)
38 opType := "{ptr, ptr, i32, ptr}"
39
40 e.nextReg++
41 valAlloca := "%chanrv" | irItoa(e.nextReg)
42 e.w(" ") ; e.w(valAlloca) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n")
43 e.w(" store ") ; e.w(elemType) ; e.w(" zeroinitializer, ptr ") ; e.w(valAlloca) ; e.w("\n")
44
45 e.nextReg++
46 opAlloca := "%chanrop" | irItoa(e.nextReg)
47 e.w(" ") ; e.w(opAlloca) ; e.w(" = alloca ") ; e.w(opType) ; e.w("\n")
48 e.w(" store ") ; e.w(opType) ; e.w(" zeroinitializer, ptr ") ; e.w(opAlloca) ; e.w("\n")
49
50 e.nextReg++
51 okReg := "%chanrok" | irItoa(e.nextReg)
52 e.w(" ") ; e.w(okReg) ; e.w(" = call i1 @runtime.chanRecv(ptr ") ; e.w(ch) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w(", ptr ") ; e.w(opAlloca) ; e.w(", ptr null)\n")
53 e.declareRuntime("runtime.chanRecv", "i1", "ptr, ptr, ptr")
54
55 if u.CommaOk {
56 e.nextReg++
57 loadedVal := "%chanrlv" | irItoa(e.nextReg)
58 e.w(" ") ; e.w(loadedVal) ; e.w(" = load ") ; e.w(elemType) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n")
59
60 tupleType := "{" | elemType | ", i1}"
61 e.nextReg++
62 t1 := "%chanrt1_" | irItoa(e.nextReg)
63 e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupleType) ; e.w(" zeroinitializer, ") ; e.w(elemType) ; e.w(" ") ; e.w(loadedVal) ; e.w(", 0\n")
64 e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupleType) ; e.w(" ") ; e.w(t1) ; e.w(", i1 ") ; e.w(okReg) ; e.w(", 1\n")
65 } else {
66 e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(elemType) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n")
67 }
68 }
69
70 func (e *irEmitter) emitChanSend(s *SSASend) {
71 elemType := e.chanElemType(s.Chan.SSAType())
72 ch := e.operand(s.Chan)
73 val := e.operand(s.X)
74 opType := "{ptr, ptr, i32, ptr}"
75
76 e.nextReg++
77 valAlloca := "%chansv" | irItoa(e.nextReg)
78 e.w(" ") ; e.w(valAlloca) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n")
79
80 valSrcType := e.llvmType(s.X.SSAType())
81 resolved := e.resolvedType(s.X, valSrcType)
82 if resolved != valSrcType {
83 valSrcType = resolved
84 }
85 if valSrcType == elemType {
86 e.w(" store ") ; e.w(elemType) ; e.w(" ") ; e.w(val) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n")
87 } else {
88 e.w(" store ") ; e.w(valSrcType) ; e.w(" ") ; e.w(val) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n")
89 }
90
91 e.nextReg++
92 opAlloca := "%chansop" | irItoa(e.nextReg)
93 e.w(" ") ; e.w(opAlloca) ; e.w(" = alloca ") ; e.w(opType) ; e.w("\n")
94 e.w(" store ") ; e.w(opType) ; e.w(" zeroinitializer, ptr ") ; e.w(opAlloca) ; e.w("\n")
95
96 e.w(" call void @runtime.chanSend(ptr ") ; e.w(ch) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w(", ptr ") ; e.w(opAlloca) ; e.w(", ptr null)\n")
97 e.declareRuntime("runtime.chanSend", "void", "ptr, ptr, ptr")
98 }
99
100 func (e *irEmitter) emitSelect(s *SSASelect) {
101 _ = e.regName(s)
102 ipt := e.intptrType()
103 sty := e.sliceType()
104 nStates := int32(len(s.States))
105 stateType := "{ptr, ptr}" // chanSelectState: {ch, value}
106 opType := "{ptr, ptr, i32, ptr}" // channelOp
107
108 // Find largest recv element type for recvbuf.
109 maxElemType := "i8"
110 for _, st := range s.States {
111 if st.Dir == SelectDirRecv && st.Chan != nil {
112 et := e.chanElemType(st.Chan.SSAType())
113 if et != "i8" {
114 maxElemType = et
115 }
116 }
117 }
118
119 // Alloca recvbuf.
120 e.nextReg++
121 recvbuf := "%selrb" | irItoa(e.nextReg)
122 e.w(" ") ; e.w(recvbuf) ; e.w(" = alloca ") ; e.w(maxElemType) ; e.w("\n")
123 e.w(" store ") ; e.w(maxElemType) ; e.w(" zeroinitializer, ptr ") ; e.w(recvbuf) ; e.w("\n")
124
125 // Alloca states array.
126 statesArrayType := "[" | irItoa(nStates) | " x " | stateType | "]"
127 e.nextReg++
128 statesArr := "%selst" | irItoa(e.nextReg)
129 e.w(" ") ; e.w(statesArr) ; e.w(" = alloca ") ; e.w(statesArrayType) ; e.w("\n")
130 e.w(" store ") ; e.w(statesArrayType) ; e.w(" zeroinitializer, ptr ") ; e.w(statesArr) ; e.w("\n")
131
132 // Fill each state.
133 for i, st := range s.States {
134 ch := "null"
135 if st.Chan != nil {
136 ch = e.operand(st.Chan)
137 }
138 e.nextReg++
139 gep := "%selgep" | irItoa(e.nextReg)
140 e.w(" ") ; e.w(gep) ; e.w(" = getelementptr inbounds ") ; e.w(stateType)
141 e.w(", ptr ") ; e.w(statesArr) ; e.w(", i32 ") ; e.w(irItoa(int32(i))) ; e.w("\n")
142
143 // Store ch at field 0.
144 e.nextReg++
145 chGep := "%selch" | irItoa(e.nextReg)
146 e.w(" ") ; e.w(chGep) ; e.w(" = getelementptr inbounds ") ; e.w(stateType)
147 e.w(", ptr ") ; e.w(gep) ; e.w(", i32 0, i32 0\n")
148 e.w(" store ptr ") ; e.w(ch) ; e.w(", ptr ") ; e.w(chGep) ; e.w("\n")
149
150 // Store value at field 1 (nil for recv, ptr for send).
151 e.nextReg++
152 valGep := "%selval" | irItoa(e.nextReg)
153 e.w(" ") ; e.w(valGep) ; e.w(" = getelementptr inbounds ") ; e.w(stateType)
154 e.w(", ptr ") ; e.w(gep) ; e.w(", i32 0, i32 1\n")
155 if st.Dir == SelectDirSend && st.Send != nil {
156 // Alloca for send value.
157 sendElemType := e.chanElemType(st.Chan.SSAType())
158 sendVal := e.operand(st.Send)
159 e.nextReg++
160 svAlloca := "%selsv" | irItoa(e.nextReg)
161 e.w(" ") ; e.w(svAlloca) ; e.w(" = alloca ") ; e.w(sendElemType) ; e.w("\n")
162 e.w(" store ") ; e.w(sendElemType) ; e.w(" ") ; e.w(sendVal) ; e.w(", ptr ") ; e.w(svAlloca) ; e.w("\n")
163 e.w(" store ptr ") ; e.w(svAlloca) ; e.w(", ptr ") ; e.w(valGep) ; e.w("\n")
164 } else {
165 e.w(" store ptr null, ptr ") ; e.w(valGep) ; e.w("\n")
166 }
167 }
168
169 // Build states slice: {ptr, ipt len, ipt cap}.
170 e.nextReg++
171 statesSlice := "%selss" | irItoa(e.nextReg)
172 nStr := irItoa(nStates)
173 e.w(" ") ; e.w(statesSlice) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" zeroinitializer, ptr ") ; e.w(statesArr) ; e.w(", 0\n")
174 e.nextReg++
175 ss2 := "%selss" | irItoa(e.nextReg)
176 e.w(" ") ; e.w(ss2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(statesSlice) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(nStr) ; e.w(", 1\n")
177 e.nextReg++
178 ss3 := "%selss" | irItoa(e.nextReg)
179 e.w(" ") ; e.w(ss3) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(ss2) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(nStr) ; e.w(", 2\n")
180
181 // Alloca ops array (only needed for blocking select).
182 opsArrayType := "[" | irItoa(nStates) | " x " | opType | "]"
183 e.nextReg++
184 opsArr := "%selops" | irItoa(e.nextReg)
185 if s.Blocking {
186 e.w(" ") ; e.w(opsArr) ; e.w(" = alloca ") ; e.w(opsArrayType) ; e.w("\n")
187 e.w(" store ") ; e.w(opsArrayType) ; e.w(" zeroinitializer, ptr ") ; e.w(opsArr) ; e.w("\n")
188 } else {
189 e.w(" ") ; e.w(opsArr) ; e.w(" = alloca ") ; e.w(opType) ; e.w(" ; unused\n")
190 }
191
192 // Build ops slice.
193 e.nextReg++
194 opsSlice := "%selos" | irItoa(e.nextReg)
195 if s.Blocking {
196 e.w(" ") ; e.w(opsSlice) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" zeroinitializer, ptr ") ; e.w(opsArr) ; e.w(", 0\n")
197 e.nextReg++
198 os2 := "%selos" | irItoa(e.nextReg)
199 e.w(" ") ; e.w(os2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(opsSlice) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(nStr) ; e.w(", 1\n")
200 e.nextReg++
201 os3 := "%selos" | irItoa(e.nextReg)
202 e.w(" ") ; e.w(os3) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(os2) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(nStr) ; e.w(", 2\n")
203 opsSlice = os3
204 } else {
205 // Non-blocking: pass empty slice (len=0).
206 e.w(" ") ; e.w(opsSlice) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" zeroinitializer, ptr null, 0\n")
207 }
208
209 // Call runtime.chanSelect(recvbuf ptr, states slice, ops slice) -> {i32, i1}.
210 e.nextReg++
211 selResult := "%selres" | irItoa(e.nextReg)
212 e.w(" ") ; e.w(selResult) ; e.w(" = call {i32, i1} @runtime.chanSelect(ptr ") ; e.w(recvbuf)
213 e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(ss3)
214 e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(opsSlice) ; e.w(", ptr null)\n")
215 e.declareRuntime("runtime.chanSelect", "{i32, i1}", "ptr, " | sty | ", " | sty)
216
217 // Extract index and ok from runtime result.
218 e.nextReg++
219 selIdx := "%selidx" | irItoa(e.nextReg)
220 e.w(" ") ; e.w(selIdx) ; e.w(" = extractvalue {i32, i1} ") ; e.w(selResult) ; e.w(", 0\n")
221 e.nextReg++
222 selOk := "%selok" | irItoa(e.nextReg)
223 e.w(" ") ; e.w(selOk) ; e.w(" = extractvalue {i32, i1} ") ; e.w(selResult) ; e.w(", 1\n")
224
225 // Build the SSA result tuple: {i32, i1, recvElem0, recvElem1, ...}.
226 // Index 0 = selected index (i32), index 1 = recvOk (i1).
227 // Index 2+ = received value for each recv case (loaded from recvbuf).
228 tupType := "{i32, i1"
229 for _, st := range s.States {
230 if st.Dir == SelectDirRecv && st.Chan != nil {
231 et := e.chanElemType(st.Chan.SSAType())
232 tupType = tupType | ", " | et
233 }
234 }
235 tupType = tupType | "}"
236
237 // Start building tuple.
238 e.nextReg++
239 tup := "%selt" | irItoa(e.nextReg)
240 e.w(" ") ; e.w(tup) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" zeroinitializer, i32 ") ; e.w(selIdx) ; e.w(", 0\n")
241
242 e.nextReg++
243 tup2 := "%selt" | irItoa(e.nextReg)
244 e.w(" ") ; e.w(tup2) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(tup) ; e.w(", i1 ") ; e.w(selOk) ; e.w(", 1\n")
245 prev := tup2
246
247 // For each recv state, load from recvbuf and insert at tuple index 2+.
248 tupIdx := int32(2)
249 for _, st := range s.States {
250 if st.Dir == SelectDirRecv && st.Chan != nil {
251 et := e.chanElemType(st.Chan.SSAType())
252 e.nextReg++
253 rv := "%selrv" | irItoa(e.nextReg)
254 e.w(" ") ; e.w(rv) ; e.w(" = load ") ; e.w(et) ; e.w(", ptr ") ; e.w(recvbuf) ; e.w("\n")
255 e.nextReg++
256 next := "%selt" | irItoa(e.nextReg)
257 e.w(" ") ; e.w(next) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(prev) ; e.w(", ") ; e.w(et) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(irItoa(tupIdx)) ; e.w("\n")
258 prev = next
259 tupIdx++
260 }
261 }
262
263 // Map the select SSA value to the final tuple register.
264 e.allocTypes[s] = tupType
265 e.valName[s] = prev
266 }
267
268 func (e *irEmitter) emitMakeChan(m *SSAMakeChan) {
269 reg := e.regName(m)
270 ipt := e.intptrType()
271 elemType := "i8"
272 if ch, ok := SafeUnderlying(m.SSAType()).(*TCChan); ok && ch.Elem != nil {
273 et := e.llvmType(ch.Elem)
274 if et != "void" && et != "" {
275 elemType = et
276 }
277 }
278 e.nextReg++
279 elemSz := "%mc" | irItoa(e.nextReg)
280 e.w(" ") ; e.w(elemSz) ; e.w(" = ptrtoint ptr getelementptr (")
281 e.w(elemType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n")
282 bufSz := "0"
283 if m.Size != nil {
284 bufSz = e.sextToIpt(m.Size, e.operand(m.Size))
285 }
286 e.w(" ") ; e.w(reg) ; e.w(" = call ptr @runtime.chanMake(")
287 e.w(ipt) ; e.w(" ") ; e.w(elemSz) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(bufSz) ; e.w(", ptr null)\n")
288 e.declareRuntime("runtime.chanMake", "ptr", ipt | ", " | ipt)
289 e.scopeTrackAlloc(reg)
290 }
291