// Copyright 2014 The llgo Authors. // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package ssaopt import ( "go/token" "llvm.org/llgo/third_party/gotools/go/ssa" ) func escapes(val ssa.Value, bb *ssa.BasicBlock, pending []ssa.Value) bool { for _, p := range pending { if val == p { return false } } for _, ref := range *val.Referrers() { switch ref := ref.(type) { case *ssa.Phi: // We must consider the variable to have escaped if it is // possible for the program to see more than one "version" // of the variable at once, as this requires the program // to use heap allocation for the multiple versions. // // I (pcc) think that this is only possible (without stores) // in the case where a phi node that (directly or indirectly) // refers to the allocation dominates the allocation. if ref.Block().Dominates(bb) { return true } if escapes(ref, bb, append(pending, val)) { return true } case *ssa.BinOp, *ssa.ChangeType, *ssa.Convert, *ssa.ChangeInterface, *ssa.MakeInterface, *ssa.Slice, *ssa.FieldAddr, *ssa.IndexAddr, *ssa.TypeAssert, *ssa.Extract: if escapes(ref.(ssa.Value), bb, append(pending, val)) { return true } case *ssa.Range, *ssa.DebugRef: continue case *ssa.UnOp: if ref.Op == token.MUL || ref.Op == token.ARROW { continue } if escapes(ref, bb, append(pending, val)) { return true } case *ssa.Store: if val == ref.Val { return true } case *ssa.Call: if builtin, ok := ref.Call.Value.(*ssa.Builtin); ok { switch builtin.Name() { case "cap", "len", "copy", "ssa:wrapnilchk": continue case "append": if ref.Call.Args[0] == val && escapes(ref, bb, append(pending, val)) { return true } default: return true } } else { return true } default: return true } } return false } func LowerAllocsToStack(f *ssa.Function) { pending := make([]ssa.Value, 0, 10) for _, b := range f.Blocks { for _, instr := range b.Instrs { if alloc, ok := instr.(*ssa.Alloc); ok && alloc.Heap && !escapes(alloc, alloc.Block(), pending) { alloc.Heap = false f.Locals = append(f.Locals, alloc) } } } }