dealloc.mx raw

   1  package emit
   2  
   3  import (
   4  	"git.smesh.lol/moxie/pkg/ssa"
   5  )
   6  
   7  func (e *irEmitter) instrOperands(instr ssa.SSAInstruction) []ssa.SSAValue {
   8  	switch i := instr.(type) {
   9  	case *ssa.SSAStore:
  10  		return []ssa.SSAValue{i.Addr, i.Val}
  11  	case *ssa.SSAUnOp:
  12  		return []ssa.SSAValue{i.X}
  13  	case *ssa.SSABinOp:
  14  		return []ssa.SSAValue{i.X, i.Y}
  15  	case *ssa.SSACall:
  16  		out := []ssa.SSAValue{i.Call.Value}
  17  		for _, a := range i.Call.Args {
  18  			out = append(out, a)
  19  		}
  20  		return out
  21  	case *ssa.SSAFieldAddr:
  22  		return []ssa.SSAValue{i.X}
  23  	case *ssa.SSAIndexAddr:
  24  		return []ssa.SSAValue{i.X, i.Index}
  25  	case *ssa.SSAExtract:
  26  		return []ssa.SSAValue{i.Tuple}
  27  	case *ssa.SSAPhi:
  28  		return i.Edges
  29  	case *ssa.SSAReturn:
  30  		var out []ssa.SSAValue
  31  		for _, r := range i.Results {
  32  			out = append(out, r)
  33  		}
  34  		return out
  35  	case *ssa.SSAIf:
  36  		return []ssa.SSAValue{i.Cond}
  37  	case *ssa.SSAConvert:
  38  		return []ssa.SSAValue{i.X}
  39  	case *ssa.SSAChangeType:
  40  		return []ssa.SSAValue{i.X}
  41  	case *ssa.SSAMakeInterface:
  42  		return []ssa.SSAValue{i.X}
  43  	case *ssa.SSATypeAssert:
  44  		return []ssa.SSAValue{i.X}
  45  	case *ssa.SSASlice:
  46  		out := []ssa.SSAValue{i.X}
  47  		if i.Low != nil { out = append(out, i.Low) }
  48  		if i.High != nil { out = append(out, i.High) }
  49  		if i.Max != nil { out = append(out, i.Max) }
  50  		return out
  51  	case *ssa.SSAMapUpdate:
  52  		return []ssa.SSAValue{i.Map, i.Key, i.Value}
  53  	case *ssa.SSALookup:
  54  		return []ssa.SSAValue{i.X, i.Index}
  55  	case *ssa.SSARange:
  56  		return []ssa.SSAValue{i.X}
  57  	case *ssa.SSANext:
  58  		return []ssa.SSAValue{i.Iter}
  59  	case *ssa.SSASend:
  60  		return []ssa.SSAValue{i.Chan, i.X}
  61  	case *ssa.SSAMakeSlice:
  62  		out := []ssa.SSAValue{i.Len}
  63  		if i.Cap != nil { out = append(out, i.Cap) }
  64  		if i.Data != nil { out = append(out, i.Data) }
  65  		return out
  66  	}
  67  	return nil
  68  }
  69  
  70  func simpleItoa(n int32) string {
  71  	if n == 0 {
  72  		return "0"
  73  	}
  74  	neg := n < 0
  75  	if neg {
  76  		n = -n
  77  	}
  78  	buf := []byte{:0:20}
  79  	for n > 0 {
  80  		buf = append(buf, byte('0'+n%10))
  81  		n /= 10
  82  	}
  83  	if neg {
  84  		buf = append(buf, '-')
  85  	}
  86  	for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
  87  		buf[i], buf[j] = buf[j], buf[i]
  88  	}
  89  	return string(buf)
  90  }
  91  
  92  func (e *irEmitter) deallocSetup(f *ssa.SSAFunction) {
  93  	heapCount := int32(0)
  94  	for _, b := range f.Blocks {
  95  		for _, instr := range b.Instrs {
  96  			if a, ok := instr.(*ssa.SSAAlloc); ok && a.Heap {
  97  				heapCount++
  98  			}
  99  		}
 100  	}
 101  	if heapCount == 0 {
 102  		return
 103  	}
 104  	e.deallocActive = true
 105  	e.deallocAllocs = map[ssa.SSAValue]int32{}
 106  	e.w("  %dealloc.head = alloca ptr\n")
 107  	e.w("  store ptr null, ptr %dealloc.head\n")
 108  	idx := int32(0)
 109  	for _, b := range f.Blocks {
 110  		for _, instr := range b.Instrs {
 111  			if a, ok := instr.(*ssa.SSAAlloc); ok && a.Heap {
 112  				e.deallocAllocs[a] = idx
 113  				e.w("  %dealloc.entry." | irItoa(idx) | " = alloca {ptr, ptr}\n")
 114  				e.w("  store {ptr, ptr} zeroinitializer, ptr %dealloc.entry." | irItoa(idx) | "\n")
 115  				idx++
 116  			}
 117  		}
 118  	}
 119  	e.deallocCount = idx
 120  	e.declareRuntime("runtime.deallocPush", "void", "ptr, ptr, ptr")
 121  	e.declareRuntime("runtime.deallocFree", "void", "ptr")
 122  	e.declareRuntime("runtime.deallocEscape", "void", "ptr, ptr")
 123  }
 124  
 125  func (e *irEmitter) deallocAfterAlloc(a *ssa.SSAAlloc, reg string) {
 126  	if !e.deallocActive {
 127  		return
 128  	}
 129  	idx, ok := e.deallocAllocs[a]
 130  	if !ok {
 131  		return
 132  	}
 133  	entry := "%dealloc.entry." | irItoa(idx)
 134  	e.w("  call void @runtime.deallocPush(ptr " | reg | ", ptr %dealloc.head, ptr " | entry | ")\n")
 135  }
 136  
 137  func (e *irEmitter) deallocBeforeReturn(r *ssa.SSAReturn) {
 138  	if !e.deallocActive {
 139  		return
 140  	}
 141  	for _, res := range r.Results {
 142  		e.deallocEscapeResult(res)
 143  	}
 144  	e.nextReg++
 145  	tmp := "%dealloc.list" | irItoa(e.nextReg)
 146  	e.w("  " | tmp | " = load ptr, ptr %dealloc.head\n")
 147  	e.w("  call void @runtime.deallocFree(ptr " | tmp | ")\n")
 148  }
 149  
 150  func (e *irEmitter) deallocEscapeResult(v ssa.SSAValue) {
 151  	if v == nil {
 152  		return
 153  	}
 154  	if a, ok := v.(*ssa.SSAAlloc); ok {
 155  		if _, tracked := e.deallocAllocs[a]; tracked {
 156  			e.nextReg++
 157  			tmp := "%dealloc.esc.list" | irItoa(e.nextReg)
 158  			e.w("  " | tmp | " = load ptr, ptr %dealloc.head\n")
 159  			e.w("  call void @runtime.deallocEscape(ptr " | e.operand(v) | ", ptr " | tmp | ")\n")
 160  		}
 161  	}
 162  }
 163  
 164  func (e *irEmitter) scopeTrackAlloc(ptrReg string) {
 165  	if !e.scopeActive {
 166  		return
 167  	}
 168  	e.w("  call void @runtime.scopePush(ptr ")
 169  	e.w(ptrReg)
 170  	e.w(", ptr %scope.head)\n")
 171  }
 172  
 173  func (e *irEmitter) scopeEscapeReturnResults(r *ssa.SSAReturn) {
 174  	if !e.scopeActive {
 175  		return
 176  	}
 177  	for _, res := range r.Results {
 178  		if res == nil {
 179  			continue
 180  		}
 181  		typ := e.llvmType(res.SSAType())
 182  		val := e.operand(res)
 183  		if val == "null" || val == "zeroinitializer" || val == "0" {
 184  			continue
 185  		}
 186  		ptrReg := ""
 187  		sty := e.sliceType()
 188  		if typ == "ptr" {
 189  			ptrReg = val
 190  		} else if typ == sty {
 191  			e.nextReg++
 192  			ptrReg = "%esc.p" | irItoa(e.nextReg)
 193  			e.w("  ") ; e.w(ptrReg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", 0\n")
 194  		} else if typ == "{ptr, ptr}" {
 195  			e.nextReg++
 196  			ptrReg = "%esc.p" | irItoa(e.nextReg)
 197  			e.w("  ") ; e.w(ptrReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 0\n")
 198  		}
 199  		if ptrReg == "" {
 200  			continue
 201  		}
 202  		e.w("  call void @runtime.scopeEscapeReturn(ptr ")
 203  		e.w(ptrReg)
 204  		e.w(", ptr %scope.head)\n")
 205  		e.declareRuntime("runtime.scopeEscapeReturn", "void", "ptr, ptr")
 206  	}
 207  	if len(e.curFunc.NamedResults) > 0 {
 208  		for _, nr := range e.curFunc.NamedResults {
 209  			nrt := e.namedResultType(nr)
 210  			if nrt != "ptr" && nrt != e.sliceType() && nrt != "{ptr, ptr}" {
 211  				continue
 212  			}
 213  			e.nextReg++
 214  			tmp := "%esc.nr" | irItoa(e.nextReg)
 215  			e.w("  ") ; e.w(tmp) ; e.w(" = load ") ; e.w(nrt) ; e.w(", ptr ") ; e.w(e.regName(nr)) ; e.w("\n")
 216  			ptrReg := ""
 217  			sty := e.sliceType()
 218  			if nrt == "ptr" {
 219  				ptrReg = tmp
 220  			} else if nrt == sty {
 221  				e.nextReg++
 222  				ptrReg = "%esc.nrp" | irItoa(e.nextReg)
 223  				e.w("  ") ; e.w(ptrReg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(tmp) ; e.w(", 0\n")
 224  			} else if nrt == "{ptr, ptr}" {
 225  				e.nextReg++
 226  				ptrReg = "%esc.nrp" | irItoa(e.nextReg)
 227  				e.w("  ") ; e.w(ptrReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(tmp) ; e.w(", 0\n")
 228  			}
 229  			if ptrReg == "" {
 230  				continue
 231  			}
 232  			e.w("  call void @runtime.scopeEscapeReturn(ptr ")
 233  			e.w(ptrReg)
 234  			e.w(", ptr %scope.head)\n")
 235  			e.declareRuntime("runtime.scopeEscapeReturn", "void", "ptr, ptr")
 236  		}
 237  	}
 238  }
 239  
 240  func (e *irEmitter) emitScopeFreeAll() {
 241  	if !e.scopeActive {
 242  		return
 243  	}
 244  	e.nextReg++
 245  	cur := "%sf.all" | irItoa(e.nextReg)
 246  	e.w("  ")
 247  	e.w(cur)
 248  	e.w(" = load ptr, ptr %scope.head\n")
 249  	e.nextReg++
 250  	fn := "%sf.fn" | irItoa(e.nextReg)
 251  	e.w("  ")
 252  	e.w(fn)
 253  	e.w(" = call ptr @runtime.scopeFreeTo(ptr ")
 254  	e.w(cur)
 255  	e.w(", ptr null)\n")
 256  	e.w("  store ptr ")
 257  	e.w(fn)
 258  	e.w(", ptr %scope.head\n")
 259  }
 260  
 261  func (e *irEmitter) emitScopeFreesTo(scopeID int32) {
 262  	e.nextReg++
 263  	saved := "%sf.sv" | irItoa(e.nextReg)
 264  	e.w("  ")
 265  	e.w(saved)
 266  	e.w(" = load ptr, ptr %scope.save.")
 267  	e.w(irItoa(scopeID))
 268  	e.w("\n")
 269  	e.nextReg++
 270  	cur := "%sf.cur" | irItoa(e.nextReg)
 271  	e.w("  ")
 272  	e.w(cur)
 273  	e.w(" = load ptr, ptr %scope.head\n")
 274  	e.nextReg++
 275  	fn := "%sf.fn" | irItoa(e.nextReg)
 276  	e.w("  ")
 277  	e.w(fn)
 278  	e.w(" = call ptr @runtime.scopeFreeTo(ptr ")
 279  	e.w(cur)
 280  	e.w(", ptr ")
 281  	e.w(saved)
 282  	e.w(")\n")
 283  	e.w("  store ptr ")
 284  	e.w(fn)
 285  	e.w(", ptr %scope.head\n")
 286  }
 287  
 288  func (e *irEmitter) emitScopeSave(b *ssa.SSABasicBlock) {
 289  	if b.ScopeID <= 0 {
 290  		return
 291  	}
 292  	enteringScope := false
 293  	for _, pred := range b.Preds {
 294  		if pred.ScopeID != b.ScopeID {
 295  			enteringScope = true
 296  			break
 297  		}
 298  	}
 299  	if enteringScope || len(b.Preds) == 0 {
 300  		e.nextReg++
 301  		tmp := "%ss" | irItoa(e.nextReg)
 302  		e.w("  ")
 303  		e.w(tmp)
 304  		e.w(" = load ptr, ptr %scope.head\n")
 305  		e.w("  store ptr ")
 306  		e.w(tmp)
 307  		e.w(", ptr %scope.save.")
 308  		e.w(irItoa(b.ScopeID))
 309  		e.w("\n")
 310  	}
 311  }
 312  
 313  func (e *irEmitter) emitScopeRelocateOnStore(s *ssa.SSAStore, val, valType string) {
 314  	if !e.scopeActive {
 315  		return
 316  	}
 317  	blk := s.InstrBlock()
 318  	if blk == nil || blk.ScopeID == 0 {
 319  		return
 320  	}
 321  	addrScope := int32(0)
 322  	if aid, ok := e.instrScope[s.Addr]; ok {
 323  		addrScope = aid
 324  	}
 325  	if addrScope >= blk.ScopeID {
 326  		return
 327  	}
 328  	if val == "null" || val == "zeroinitializer" || val == "0" {
 329  		return
 330  	}
 331  	ptrReg := ""
 332  	sty := e.sliceType()
 333  	if valType == "ptr" {
 334  		ptrReg = val
 335  	} else if valType == sty {
 336  		e.nextReg++
 337  		ptrReg = "%sr.p" | irItoa(e.nextReg)
 338  		e.w("  ") ; e.w(ptrReg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(", 0\n")
 339  	} else if valType == "{ptr, ptr}" {
 340  		e.nextReg++
 341  		ptrReg = "%sr.p" | irItoa(e.nextReg)
 342  		e.w("  ") ; e.w(ptrReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 0\n")
 343  	}
 344  	if ptrReg == "" {
 345  		return
 346  	}
 347  	e.w("  call void @runtime.scopeRelocate(ptr ")
 348  	e.w(ptrReg)
 349  	e.w(", ptr %scope.head, ptr %scope.save.")
 350  	e.w(irItoa(blk.ScopeID))
 351  	e.w(")\n")
 352  	e.declareRuntime("runtime.scopeRelocate", "void", "ptr, ptr, ptr")
 353  }
 354