github.com/axw/llgo@v0.0.0-20160805011314-95b5fe4dca20/ssaopt/esc.go (about) 1 // Copyright 2014 The llgo Authors. 2 // Use of this source code is governed by an MIT-style 3 // license that can be found in the LICENSE file. 4 5 package ssaopt 6 7 import ( 8 "go/token" 9 10 "llvm.org/llgo/third_party/gotools/go/ssa" 11 ) 12 13 func escapes(val ssa.Value, bb *ssa.BasicBlock, pending []ssa.Value) bool { 14 for _, p := range pending { 15 if val == p { 16 return false 17 } 18 } 19 20 for _, ref := range *val.Referrers() { 21 switch ref := ref.(type) { 22 case *ssa.Phi: 23 // We must consider the variable to have escaped if it is 24 // possible for the program to see more than one "version" 25 // of the variable at once, as this requires the program 26 // to use heap allocation for the multiple versions. 27 // 28 // I (pcc) think that this is only possible (without stores) 29 // in the case where a phi node that (directly or indirectly) 30 // refers to the allocation dominates the allocation. 31 if ref.Block().Dominates(bb) { 32 return true 33 } 34 if escapes(ref, bb, append(pending, val)) { 35 return true 36 } 37 38 case *ssa.BinOp, *ssa.ChangeType, *ssa.Convert, *ssa.ChangeInterface, *ssa.MakeInterface, *ssa.Slice, *ssa.FieldAddr, *ssa.IndexAddr, *ssa.TypeAssert, *ssa.Extract: 39 if escapes(ref.(ssa.Value), bb, append(pending, val)) { 40 return true 41 } 42 43 case *ssa.Range, *ssa.DebugRef: 44 continue 45 46 case *ssa.UnOp: 47 if ref.Op == token.MUL || ref.Op == token.ARROW { 48 continue 49 } 50 if escapes(ref, bb, append(pending, val)) { 51 return true 52 } 53 54 case *ssa.Store: 55 if val == ref.Val { 56 return true 57 } 58 59 case *ssa.Call: 60 if builtin, ok := ref.Call.Value.(*ssa.Builtin); ok { 61 switch builtin.Name() { 62 case "cap", "len", "copy", "ssa:wrapnilchk": 63 continue 64 case "append": 65 if ref.Call.Args[0] == val && escapes(ref, bb, append(pending, val)) { 66 return true 67 } 68 default: 69 return true 70 } 71 } else { 72 return true 73 } 74 75 default: 76 return true 77 } 78 } 79 80 return false 81 } 82 83 func LowerAllocsToStack(f *ssa.Function) { 84 pending := make([]ssa.Value, 0, 10) 85 86 for _, b := range f.Blocks { 87 for _, instr := range b.Instrs { 88 if alloc, ok := instr.(*ssa.Alloc); ok && alloc.Heap && !escapes(alloc, alloc.Block(), pending) { 89 alloc.Heap = false 90 f.Locals = append(f.Locals, alloc) 91 } 92 } 93 } 94 }