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