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