ssa_pkg_builder.mx raw

   1  package ssa
   2  
   3  import (
   4  	"git.smesh.lol/moxie/pkg/token"
   5  	"git.smesh.lol/moxie/pkg/syntax"
   6  	"git.smesh.lol/moxie/pkg/types"
   7  )
   8  
   9  func asType(t types.Type) (tv types.Type) { return t }
  10  
  11  // CreatePackage builds the SSA Package from type-checked syntax files.
  12  func (prog *SSAProgram) CreatePackage(pkg *types.TCPackage, files []*syntax.File, info *types.Info) (s *SSAPackage) {
  13  	p := &SSAPackage{
  14  		Prog:    prog,
  15  		Pkg:     pkg,
  16  		Members: map[string]SSAMember{},
  17  	}
  18  	prog.packages[pkg] = p
  19  	prog.imported[pkg.Path()] = p
  20  
  21  	pb := &ssaPkgBuilder{pkg: p, info: info, prog: prog}
  22  
  23  	for _, f := range files {
  24  		pb.registerImportsAndTypes(f)
  25  	}
  26  	for _, f := range files {
  27  		pb.registerFile(f)
  28  	}
  29  	for _, f := range files {
  30  		pb.buildFile(f)
  31  	}
  32  	if len(pb.varInits) > 0 {
  33  		pb.buildVarInits()
  34  	}
  35  	return p
  36  }
  37  
  38  // ssaPkgBuilder builds one SSAPackage.
  39  type ssaPkgBuilder struct {
  40  	pkg      *SSAPackage
  41  	info     *types.Info
  42  	prog     *SSAProgram
  43  	varInits []*syntax.VarDecl
  44  }
  45  
  46  func (pb *ssaPkgBuilder) registerImportsAndTypes(f *syntax.File) {
  47  	for _, d := range f.DeclList {
  48  		switch d := d.(type) {
  49  		case *syntax.ImportDecl:
  50  			pb.registerImport(d)
  51  		case *types.TypeDecl:
  52  			pb.registerType(d)
  53  		}
  54  	}
  55  }
  56  
  57  func (pb *ssaPkgBuilder) registerFile(f *syntax.File) {
  58  	for _, d := range f.DeclList {
  59  		switch d := d.(type) {
  60  		case *syntax.FuncDecl:
  61  			pb.registerFunc(d)
  62  		case *syntax.VarDecl:
  63  			pb.registerVar(d)
  64  		case *syntax.ConstDecl:
  65  			pb.registerConst(d)
  66  		}
  67  	}
  68  }
  69  
  70  func (pb *ssaPkgBuilder) registerImport(d *syntax.ImportDecl) {
  71  	if d.Path == nil {
  72  		return
  73  	}
  74  	path := d.Path.Value
  75  	if len(path) >= 2 && path[0] == '"' {
  76  		path = path[1 : len(path)-1]
  77  	}
  78  	localName := ""
  79  	if d.LocalPkgName != nil {
  80  		localName = d.LocalPkgName.Value
  81  	}
  82  	if localName == "" {
  83  		for i := len(path) - 1; i >= 0; i-- {
  84  			if path[i] == '/' {
  85  				localName = path[i+1:]
  86  				break
  87  			}
  88  		}
  89  		if localName == "" {
  90  			localName = path
  91  		}
  92  	}
  93  	if localName == "." || localName == "_" {
  94  		return
  95  	}
  96  	existing := pb.pkg.Pkg.Scope().Lookup(localName)
  97  	if existing != nil {
  98  		return
  99  	}
 100  	impPkg := pb.prog.ImportedPackage(path)
 101  	if impPkg != nil && impPkg.Pkg != nil {
 102  		pn := types.NewPkgName(nil, localName, impPkg.Pkg)
 103  		pb.pkg.Pkg.Scope().Insert(pn)
 104  	} else {
 105  		ensureImportRegistry()
 106  		regPkg := importRegistry[path]
 107  		if regPkg != nil {
 108  			pn := types.NewPkgName(nil, localName, regPkg)
 109  			pb.pkg.Pkg.Scope().Insert(pn)
 110  		}
 111  	}
 112  }
 113  
 114  func pkgResolveTypeAST(pkg *SSAPackage, e syntax.Expr) (t types.Type) {
 115  	if e == nil {
 116  		return nil
 117  	}
 118  	switch e := e.(type) {
 119  	case *syntax.Name:
 120  		if _, obj := types.Universe.LookupParent(e.Value); obj != nil {
 121  			if tn, ok := obj.(*types.TypeName); ok {
 122  				return tn.Type()
 123  			}
 124  		}
 125  		if pkg != nil {
 126  			obj := pkg.Pkg.Scope().Lookup(e.Value)
 127  			if tn, ok := obj.(*types.TypeName); ok {
 128  				return tn.Type()
 129  			}
 130  		}
 131  	case *syntax.Operation:
 132  		if e.Y == nil && e.Op == token.Mul {
 133  			base := pkgResolveTypeAST(pkg, e.X)
 134  			if base != nil {
 135  				return types.NewPointer(base)
 136  			}
 137  		}
 138  	case *syntax.ArrayType:
 139  		elem := pkgResolveTypeAST(pkg, e.Elem)
 140  		if elem != nil {
 141  			n := int64(-1)
 142  			if lit, ok := e.Len.(*syntax.BasicLit); ok {
 143  				n = ssaParseInt64(lit.Value)
 144  			}
 145  			return types.NewArray(elem, n)
 146  		}
 147  	case *syntax.SliceType:
 148  		elem := pkgResolveTypeAST(pkg, e.Elem)
 149  		if elem != nil {
 150  			if b, ok := elem.(*types.Basic); ok && b.Kind() == types.Uint8 {
 151  				return types.Typ[types.TCString]
 152  			}
 153  			return types.NewSlice(elem)
 154  		}
 155  	case *syntax.MapType:
 156  		key := pkgResolveTypeAST(pkg, e.Key)
 157  		val := pkgResolveTypeAST(pkg, e.Value)
 158  		if key != nil && val != nil {
 159  			return types.NewTCMap(key, val)
 160  		}
 161  	case *syntax.FuncType:
 162  		return pkgResolveSigAST(pkg, e)
 163  	case *syntax.SelectorExpr:
 164  		if x, ok := e.X.(*syntax.Name); ok {
 165  			pn, _ := pkg.Pkg.Scope().Lookup(x.Value).(*types.PkgName)
 166  			if pn != nil && pn.Imported() != nil {
 167  				sel := pn.Imported().Scope().Lookup(e.Sel.Value)
 168  				if tn, ok2 := sel.(*types.TypeName); ok2 {
 169  					return tn.Type()
 170  				}
 171  			}
 172  		}
 173  	case *syntax.InterfaceType:
 174  		return types.NewTCInterface(nil, nil)
 175  	case *syntax.DotsType:
 176  		elem := pkgResolveTypeAST(pkg, e.Elem)
 177  		if elem != nil {
 178  			if b, ok := elem.(*types.Basic); ok && b.Kind() == types.Uint8 {
 179  				return types.Typ[types.TCString]
 180  			}
 181  			return types.NewSlice(elem)
 182  		}
 183  	}
 184  	return nil
 185  }
 186  
 187  func pkgResolveSigAST(pkg *SSAPackage, ft *syntax.FuncType) (s *types.Signature) {
 188  	if ft == nil {
 189  		return nil
 190  	}
 191  	var params []*types.TCVar
 192  	for _, p := range ft.ParamList {
 193  		typ := pkgResolveTypeAST(pkg, p.Type)
 194  		pname := ""
 195  		if p.Name != nil {
 196  			pname = p.Name.Value
 197  		}
 198  		params = append(params, types.NewTCVar(nil, pname, typ))
 199  	}
 200  	var results []*types.TCVar
 201  	for _, r := range ft.ResultList {
 202  		typ := pkgResolveTypeAST(pkg, r.Type)
 203  		rname := ""
 204  		if r.Name != nil {
 205  			rname = r.Name.Value
 206  		}
 207  		results = append(results, types.NewTCVar(nil, rname, typ))
 208  	}
 209  	variadic := false
 210  	if len(ft.ParamList) > 0 {
 211  		if _, ok := ft.ParamList[len(ft.ParamList)-1].Type.(*syntax.DotsType); ok {
 212  			variadic = true
 213  		}
 214  	}
 215  	var pTuple, rTuple *types.Tuple
 216  	if len(params) > 0 {
 217  		pTuple = types.NewTuple(params...)
 218  	}
 219  	if len(results) > 0 {
 220  		rTuple = types.NewTuple(results...)
 221  	}
 222  	return types.NewSignature(nil, pTuple, rTuple, variadic)
 223  }
 224  
 225  func (pb *ssaPkgBuilder) registerFunc(d *syntax.FuncDecl) {
 226  	if len(d.TParamList) > 0 {
 227  		return
 228  	}
 229  	if d.Recv != nil {
 230  		pb.registerMethod(d)
 231  		return
 232  	}
 233  	if d.Name.Value == "init" {
 234  		writeStr(2, "compile error: init() is not allowed; use main() for package initialization\n")
 235  		return
 236  	}
 237  	var obj *types.TCFunc
 238  	if o := pb.pkg.Pkg.Scope().Lookup(d.Name.Value); o != nil {
 239  		obj, _ = o.(*types.TCFunc)
 240  	}
 241  	var sig *types.Signature
 242  	if obj != nil {
 243  		sig, _ = obj.Type().(*types.Signature)
 244  	}
 245  	if sig == nil && d.Type != nil {
 246  		sig = pkgResolveSigAST(pb.pkg, d.Type)
 247  	}
 248  	fn := &SSAFunction{
 249  		name:      d.Name.Value,
 250  		object:    obj,
 251  		Signature: sig,
 252  		pos:       0,
 253  		Pkg:       pb.pkg,
 254  		Prog:      pb.prog,
 255  	}
 256  	if CompileExportMap != nil {
 257  		if ename, ok := CompileExportMap[d.Name.Value]; ok {
 258  			fn.externalSymbol = ename
 259  			fn.isExternC = true
 260  		}
 261  	}
 262  	if d.Body == nil && isNoContextExtern(pb.pkg.Pkg.Path(), d.Name.Value) {
 263  		fn.isExternC = true
 264  	}
 265  	pb.pkg.Members[fn.name] = fn
 266  }
 267  
 268  func checkInitBody(body *syntax.BlockStmt) (s string) {
 269  	for _, s := range body.List {
 270  		if err := checkInitStmt(s); err != "" {
 271  			return err
 272  		}
 273  	}
 274  	return ""
 275  }
 276  
 277  func checkInitStmt(s syntax.Stmt) (sv string) {
 278  	switch s := s.(type) {
 279  	case *syntax.SelectStmt:
 280  		return "select{} not allowed in init()"
 281  	case *syntax.BlockStmt:
 282  		for _, sub := range s.List {
 283  			if err := checkInitStmt(sub); err != "" {
 284  				return err
 285  			}
 286  		}
 287  	case *syntax.IfStmt:
 288  		if s.Then != nil {
 289  			if err := checkInitStmt(s.Then); err != "" {
 290  				return err
 291  			}
 292  		}
 293  		if s.Else != nil {
 294  			if err := checkInitStmt(s.Else); err != "" {
 295  				return err
 296  			}
 297  		}
 298  	case *syntax.ForStmt:
 299  		if s.Body != nil {
 300  			if err := checkInitStmt(s.Body); err != "" {
 301  				return err
 302  			}
 303  		}
 304  	case *syntax.SwitchStmt:
 305  		for _, cc := range s.Body {
 306  			for _, sub := range cc.Body {
 307  				if err := checkInitStmt(sub); err != "" {
 308  					return err
 309  				}
 310  			}
 311  		}
 312  	case *syntax.ExprStmt:
 313  		if err := checkInitExpr(s.X); err != "" {
 314  			return err
 315  		}
 316  	}
 317  	return ""
 318  }
 319  
 320  func checkInitExpr(x syntax.Expr) (s string) {
 321  	if x == nil {
 322  		return ""
 323  	}
 324  	if c, ok := x.(*syntax.CallExpr); ok {
 325  		if n, ok2 := c.Fun.(*syntax.Name); ok2 && n.Value == "spawn" {
 326  			return "spawn() not allowed in init()"
 327  		}
 328  	}
 329  	return ""
 330  }
 331  
 332  func isNoContextExtern(pkg, name string) (ok bool) {
 333  	if pkg == "internal/runtime/syscall" && name == "Syscall6" {
 334  		return true
 335  	}
 336  	if pkg == "syscall" {
 337  		switch name {
 338  		case "runtime_entersyscall", "runtime_exitsyscall",
 339  			"Exit", "Getpagesize", "gettimeofday",
 340  			"runtime_BeforeExec", "runtime_AfterExec",
 341  			"runtime_BeforeFork", "runtime_AfterFork", "runtime_AfterForkInChild":
 342  			return true
 343  		}
 344  	}
 345  	if pkg == "os" {
 346  		switch name {
 347  		case "putchar", "getchar", "buffered", "gosched", "runtime_args":
 348  			return true
 349  		}
 350  	}
 351  	return false
 352  }
 353  
 354  func (pb *ssaPkgBuilder) registerMethod(d *syntax.FuncDecl) {
 355  	recvTypeName := ssaRecvTypeName(d.Recv)
 356  	if recvTypeName == "" {
 357  		return
 358  	}
 359  	mangledName := recvTypeName | "." | d.Name.Value
 360  	var obj *types.TCFunc
 361  	obj = pb.findMethod(recvTypeName, d.Name.Value)
 362  	var sig *types.Signature
 363  	if obj != nil {
 364  		sig, _ = obj.Type().(*types.Signature)
 365  	}
 366  	if sig == nil && d.Type != nil {
 367  		sig = pkgResolveSigAST(pb.pkg, d.Type)
 368  	}
 369  	fn := &SSAFunction{
 370  		name:      mangledName,
 371  		object:    obj,
 372  		Signature: sig,
 373  		pos:       0,
 374  		Pkg:       pb.pkg,
 375  		Prog:      pb.prog,
 376  	}
 377  	pb.pkg.Members[mangledName] = fn
 378  }
 379  
 380  func ssaRecvTypeName(recv *syntax.Field) (s string) {
 381  	if recv == nil {
 382  		return ""
 383  	}
 384  	switch t := recv.Type.(type) {
 385  	case *syntax.Name:
 386  		return t.Value
 387  	case *syntax.Operation:
 388  		if t.Y == nil && t.Op == token.Mul {
 389  			if n, ok := t.X.(*syntax.Name); ok {
 390  				return n.Value
 391  			}
 392  		}
 393  	}
 394  	return ""
 395  }
 396  
 397  func (pb *ssaPkgBuilder) findMethod(typeName string, methodName string) (t *types.TCFunc) {
 398  	obj := pb.pkg.Pkg.Scope().Lookup(typeName)
 399  	if obj == nil && types.Universe != nil {
 400  		obj = types.Universe.Lookup(typeName)
 401  	}
 402  	if obj == nil {
 403  		return nil
 404  	}
 405  	tn, ok := obj.(*types.TypeName)
 406  	if !ok {
 407  		return nil
 408  	}
 409  	switch t := tn.Type().(type) {
 410  	case *types.Named:
 411  		for i := int32(0); i < t.NumMethods(); i++ {
 412  			m := t.Method(i)
 413  			if m.Name() == methodName {
 414  				return m
 415  			}
 416  		}
 417  	case *types.Basic:
 418  		for i := int32(0); i < t.NumMethods(); i++ {
 419  			m := t.Method(i)
 420  			if m.Name() == methodName {
 421  				return m
 422  			}
 423  		}
 424  	}
 425  	return nil
 426  }
 427  
 428  func (pb *ssaPkgBuilder) registerVar(d *syntax.VarDecl) {
 429  	for _, name := range d.NameList {
 430  		var obj *types.TCVar
 431  		if o := pb.pkg.Pkg.Scope().Lookup(name.Value); o != nil {
 432  			obj, _ = o.(*types.TCVar)
 433  		}
 434  		var typ types.Type
 435  		if obj != nil {
 436  			typ = obj.Type()
 437  		}
 438  		if typ == nil && d.Type != nil {
 439  			typ = pkgResolveTypeAST(pb.pkg, d.Type)
 440  		}
 441  		if typ == nil && d.Values != nil {
 442  			typ = pkgResolveTypeAST(pb.pkg, d.Values)
 443  		}
 444  		g := &SSAGlobal{
 445  			name:   name.Value,
 446  			object: obj,
 447  			typ:    types.NewPointer(typ),
 448  			pos:    0,
 449  			pkg:    pb.pkg,
 450  		}
 451  		pb.pkg.Members[name.Value] = g
 452  	}
 453  	if d.Values != nil {
 454  		pb.varInits = append(pb.varInits, d)
 455  	}
 456  }
 457  
 458  func (pb *ssaPkgBuilder) registerType(d *types.TypeDecl) {
 459  	var obj *types.TypeName
 460  	if o := pb.pkg.Pkg.Scope().Lookup(d.Name.Value); o != nil {
 461  		obj, _ = o.(*types.TypeName)
 462  	}
 463  	if obj == nil {
 464  		underlying := pkgResolveTypeAST(pb.pkg, d.Type)
 465  		if underlying == nil {
 466  			underlying = &types.TCStruct{}
 467  		}
 468  		named := types.NewNamed(types.NewTypeName(pb.pkg.Pkg, d.Name.Value, nil), underlying)
 469  		named.Obj().SetType(named)
 470  		pb.pkg.Pkg.Scope().Insert(named.Obj())
 471  		obj = named.Obj()
 472  	}
 473  	t := &SSAType_{object: obj, pkg: pb.pkg}
 474  	pb.pkg.Members[d.Name.Value] = t
 475  }
 476  
 477  func (pb *ssaPkgBuilder) registerConst(d *syntax.ConstDecl) {
 478  	for _, name := range d.NameList {
 479  		var obj *types.TCConst
 480  		if o := pb.pkg.Pkg.Scope().Lookup(name.Value); o != nil {
 481  			obj, _ = o.(*types.TCConst)
 482  		}
 483  		if obj == nil {
 484  			continue
 485  		}
 486  		c := &SSANamedConst{
 487  			object: obj,
 488  			Value:  &SSAConst{typ: obj.Type(), val: localizeConstVal(obj.Val(), obj.Type())},
 489  			pkg:    pb.pkg,
 490  		}
 491  		pb.pkg.Members[name.Value] = c
 492  	}
 493  }
 494  
 495  func (pb *ssaPkgBuilder) buildFile(f *syntax.File) {
 496  	for _, d := range f.DeclList {
 497  		if fd, ok := d.(*syntax.FuncDecl); ok {
 498  			pb.buildFunc(fd)
 499  		}
 500  	}
 501  }
 502  
 503  func (pb *ssaPkgBuilder) buildVarInits() {
 504  	sig := types.NewSignature(nil, types.NewTuple(), types.NewTuple(), false)
 505  	fn, _ := pb.pkg.Members["main"].(*SSAFunction)
 506  	if fn == nil {
 507  		fn = &SSAFunction{
 508  			name:      "main",
 509  			Signature: sig,
 510  			Pkg:       pb.pkg,
 511  			Prog:      pb.prog,
 512  		}
 513  		pb.pkg.Members["main"] = fn
 514  	}
 515  	if len(fn.Blocks) > 0 {
 516  		initSig := types.NewSignature(nil, types.NewTuple(), types.NewTuple(), false)
 517  		initFn := &SSAFunction{
 518  			name:      "__varinit",
 519  			Signature: initSig,
 520  			Pkg:       pb.pkg,
 521  			Prog:      pb.prog,
 522  		}
 523  		pb.pkg.Members["__varinit"] = initFn
 524  		fb := newSSAFuncBuilder(initFn, pb.info)
 525  		entry := fb.newBlock("entry")
 526  		fb.currentBlock = entry
 527  		for _, d := range pb.varInits {
 528  			if d.Values == nil {
 529  				continue
 530  			}
 531  			rhs := fb.buildExpr(d.Values)
 532  			if rhs == nil {
 533  				continue
 534  			}
 535  			for _, name := range d.NameList {
 536  				if name.Value == "_" {
 537  					continue
 538  				}
 539  				fb.buildStore(name, rhs)
 540  			}
 541  		}
 542  		fb.emit(&SSAReturn{})
 543  		// Inject call to __varinit at start of main
 544  		callInstr := &SSACall{Call: SSACallCommon{Value: initFn}}
 545  		callInstr.name = "vinit"
 546  		oldEntry := fn.Blocks[0]
 547  		n := int32(len(oldEntry.Instrs))
 548  		combined := []SSAInstruction{:n+1}
 549  		combined[0] = callInstr
 550  		for i := int32(0); i < n; i++ {
 551  			combined[i+1] = oldEntry.Instrs[i]
 552  		}
 553  		oldEntry.Instrs = combined
 554  		return
 555  	}
 556  	fb := newSSAFuncBuilder(fn, pb.info)
 557  	entry := fb.newBlock("entry")
 558  	fb.currentBlock = entry
 559  	for _, d := range pb.varInits {
 560  		if d.Values == nil {
 561  			continue
 562  		}
 563  		rhs := fb.buildExpr(d.Values)
 564  		if rhs == nil {
 565  			continue
 566  		}
 567  		for _, name := range d.NameList {
 568  			if name.Value == "_" {
 569  				continue
 570  			}
 571  			fb.buildStore(name, rhs)
 572  		}
 573  	}
 574  	fb.emit(&SSAReturn{})
 575  }
 576  
 577  func (pb *ssaPkgBuilder) buildFunc(d *syntax.FuncDecl) {
 578  	if len(d.TParamList) > 0 {
 579  		return
 580  	}
 581  	if d.Body == nil {
 582  		return
 583  	}
 584  	name := d.Name.Value
 585  	if d.Recv != nil {
 586  		rtn := ssaRecvTypeName(d.Recv)
 587  		if rtn != "" {
 588  			name = rtn | "." | d.Name.Value
 589  		}
 590  	}
 591  	fn, _ := pb.pkg.Members[name].(*SSAFunction)
 592  	if fn == nil {
 593  		return
 594  	}
 595  	fb := newSSAFuncBuilder(fn, pb.info)
 596  	fb.buildBody(d)
 597  }
 598  
 599  // ssaFuncBuilder builds one SSAFunction body.
 600  type ssaFuncBuilder struct {
 601  	fn           *SSAFunction
 602  	info         *types.Info
 603  	parent       *ssaFuncBuilder
 604  	currentBlock *SSABasicBlock
 605  	vars         map[types.Object]*SSAAlloc
 606  	localTypes   map[string]types.Type
 607  	localConsts  map[string]*SSAConst
 608  	freeVarPtrs  map[string]*SSAFreeVar
 609  	namedResults []*SSAAlloc
 610  	paramAllocas []*SSAAlloc
 611  	tailBlock    *SSABasicBlock
 612  	counter      int32
 613  
 614  	loops        []ssaLoopState
 615  	pendingLabel string
 616  	labels       map[string]*SSABasicBlock
 617  	deferred     int32
 618  	typeSubst    map[string]types.Type
 619  	srcScope     *types.Scope
 620  	scopeID      int32
 621  	nextScopeID  int32
 622  }
 623  
 624  type ssaLoopState struct {
 625  	label string
 626  	body  *SSABasicBlock
 627  	post  *SSABasicBlock
 628  	done  *SSABasicBlock
 629  }
 630