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