function.mx raw

   1  package emit
   2  
   3  import (
   4  	"git.smesh.lol/moxie/pkg/syntax"
   5  	"git.smesh.lol/moxie/pkg/ssa"
   6  	"git.smesh.lol/moxie/pkg/types"
   7  )
   8  
   9  func (e *irEmitter) emitFunction(f *ssa.SSAFunction) {
  10  	e.w("; [emit] " | f.SSAName() | "\n")
  11  	if len(f.Blocks) == 0 {
  12  		linkName := f.SSAName()
  13  		if f.Pkg != nil && f.Pkg.Pkg != nil {
  14  			linkName = f.Pkg.Pkg.Path() | "." | f.SSAName()
  15  		}
  16  		alias := e.asmAlias(linkName)
  17  		if alias != "" {
  18  			e.emitAsmAlias(f, alias)
  19  			return
  20  		}
  21  		e.emitFuncDecl(f)
  22  		return
  23  	}
  24  	e.curFunc = f
  25  	e.nextReg = 0
  26  	e.deferList = nil
  27  	e.deferID = 0
  28  	e.deallocActive = false
  29  	e.deallocAllocs = nil
  30  	e.deallocCount = 0
  31  	e.scopeActive = false
  32  	e.scopeAllocs = map[int32][]string{}
  33  	e.instrScope = map[ssa.SSAInstruction]int32{}
  34  	e.valName = map[ssa.SSAValue]string{}
  35  	e.nameUsed = map[string]bool{}
  36  	e.allocTypes = map[ssa.SSAValue]string{}
  37  	e.regTypes = map[string]string{}
  38  	e.blockExitLabel = map[int32]string{}
  39  
  40  	usedNames := map[string]int32{}
  41  	for i, p := range f.Params {
  42  		pname := p.SSAName()
  43  		if pname == "" {
  44  			pname = "p" | irItoa(i)
  45  		}
  46  		if cnt, ok := usedNames[pname]; ok {
  47  			pname = pname | "." | irItoa(cnt)
  48  		}
  49  		usedNames[p.SSAName()]++
  50  		e.valName[p] = "%" | pname
  51  		e.nameUsed["%" | pname] = true
  52  	}
  53  
  54  	e.w("\ndefine hidden ")
  55  	e.w(e.funcRetType(f))
  56  	e.w(" ")
  57  	e.w(e.funcSymbol(f))
  58  	e.w("(")
  59  	for i, p := range f.Params {
  60  		if i > 0 {
  61  			e.w(", ")
  62  		}
  63  		pt := e.llvmType(p.SSAType())
  64  		if pt == "void" {
  65  			pt = "ptr"
  66  		}
  67  		e.w(pt)
  68  		e.w(" ")
  69  		e.w(e.regName(p))
  70  	}
  71  	if !f.IsExternC() {
  72  		if len(f.Params) > 0 {
  73  			e.w(", ")
  74  		}
  75  		ctxName := "context"
  76  		for _, p := range f.Params {
  77  			if p.SSAName() == "context" {
  78  				ctxName = "context.1"
  79  				break
  80  			}
  81  		}
  82  		e.w("ptr %")
  83  		e.w(ctxName)
  84  	}
  85  	e.w(") {\n")
  86  
  87  	// Pre-scan: set allocTypes, detect cross-block alloca references
  88  	e.allocBlock = map[ssa.SSAValue]int32{}
  89  	for _, b := range f.Blocks {
  90  		for _, instr := range b.Instrs {
  91  			if n, ok := instr.(*ssa.SSANext); ok {
  92  				if ri, ok2 := n.Iter.(*ssa.SSARange); ok2 {
  93  					rangeSSAType := ri.X.SSAType()
  94  					rangeUnderlying := types.SafeUnderlying(rangeSSAType)
  95  					if mt, ok3 := rangeUnderlying.(*types.TCMap); ok3 {
  96  						e.allocTypes[n] = "{i1, " | e.llvmType(mt.Key()) | ", " | e.llvmType(mt.Elem()) | "}"
  97  					} else if arr, ok3 := rangeUnderlying.(*types.Array); ok3 {
  98  						elemType := e.llvmType(arr.Elem())
  99  						e.allocTypes[n] = "{i1, i32, " | elemType | "}"
 100  					} else if sl, ok3 := rangeUnderlying.(*types.Slice); ok3 {
 101  						elemType := e.llvmType(sl.Elem())
 102  						e.allocTypes[n] = "{i1, i32, " | elemType | "}"
 103  					} else if p, ok3 := rangeUnderlying.(*types.Pointer); ok3 && p.Elem() != nil {
 104  						if arr2, ok4 := types.SafeUnderlying(p.Elem()).(*types.Array); ok4 {
 105  							elemType := e.llvmType(arr2.Elem())
 106  							e.allocTypes[n] = "{i1, i32, " | elemType | "}"
 107  						}
 108  					} else if n.IsString {
 109  						e.allocTypes[n] = "{i1, i32, i8}"
 110  					} else {
 111  						et := "i32"
 112  						if tup, ok3 := n.SSAType().(*types.Tuple); ok3 && tup.Len() >= 3 {
 113  							vt := tup.At(2).Type()
 114  							if vt != nil {
 115  								vlt := e.llvmType(vt)
 116  								if vlt != "void" && vlt != "i32" {
 117  									et = vlt
 118  								}
 119  							}
 120  						}
 121  						if et != "i32" {
 122  							e.allocTypes[n] = "{i1, i32, " | et | "}"
 123  						}
 124  					}
 125  				}
 126  			}
 127  			if c, ok := instr.(*ssa.SSACall); ok {
 128  				if b2, ok2 := c.Call.Value.(*ssa.SSABuiltin); ok2 && b2.SSAName() == "recover" {
 129  					e.allocTypes[c] = e.ifaceType()
 130  				}
 131  			}
 132  			if a, ok := instr.(*ssa.SSAAlloc); ok {
 133  				e.allocBlock[a] = b.Index
 134  			}
 135  		}
 136  	}
 137  	hoistAllocs := map[ssa.SSAValue]bool{}
 138  	for _, b := range f.Blocks {
 139  		for _, instr := range b.Instrs {
 140  			refs := e.instrOperands(instr)
 141  			for _, ref := range refs {
 142  				if ab, ok := e.allocBlock[ref]; ok && ab != 0 && ab != b.Index {
 143  					hoistAllocs[ref] = true
 144  				}
 145  			}
 146  		}
 147  	}
 148  	e.hoisted = hoistAllocs
 149  	e.missingStores = nil
 150  	e.storedTo = map[string]bool{}
 151  	for _, b := range f.Blocks {
 152  		for _, instr := range b.Instrs {
 153  			if s, ok := instr.(*ssa.SSAStore); ok && s.Addr != nil {
 154  				e.storedTo[s.Addr.SSAName()] = true
 155  			}
 156  		}
 157  	}
 158  	e.usedAs = map[string]bool{}
 159  	for _, b := range f.Blocks {
 160  		for _, instr := range b.Instrs {
 161  			refs := e.instrOperands(instr)
 162  			for _, ref := range refs {
 163  				if ref != nil {
 164  					e.usedAs[ref.SSAName()] = true
 165  				}
 166  			}
 167  		}
 168  	}
 169  	for _, b := range f.Blocks {
 170  		for i := 0; i+1 < len(b.Instrs); i++ {
 171  			load, isLoad := b.Instrs[i].(*ssa.SSAUnOp)
 172  			if !isLoad || load.Op != ssa.OpMul {
 173  				continue
 174  			}
 175  			alloc, isAlloc := b.Instrs[i+1].(*ssa.SSAAlloc)
 176  			if !isAlloc {
 177  				continue
 178  			}
 179  			if !e.usedAs[load.SSAName()] && !e.storedTo[alloc.SSAName()] && hoistAllocs[alloc] {
 180  				srcAlloc, isSrcAlloc := load.X.(*ssa.SSAAlloc)
 181  				if !isSrcAlloc {
 182  					continue
 183  				}
 184  				srcType := e.llvmType(srcAlloc.SSAType())
 185  				if p, ok := types.SafeUnderlying(srcAlloc.SSAType()).(*types.Pointer); ok && p.Elem() != nil {
 186  					srcType = e.llvmType(p.Elem())
 187  				}
 188  				if len(srcType) > 0 && srcType[0] == '[' {
 189  					if e.missingStores == nil {
 190  						e.missingStores = map[ssa.SSAValue]ssa.SSAValue{}
 191  					}
 192  					e.missingStores[load] = alloc
 193  					e.allocTypes[alloc] = srcType
 194  				}
 195  			}
 196  		}
 197  	}
 198  	for _, b := range f.Blocks {
 199  		for _, instr := range b.Instrs {
 200  			e.instrScope[instr] = b.ScopeID
 201  		}
 202  	}
 203  	var hoistList []*ssa.SSAAlloc
 204  	for v := range hoistAllocs {
 205  		if a, ok := v.(*ssa.SSAAlloc); ok {
 206  			hoistList = append(hoistList, a)
 207  		}
 208  	}
 209  	for i := 1; i < len(hoistList); i++ {
 210  		for j := i; j > 0 && hoistList[j].SSAName() < hoistList[j-1].SSAName(); j-- {
 211  			hoistList[j], hoistList[j-1] = hoistList[j-1], hoistList[j]
 212  		}
 213  	}
 214  	for _, b := range f.Blocks {
 215  		for _, instr := range b.Instrs {
 216  			if d, ok := instr.(*ssa.SSADefer); ok {
 217  				e.deferList = append(e.deferList, d)
 218  			}
 219  		}
 220  	}
 221  	scopeIDs := map[int32]bool{}
 222  	hasHeapAllocs := false
 223  	for _, b := range f.Blocks {
 224  		if b.ScopeID > 0 {
 225  			scopeIDs[b.ScopeID] = true
 226  		}
 227  		for _, instr := range b.Instrs {
 228  			switch instr.(type) {
 229  			case *ssa.SSAAlloc:
 230  				if instr.(*ssa.SSAAlloc).Heap {
 231  					hasHeapAllocs = true
 232  				}
 233  			case *ssa.SSAMakeSlice, *ssa.SSAMakeMap, *ssa.SSAMakeChan, *ssa.SSAMakeClosure, *ssa.SSASlice:
 234  				hasHeapAllocs = true
 235  			}
 236  		}
 237  	}
 238  	for _, b := range f.Blocks {
 239  		if b.Index == 0 {
 240  			e.w("bb.entry:\n")
 241  			e.blockExitLabel[0] = "%bb.entry"
 242  			for _, a := range hoistList {
 243  				e.emitAlloc(a)
 244  			}
 245  			if len(e.deferList) > 0 {
 246  				e.w("  %deferPtr = alloca ptr\n")
 247  				e.w("  store ptr null, ptr %deferPtr\n")
 248  			}
 249  			if len(scopeIDs) > 0 || hasHeapAllocs {
 250  				e.scopeActive = true
 251  				e.w("  %scope.head = alloca ptr\n")
 252  				e.w("  store ptr null, ptr %scope.head\n")
 253  				for sid := range scopeIDs {
 254  					e.w("  %scope.save.")
 255  					e.w(irItoa(sid))
 256  					e.w(" = alloca ptr\n")
 257  					e.w("  store ptr null, ptr %scope.save.")
 258  					e.w(irItoa(sid))
 259  					e.w("\n")
 260  				}
 261  				e.declareRuntime("runtime.scopePush", "void", "ptr, ptr")
 262  				e.declareRuntime("runtime.scopeFreeTo", "ptr", "ptr, ptr")
 263  				e.declareRuntime("runtime.scopeRelocate", "void", "ptr, ptr, ptr")
 264  			}
 265  			e.deallocSetup(f)
 266  			if len(e.curFunc.FreeVars) > 0 {
 267  				e.emitFreeVarUnpack(e.curFunc)
 268  			}
 269  			for _, instr := range b.Instrs {
 270  				e.emitInstr(instr)
 271  			}
 272  		} else {
 273  			e.emitBlock(b)
 274  		}
 275  	}
 276  	e.hoisted = nil
 277  
 278  	e.w("}\n")
 279  }
 280  
 281  func (e *irEmitter) emitAsmAlias(f *ssa.SSAFunction, targetName string) {
 282  	sym := e.funcSymbol(f)
 283  	retType := e.funcRetType(f)
 284  	e.w("\ndefine hidden ")
 285  	e.w(retType)
 286  	e.w(" ")
 287  	e.w(sym)
 288  	e.w("(")
 289  	var paramTypes []string
 290  	var paramNames []string
 291  	n := 0
 292  	if f.Signature != nil && f.Signature.Params() != nil {
 293  		for i := 0; i < f.Signature.Params().Len(); i++ {
 294  			if n > 0 {
 295  				e.w(", ")
 296  			}
 297  			pt := e.llvmType(f.Signature.Params().At(i).Type())
 298  			pn := "%p" | irItoa(i)
 299  			e.w(pt)
 300  			e.w(" ")
 301  			e.w(pn)
 302  			paramTypes = append(paramTypes, pt)
 303  			paramNames = append(paramNames, pn)
 304  			n++
 305  		}
 306  	}
 307  	if n > 0 {
 308  		e.w(", ")
 309  	}
 310  	e.w("ptr %context) {\nentry:\n")
 311  	target := "@\"" | targetName | "\""
 312  	if retType == "void" {
 313  		e.w("  call void ")
 314  		e.w(target)
 315  		e.w("(")
 316  		for i, pt := range paramTypes {
 317  			if i > 0 {
 318  				e.w(", ")
 319  			}
 320  			e.w(pt)
 321  			e.w(" ")
 322  			e.w(paramNames[i])
 323  		}
 324  		e.w(", ptr null)\n  ret void\n}\n")
 325  	} else {
 326  		e.w("  %r = call ")
 327  		e.w(retType)
 328  		e.w(" ")
 329  		e.w(target)
 330  		e.w("(")
 331  		for i, pt := range paramTypes {
 332  			if i > 0 {
 333  				e.w(", ")
 334  			}
 335  			e.w(pt)
 336  			e.w(" ")
 337  			e.w(paramNames[i])
 338  		}
 339  		e.w(", ptr null)\n  ret ")
 340  		e.w(retType)
 341  		e.w(" %r\n}\n")
 342  	}
 343  }
 344  
 345  func (e *irEmitter) emitFuncDecl(f *ssa.SSAFunction) {
 346  	sym := e.funcSymbol(f)
 347  	if _, ok := e.extDecls[sym]; ok {
 348  		return
 349  	}
 350  	e.w("\ndeclare ")
 351  	e.w(e.funcRetType(f))
 352  	e.w(" ")
 353  	e.w(sym)
 354  	e.w("(")
 355  	n := 0
 356  	hasRecv := f.Signature != nil && f.Signature.Recv() != nil
 357  	if hasRecv {
 358  		e.w("ptr")
 359  		n++
 360  	}
 361  	if f.Signature != nil && f.Signature.Params() != nil {
 362  		for i := 0; i < f.Signature.Params().Len(); i++ {
 363  			if n > 0 {
 364  				e.w(", ")
 365  			}
 366  			e.w(e.llvmType(f.Signature.Params().At(i).Type()))
 367  			n++
 368  		}
 369  	}
 370  	if !f.IsExternC() {
 371  		if n > 0 {
 372  			e.w(", ")
 373  		}
 374  		e.w("ptr")
 375  	}
 376  	e.w(")\n")
 377  	e.extDecls[sym] = ""
 378  }
 379  
 380  func (e *irEmitter) resolveResultType(t syntax.Type) string {
 381  	rt := e.llvmType(t)
 382  	if rt != "void" { return rt }
 383  	if t == nil { return "ptr" }
 384  	u := types.SafeUnderlying(t)
 385  	if u == nil { return "ptr" }
 386  	switch u.(type) {
 387  	case *types.TCInterface:
 388  		return e.ifaceType()
 389  	case *types.Signature:
 390  		return "{ptr, ptr}"
 391  	case *types.TCStruct:
 392  		return e.llvmStructType(u.(*types.TCStruct))
 393  	case *types.Slice:
 394  		return e.sliceType()
 395  	}
 396  	return "ptr"
 397  }
 398  
 399  func (e *irEmitter) funcRetType(f *ssa.SSAFunction) string {
 400  	if f.Signature == nil || f.Signature.Results() == nil || f.Signature.Results().Len() == 0 {
 401  		return "void"
 402  	}
 403  	if f.Signature.Results().Len() == 1 {
 404  		return e.resolveResultType(f.Signature.Results().At(0).Type())
 405  	}
 406  	s := "{"
 407  	for i := 0; i < f.Signature.Results().Len(); i++ {
 408  		if i > 0 {
 409  			s = s | ", "
 410  		}
 411  		s = s | e.resolveResultType(f.Signature.Results().At(i).Type())
 412  	}
 413  	return s | "}"
 414  }
 415  
 416  func (e *irEmitter) emitBlock(b *ssa.SSABasicBlock) {
 417  	label := "b" | irItoa(b.Index)
 418  	if b.Index == 0 {
 419  		label = "bb.entry"
 420  	}
 421  	e.w(label)
 422  	e.w(":\n")
 423  	e.blockExitLabel[b.Index] = "%" | label
 424  
 425  	if b.Index == 0 && len(e.curFunc.FreeVars) > 0 {
 426  		e.emitFreeVarUnpack(e.curFunc)
 427  	}
 428  
 429  	scopeSaveEmitted := false
 430  	for _, instr := range b.Instrs {
 431  		if !scopeSaveEmitted {
 432  			if _, isPhi := instr.(*ssa.SSAPhi); !isPhi {
 433  				scopeSaveEmitted = true
 434  				e.emitScopeSave(b)
 435  			}
 436  		}
 437  		e.emitInstr(instr)
 438  		if e.missingStores != nil {
 439  			if v, ok2 := instr.(ssa.SSAValue); ok2 {
 440  				if dst, ok3 := e.missingStores[v]; ok3 {
 441  					loadReg := e.regName(v)
 442  					dstReg := e.regName(dst)
 443  					arrType := e.allocTypes[dst]
 444  					e.w("  store ") ; e.w(arrType) ; e.w(" ") ; e.w(loadReg) ; e.w(", ptr ") ; e.w(dstReg) ; e.w("\n")
 445  				}
 446  			}
 447  		}
 448  	}
 449  
 450  	hasTerminator := false
 451  	if n := len(b.Instrs); n > 0 {
 452  		switch b.Instrs[n-1].(type) {
 453  		case *ssa.SSAJump, *ssa.SSAIf, *ssa.SSAReturn:
 454  			hasTerminator = true
 455  		}
 456  	}
 457  	if !hasTerminator {
 458  		e.w("  unreachable\n")
 459  	}
 460  }
 461  
 462  func (e *irEmitter) blockLabel(b *ssa.SSABasicBlock) string {
 463  	if b.Index == 0 {
 464  		return "%entry"
 465  	}
 466  	return "%b" | irItoa(b.Index)
 467  }
 468