ssa_func.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 newSSAFuncBuilder(fn *SSAFunction, info *types.Info) (p *ssaFuncBuilder) {
10 return &ssaFuncBuilder{
11 fn: fn,
12 info: info,
13 vars: map[types.Object]*SSAAlloc{},
14 localTypes: map[string]types.Type{},
15 }
16 }
17
18 func (fb *ssaFuncBuilder) newBlock(comment string) (s *SSABasicBlock) {
19 b := NewSSABasicBlock(fb.fn, comment)
20 b.ScopeID = fb.scopeID
21 return b
22 }
23
24 func (fb *ssaFuncBuilder) enterScope() int32 {
25 fb.nextScopeID++
26 old := fb.scopeID
27 fb.scopeID = fb.nextScopeID
28 return old
29 }
30
31 func (fb *ssaFuncBuilder) exitScope(old int32) {
32 fb.scopeID = old
33 }
34
35 func (fb *ssaFuncBuilder) labelBlock(name string) (s *SSABasicBlock) {
36 if fb.labels == nil {
37 fb.labels = map[string]*SSABasicBlock{}
38 }
39 if b := fb.labels[name]; b != nil {
40 return b
41 }
42 b := fb.newBlock("label." | name)
43 fb.labels[name] = b
44 return b
45 }
46
47 func (fb *ssaFuncBuilder) emit(instr SSAInstruction) {
48 if fb.currentBlock == nil {
49 return
50 }
51 instr.setBlock(fb.currentBlock)
52 fb.currentBlock.Instrs = append(fb.currentBlock.Instrs, instr)
53 }
54
55 func (fb *ssaFuncBuilder) nextName() (s string) {
56 fb.counter++
57 return "t" | ssaItoa(fb.counter)
58 }
59
60 func (fb *ssaFuncBuilder) buildBody(d *syntax.FuncDecl) {
61 entry := fb.newBlock("entry")
62 fb.currentBlock = entry
63
64 sig := fb.fn.Signature
65 if sig == nil {
66 return
67 }
68 params := sig.Params()
69 if d.Recv != nil {
70 recv := sig.Recv()
71 if recv != nil {
72 recvName := recv.Name()
73 if recvName == "" && d.Recv.Name != nil {
74 recvName = d.Recv.Name.Value
75 }
76 p := &SSAParameter{
77 name: recvName,
78 typ: recv.Type(),
79 pos: 0,
80 parent: fb.fn,
81 }
82 fb.fn.Params = append(fb.fn.Params, p)
83 if recvName != "" && recvName != "_" {
84 recvObj := types.NewTCVar(fb.fn.Pkg.Pkg, recvName, recv.Type())
85 p.object = recvObj
86 alloc := fb.emitAlloc(recv.Type(), 0)
87 fb.vars[recvObj] = alloc
88 fb.paramAllocas = append(fb.paramAllocas, alloc)
89 fb.emitStore(alloc, p)
90 }
91 }
92 }
93 if params != nil {
94 for i := 0; i < params.Len(); i++ {
95 pvar := params.At(i)
96 p := &SSAParameter{
97 name: pvar.Name(),
98 typ: pvar.Type(),
99 pos: fb.fn.pos,
100 parent: fb.fn,
101 }
102 fb.fn.Params = append(fb.fn.Params, p)
103 if pvar.Name() != "" && pvar.Name() != "_" {
104 obj := types.NewTCVar(fb.fn.Pkg.Pkg, pvar.Name(), pvar.Type())
105 alloc := fb.emitAlloc(pvar.Type(), p.pos)
106 fb.vars[obj] = alloc
107 fb.paramAllocas = append(fb.paramAllocas, alloc)
108 fb.emitStore(alloc, p)
109 }
110 }
111 }
112
113 if sig.Results() != nil {
114 for i := 0; i < sig.Results().Len(); i++ {
115 r := sig.Results().At(i)
116 if r.Name() != "" && r.Name() != "_" {
117 alloc := fb.emitAlloc(r.Type(), fb.fn.pos)
118 fb.namedResults = append(fb.namedResults, alloc)
119 fb.fn.Locals = append(fb.fn.Locals, alloc)
120 fb.vars[r] = alloc
121 }
122 }
123 }
124
125 if len(fb.paramAllocas) > 0 {
126 fb.tailBlock = fb.newBlock("tailcall")
127 fb.emit(&SSAJump{Comment: "enter.body"})
128 fb.currentBlock.Succs = append(fb.currentBlock.Succs, fb.tailBlock)
129 fb.tailBlock.Preds = append(fb.tailBlock.Preds, fb.currentBlock)
130 fb.currentBlock = fb.tailBlock
131 }
132
133 if d.Body != nil {
134 fb.buildBlock(d.Body)
135 }
136
137 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
138 if len(fb.namedResults) > 0 {
139 var vals []SSAValue
140 for _, nr := range fb.namedResults {
141 elemType := nr.SSAType()
142 if p, ok := elemType.(*types.Pointer); ok {
143 elemType = p.Elem()
144 }
145 vals = append(vals, fb.emitLoad(nr, elemType))
146 }
147 fb.emitReturn(vals, 0)
148 } else {
149 fb.emitReturn(nil, 0)
150 }
151 }
152 fb.fn.NamedResults = fb.namedResults
153 }
154
155 func (fb *ssaFuncBuilder) emitAlloc(typ types.Type, pos int32) (s *SSAAlloc) {
156 a := &SSAAlloc{Heap: false}
157 a.typ = types.NewPointer(typ)
158 a.pos = pos
159 a.name = fb.nextName()
160 fb.emit(a)
161 fb.fn.Locals = append(fb.fn.Locals, a)
162 return a
163 }
164
165 func (fb *ssaFuncBuilder) emitStore(addr SSAValue, val SSAValue) {
166 fb.emit(&SSAStore{Addr: addr, Val: val})
167 }
168
169 func (fb *ssaFuncBuilder) emitLoad(addr SSAValue, typ types.Type) (s SSAValue) {
170 u := &SSAUnOp{Op: OpMul, X: addr}
171 u.typ = typ
172 u.name = fb.nextName()
173 fb.emit(u)
174 return u
175 }
176
177 func (fb *ssaFuncBuilder) emitReturn(vals []SSAValue, pos int32) {
178 fb.emit(&SSAReturn{Results: vals, pos: pos})
179 fb.currentBlock = nil
180 }
181
182 func (fb *ssaFuncBuilder) blockTerminated(b *SSABasicBlock) (ok bool) {
183 if len(b.Instrs) == 0 {
184 return false
185 }
186 switch b.Instrs[len(b.Instrs)-1].(type) {
187 case *SSAReturn, *SSAJump, *SSAIf, *SSAPanic:
188 return true
189 }
190 return false
191 }
192
193 // Statement builders.
194
195 func (fb *ssaFuncBuilder) buildBlock(b *syntax.BlockStmt) {
196 if b == nil {
197 return
198 }
199 for _, s := range b.List {
200 fb.buildStmt(s)
201 }
202 }
203
204 func (fb *ssaFuncBuilder) buildStmt(s syntax.Stmt) {
205 if s == nil {
206 return
207 }
208 if ls, ok := s.(*syntax.LabeledStmt); ok {
209 if ls.Label != nil {
210 fb.pendingLabel = ls.Label.Value
211 lblBlock := fb.labelBlock(ls.Label.Value)
212 if fb.currentBlock != nil {
213 fb.emit(&SSAJump{Comment: "goto.label." | ls.Label.Value})
214 fb.currentBlock.Succs = append(fb.currentBlock.Succs, lblBlock)
215 lblBlock.Preds = append(lblBlock.Preds, fb.currentBlock)
216 }
217 fb.currentBlock = lblBlock
218 }
219 fb.buildStmt(ls.Stmt)
220 fb.pendingLabel = ""
221 return
222 }
223 if fb.currentBlock == nil {
224 return
225 }
226 switch s := s.(type) {
227 case *syntax.EmptyStmt:
228 // nothing
229 case *syntax.ExprStmt:
230 fb.buildExpr(s.X)
231 case *syntax.AssignStmt:
232 fb.buildAssign(s)
233 case *syntax.BlockStmt:
234 fb.buildBlock(s)
235 case *syntax.DeclStmt:
236 for _, d := range s.DeclList {
237 fb.buildLocalDecl(d)
238 }
239 case *syntax.IfStmt:
240 fb.buildIf(s)
241 case *syntax.ForStmt:
242 fb.buildFor(s)
243 case *syntax.SwitchStmt:
244 fb.buildSwitch(s)
245 case *syntax.SelectStmt:
246 fb.buildSelect(s)
247 case *syntax.ReturnStmt:
248 fb.buildReturn(s)
249 case *syntax.BranchStmt:
250 fb.buildBranch(s)
251 case *syntax.SendStmt:
252 ch := fb.buildExpr(s.Chan)
253 val := fb.buildExpr(s.Value)
254 if ch != nil && val != nil {
255 fb.emit(&SSASend{Chan: ch, X: val})
256 }
257 case *syntax.CallStmt:
258 call, _ := s.Call.(*syntax.CallExpr)
259 switch s.Tok {
260 case token.Go:
261 if call != nil {
262 fb.buildGoStmt(call)
263 }
264 case token.Defer:
265 if call != nil {
266 fb.buildDeferStmt(call)
267 }
268 default:
269 fb.buildExpr(s.Call)
270 }
271 }
272 }
273
274 func (fb *ssaFuncBuilder) buildAssign(s *syntax.AssignStmt) {
275 if s.Rhs == nil {
276 lv := fb.buildExpr(s.Lhs)
277 if lv == nil {
278 return
279 }
280 one := &SSAConst{typ: lv.SSAType(), val: &types.ConstInt{V:1}}
281 op := OpAdd
282 if s.Op == token.Sub {
283 op = OpSub
284 }
285 bin := &SSABinOp{Op: op, X: lv, Y: one}
286 bin.typ = lv.SSAType()
287 bin.name = fb.nextName()
288 fb.emit(bin)
289 fb.buildStore(s.Lhs, bin)
290 return
291 }
292 if s.Op == token.Def {
293 fb.buildShortVarDecl(s)
294 return
295 }
296 if s.Op != 0 {
297 lv := fb.buildExpr(s.Lhs)
298 rv := fb.buildExpr(s.Rhs)
299 if lv == nil || rv == nil {
300 return
301 }
302 op := compoundOp(s.Op)
303 bin := &SSABinOp{Op: op, X: lv, Y: rv}
304 bin.typ = lv.SSAType()
305 bin.name = fb.nextName()
306 fb.emit(bin)
307 fb.buildStore(s.Lhs, bin)
308 return
309 }
310 lhsList, lhsIsList := s.Lhs.(*syntax.ListExpr)
311 rhsList, rhsIsList := s.Rhs.(*syntax.ListExpr)
312 if lhsIsList && rhsIsList && len(lhsList.ElemList) == len(rhsList.ElemList) {
313 vals := []SSAValue{:len(rhsList.ElemList)}
314 for i, re := range rhsList.ElemList {
315 vals[i] = fb.buildExpr(re)
316 }
317 for i, le := range lhsList.ElemList {
318 if vals[i] != nil {
319 fb.buildStore(le, vals[i])
320 }
321 }
322 return
323 }
324 if lhsIsList && len(lhsList.ElemList) == 2 {
325 if ae, ok := s.Rhs.(*syntax.AssertExpr); ok {
326 x := fb.buildExpr(ae.X)
327 assertedType := fb.resolveType(ae.Type)
328 if x != nil && assertedType != nil {
329 ta := &SSATypeAssert{X: x, AssertedType: assertedType, CommaOk: true}
330 ta.typ = types.NewTuple(
331 types.NewTCVar(nil, "val", assertedType),
332 types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
333 )
334 ta.name = fb.nextName()
335 fb.emit(ta)
336 for i, le := range lhsList.ElemList {
337 if n, ok := le.(*syntax.Name); ok && n.Value == "_" {
338 continue
339 }
340 ext := &SSAExtract{Tuple: ta, Index: i}
341 ext.typ = ssaTupleElemType(ta.typ, i)
342 ext.name = fb.nextName()
343 fb.emit(ext)
344 fb.buildStore(le, ext)
345 }
346 return
347 }
348 }
349 if ie, ok := s.Rhs.(*syntax.IndexExpr); ok {
350 x := fb.buildExpr(ie.X)
351 idx := fb.buildExpr(ie.Index)
352 if x != nil && idx != nil {
353 if mt, ok2 := types.SafeUnderlying(x.SSAType()).(*types.TCMap); ok2 {
354 idx = fb.coerceToInterface(idx, mt.Key())
355 l := &SSALookup{X: x, Index: idx, CommaOk: true}
356 l.typ = types.NewTuple(
357 types.NewTCVar(nil, "v", mt.Elem()),
358 types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
359 )
360 l.name = fb.nextName()
361 fb.emit(l)
362 for i, le := range lhsList.ElemList {
363 if n, ok := le.(*syntax.Name); ok && n.Value == "_" {
364 continue
365 }
366 ext := &SSAExtract{Tuple: l, Index: i}
367 ext.typ = ssaTupleElemType(l.typ, i)
368 ext.name = fb.nextName()
369 fb.emit(ext)
370 fb.buildStore(le, ext)
371 }
372 return
373 }
374 }
375 }
376 }
377 rhs := fb.buildExpr(s.Rhs)
378 if rhs == nil {
379 return
380 }
381 fb.buildStore(s.Lhs, rhs)
382 }
383
384 func (fb *ssaFuncBuilder) coerceToInterface(val SSAValue, targetType types.Type) (s SSAValue) {
385 if targetType == nil || val == nil || val.SSAType() == nil {
386 return val
387 }
388 if _, isIface := types.SafeUnderlying(targetType).(*types.TCInterface); isIface {
389 if _, alreadyIface := types.SafeUnderlying(val.SSAType()).(*types.TCInterface); !alreadyIface {
390 mi := &SSAMakeInterface{X: val}
391 mi.typ = targetType
392 mi.name = fb.nextName()
393 fb.emit(mi)
394 return mi
395 }
396 }
397 return val
398 }
399
400 func (fb *ssaFuncBuilder) buildStore(lhs syntax.Expr, val SSAValue) {
401 if lhs == nil {
402 panic("buildStore: nil lhs")
403 }
404 switch lhs := lhs.(type) {
405 case *syntax.Name:
406 if lhs == nil {
407 panic("buildStore: nil *syntax.Name after type switch")
408 }
409 if lhs.Value == "_" {
410 return
411 }
412 obj := fb.lookupObject(lhs.Value)
413 if obj == nil {
414 return
415 }
416 if alloc, ok := fb.vars[obj]; ok {
417 val = fb.coerceToInterface(val, obj.Type())
418 fb.emitStore(alloc, val)
419 } else if fv, ok := fb.freeVarPtrs[lhs.Value]; ok {
420 val = fb.coerceToInterface(val, obj.Type())
421 fb.emitStore(fv, val)
422 } else if g, ok := fb.fn.Pkg.Members[lhs.Value].(*SSAGlobal); ok {
423 if p, ok2 := g.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
424 val = fb.coerceToInterface(val, p.Elem())
425 }
426 fb.emitStore(g, val)
427 }
428 case *syntax.Operation:
429 if lhs.Y == nil && lhs.Op == token.Mul {
430 ptr := fb.buildExpr(lhs.X)
431 if ptr != nil {
432 if p, ok2 := ptr.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
433 val = fb.coerceToInterface(val, p.Elem())
434 }
435 fb.emitStore(ptr, val)
436 }
437 }
438 case *syntax.SelectorExpr:
439 addr := fb.buildSelectorAddr(lhs)
440 if addr == nil {
441 return
442 }
443 if p, ok2 := addr.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
444 val = fb.coerceToInterface(val, p.Elem())
445 }
446 fb.emitStore(addr, val)
447 case *syntax.IndexExpr:
448 base := fb.buildExpr(lhs.X)
449 idx := fb.buildExpr(lhs.Index)
450 if base == nil || idx == nil {
451 return
452 }
453 if m, isMap := types.SafeUnderlying(base.SSAType()).(*types.TCMap); isMap {
454 idx = fb.coerceToInterface(idx, m.Key())
455 val = fb.coerceToInterface(val, m.Elem())
456 fb.emit(&SSAMapUpdate{Map: base, Key: idx, Value: val})
457 return
458 }
459 // For array LHS assignment, resolve the address of the base directly
460 // so SSAIndexAddr GEPs into the original, not a stack copy.
461 var baseAddr SSAValue
462 if n, ok := lhs.X.(*syntax.Name); ok {
463 obj := fb.lookupObject(n.Value)
464 if obj != nil {
465 if alloc, ok2 := fb.vars[obj]; ok2 {
466 baseAddr = alloc
467 } else if g, ok2 := fb.fn.Pkg.Members[n.Value].(*SSAGlobal); ok2 {
468 baseAddr = g
469 }
470 }
471 } else if sel, ok := lhs.X.(*syntax.SelectorExpr); ok {
472 baseAddr = fb.buildSelectorAddr(sel)
473 }
474 if baseAddr != nil {
475 if _, isArr := types.SafeUnderlying(base.SSAType()).(*types.Array); isArr {
476 ia := &SSAIndexAddr{X: baseAddr, Index: idx}
477 ia.typ = types.NewPointer(ssaElemType(base.SSAType()))
478 ia.name = fb.nextName()
479 fb.emit(ia)
480 if p, ok2 := ia.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
481 val = fb.coerceToInterface(val, p.Elem())
482 }
483 fb.emitStore(ia, val)
484 return
485 }
486 }
487 ia := &SSAIndexAddr{X: base, Index: idx}
488 ia.typ = types.NewPointer(ssaElemType(base.SSAType()))
489 ia.name = fb.nextName()
490 fb.emit(ia)
491 if p, ok2 := ia.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
492 val = fb.coerceToInterface(val, p.Elem())
493 }
494 fb.emitStore(ia, val)
495 case *syntax.ListExpr:
496 for i, e := range lhs.ElemList {
497 ext := &SSAExtract{Tuple: val, Index: i}
498 ext.typ = ssaTupleElemType(val.SSAType(), i)
499 ext.name = fb.nextName()
500 fb.emit(ext)
501 fb.buildStore(e, ext)
502 }
503 }
504 }
505
506 func (fb *ssaFuncBuilder) buildShortVarDecl(s *syntax.AssignStmt) {
507 names := ssaExprNames(s.Lhs)
508 if len(names) == 2 {
509 if ae, ok := s.Rhs.(*syntax.AssertExpr); ok {
510 x := fb.buildExpr(ae.X)
511 assertedType := fb.resolveType(ae.Type)
512 if assertedType != nil {
513 if x == nil {
514 x = &SSAConst{typ: types.NewTCInterface(nil, nil)}
515 }
516 ta := &SSATypeAssert{X: x, AssertedType: assertedType, CommaOk: true}
517 ta.typ = types.NewTuple(
518 types.NewTCVar(nil, "val", assertedType),
519 types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
520 )
521 ta.name = fb.nextName()
522 fb.emit(ta)
523 for i, name := range names {
524 if name.Value == "_" {
525 continue
526 }
527 ext := &SSAExtract{Tuple: ta, Index: i}
528 ext.typ = ssaTupleElemType(ta.typ, i)
529 ext.name = fb.nextName()
530 fb.emit(ext)
531 fb.removeVar(name.Value)
532 obj := types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, ext.typ)
533 alloc := fb.emitAlloc(ext.typ, 0)
534 fb.vars[obj] = alloc
535 fb.emitStore(alloc, ext)
536 }
537 return
538 }
539 }
540 if ie, ok := s.Rhs.(*syntax.IndexExpr); ok {
541 x := fb.buildExpr(ie.X)
542 idx := fb.buildExpr(ie.Index)
543 if x != nil && idx != nil {
544 if mt, ok2 := types.SafeUnderlying(x.SSAType()).(*types.TCMap); ok2 {
545 idx = fb.coerceToInterface(idx, mt.Key())
546 l := &SSALookup{X: x, Index: idx, CommaOk: true}
547 l.typ = types.NewTuple(
548 types.NewTCVar(nil, "v", mt.Elem()),
549 types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
550 )
551 l.name = fb.nextName()
552 fb.emit(l)
553 for i, name := range names {
554 if name.Value == "_" {
555 continue
556 }
557 ext := &SSAExtract{Tuple: l, Index: i}
558 ext.typ = ssaTupleElemType(l.typ, i)
559 ext.name = fb.nextName()
560 fb.emit(ext)
561 fb.removeVar(name.Value)
562 var obj types.Object
563 if fb.info != nil {
564 obj = fb.info.Defs[name]
565 }
566 if obj == nil {
567 obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, ext.typ)
568 }
569 alloc := fb.emitAlloc(ext.typ, 0)
570 fb.vars[obj] = alloc
571 fb.emitStore(alloc, ext)
572 }
573 return
574 }
575 }
576 }
577 }
578 if rhsList, ok := s.Rhs.(*syntax.ListExpr); ok && len(rhsList.ElemList) == len(names) {
579 vals := []SSAValue{:len(rhsList.ElemList)}
580 for i, re := range rhsList.ElemList {
581 vals[i] = fb.buildExpr(re)
582 }
583 for i, name := range names {
584 if name.Value == "_" {
585 continue
586 }
587 var typ types.Type
588 if vals[i] != nil {
589 typ = vals[i].SSAType()
590 }
591 fb.removeVar(name.Value)
592 var obj types.Object
593 if fb.info != nil {
594 obj = fb.info.Defs[name]
595 }
596 if obj == nil {
597 obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, typ)
598 }
599 alloc := fb.emitAlloc(typ, 0)
600 fb.vars[obj] = alloc
601 if vals[i] != nil {
602 fb.emitStore(alloc, vals[i])
603 }
604 }
605 return
606 }
607
608 rhs := fb.buildExpr(s.Rhs)
609
610 isTuple := false
611 var tup *types.Tuple
612 if rhs != nil {
613 tup, isTuple = rhs.SSAType().(*types.Tuple)
614 }
615 for i, name := range names {
616 if name.Value == "_" {
617 continue
618 }
619 var typ types.Type
620 if rhs != nil {
621 if isTuple && i < tup.Len() {
622 typ = tup.At(i).Type()
623 } else if i == 0 {
624 typ = rhs.SSAType()
625 }
626 }
627 if typ == nil {
628 rhsInfo := "nil"
629 if rhs != nil {
630 if rhs.SSAType() == nil {
631 rhsInfo = "ssatype=nil"
632 } else if tup2, ok2 := rhs.SSAType().(*types.Tuple); ok2 {
633 rhsInfo = "tuple[" | simpleItoa(tup2.Len()) | "] i=" | simpleItoa(i)
634 if i < tup2.Len() {
635 if tup2.At(i).Type() == nil {
636 rhsInfo = rhsInfo | " At.Type=nil"
637 } else {
638 rhsInfo = rhsInfo | " At.Type=set"
639 }
640 }
641 } else {
642 rhsInfo = "non-tuple"
643 }
644 }
645 typ = types.Typ[types.Int32]
646 }
647 fb.removeVar(name.Value)
648 var obj types.Object
649 if fb.info != nil {
650 obj = fb.info.Defs[name]
651 }
652 if obj == nil {
653 obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, typ)
654 }
655 alloc := fb.emitAlloc(typ, 0)
656 fb.vars[obj] = alloc
657 var initVal SSAValue
658 if rhs != nil {
659 if isTuple {
660 ext := &SSAExtract{Tuple: rhs, Index: i}
661 ext.typ = typ
662 ext.name = fb.nextName()
663 fb.emit(ext)
664 initVal = ext
665 } else if i == 0 {
666 initVal = rhs
667 }
668 }
669 if initVal != nil {
670 fb.emitStore(alloc, initVal)
671 }
672 }
673 }
674
675 func (fb *ssaFuncBuilder) buildLocalDecl(d syntax.Decl) {
676 switch d := d.(type) {
677 case *syntax.VarDecl:
678 var typ types.Type
679 if fb.info != nil && len(d.NameList) > 0 {
680 if obj := fb.info.Defs[d.NameList[0]]; obj != nil {
681 typ = obj.Type()
682 }
683 }
684 if typ == nil && d.Type != nil {
685 typ = fb.resolveType(d.Type)
686 }
687 initVal := fb.buildExpr(d.Values)
688 for _, name := range d.NameList {
689 if name.Value == "_" {
690 continue
691 }
692 fb.removeVar(name.Value)
693 var obj types.Object
694 if fb.info != nil {
695 obj = fb.info.Defs[name]
696 }
697 if obj == nil {
698 obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, typ)
699 }
700 alloc := fb.emitAlloc(obj.Type(), 0)
701 fb.vars[obj] = alloc
702 fb.fn.Locals = append(fb.fn.Locals, alloc)
703 if initVal != nil {
704 initVal = fb.coerceToInterface(initVal, obj.Type())
705 fb.emitStore(alloc, initVal)
706 } else {
707 fb.emitStore(alloc, &SSAConst{typ: obj.Type(), val: nil})
708 }
709 }
710 case *syntax.ConstDecl:
711 if d.Values != nil && len(d.NameList) > 0 {
712 if fb.localConsts == nil {
713 fb.localConsts = map[string]*SSAConst{}
714 }
715 if list, ok := d.Values.(*syntax.ListExpr); ok {
716 for i, name := range d.NameList {
717 if i < len(list.ElemList) {
718 if v := fb.buildExpr(list.ElemList[i]); v != nil {
719 if c := ssaExtractConst(v); c != nil {
720 fb.localConsts[name.Value] = c
721 }
722 }
723 }
724 }
725 } else if len(d.NameList) == 1 {
726 if v := fb.buildExpr(d.Values); v != nil {
727 if c := ssaExtractConst(v); c != nil {
728 fb.localConsts[d.NameList[0].Value] = c
729 }
730 }
731 }
732 }
733 case *types.TypeDecl:
734 if d.Name != nil {
735 resolved := fb.resolveTypeAST(d.Type)
736 if resolved != nil {
737 fb.localTypes[d.Name.Value] = resolved
738 }
739 }
740 }
741 }
742
743 func (fb *ssaFuncBuilder) evalLocalConstExpr(e syntax.Expr) (c types.ConstVal) {
744 if e == nil {
745 return nil
746 }
747 switch e := e.(type) {
748 case *syntax.BasicLit:
749 return evalBasicLitLocal(e)
750 case *syntax.Name:
751 if c, ok := fb.localConsts[e.Value]; ok && c != nil && c.val != nil {
752 return c.val
753 }
754 return tcEvalConstExpr(e, fb.fn.Pkg.Pkg.Scope(), -1)
755 case *syntax.Operation:
756 if e.Y == nil {
757 xr := fb.evalLocalConstExpr(e.X)
758 if xr == nil {
759 return nil
760 }
761 return evalUnaryLocal(e.Op, xr)
762 }
763 xr := fb.evalLocalConstExpr(e.X)
764 yr := fb.evalLocalConstExpr(e.Y)
765 if xr == nil || yr == nil {
766 return nil
767 }
768 return evalBinaryLocal(e.Op, xr, yr)
769 case *syntax.ParenExpr:
770 return fb.evalLocalConstExpr(e.X)
771 }
772 return nil
773 }
774