github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gotools/go/pointer/api.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pointer 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/token" 11 "io" 12 13 "llvm.org/llgo/third_party/gotools/container/intsets" 14 "llvm.org/llgo/third_party/gotools/go/callgraph" 15 "llvm.org/llgo/third_party/gotools/go/ssa" 16 "llvm.org/llgo/third_party/gotools/go/types/typeutil" 17 ) 18 19 // A Config formulates a pointer analysis problem for Analyze(). 20 type Config struct { 21 // Mains contains the set of 'main' packages to analyze 22 // Clients must provide the analysis with at least one 23 // package defining a main() function. 24 // 25 // Non-main packages in the ssa.Program that are not 26 // dependencies of any main package may still affect the 27 // analysis result, because they contribute runtime types and 28 // thus methods. 29 // TODO(adonovan): investigate whether this is desirable. 30 Mains []*ssa.Package 31 32 // Reflection determines whether to handle reflection 33 // operators soundly, which is currently rather slow since it 34 // causes constraint to be generated during solving 35 // proportional to the number of constraint variables, which 36 // has not yet been reduced by presolver optimisation. 37 Reflection bool 38 39 // BuildCallGraph determines whether to construct a callgraph. 40 // If enabled, the graph will be available in Result.CallGraph. 41 BuildCallGraph bool 42 43 // The client populates Queries[v] or IndirectQueries[v] 44 // for each ssa.Value v of interest, to request that the 45 // points-to sets pts(v) or pts(*v) be computed. If the 46 // client needs both points-to sets, v may appear in both 47 // maps. 48 // 49 // (IndirectQueries is typically used for Values corresponding 50 // to source-level lvalues, e.g. an *ssa.Global.) 51 // 52 // The analysis populates the corresponding 53 // Result.{Indirect,}Queries map when it creates the pointer 54 // variable for v or *v. Upon completion the client can 55 // inspect that map for the results. 56 // 57 // TODO(adonovan): this API doesn't scale well for batch tools 58 // that want to dump the entire solution. Perhaps optionally 59 // populate a map[*ssa.DebugRef]Pointer in the Result, one 60 // entry per source expression. 61 // 62 Queries map[ssa.Value]struct{} 63 IndirectQueries map[ssa.Value]struct{} 64 65 // If Log is non-nil, log messages are written to it. 66 // Logging is extremely verbose. 67 Log io.Writer 68 } 69 70 type track uint32 71 72 const ( 73 trackChan track = 1 << iota // track 'chan' references 74 trackMap // track 'map' references 75 trackPtr // track regular pointers 76 trackSlice // track slice references 77 78 trackAll = ^track(0) 79 ) 80 81 // AddQuery adds v to Config.Queries. 82 // Precondition: CanPoint(v.Type()). 83 // TODO(adonovan): consider returning a new Pointer for this query, 84 // which will be initialized during analysis. That avoids the needs 85 // for the corresponding ssa.Value-keyed maps in Config and Result. 86 func (c *Config) AddQuery(v ssa.Value) { 87 if !CanPoint(v.Type()) { 88 panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type())) 89 } 90 if c.Queries == nil { 91 c.Queries = make(map[ssa.Value]struct{}) 92 } 93 c.Queries[v] = struct{}{} 94 } 95 96 // AddQuery adds v to Config.IndirectQueries. 97 // Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()). 98 func (c *Config) AddIndirectQuery(v ssa.Value) { 99 if c.IndirectQueries == nil { 100 c.IndirectQueries = make(map[ssa.Value]struct{}) 101 } 102 if !CanPoint(mustDeref(v.Type())) { 103 panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type())) 104 } 105 c.IndirectQueries[v] = struct{}{} 106 } 107 108 func (c *Config) prog() *ssa.Program { 109 for _, main := range c.Mains { 110 return main.Prog 111 } 112 panic("empty scope") 113 } 114 115 type Warning struct { 116 Pos token.Pos 117 Message string 118 } 119 120 // A Result contains the results of a pointer analysis. 121 // 122 // See Config for how to request the various Result components. 123 // 124 type Result struct { 125 CallGraph *callgraph.Graph // discovered call graph 126 Queries map[ssa.Value]Pointer // pts(v) for each v in Config.Queries. 127 IndirectQueries map[ssa.Value]Pointer // pts(*v) for each v in Config.IndirectQueries. 128 Warnings []Warning // warnings of unsoundness 129 } 130 131 // A Pointer is an equivalence class of pointer-like values. 132 // 133 // A Pointer doesn't have a unique type because pointers of distinct 134 // types may alias the same object. 135 // 136 type Pointer struct { 137 a *analysis 138 n nodeid 139 } 140 141 // A PointsToSet is a set of labels (locations or allocations). 142 type PointsToSet struct { 143 a *analysis // may be nil if pts is nil 144 pts *nodeset 145 } 146 147 func (s PointsToSet) String() string { 148 var buf bytes.Buffer 149 buf.WriteByte('[') 150 if s.pts != nil { 151 var space [50]int 152 for i, l := range s.pts.AppendTo(space[:0]) { 153 if i > 0 { 154 buf.WriteString(", ") 155 } 156 buf.WriteString(s.a.labelFor(nodeid(l)).String()) 157 } 158 } 159 buf.WriteByte(']') 160 return buf.String() 161 } 162 163 // PointsTo returns the set of labels that this points-to set 164 // contains. 165 func (s PointsToSet) Labels() []*Label { 166 var labels []*Label 167 if s.pts != nil { 168 var space [50]int 169 for _, l := range s.pts.AppendTo(space[:0]) { 170 labels = append(labels, s.a.labelFor(nodeid(l))) 171 } 172 } 173 return labels 174 } 175 176 // If this PointsToSet came from a Pointer of interface kind 177 // or a reflect.Value, DynamicTypes returns the set of dynamic 178 // types that it may contain. (For an interface, they will 179 // always be concrete types.) 180 // 181 // The result is a mapping whose keys are the dynamic types to which 182 // it may point. For each pointer-like key type, the corresponding 183 // map value is the PointsToSet for pointers of that type. 184 // 185 // The result is empty unless CanHaveDynamicTypes(T). 186 // 187 func (s PointsToSet) DynamicTypes() *typeutil.Map { 188 var tmap typeutil.Map 189 tmap.SetHasher(s.a.hasher) 190 if s.pts != nil { 191 var space [50]int 192 for _, x := range s.pts.AppendTo(space[:0]) { 193 ifaceObjId := nodeid(x) 194 if !s.a.isTaggedObject(ifaceObjId) { 195 continue // !CanHaveDynamicTypes(tDyn) 196 } 197 tDyn, v, indirect := s.a.taggedValue(ifaceObjId) 198 if indirect { 199 panic("indirect tagged object") // implement later 200 } 201 pts, ok := tmap.At(tDyn).(PointsToSet) 202 if !ok { 203 pts = PointsToSet{s.a, new(nodeset)} 204 tmap.Set(tDyn, pts) 205 } 206 pts.pts.addAll(&s.a.nodes[v].solve.pts) 207 } 208 } 209 return &tmap 210 } 211 212 // Intersects reports whether this points-to set and the 213 // argument points-to set contain common members. 214 func (x PointsToSet) Intersects(y PointsToSet) bool { 215 if x.pts == nil || y.pts == nil { 216 return false 217 } 218 // This takes Θ(|x|+|y|) time. 219 var z intsets.Sparse 220 z.Intersection(&x.pts.Sparse, &y.pts.Sparse) 221 return !z.IsEmpty() 222 } 223 224 func (p Pointer) String() string { 225 return fmt.Sprintf("n%d", p.n) 226 } 227 228 // PointsTo returns the points-to set of this pointer. 229 func (p Pointer) PointsTo() PointsToSet { 230 if p.n == 0 { 231 return PointsToSet{} 232 } 233 return PointsToSet{p.a, &p.a.nodes[p.n].solve.pts} 234 } 235 236 // MayAlias reports whether the receiver pointer may alias 237 // the argument pointer. 238 func (p Pointer) MayAlias(q Pointer) bool { 239 return p.PointsTo().Intersects(q.PointsTo()) 240 } 241 242 // DynamicTypes returns p.PointsTo().DynamicTypes(). 243 func (p Pointer) DynamicTypes() *typeutil.Map { 244 return p.PointsTo().DynamicTypes() 245 }