ssa_stmt.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 (fb *ssaFuncBuilder) buildIf(s *syntax.IfStmt) {
  10  	var savedVars map[types.Object]*SSAAlloc
  11  	if s.Init != nil {
  12  		savedVars = fb.saveVars()
  13  		fb.buildStmt(s.Init)
  14  	}
  15  	if fb.currentBlock == nil {
  16  		if savedVars != nil {
  17  			fb.vars = savedVars
  18  		}
  19  		return
  20  	}
  21  	cond := fb.buildExpr(s.Cond)
  22  	if cond == nil {
  23  		if savedVars != nil {
  24  			fb.vars = savedVars
  25  		}
  26  		return
  27  	}
  28  
  29  	thenBlock := fb.newBlock("if.then")
  30  	var elseBlock *SSABasicBlock
  31  	doneBlock := fb.newBlock("if.done")
  32  
  33  	if s.Else != nil {
  34  		elseBlock = fb.newBlock("if.else")
  35  	} else {
  36  		elseBlock = doneBlock
  37  	}
  38  
  39  	fb.emit(&SSAIf{Cond: cond})
  40  	fb.currentBlock.Succs = append(fb.currentBlock.Succs, thenBlock, elseBlock)
  41  	thenBlock.Preds = append(thenBlock.Preds, fb.currentBlock)
  42  	elseBlock.Preds = append(elseBlock.Preds, fb.currentBlock)
  43  
  44  	oldThen := fb.enterScope()
  45  	thenBlock.ScopeID = fb.scopeID
  46  	fb.currentBlock = thenBlock
  47  	fb.buildBlock(s.Then)
  48  	if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
  49  		fb.emit(&SSAJump{Comment: "if.done"})
  50  		fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
  51  		doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
  52  	}
  53  	fb.exitScope(oldThen)
  54  
  55  	if s.Else != nil {
  56  		oldElse := fb.enterScope()
  57  		elseBlock.ScopeID = fb.scopeID
  58  		fb.currentBlock = elseBlock
  59  		fb.buildStmt(s.Else)
  60  		if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
  61  			fb.emit(&SSAJump{Comment: "if.done"})
  62  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
  63  			doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
  64  		}
  65  		fb.exitScope(oldElse)
  66  	}
  67  
  68  	fb.currentBlock = doneBlock
  69  	if savedVars != nil {
  70  		fb.vars = savedVars
  71  	}
  72  }
  73  
  74  func (fb *ssaFuncBuilder) buildFor(s *syntax.ForStmt) {
  75  	var savedVars map[types.Object]*SSAAlloc
  76  	if s.Init != nil {
  77  		if rc, ok := s.Init.(*syntax.RangeClause); ok {
  78  			fb.buildRangeLoop(s, rc)
  79  			return
  80  		}
  81  		savedVars = fb.saveVars()
  82  		fb.buildStmt(s.Init)
  83  	}
  84  
  85  	condBlock := fb.newBlock("for.cond")
  86  	bodyBlock := fb.newBlock("for.body")
  87  	postBlock := fb.newBlock("for.post")
  88  	doneBlock := fb.newBlock("for.done")
  89  
  90  	fb.emit(&SSAJump{Comment: "for.cond"})
  91  	fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock)
  92  	condBlock.Preds = append(condBlock.Preds, fb.currentBlock)
  93  
  94  	fb.loops = append(fb.loops, ssaLoopState{label: fb.pendingLabel, body: bodyBlock, post: postBlock, done: doneBlock})
  95  	fb.pendingLabel = ""
  96  
  97  	fb.currentBlock = condBlock
  98  	if s.Cond != nil {
  99  		cond := fb.buildExpr(s.Cond)
 100  		if cond != nil {
 101  			fb.emit(&SSAIf{Cond: cond})
 102  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock, doneBlock)
 103  			bodyBlock.Preds = append(bodyBlock.Preds, condBlock)
 104  			doneBlock.Preds = append(doneBlock.Preds, condBlock)
 105  		}
 106  	} else {
 107  		fb.emit(&SSAJump{Comment: "for.body"})
 108  		fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock)
 109  		bodyBlock.Preds = append(bodyBlock.Preds, condBlock)
 110  	}
 111  
 112  	forScope := fb.enterScope()
 113  	bodyBlock.ScopeID = fb.scopeID
 114  	fb.currentBlock = bodyBlock
 115  	fb.buildBlock(s.Body)
 116  	if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
 117  		fb.emit(&SSAJump{Comment: "for.post"})
 118  		fb.currentBlock.Succs = append(fb.currentBlock.Succs, postBlock)
 119  		postBlock.Preds = append(postBlock.Preds, fb.currentBlock)
 120  	}
 121  
 122  	postBlock.ScopeID = fb.scopeID
 123  	fb.currentBlock = postBlock
 124  	if s.Post != nil {
 125  		fb.buildStmt(s.Post)
 126  	}
 127  	if fb.currentBlock != nil {
 128  		fb.emit(&SSAJump{Comment: "for.cond"})
 129  		fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock)
 130  		condBlock.Preds = append(condBlock.Preds, fb.currentBlock)
 131  	}
 132  	fb.exitScope(forScope)
 133  
 134  	fb.loops = fb.loops[:len(fb.loops)-1]
 135  	fb.currentBlock = doneBlock
 136  	if savedVars != nil {
 137  		fb.vars = savedVars
 138  	}
 139  }
 140  
 141  func (fb *ssaFuncBuilder) buildRangeLoop(s *syntax.ForStmt, rc *syntax.RangeClause) {
 142  	savedVars := fb.saveVars()
 143  	iterExpr := fb.buildExpr(rc.X)
 144  	if iterExpr == nil {
 145  		return
 146  	}
 147  
 148  	r := &SSARange{X: iterExpr}
 149  	r.typ = types.Typ[types.Invalid]
 150  	r.name = fb.nextName()
 151  	fb.emit(r)
 152  
 153  	bodyBlock := fb.newBlock("range.body")
 154  	doneBlock := fb.newBlock("range.done")
 155  
 156  	condBlock := fb.newBlock("range.cond")
 157  
 158  	fb.loops = append(fb.loops, ssaLoopState{label: fb.pendingLabel, body: bodyBlock, post: condBlock, done: doneBlock})
 159  	fb.pendingLabel = ""
 160  	fb.emit(&SSAJump{Comment: "range.cond"})
 161  	fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock)
 162  	condBlock.Preds = append(condBlock.Preds, fb.currentBlock)
 163  
 164  	fb.currentBlock = condBlock
 165  	iterSSAType := iterExpr.SSAType()
 166  	if iterSSAType == nil {
 167  		switch x := rc.X.(type) {
 168  		case *syntax.SelectorExpr:
 169  			if x != nil {
 170  				tv := x.GetTypeInfo()
 171  				if tv.Type != nil {
 172  					iterSSAType = tv.Type
 173  				}
 174  			}
 175  		case *syntax.Name:
 176  			if x != nil {
 177  				tv := x.GetTypeInfo()
 178  				if tv.Type != nil {
 179  					iterSSAType = tv.Type
 180  				}
 181  			}
 182  		}
 183  		if iterSSAType == nil {
 184  			writeStr(2, "WARN: unresolved range type in " | fb.fn.Pkg.Pkg.Path() | "." | fb.fn.name | "\n")
 185  			return
 186  		}
 187  	}
 188  	nxt := &SSANext{Iter: r, IsString: ssaIsStringType(iterSSAType)}
 189  	keyTyp := asType(types.Typ[types.Int32])
 190  	valTyp := asType(types.Typ[types.Invalid])
 191  	iterT := iterSSAType
 192  	iterU := types.SafeUnderlying(iterT)
 193  	if sl, ok := iterU.(*types.Slice); ok {
 194  		valTyp = sl.Elem()
 195  	} else if sl, ok := iterT.(*types.Slice); ok {
 196  		valTyp = sl.Elem()
 197  	} else if mt, ok := iterU.(*types.TCMap); ok {
 198  		keyTyp = mt.Key()
 199  		valTyp = mt.Elem()
 200  	} else if mt, ok := iterT.(*types.TCMap); ok {
 201  		keyTyp = mt.Key()
 202  		valTyp = mt.Elem()
 203  	} else if ar, ok := iterU.(*types.Array); ok {
 204  		valTyp = ar.Elem()
 205  	} else if ar, ok := iterT.(*types.Array); ok {
 206  		valTyp = ar.Elem()
 207  	} else if p, ok := iterU.(*types.Pointer); ok && p.Elem() != nil {
 208  		if ar, ok2 := types.SafeUnderlying(p.Elem()).(*types.Array); ok2 {
 209  			valTyp = ar.Elem()
 210  		}
 211  	}
 212  	if valTyp == asType(types.Typ[types.Invalid]) && ssaIsStringType(iterSSAType) {
 213  		valTyp = asType(types.Typ[types.Uint8])
 214  	}
 215  	nxt.typ = types.NewTuple(
 216  		types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
 217  		types.NewTCVar(nil, "k", keyTyp),
 218  		types.NewTCVar(nil, "v", valTyp),
 219  	)
 220  	nxt.name = fb.nextName()
 221  	fb.emit(nxt)
 222  
 223  	okExt := &SSAExtract{Tuple: nxt, Index: 0}
 224  	okExt.typ = types.Typ[types.Bool]
 225  	okExt.name = fb.nextName()
 226  	fb.emit(okExt)
 227  
 228  	fb.emit(&SSAIf{Cond: okExt})
 229  	fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock, doneBlock)
 230  	bodyBlock.Preds = append(bodyBlock.Preds, condBlock)
 231  	doneBlock.Preds = append(doneBlock.Preds, condBlock)
 232  
 233  	rangeScope := fb.enterScope()
 234  	bodyBlock.ScopeID = fb.scopeID
 235  	fb.currentBlock = bodyBlock
 236  	if rc.Lhs != nil && rc.Def {
 237  		names := ssaExprNames(rc.Lhs)
 238  		for i, name := range names {
 239  			if name.Value == "_" {
 240  				continue
 241  			}
 242  			ext := &SSAExtract{Tuple: nxt, Index: i + 1}
 243  			ext.typ = ssaTupleElemType(nxt.typ, i+1)
 244  			ext.name = fb.nextName()
 245  			fb.emit(ext)
 246  			var obj types.Object
 247  			if fb.info != nil {
 248  				obj = fb.info.Defs[name]
 249  			}
 250  			if obj == nil {
 251  				obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, ext.typ)
 252  			}
 253  			fb.removeVar(name.Value)
 254  			alloc := fb.emitAlloc(ext.typ, 0)
 255  			fb.vars[obj] = alloc
 256  			fb.emitStore(alloc, ext)
 257  		}
 258  	} else if rc.Lhs != nil && !rc.Def {
 259  		names := ssaExprNames(rc.Lhs)
 260  		for i, name := range names {
 261  			if name.Value == "_" {
 262  				continue
 263  			}
 264  			ext := &SSAExtract{Tuple: nxt, Index: i + 1}
 265  			ext.typ = ssaTupleElemType(nxt.typ, i+1)
 266  			ext.name = fb.nextName()
 267  			fb.emit(ext)
 268  			fb.buildStore(name, ext)
 269  		}
 270  	}
 271  	fb.buildBlock(s.Body)
 272  	if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
 273  		fb.emit(&SSAJump{Comment: "range.cond"})
 274  		fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock)
 275  		condBlock.Preds = append(condBlock.Preds, fb.currentBlock)
 276  	}
 277  	fb.exitScope(rangeScope)
 278  
 279  	fb.loops = fb.loops[:len(fb.loops)-1]
 280  	fb.currentBlock = doneBlock
 281  	fb.vars = savedVars
 282  }
 283  
 284  func (fb *ssaFuncBuilder) buildSwitch(s *syntax.SwitchStmt) {
 285  	var savedVars map[types.Object]*SSAAlloc
 286  	if s.Init != nil {
 287  		savedVars = fb.saveVars()
 288  		fb.buildStmt(s.Init)
 289  	}
 290  	if fb.currentBlock == nil {
 291  		if savedVars != nil {
 292  			fb.vars = savedVars
 293  		}
 294  		return
 295  	}
 296  
 297  	doneBlock := fb.newBlock("switch.done")
 298  	savedLoops := fb.loops
 299  	fb.loops = append(fb.loops, ssaLoopState{label: fb.pendingLabel, done: doneBlock})
 300  	fb.pendingLabel = ""
 301  
 302  	if s.Tag != nil {
 303  		if tsg, ok := s.Tag.(*types.TypeSwitchGuard); ok {
 304  			fb.buildTypeSwitch(s, tsg, doneBlock)
 305  			fb.loops = savedLoops
 306  			fb.currentBlock = doneBlock
 307  			if savedVars != nil {
 308  				fb.vars = savedVars
 309  			}
 310  			return
 311  		}
 312  	}
 313  
 314  	var tag SSAValue
 315  	if s.Tag != nil {
 316  		tag = fb.buildExpr(s.Tag)
 317  	}
 318  
 319  	for _, clause := range s.Body {
 320  		caseBlock := fb.newBlock("switch.case")
 321  		nextBlock := fb.newBlock("switch.next")
 322  
 323  		if clause.Cases != nil && tag != nil {
 324  			var cond SSAValue
 325  			caseExprs := []syntax.Expr{clause.Cases}
 326  			if list, ok := clause.Cases.(*syntax.ListExpr); ok {
 327  				caseExprs = list.ElemList
 328  			}
 329  			for _, ce := range caseExprs {
 330  				caseVal := fb.buildExpr(ce)
 331  				cmp := &SSABinOp{Op: OpEql, X: tag, Y: caseVal}
 332  				cmp.typ = types.Typ[types.Bool]
 333  				cmp.name = fb.nextName()
 334  				fb.emit(cmp)
 335  				if cond == nil {
 336  					cond = cmp
 337  				} else {
 338  					orOp := &SSABinOp{Op: OpLor, X: cond, Y: cmp}
 339  					orOp.typ = types.Typ[types.Bool]
 340  					orOp.name = fb.nextName()
 341  					fb.emit(orOp)
 342  					cond = orOp
 343  				}
 344  			}
 345  			fb.emit(&SSAIf{Cond: cond})
 346  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock, nextBlock)
 347  			caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
 348  			nextBlock.Preds = append(nextBlock.Preds, fb.currentBlock)
 349  		} else if clause.Cases != nil && tag == nil {
 350  			cond := fb.buildExpr(clause.Cases)
 351  			fb.emit(&SSAIf{Cond: cond})
 352  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock, nextBlock)
 353  			caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
 354  			nextBlock.Preds = append(nextBlock.Preds, fb.currentBlock)
 355  		} else {
 356  			fb.emit(&SSAJump{Comment: "switch.case"})
 357  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock)
 358  			caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
 359  		}
 360  
 361  		caseScope := fb.enterScope()
 362  		caseBlock.ScopeID = fb.scopeID
 363  		fb.currentBlock = caseBlock
 364  		caseSaved := fb.saveVars()
 365  		for _, stmt := range clause.Body {
 366  			fb.buildStmt(stmt)
 367  			if fb.currentBlock == nil {
 368  				break
 369  			}
 370  		}
 371  		if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
 372  			fb.emit(&SSAJump{Comment: "switch.done"})
 373  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
 374  			doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
 375  		}
 376  		fb.exitScope(caseScope)
 377  		fb.vars = caseSaved
 378  		fb.currentBlock = nextBlock
 379  	}
 380  
 381  	if fb.currentBlock != nil {
 382  		fb.emit(&SSAJump{Comment: "switch.done"})
 383  		fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
 384  		doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
 385  	}
 386  
 387  	fb.loops = savedLoops
 388  	fb.currentBlock = doneBlock
 389  	if savedVars != nil {
 390  		fb.vars = savedVars
 391  	}
 392  }
 393  
 394  func (fb *ssaFuncBuilder) buildTypeSwitch(s *syntax.SwitchStmt, tsg *types.TypeSwitchGuard, doneBlock *SSABasicBlock) {
 395  	x := fb.buildExpr(tsg.X)
 396  	var savedObj types.Object
 397  	var savedAlloc *SSAAlloc
 398  	if tsg.Lhs != nil {
 399  		for obj, alloc := range fb.vars {
 400  			if obj.Name() == tsg.Lhs.Value {
 401  				savedObj = obj
 402  				savedAlloc = alloc
 403  				break
 404  			}
 405  		}
 406  	}
 407  	for _, clause := range s.Body {
 408  		caseBlock := fb.newBlock("typeswitch.case")
 409  		nextBlock := fb.newBlock("typeswitch.next")
 410  
 411  		if clause.Cases != nil {
 412  			caseExprs := []syntax.Expr{clause.Cases}
 413  			if list, ok := clause.Cases.(*syntax.ListExpr); ok {
 414  				caseExprs = list.ElemList
 415  			}
 416  
 417  			var cond SSAValue
 418  			var firstTA *SSATypeAssert
 419  			var firstType types.Type
 420  			for _, ce := range caseExprs {
 421  				if n, isName := ce.(*syntax.Name); isName && n.Value == "nil" {
 422  					nilType := x.SSAType()
 423  					if nilType == nil {
 424  						nilType = types.NewTCInterface(nil, nil)
 425  					}
 426  					nilCheck := &SSABinOp{Op: OpEql, X: x, Y: &SSAConst{typ: nilType, val: nil}}
 427  					nilCheck.typ = types.Typ[types.Bool]
 428  					nilCheck.name = fb.nextName()
 429  					fb.emit(nilCheck)
 430  					if cond == nil {
 431  						cond = nilCheck
 432  					} else {
 433  						orOp := &SSABinOp{Op: OpLor, X: cond, Y: nilCheck}
 434  						orOp.typ = types.Typ[types.Bool]
 435  						orOp.name = fb.nextName()
 436  						fb.emit(orOp)
 437  						cond = orOp
 438  					}
 439  					continue
 440  				}
 441  				var assertedType types.Type
 442  				if fb.info != nil {
 443  					tv := fb.info.Types[ce]
 444  					assertedType = tv.Type
 445  				}
 446  				if assertedType == nil {
 447  					assertedType = fb.resolveType(ce)
 448  				}
 449  				if assertedType == nil {
 450  					assertedType = types.Typ[types.Invalid]
 451  				}
 452  				ta := &SSATypeAssert{X: x, AssertedType: assertedType, CommaOk: true}
 453  				ta.typ = types.NewTuple(
 454  					types.NewTCVar(nil, "val", assertedType),
 455  					types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
 456  				)
 457  				ta.name = fb.nextName()
 458  				fb.emit(ta)
 459  				okExt := &SSAExtract{Tuple: ta, Index: 1}
 460  				okExt.typ = types.Typ[types.Bool]
 461  				okExt.name = fb.nextName()
 462  				fb.emit(okExt)
 463  				if firstTA == nil {
 464  					firstTA = ta
 465  					firstType = assertedType
 466  				}
 467  				if cond == nil {
 468  					cond = okExt
 469  				} else {
 470  					orOp := &SSABinOp{Op: OpLor, X: cond, Y: okExt}
 471  					orOp.typ = types.Typ[types.Bool]
 472  					orOp.name = fb.nextName()
 473  					fb.emit(orOp)
 474  					cond = orOp
 475  				}
 476  			}
 477  			fb.emit(&SSAIf{Cond: cond})
 478  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock, nextBlock)
 479  			caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
 480  			nextBlock.Preds = append(nextBlock.Preds, fb.currentBlock)
 481  
 482  			fb.currentBlock = caseBlock
 483  			if tsg.Lhs != nil {
 484  				var guardVal SSAValue
 485  				var guardType types.Type
 486  				if firstTA != nil {
 487  					ext := &SSAExtract{Tuple: firstTA, Index: 0}
 488  					ext.typ = firstType
 489  					ext.name = fb.nextName()
 490  					fb.emit(ext)
 491  					guardVal = ext
 492  					guardType = firstType
 493  				} else {
 494  					guardType = x.SSAType()
 495  					if guardType == nil {
 496  						guardType = types.NewTCInterface(nil, nil)
 497  					}
 498  					guardVal = &SSAConst{typ: guardType, val: nil}
 499  				}
 500  				for old := range fb.vars {
 501  					if old.Name() == tsg.Lhs.Value {
 502  						delete(fb.vars, old)
 503  						break
 504  					}
 505  				}
 506  				obj := types.NewTCVar(fb.fn.Pkg.Pkg, tsg.Lhs.Value, guardType)
 507  				alloc := fb.emitAlloc(guardType, 0)
 508  				fb.vars[obj] = alloc
 509  				fb.emitStore(alloc, guardVal)
 510  			}
 511  		} else {
 512  			fb.emit(&SSAJump{Comment: "typeswitch.case"})
 513  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock)
 514  			caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
 515  			fb.currentBlock = caseBlock
 516  			if tsg.Lhs != nil {
 517  				guardType := x.SSAType()
 518  				if guardType == nil {
 519  					guardType = types.NewTCInterface(nil, nil)
 520  				}
 521  				for old := range fb.vars {
 522  					if old.Name() == tsg.Lhs.Value {
 523  						delete(fb.vars, old)
 524  						break
 525  					}
 526  				}
 527  				obj := types.NewTCVar(fb.fn.Pkg.Pkg, tsg.Lhs.Value, guardType)
 528  				alloc := fb.emitAlloc(guardType, 0)
 529  				fb.vars[obj] = alloc
 530  				fb.emitStore(alloc, x)
 531  			}
 532  		}
 533  
 534  		tsScope := fb.enterScope()
 535  		caseBlock.ScopeID = fb.scopeID
 536  		caseSaved := fb.saveVars()
 537  		for _, stmt := range clause.Body {
 538  			fb.buildStmt(stmt)
 539  			if fb.currentBlock == nil {
 540  				break
 541  			}
 542  		}
 543  		if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
 544  			fb.emit(&SSAJump{Comment: "switch.done"})
 545  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
 546  			doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
 547  		}
 548  		fb.exitScope(tsScope)
 549  		fb.vars = caseSaved
 550  		fb.currentBlock = nextBlock
 551  	}
 552  	if savedObj != nil && savedAlloc != nil {
 553  		for old := range fb.vars {
 554  			if old.Name() == tsg.Lhs.Value {
 555  				delete(fb.vars, old)
 556  				break
 557  			}
 558  		}
 559  		fb.vars[savedObj] = savedAlloc
 560  	}
 561  	if fb.currentBlock != nil {
 562  		fb.emit(&SSAJump{Comment: "switch.done"})
 563  		fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
 564  		doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
 565  	}
 566  }
 567  
 568  func (fb *ssaFuncBuilder) buildSelect(s *syntax.SelectStmt) {
 569  	doneBlock := fb.newBlock("select.done")
 570  
 571  	var states []*SSASelectState
 572  	caseBlocks := [](*SSABasicBlock){:0:len(s.Body)}
 573  	for i, clause := range s.Body {
 574  		cb := fb.newBlock("select.case")
 575  		caseBlocks = append(caseBlocks, cb)
 576  		state := &SSASelectState{}
 577  		if clause.Comm != nil {
 578  			switch comm := clause.Comm.(type) {
 579  			case *syntax.SendStmt:
 580  				state.Dir = SelectDirSend
 581  				state.Chan = fb.buildExpr(comm.Chan)
 582  				state.Send = fb.buildExpr(comm.Value)
 583  			case *syntax.AssignStmt:
 584  				var chanExpr syntax.Expr
 585  				if op, ok2 := comm.Rhs.(*syntax.Operation); ok2 && op.Y == nil && op.Op == token.Recv {
 586  					chanExpr = op.X
 587  				}
 588  				if chanExpr != nil {
 589  					state.Dir = SelectDirRecv
 590  					state.Chan = fb.buildExpr(chanExpr)
 591  				}
 592  			case *syntax.ExprStmt:
 593  				if op, ok2 := comm.X.(*syntax.Operation); ok2 && op.Y == nil && op.Op == token.Recv {
 594  					state.Dir = SelectDirRecv
 595  					state.Chan = fb.buildExpr(op.X)
 596  				}
 597  			}
 598  		}
 599  		states = append(states, state)
 600  		_ = i
 601  	}
 602  
 603  	sel := &SSASelect{States: states, Blocking: true}
 604  	sel.typ = types.NewTuple(
 605  		types.NewTCVar(nil, "index", types.Typ[types.Int32]),
 606  		types.NewTCVar(nil, "recvOk", types.Typ[types.Bool]),
 607  	)
 608  	sel.name = fb.nextName()
 609  	fb.emit(sel)
 610  
 611  	for i, clause := range s.Body {
 612  		selScope := fb.enterScope()
 613  		caseBlocks[i].ScopeID = fb.scopeID
 614  		fb.currentBlock = caseBlocks[i]
 615  		if clause.Comm != nil {
 616  			fb.buildStmt(clause.Comm)
 617  		}
 618  		for _, stmt := range clause.Body {
 619  			fb.buildStmt(stmt)
 620  			if fb.currentBlock == nil {
 621  				break
 622  			}
 623  		}
 624  		if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
 625  			fb.emit(&SSAJump{Comment: "select.done"})
 626  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
 627  			doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
 628  		}
 629  		fb.exitScope(selScope)
 630  	}
 631  
 632  	fb.currentBlock = doneBlock
 633  }
 634  
 635  func (fb *ssaFuncBuilder) isSelfTailCall(e syntax.Expr) (*syntax.CallExpr, bool) {
 636  	call, ok := e.(*syntax.CallExpr)
 637  	if !ok || fb.tailBlock == nil || fb.deferred > 0 {
 638  		return nil, false
 639  	}
 640  	name, ok := call.Fun.(*syntax.Name)
 641  	if !ok {
 642  		return nil, false
 643  	}
 644  	obj := fb.lookupObject(name.Value)
 645  	if obj == nil {
 646  		return nil, false
 647  	}
 648  	tc, ok := obj.(*types.TCFunc)
 649  	if !ok || tc == nil {
 650  		return nil, false
 651  	}
 652  	fn, _ := fb.fn.Pkg.Members[name.Value].(*SSAFunction)
 653  	if fn != fb.fn {
 654  		return nil, false
 655  	}
 656  	return call, true
 657  }
 658  
 659  func (fb *ssaFuncBuilder) buildReturn(s *syntax.ReturnStmt) {
 660  	if s.Results != nil {
 661  		if call, ok := fb.isSelfTailCall(s.Results); ok {
 662  			args := fb.buildArgs(call.ArgList)
 663  			if len(args) == len(fb.paramAllocas) {
 664  				for i, arg := range args {
 665  					fb.emitStore(fb.paramAllocas[i], arg)
 666  				}
 667  				fb.emit(&SSAJump{Comment: "tailcall"})
 668  				fb.currentBlock.Succs = append(fb.currentBlock.Succs, fb.tailBlock)
 669  				fb.tailBlock.Preds = append(fb.tailBlock.Preds, fb.currentBlock)
 670  				fb.currentBlock = nil
 671  				return
 672  			}
 673  		}
 674  	}
 675  	var vals []SSAValue
 676  	if s.Results != nil {
 677  		if list, ok := s.Results.(*syntax.ListExpr); ok {
 678  			for i, el := range list.ElemList {
 679  				v := fb.buildExpr(el)
 680  				if v != nil {
 681  					vals = append(vals, v)
 682  				} else {
 683  					var zeroType types.Type
 684  					if fb.fn.Signature != nil && fb.fn.Signature.Results() != nil && int32(i) < int32(fb.fn.Signature.Results().Len()) {
 685  						zeroType = fb.fn.Signature.Results().At(int32(i)).Type()
 686  					}
 687  					vals = append(vals, &SSAConst{typ: zeroType, val: nil})
 688  				}
 689  			}
 690  		} else {
 691  			v := fb.buildExpr(s.Results)
 692  			if v != nil {
 693  				if v.SSAType() == nil {
 694  					vals = append(vals, v)
 695  				} else if tup, ok := v.SSAType().(*types.Tuple); ok && tup.Len() > 1 {
 696  					for i := 0; i < tup.Len(); i++ {
 697  						ext := &SSAExtract{Tuple: v, Index: i}
 698  						ext.typ = tup.At(i).Type()
 699  						ext.name = fb.nextName()
 700  						fb.emit(ext)
 701  						vals = append(vals, ext)
 702  					}
 703  				} else {
 704  					vals = append(vals, v)
 705  				}
 706  			}
 707  		}
 708  	} else if len(fb.namedResults) > 0 {
 709  		for _, nr := range fb.namedResults {
 710  			elemType := nr.SSAType()
 711  			if p, ok := elemType.(*types.Pointer); ok {
 712  				elemType = p.Elem()
 713  			}
 714  			vals = append(vals, fb.emitLoad(nr, elemType))
 715  		}
 716  	}
 717  	if fb.fn.Signature != nil && fb.fn.Signature.Results() != nil {
 718  		for i, v := range vals {
 719  			if i < fb.fn.Signature.Results().Len() {
 720  				retType := fb.fn.Signature.Results().At(i).Type()
 721  				vals[i] = fb.coerceToInterface(v, retType)
 722  			}
 723  		}
 724  	}
 725  	fb.emitReturn(vals, 0)
 726  }
 727  
 728  func (fb *ssaFuncBuilder) buildBranch(s *syntax.BranchStmt) {
 729  	if fb.currentBlock == nil {
 730  		return
 731  	}
 732  	switch s.Tok {
 733  	case token.Break:
 734  		if len(fb.loops) > 0 {
 735  			var doneBlock *SSABasicBlock
 736  			if s.Label != nil {
 737  				for i := len(fb.loops) - 1; i >= 0; i-- {
 738  					if fb.loops[i].label == s.Label.Value {
 739  						doneBlock = fb.loops[i].done
 740  						break
 741  					}
 742  				}
 743  			}
 744  			if doneBlock == nil {
 745  				doneBlock = fb.loops[len(fb.loops)-1].done
 746  			}
 747  			fb.emit(&SSAJump{Comment: "break"})
 748  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
 749  			doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
 750  			fb.currentBlock = nil
 751  		}
 752  	case token.Continue:
 753  		for i := len(fb.loops) - 1; i >= 0; i-- {
 754  			postBlock := fb.loops[i].post
 755  			if postBlock == nil {
 756  				postBlock = fb.loops[i].body
 757  			}
 758  			if postBlock == nil {
 759  				continue
 760  			}
 761  			fb.emit(&SSAJump{Comment: "continue"})
 762  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, postBlock)
 763  			postBlock.Preds = append(postBlock.Preds, fb.currentBlock)
 764  			fb.currentBlock = nil
 765  			break
 766  		}
 767  	case token.Goto:
 768  		if s.Label != nil {
 769  			target := fb.labelBlock(s.Label.Value)
 770  			fb.emit(&SSAJump{Comment: "goto." | s.Label.Value})
 771  			fb.currentBlock.Succs = append(fb.currentBlock.Succs, target)
 772  			target.Preds = append(target.Preds, fb.currentBlock)
 773  			fb.currentBlock = nil
 774  		}
 775  	case token.Return:
 776  		fb.emitReturn(nil, 0)
 777  	}
 778  }
 779  
 780  func (fb *ssaFuncBuilder) buildGoStmt(call *syntax.CallExpr) {
 781  	fn := fb.buildExpr(call.Fun)
 782  	if fn == nil {
 783  		return
 784  	}
 785  	args := fb.buildArgs(call.ArgList)
 786  	g := &SSAGo{Call: SSACallCommon{Value: fn, Args: args}}
 787  	fb.emit(g)
 788  }
 789  
 790  func (fb *ssaFuncBuilder) buildDeferStmt(call *syntax.CallExpr) {
 791  	fn := fb.buildExpr(call.Fun)
 792  	if fn == nil {
 793  		return
 794  	}
 795  	args := fb.buildArgs(call.ArgList)
 796  	d := &SSADefer{Call: SSACallCommon{Value: fn, Args: args}}
 797  	fb.emit(d)
 798  	fb.deferred++
 799  }
 800