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  }