Alan Donovan | 713699d | 2013-08-27 18:49:13 -0400 | [diff] [blame] | 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 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 5 | package pointer |
| 6 | |
| 7 | // This file defines the constraint generation phase. |
| 8 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 9 | // TODO(adonovan): move the constraint definitions and the store() etc |
| 10 | // functions which add them (and are also used by the solver) into a |
| 11 | // new file, constraints.go. |
| 12 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 13 | import ( |
| 14 | "fmt" |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 15 | "go/token" |
Alan Donovan | 542ffc7 | 2015-12-29 13:06:30 -0500 | [diff] [blame] | 16 | "go/types" |
Tim King | 36a5c6a | 2022-08-25 11:09:24 -0400 | [diff] [blame] | 17 | "strings" |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 18 | |
Andrew Gerrand | 5ebbcd1 | 2014-11-10 08:50:40 +1100 | [diff] [blame] | 19 | "golang.org/x/tools/go/callgraph" |
| 20 | "golang.org/x/tools/go/ssa" |
Tim King | 36a5c6a | 2022-08-25 11:09:24 -0400 | [diff] [blame] | 21 | "golang.org/x/tools/internal/typeparams" |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 22 | ) |
| 23 | |
| 24 | var ( |
Rebecca Stambler | 207d3de | 2019-11-20 22:43:00 -0500 | [diff] [blame] | 25 | tEface = types.NewInterfaceType(nil, nil).Complete() |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 26 | tInvalid = types.Typ[types.Invalid] |
| 27 | tUnsafePtr = types.Typ[types.UnsafePointer] |
| 28 | ) |
| 29 | |
| 30 | // ---------- Node creation ---------- |
| 31 | |
| 32 | // nextNode returns the index of the next unused node. |
| 33 | func (a *analysis) nextNode() nodeid { |
| 34 | return nodeid(len(a.nodes)) |
| 35 | } |
| 36 | |
| 37 | // addNodes creates nodes for all scalar elements in type typ, and |
| 38 | // returns the id of the first one, or zero if the type was |
| 39 | // analytically uninteresting. |
| 40 | // |
| 41 | // comment explains the origin of the nodes, as a debugging aid. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 42 | func (a *analysis) addNodes(typ types.Type, comment string) nodeid { |
| 43 | id := a.nextNode() |
| 44 | for _, fi := range a.flatten(typ) { |
| 45 | a.addOneNode(fi.typ, comment, fi) |
| 46 | } |
| 47 | if id == a.nextNode() { |
| 48 | return 0 // type contained no pointers |
| 49 | } |
| 50 | return id |
| 51 | } |
| 52 | |
| 53 | // addOneNode creates a single node with type typ, and returns its id. |
| 54 | // |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 55 | // typ should generally be scalar (except for tagged.T nodes |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 56 | // and struct/array identity nodes). Use addNodes for non-scalar types. |
| 57 | // |
| 58 | // comment explains the origin of the nodes, as a debugging aid. |
| 59 | // subelement indicates the subelement, e.g. ".a.b[*].c". |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 60 | func (a *analysis) addOneNode(typ types.Type, comment string, subelement *fieldInfo) nodeid { |
| 61 | id := a.nextNode() |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 62 | a.nodes = append(a.nodes, &node{typ: typ, subelement: subelement, solve: new(solverState)}) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 63 | if a.log != nil { |
| 64 | fmt.Fprintf(a.log, "\tcreate n%d %s for %s%s\n", |
| 65 | id, typ, comment, subelement.path()) |
| 66 | } |
| 67 | return id |
| 68 | } |
| 69 | |
| 70 | // setValueNode associates node id with the value v. |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 71 | // cgn identifies the context iff v is a local variable. |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 72 | func (a *analysis) setValueNode(v ssa.Value, id nodeid, cgn *cgnode) { |
| 73 | if cgn != nil { |
| 74 | a.localval[v] = id |
| 75 | } else { |
| 76 | a.globalval[v] = id |
| 77 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 78 | if a.log != nil { |
| 79 | fmt.Fprintf(a.log, "\tval[%s] = n%d (%T)\n", v.Name(), id, v) |
| 80 | } |
| 81 | |
Alan Donovan | 28104d2 | 2014-02-20 11:35:09 -0500 | [diff] [blame] | 82 | // Due to context-sensitivity, we may encounter the same Value |
| 83 | // in many contexts. We merge them to a canonical node, since |
| 84 | // that's what all clients want. |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 85 | |
Alan Donovan | 7f8168b | 2013-12-05 22:30:42 -0500 | [diff] [blame] | 86 | // Record the (v, id) relation if the client has queried pts(v). |
| 87 | if _, ok := a.config.Queries[v]; ok { |
Alan Donovan | 28104d2 | 2014-02-20 11:35:09 -0500 | [diff] [blame] | 88 | t := v.Type() |
| 89 | ptr, ok := a.result.Queries[v] |
| 90 | if !ok { |
| 91 | // First time? Create the canonical query node. |
| 92 | ptr = Pointer{a, a.addNodes(t, "query")} |
| 93 | a.result.Queries[v] = ptr |
| 94 | } |
| 95 | a.result.Queries[v] = ptr |
| 96 | a.copy(ptr.n, id, a.sizeof(t)) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 97 | } |
Alan Donovan | 7f8168b | 2013-12-05 22:30:42 -0500 | [diff] [blame] | 98 | |
| 99 | // Record the (*v, id) relation if the client has queried pts(*v). |
| 100 | if _, ok := a.config.IndirectQueries[v]; ok { |
Alan Donovan | 28104d2 | 2014-02-20 11:35:09 -0500 | [diff] [blame] | 101 | t := v.Type() |
| 102 | ptr, ok := a.result.IndirectQueries[v] |
| 103 | if !ok { |
| 104 | // First time? Create the canonical indirect query node. |
| 105 | ptr = Pointer{a, a.addNodes(v.Type(), "query.indirect")} |
| 106 | a.result.IndirectQueries[v] = ptr |
| 107 | } |
| 108 | a.genLoad(cgn, ptr.n, v, 0, a.sizeof(t)) |
Alan Donovan | 7f8168b | 2013-12-05 22:30:42 -0500 | [diff] [blame] | 109 | } |
Dominik Honnef | a99f4ec | 2017-03-01 20:43:03 +0100 | [diff] [blame] | 110 | |
| 111 | for _, query := range a.config.extendedQueries[v] { |
| 112 | t, nid := a.evalExtendedQuery(v.Type().Underlying(), id, query.ops) |
| 113 | |
| 114 | if query.ptr.a == nil { |
| 115 | query.ptr.a = a |
| 116 | query.ptr.n = a.addNodes(t, "query.extended") |
| 117 | } |
| 118 | a.copy(query.ptr.n, nid, a.sizeof(t)) |
| 119 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | // endObject marks the end of a sequence of calls to addNodes denoting |
| 123 | // a single object allocation. |
| 124 | // |
| 125 | // obj is the start node of the object, from a prior call to nextNode. |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 126 | // Its size, flags and optional data will be updated. |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 127 | func (a *analysis) endObject(obj nodeid, cgn *cgnode, data interface{}) *object { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 128 | // Ensure object is non-empty by padding; |
| 129 | // the pad will be the object node. |
| 130 | size := uint32(a.nextNode() - obj) |
| 131 | if size == 0 { |
| 132 | a.addOneNode(tInvalid, "padding", nil) |
| 133 | } |
| 134 | objNode := a.nodes[obj] |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 135 | o := &object{ |
| 136 | size: size, // excludes padding |
| 137 | cgn: cgn, |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 138 | data: data, |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 139 | } |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 140 | objNode.obj = o |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 141 | |
| 142 | return o |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 143 | } |
| 144 | |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 145 | // makeFunctionObject creates and returns a new function object |
| 146 | // (contour) for fn, and returns the id of its first node. It also |
| 147 | // enqueues fn for subsequent constraint generation. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 148 | // |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 149 | // For a context-sensitive contour, callersite identifies the sole |
| 150 | // callsite; for shared contours, caller is nil. |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 151 | func (a *analysis) makeFunctionObject(fn *ssa.Function, callersite *callsite) nodeid { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 152 | if a.log != nil { |
| 153 | fmt.Fprintf(a.log, "\t---- makeFunctionObject %s\n", fn) |
| 154 | } |
| 155 | |
| 156 | // obj is the function object (identity, params, results). |
| 157 | obj := a.nextNode() |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 158 | cgn := a.makeCGNode(fn, obj, callersite) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 159 | sig := fn.Signature |
| 160 | a.addOneNode(sig, "func.cgnode", nil) // (scalar with Signature type) |
| 161 | if recv := sig.Recv(); recv != nil { |
| 162 | a.addNodes(recv.Type(), "func.recv") |
| 163 | } |
| 164 | a.addNodes(sig.Params(), "func.params") |
| 165 | a.addNodes(sig.Results(), "func.results") |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 166 | a.endObject(obj, cgn, fn).flags |= otFunction |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 167 | |
| 168 | if a.log != nil { |
| 169 | fmt.Fprintf(a.log, "\t----\n") |
| 170 | } |
| 171 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 172 | // Queue it up for constraint processing. |
| 173 | a.genq = append(a.genq, cgn) |
| 174 | |
| 175 | return obj |
| 176 | } |
| 177 | |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 178 | // makeTagged creates a tagged object of type typ. |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 179 | func (a *analysis) makeTagged(typ types.Type, cgn *cgnode, data interface{}) nodeid { |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 180 | obj := a.addOneNode(typ, "tagged.T", nil) // NB: type may be non-scalar! |
| 181 | a.addNodes(typ, "tagged.v") |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 182 | a.endObject(obj, cgn, data).flags |= otTagged |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 183 | return obj |
| 184 | } |
| 185 | |
| 186 | // makeRtype returns the canonical tagged object of type *rtype whose |
| 187 | // payload points to the sole rtype object for T. |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 188 | // |
| 189 | // TODO(adonovan): move to reflect.go; it's part of the solver really. |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 190 | func (a *analysis) makeRtype(T types.Type) nodeid { |
| 191 | if v := a.rtypes.At(T); v != nil { |
| 192 | return v.(nodeid) |
| 193 | } |
| 194 | |
| 195 | // Create the object for the reflect.rtype itself, which is |
| 196 | // ordinarily a large struct but here a single node will do. |
| 197 | obj := a.nextNode() |
| 198 | a.addOneNode(T, "reflect.rtype", nil) |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 199 | a.endObject(obj, nil, T) |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 200 | |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 201 | id := a.makeTagged(a.reflectRtypePtr, nil, T) |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 202 | a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton) |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 203 | a.addressOf(a.reflectRtypePtr, id+1, obj) |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 204 | |
| 205 | a.rtypes.Set(T, id) |
| 206 | return id |
| 207 | } |
| 208 | |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 209 | // rtypeValue returns the type of the *reflect.rtype-tagged object obj. |
| 210 | func (a *analysis) rtypeTaggedValue(obj nodeid) types.Type { |
| 211 | tDyn, t, _ := a.taggedValue(obj) |
| 212 | if tDyn != a.reflectRtypePtr { |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 213 | panic(fmt.Sprintf("not a *reflect.rtype-tagged object: obj=n%d tag=%v payload=n%d", obj, tDyn, t)) |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 214 | } |
| 215 | return a.nodes[t].typ |
| 216 | } |
| 217 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 218 | // valueNode returns the id of the value node for v, creating it (and |
| 219 | // the association) as needed. It may return zero for uninteresting |
| 220 | // values containing no pointers. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 221 | func (a *analysis) valueNode(v ssa.Value) nodeid { |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 222 | // Value nodes for locals are created en masse by genFunc. |
| 223 | if id, ok := a.localval[v]; ok { |
| 224 | return id |
| 225 | } |
| 226 | |
| 227 | // Value nodes for globals are created on demand. |
| 228 | id, ok := a.globalval[v] |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 229 | if !ok { |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 230 | var comment string |
| 231 | if a.log != nil { |
| 232 | comment = v.String() |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 233 | } |
Alan Donovan | 5c5c4f4 | 2014-06-19 15:30:51 -0400 | [diff] [blame] | 234 | id = a.addNodes(v.Type(), comment) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 235 | if obj := a.objectNode(nil, v); obj != 0 { |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 236 | a.addressOf(v.Type(), id, obj) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 237 | } |
| 238 | a.setValueNode(v, id, nil) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 239 | } |
| 240 | return id |
| 241 | } |
| 242 | |
| 243 | // valueOffsetNode ascertains the node for tuple/struct value v, |
| 244 | // then returns the node for its subfield #index. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 245 | func (a *analysis) valueOffsetNode(v ssa.Value, index int) nodeid { |
| 246 | id := a.valueNode(v) |
| 247 | if id == 0 { |
| 248 | panic(fmt.Sprintf("cannot offset within n0: %s = %s", v.Name(), v)) |
| 249 | } |
| 250 | return id + nodeid(a.offsetOf(v.Type(), index)) |
| 251 | } |
| 252 | |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 253 | // isTaggedObject reports whether object obj is a tagged object. |
| 254 | func (a *analysis) isTaggedObject(obj nodeid) bool { |
| 255 | return a.nodes[obj].obj.flags&otTagged != 0 |
| 256 | } |
| 257 | |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 258 | // taggedValue returns the dynamic type tag, the (first node of the) |
| 259 | // payload, and the indirect flag of the tagged object starting at id. |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 260 | // Panic ensues if !isTaggedObject(id). |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 261 | func (a *analysis) taggedValue(obj nodeid) (tDyn types.Type, v nodeid, indirect bool) { |
| 262 | n := a.nodes[obj] |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 263 | flags := n.obj.flags |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 264 | if flags&otTagged == 0 { |
| 265 | panic(fmt.Sprintf("not a tagged object: n%d", obj)) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 266 | } |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 267 | return n.typ, obj + 1, flags&otIndirect != 0 |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 268 | } |
| 269 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 270 | // funcParams returns the first node of the params (P) block of the |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 271 | // function whose object node (obj.flags&otFunction) is id. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 272 | func (a *analysis) funcParams(id nodeid) nodeid { |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 273 | n := a.nodes[id] |
| 274 | if n.obj == nil || n.obj.flags&otFunction == 0 { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 275 | panic(fmt.Sprintf("funcParams(n%d): not a function object block", id)) |
| 276 | } |
| 277 | return id + 1 |
| 278 | } |
| 279 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 280 | // funcResults returns the first node of the results (R) block of the |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 281 | // function whose object node (obj.flags&otFunction) is id. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 282 | func (a *analysis) funcResults(id nodeid) nodeid { |
| 283 | n := a.nodes[id] |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 284 | if n.obj == nil || n.obj.flags&otFunction == 0 { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 285 | panic(fmt.Sprintf("funcResults(n%d): not a function object block", id)) |
| 286 | } |
| 287 | sig := n.typ.(*types.Signature) |
| 288 | id += 1 + nodeid(a.sizeof(sig.Params())) |
| 289 | if sig.Recv() != nil { |
| 290 | id += nodeid(a.sizeof(sig.Recv().Type())) |
| 291 | } |
| 292 | return id |
| 293 | } |
| 294 | |
| 295 | // ---------- Constraint creation ---------- |
| 296 | |
| 297 | // copy creates a constraint of the form dst = src. |
| 298 | // sizeof is the width (in logical fields) of the copied type. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 299 | func (a *analysis) copy(dst, src nodeid, sizeof uint32) { |
| 300 | if src == dst || sizeof == 0 { |
| 301 | return // trivial |
| 302 | } |
| 303 | if src == 0 || dst == 0 { |
| 304 | panic(fmt.Sprintf("ill-typed copy dst=n%d src=n%d", dst, src)) |
| 305 | } |
| 306 | for i := uint32(0); i < sizeof; i++ { |
| 307 | a.addConstraint(©Constraint{dst, src}) |
| 308 | src++ |
| 309 | dst++ |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | // addressOf creates a constraint of the form id = &obj. |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 314 | // T is the type of the address. |
| 315 | func (a *analysis) addressOf(T types.Type, id, obj nodeid) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 316 | if id == 0 { |
| 317 | panic("addressOf: zero id") |
| 318 | } |
| 319 | if obj == 0 { |
| 320 | panic("addressOf: zero obj") |
| 321 | } |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 322 | if a.shouldTrack(T) { |
| 323 | a.addConstraint(&addrConstraint{id, obj}) |
| 324 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 325 | } |
| 326 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 327 | // load creates a load constraint of the form dst = src[offset]. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 328 | // offset is the pointer offset in logical fields. |
| 329 | // sizeof is the width (in logical fields) of the loaded type. |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 330 | func (a *analysis) load(dst, src nodeid, offset, sizeof uint32) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 331 | if dst == 0 { |
| 332 | return // load of non-pointerlike value |
| 333 | } |
| 334 | if src == 0 && dst == 0 { |
| 335 | return // non-pointerlike operation |
| 336 | } |
| 337 | if src == 0 || dst == 0 { |
| 338 | panic(fmt.Sprintf("ill-typed load dst=n%d src=n%d", dst, src)) |
| 339 | } |
| 340 | for i := uint32(0); i < sizeof; i++ { |
| 341 | a.addConstraint(&loadConstraint{offset, dst, src}) |
| 342 | offset++ |
| 343 | dst++ |
| 344 | } |
| 345 | } |
| 346 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 347 | // store creates a store constraint of the form dst[offset] = src. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 348 | // offset is the pointer offset in logical fields. |
| 349 | // sizeof is the width (in logical fields) of the stored type. |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 350 | func (a *analysis) store(dst, src nodeid, offset uint32, sizeof uint32) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 351 | if src == 0 { |
| 352 | return // store of non-pointerlike value |
| 353 | } |
| 354 | if src == 0 && dst == 0 { |
| 355 | return // non-pointerlike operation |
| 356 | } |
| 357 | if src == 0 || dst == 0 { |
| 358 | panic(fmt.Sprintf("ill-typed store dst=n%d src=n%d", dst, src)) |
| 359 | } |
| 360 | for i := uint32(0); i < sizeof; i++ { |
| 361 | a.addConstraint(&storeConstraint{offset, dst, src}) |
| 362 | offset++ |
| 363 | src++ |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | // offsetAddr creates an offsetAddr constraint of the form dst = &src.#offset. |
| 368 | // offset is the field offset in logical fields. |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 369 | // T is the type of the address. |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 370 | func (a *analysis) offsetAddr(T types.Type, dst, src nodeid, offset uint32) { |
| 371 | if !a.shouldTrack(T) { |
| 372 | return |
| 373 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 374 | if offset == 0 { |
| 375 | // Simplify dst = &src->f0 |
| 376 | // to dst = src |
| 377 | // (NB: this optimisation is defeated by the identity |
| 378 | // field prepended to struct and array objects.) |
| 379 | a.copy(dst, src, 1) |
| 380 | } else { |
| 381 | a.addConstraint(&offsetAddrConstraint{offset, dst, src}) |
| 382 | } |
| 383 | } |
| 384 | |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 385 | // typeAssert creates a typeFilter or untag constraint of the form dst = src.(T): |
| 386 | // typeFilter for an interface, untag for a concrete type. |
| 387 | // The exact flag is specified as for untagConstraint. |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 388 | func (a *analysis) typeAssert(T types.Type, dst, src nodeid, exact bool) { |
| 389 | if isInterface(T) { |
| 390 | a.addConstraint(&typeFilterConstraint{T, dst, src}) |
| 391 | } else { |
| 392 | a.addConstraint(&untagConstraint{T, dst, src, exact}) |
| 393 | } |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 394 | } |
| 395 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 396 | // addConstraint adds c to the constraint set. |
| 397 | func (a *analysis) addConstraint(c constraint) { |
| 398 | a.constraints = append(a.constraints, c) |
| 399 | if a.log != nil { |
| 400 | fmt.Fprintf(a.log, "\t%s\n", c) |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | // copyElems generates load/store constraints for *dst = *src, |
| 405 | // where src and dst are slices or *arrays. |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 406 | func (a *analysis) copyElems(cgn *cgnode, typ types.Type, dst, src ssa.Value) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 407 | tmp := a.addNodes(typ, "copy") |
| 408 | sz := a.sizeof(typ) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 409 | a.genLoad(cgn, tmp, src, 1, sz) |
| 410 | a.genStore(cgn, dst, tmp, 1, sz) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 411 | } |
| 412 | |
| 413 | // ---------- Constraint generation ---------- |
| 414 | |
| 415 | // genConv generates constraints for the conversion operation conv. |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 416 | func (a *analysis) genConv(conv *ssa.Convert, cgn *cgnode) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 417 | res := a.valueNode(conv) |
| 418 | if res == 0 { |
| 419 | return // result is non-pointerlike |
| 420 | } |
| 421 | |
| 422 | tSrc := conv.X.Type() |
| 423 | tDst := conv.Type() |
| 424 | |
| 425 | switch utSrc := tSrc.Underlying().(type) { |
| 426 | case *types.Slice: |
| 427 | // []byte/[]rune -> string? |
| 428 | return |
| 429 | |
| 430 | case *types.Pointer: |
| 431 | // *T -> unsafe.Pointer? |
Alan Donovan | 02dba5d | 2014-06-18 18:02:07 -0400 | [diff] [blame] | 432 | if tDst.Underlying() == tUnsafePtr { |
Alan Donovan | 79e0c7b | 2014-07-08 10:11:36 -0400 | [diff] [blame] | 433 | return // we don't model unsafe aliasing (unsound) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 434 | } |
| 435 | |
| 436 | case *types.Basic: |
Alan Donovan | 79e0c7b | 2014-07-08 10:11:36 -0400 | [diff] [blame] | 437 | switch tDst.Underlying().(type) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 438 | case *types.Pointer: |
Alan Donovan | 79e0c7b | 2014-07-08 10:11:36 -0400 | [diff] [blame] | 439 | // Treat unsafe.Pointer->*T conversions like |
| 440 | // new(T) and create an unaliased object. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 441 | if utSrc == tUnsafePtr { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 442 | obj := a.addNodes(mustDeref(tDst), "unsafe.Pointer conversion") |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 443 | a.endObject(obj, cgn, conv) |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 444 | a.addressOf(tDst, res, obj) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 445 | return |
| 446 | } |
| 447 | |
| 448 | case *types.Slice: |
| 449 | // string -> []byte/[]rune (or named aliases)? |
| 450 | if utSrc.Info()&types.IsString != 0 { |
| 451 | obj := a.addNodes(sliceToArray(tDst), "convert") |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 452 | a.endObject(obj, cgn, conv) |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 453 | a.addressOf(tDst, res, obj) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 454 | return |
| 455 | } |
| 456 | |
| 457 | case *types.Basic: |
Alan Donovan | 79e0c7b | 2014-07-08 10:11:36 -0400 | [diff] [blame] | 458 | // All basic-to-basic type conversions are no-ops. |
| 459 | // This includes uintptr<->unsafe.Pointer conversions, |
| 460 | // which we (unsoundly) ignore. |
| 461 | return |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 462 | } |
| 463 | } |
| 464 | |
| 465 | panic(fmt.Sprintf("illegal *ssa.Convert %s -> %s: %s", tSrc, tDst, conv.Parent())) |
| 466 | } |
| 467 | |
| 468 | // genAppend generates constraints for a call to append. |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 469 | func (a *analysis) genAppend(instr *ssa.Call, cgn *cgnode) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 470 | // Consider z = append(x, y). y is optional. |
| 471 | // This may allocate a new [1]T array; call its object w. |
| 472 | // We get the following constraints: |
| 473 | // z = x |
| 474 | // z = &w |
| 475 | // *z = *y |
| 476 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 477 | x := instr.Call.Args[0] |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 478 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 479 | z := instr |
| 480 | a.copy(a.valueNode(z), a.valueNode(x), 1) // z = x |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 481 | |
| 482 | if len(instr.Call.Args) == 1 { |
| 483 | return // no allocation for z = append(x) or _ = append(x). |
| 484 | } |
| 485 | |
| 486 | // TODO(adonovan): test append([]byte, ...string) []byte. |
| 487 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 488 | y := instr.Call.Args[1] |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 489 | tArray := sliceToArray(instr.Call.Args[0].Type()) |
| 490 | |
Rebecca Stambler | 207d3de | 2019-11-20 22:43:00 -0500 | [diff] [blame] | 491 | w := a.nextNode() |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 492 | a.addNodes(tArray, "append") |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 493 | a.endObject(w, cgn, instr) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 494 | |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 495 | a.copyElems(cgn, tArray.Elem(), z, y) // *z = *y |
| 496 | a.addressOf(instr.Type(), a.valueNode(z), w) // z = &w |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 497 | } |
| 498 | |
Ainar Garipov | feee8ac | 2019-09-11 09:14:36 +0300 | [diff] [blame] | 499 | // genBuiltinCall generates constraints for a call to a built-in. |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 500 | func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 501 | call := instr.Common() |
Alan Donovan | de23e2b | 2014-06-13 17:34:07 -0400 | [diff] [blame] | 502 | switch call.Value.(*ssa.Builtin).Name() { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 503 | case "append": |
| 504 | // Safe cast: append cannot appear in a go or defer statement. |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 505 | a.genAppend(instr.(*ssa.Call), cgn) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 506 | |
| 507 | case "copy": |
| 508 | tElem := call.Args[0].Type().Underlying().(*types.Slice).Elem() |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 509 | a.copyElems(cgn, tElem, call.Args[0], call.Args[1]) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 510 | |
| 511 | case "panic": |
| 512 | a.copy(a.panicNode, a.valueNode(call.Args[0]), 1) |
| 513 | |
| 514 | case "recover": |
| 515 | if v := instr.Value(); v != nil { |
| 516 | a.copy(a.valueNode(v), a.panicNode, 1) |
| 517 | } |
| 518 | |
Alan Donovan | fec2522 | 2014-06-11 13:10:26 -0400 | [diff] [blame] | 519 | case "print": |
| 520 | // In the tests, the probe might be the sole reference |
| 521 | // to its arg, so make sure we create nodes for it. |
Alan Donovan | ce7df39 | 2014-11-20 13:33:20 -0500 | [diff] [blame] | 522 | if len(call.Args) > 0 { |
| 523 | a.valueNode(call.Args[0]) |
| 524 | } |
Alan Donovan | fec2522 | 2014-06-11 13:10:26 -0400 | [diff] [blame] | 525 | |
Alan Donovan | de23e2b | 2014-06-13 17:34:07 -0400 | [diff] [blame] | 526 | case "ssa:wrapnilchk": |
| 527 | a.copy(a.valueNode(instr.Value()), a.valueNode(call.Args[0]), 1) |
| 528 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 529 | default: |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 530 | // No-ops: close len cap real imag complex print println delete. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 531 | } |
| 532 | } |
| 533 | |
| 534 | // shouldUseContext defines the context-sensitivity policy. It |
| 535 | // returns true if we should analyse all static calls to fn anew. |
| 536 | // |
| 537 | // Obviously this interface rather limits how much freedom we have to |
| 538 | // choose a policy. The current policy, rather arbitrarily, is true |
| 539 | // for intrinsics and accessor methods (actually: short, single-block, |
| 540 | // call-free functions). This is just a starting point. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 541 | func (a *analysis) shouldUseContext(fn *ssa.Function) bool { |
| 542 | if a.findIntrinsic(fn) != nil { |
| 543 | return true // treat intrinsics context-sensitively |
| 544 | } |
| 545 | if len(fn.Blocks) != 1 { |
| 546 | return false // too expensive |
| 547 | } |
| 548 | blk := fn.Blocks[0] |
| 549 | if len(blk.Instrs) > 10 { |
| 550 | return false // too expensive |
| 551 | } |
| 552 | if fn.Synthetic != "" && (fn.Pkg == nil || fn != fn.Pkg.Func("init")) { |
| 553 | return true // treat synthetic wrappers context-sensitively |
| 554 | } |
| 555 | for _, instr := range blk.Instrs { |
| 556 | switch instr := instr.(type) { |
| 557 | case ssa.CallInstruction: |
| 558 | // Disallow function calls (except to built-ins) |
| 559 | // because of the danger of unbounded recursion. |
| 560 | if _, ok := instr.Common().Value.(*ssa.Builtin); !ok { |
| 561 | return false |
| 562 | } |
| 563 | } |
| 564 | } |
| 565 | return true |
| 566 | } |
| 567 | |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 568 | // genStaticCall generates constraints for a statically dispatched function call. |
| 569 | func (a *analysis) genStaticCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 570 | fn := call.StaticCallee() |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 571 | |
| 572 | // Special cases for inlined intrinsics. |
| 573 | switch fn { |
| 574 | case a.runtimeSetFinalizer: |
| 575 | // Inline SetFinalizer so the call appears direct. |
| 576 | site.targets = a.addOneNode(tInvalid, "SetFinalizer.targets", nil) |
| 577 | a.addConstraint(&runtimeSetFinalizerConstraint{ |
| 578 | targets: site.targets, |
| 579 | x: a.valueNode(call.Args[0]), |
| 580 | f: a.valueNode(call.Args[1]), |
| 581 | }) |
| 582 | return |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 583 | |
| 584 | case a.reflectValueCall: |
| 585 | // Inline (reflect.Value).Call so the call appears direct. |
| 586 | dotdotdot := false |
| 587 | ret := reflectCallImpl(a, caller, site, a.valueNode(call.Args[0]), a.valueNode(call.Args[1]), dotdotdot) |
| 588 | if result != 0 { |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 589 | a.addressOf(fn.Signature.Results().At(0).Type(), result, ret) |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 590 | } |
| 591 | return |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 592 | } |
| 593 | |
| 594 | // Ascertain the context (contour/cgnode) for a particular call. |
| 595 | var obj nodeid |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 596 | if a.shouldUseContext(fn) { |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 597 | obj = a.makeFunctionObject(fn, site) // new contour |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 598 | } else { |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 599 | obj = a.objectNode(nil, fn) // shared contour |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 600 | } |
Alan Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 601 | a.callEdge(caller, site, obj) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 602 | |
| 603 | sig := call.Signature() |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 604 | |
| 605 | // Copy receiver, if any. |
| 606 | params := a.funcParams(obj) |
| 607 | args := call.Args |
| 608 | if sig.Recv() != nil { |
| 609 | sz := a.sizeof(sig.Recv().Type()) |
| 610 | a.copy(params, a.valueNode(args[0]), sz) |
| 611 | params += nodeid(sz) |
| 612 | args = args[1:] |
| 613 | } |
| 614 | |
| 615 | // Copy actual parameters into formal params block. |
| 616 | // Must loop, since the actuals aren't contiguous. |
| 617 | for i, arg := range args { |
| 618 | sz := a.sizeof(sig.Params().At(i).Type()) |
| 619 | a.copy(params, a.valueNode(arg), sz) |
| 620 | params += nodeid(sz) |
| 621 | } |
| 622 | |
| 623 | // Copy formal results block to actual result. |
| 624 | if result != 0 { |
| 625 | a.copy(result, a.funcResults(obj), a.sizeof(sig.Results())) |
| 626 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 627 | } |
| 628 | |
| 629 | // genDynamicCall generates constraints for a dynamic function call. |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 630 | func (a *analysis) genDynamicCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 631 | // pts(targets) will be the set of possible call targets. |
| 632 | site.targets = a.valueNode(call.Value) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 633 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 634 | // We add dynamic closure rules that store the arguments into |
| 635 | // the P-block and load the results from the R-block of each |
| 636 | // function discovered in pts(targets). |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 637 | |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 638 | sig := call.Signature() |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 639 | var offset uint32 = 1 // P/R block starts at offset 1 |
| 640 | for i, arg := range call.Args { |
| 641 | sz := a.sizeof(sig.Params().At(i).Type()) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 642 | a.genStore(caller, call.Value, a.valueNode(arg), offset, sz) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 643 | offset += sz |
| 644 | } |
| 645 | if result != 0 { |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 646 | a.genLoad(caller, result, call.Value, offset, a.sizeof(sig.Results())) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 647 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 648 | } |
| 649 | |
| 650 | // genInvoke generates constraints for a dynamic method invocation. |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 651 | func (a *analysis) genInvoke(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 652 | if call.Value.Type() == a.reflectType { |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 653 | a.genInvokeReflectType(caller, site, call, result) |
| 654 | return |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 655 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 656 | |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 657 | sig := call.Signature() |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 658 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 659 | // Allocate a contiguous targets/params/results block for this call. |
| 660 | block := a.nextNode() |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 661 | // pts(targets) will be the set of possible call targets |
| 662 | site.targets = a.addOneNode(sig, "invoke.targets", nil) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 663 | p := a.addNodes(sig.Params(), "invoke.params") |
| 664 | r := a.addNodes(sig.Results(), "invoke.results") |
| 665 | |
| 666 | // Copy the actual parameters into the call's params block. |
| 667 | for i, n := 0, sig.Params().Len(); i < n; i++ { |
| 668 | sz := a.sizeof(sig.Params().At(i).Type()) |
| 669 | a.copy(p, a.valueNode(call.Args[i]), sz) |
| 670 | p += nodeid(sz) |
| 671 | } |
| 672 | // Copy the call's results block to the actual results. |
| 673 | if result != 0 { |
| 674 | a.copy(result, r, a.sizeof(sig.Results())) |
| 675 | } |
| 676 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 677 | // We add a dynamic invoke constraint that will connect the |
| 678 | // caller's and the callee's P/R blocks for each discovered |
| 679 | // call target. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 680 | a.addConstraint(&invokeConstraint{call.Method, a.valueNode(call.Value), block}) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 681 | } |
| 682 | |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 683 | // genInvokeReflectType is a specialization of genInvoke where the |
| 684 | // receiver type is a reflect.Type, under the assumption that there |
| 685 | // can be at most one implementation of this interface, *reflect.rtype. |
| 686 | // |
| 687 | // (Though this may appear to be an instance of a pattern---method |
| 688 | // calls on interfaces known to have exactly one implementation---in |
| 689 | // practice it occurs rarely, so we special case for reflect.Type.) |
| 690 | // |
| 691 | // In effect we treat this: |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 692 | // |
Russ Cox | f9c13bb | 2022-04-11 22:40:33 -0400 | [diff] [blame] | 693 | // var rt reflect.Type = ... |
| 694 | // rt.F() |
| 695 | // |
| 696 | // as this: |
| 697 | // |
| 698 | // rt.(*reflect.rtype).F() |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 699 | func (a *analysis) genInvokeReflectType(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 700 | // Unpack receiver into rtype |
| 701 | rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil) |
| 702 | recv := a.valueNode(call.Value) |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 703 | a.typeAssert(a.reflectRtypePtr, rtype, recv, true) |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 704 | |
| 705 | // Look up the concrete method. |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 706 | fn := a.prog.LookupMethod(a.reflectRtypePtr, call.Method.Pkg(), call.Method.Name()) |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 707 | |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 708 | obj := a.makeFunctionObject(fn, site) // new contour for this call |
Alan Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 709 | a.callEdge(caller, site, obj) |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 710 | |
| 711 | // From now on, it's essentially a static call, but little is |
| 712 | // gained by factoring together the code for both cases. |
| 713 | |
| 714 | sig := fn.Signature // concrete method |
| 715 | targets := a.addOneNode(sig, "call.targets", nil) |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 716 | a.addressOf(sig, targets, obj) // (a singleton) |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 717 | |
| 718 | // Copy receiver. |
| 719 | params := a.funcParams(obj) |
| 720 | a.copy(params, rtype, 1) |
| 721 | params++ |
| 722 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 723 | // Copy actual parameters into formal P-block. |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 724 | // Must loop, since the actuals aren't contiguous. |
| 725 | for i, arg := range call.Args { |
| 726 | sz := a.sizeof(sig.Params().At(i).Type()) |
| 727 | a.copy(params, a.valueNode(arg), sz) |
| 728 | params += nodeid(sz) |
| 729 | } |
| 730 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 731 | // Copy formal R-block to actual R-block. |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 732 | if result != 0 { |
| 733 | a.copy(result, a.funcResults(obj), a.sizeof(sig.Results())) |
| 734 | } |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 735 | } |
| 736 | |
Robert Griesemer | 30b1abe | 2014-05-02 14:38:08 -0700 | [diff] [blame] | 737 | // genCall generates constraints for call instruction instr. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 738 | func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) { |
| 739 | call := instr.Common() |
| 740 | |
| 741 | // Intrinsic implementations of built-in functions. |
| 742 | if _, ok := call.Value.(*ssa.Builtin); ok { |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 743 | a.genBuiltinCall(instr, caller) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 744 | return |
| 745 | } |
| 746 | |
| 747 | var result nodeid |
| 748 | if v := instr.Value(); v != nil { |
| 749 | result = a.valueNode(v) |
| 750 | } |
| 751 | |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 752 | site := &callsite{instr: instr} |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 753 | if call.StaticCallee() != nil { |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 754 | a.genStaticCall(caller, site, call, result) |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 755 | } else if call.IsInvoke() { |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 756 | a.genInvoke(caller, site, call, result) |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 757 | } else { |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 758 | a.genDynamicCall(caller, site, call, result) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 759 | } |
| 760 | |
Alan Donovan | 785cfaa | 2013-09-25 17:17:42 -0400 | [diff] [blame] | 761 | caller.sites = append(caller.sites, site) |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 762 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 763 | if a.log != nil { |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 764 | // TODO(adonovan): debug: improve log message. |
Alan Donovan | 785cfaa | 2013-09-25 17:17:42 -0400 | [diff] [blame] | 765 | fmt.Fprintf(a.log, "\t%s to targets %s from %s\n", site, site.targets, caller) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 766 | } |
| 767 | } |
| 768 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 769 | // objectNode returns the object to which v points, if known. |
| 770 | // In other words, if the points-to set of v is a singleton, it |
| 771 | // returns the sole label, zero otherwise. |
| 772 | // |
| 773 | // We exploit this information to make the generated constraints less |
| 774 | // dynamic. For example, a complex load constraint can be replaced by |
| 775 | // a simple copy constraint when the sole destination is known a priori. |
| 776 | // |
| 777 | // Some SSA instructions always have singletons points-to sets: |
Russ Cox | f9c13bb | 2022-04-11 22:40:33 -0400 | [diff] [blame] | 778 | // |
| 779 | // Alloc, Function, Global, MakeChan, MakeClosure, MakeInterface, MakeMap, MakeSlice. |
| 780 | // |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 781 | // Others may be singletons depending on their operands: |
Russ Cox | f9c13bb | 2022-04-11 22:40:33 -0400 | [diff] [blame] | 782 | // |
| 783 | // FreeVar, Const, Convert, FieldAddr, IndexAddr, Slice, SliceToArrayPointer. |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 784 | // |
| 785 | // Idempotent. Objects are created as needed, possibly via recursion |
| 786 | // down the SSA value graph, e.g IndexAddr(FieldAddr(Alloc))). |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 787 | func (a *analysis) objectNode(cgn *cgnode, v ssa.Value) nodeid { |
Alan Donovan | 94d1589 | 2014-06-11 13:19:52 -0400 | [diff] [blame] | 788 | switch v.(type) { |
Alan Donovan | cc02c5b | 2014-06-11 14:04:45 -0400 | [diff] [blame] | 789 | case *ssa.Global, *ssa.Function, *ssa.Const, *ssa.FreeVar: |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 790 | // Global object. |
| 791 | obj, ok := a.globalobj[v] |
| 792 | if !ok { |
| 793 | switch v := v.(type) { |
| 794 | case *ssa.Global: |
| 795 | obj = a.nextNode() |
| 796 | a.addNodes(mustDeref(v.Type()), "global") |
| 797 | a.endObject(obj, nil, v) |
| 798 | |
| 799 | case *ssa.Function: |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 800 | obj = a.makeFunctionObject(v, nil) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 801 | |
| 802 | case *ssa.Const: |
Alan Donovan | 94d1589 | 2014-06-11 13:19:52 -0400 | [diff] [blame] | 803 | // not addressable |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 804 | |
Alan Donovan | cc02c5b | 2014-06-11 14:04:45 -0400 | [diff] [blame] | 805 | case *ssa.FreeVar: |
Alan Donovan | 94d1589 | 2014-06-11 13:19:52 -0400 | [diff] [blame] | 806 | // not addressable |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 807 | } |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 808 | |
| 809 | if a.log != nil { |
Alan Donovan | d7a9805 | 2013-10-14 13:53:41 -0400 | [diff] [blame] | 810 | fmt.Fprintf(a.log, "\tglobalobj[%s] = n%d\n", v, obj) |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 811 | } |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 812 | a.globalobj[v] = obj |
| 813 | } |
| 814 | return obj |
| 815 | } |
| 816 | |
| 817 | // Local object. |
| 818 | obj, ok := a.localobj[v] |
| 819 | if !ok { |
| 820 | switch v := v.(type) { |
| 821 | case *ssa.Alloc: |
| 822 | obj = a.nextNode() |
| 823 | a.addNodes(mustDeref(v.Type()), "alloc") |
| 824 | a.endObject(obj, cgn, v) |
| 825 | |
| 826 | case *ssa.MakeSlice: |
| 827 | obj = a.nextNode() |
| 828 | a.addNodes(sliceToArray(v.Type()), "makeslice") |
| 829 | a.endObject(obj, cgn, v) |
| 830 | |
| 831 | case *ssa.MakeChan: |
| 832 | obj = a.nextNode() |
| 833 | a.addNodes(v.Type().Underlying().(*types.Chan).Elem(), "makechan") |
| 834 | a.endObject(obj, cgn, v) |
| 835 | |
| 836 | case *ssa.MakeMap: |
| 837 | obj = a.nextNode() |
| 838 | tmap := v.Type().Underlying().(*types.Map) |
| 839 | a.addNodes(tmap.Key(), "makemap.key") |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 840 | elem := a.addNodes(tmap.Elem(), "makemap.value") |
| 841 | |
| 842 | // To update the value field, MapUpdate |
| 843 | // generates store-with-offset constraints which |
| 844 | // the presolver can't model, so we must mark |
| 845 | // those nodes indirect. |
| 846 | for id, end := elem, elem+nodeid(a.sizeof(tmap.Elem())); id < end; id++ { |
| 847 | a.mapValues = append(a.mapValues, id) |
| 848 | } |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 849 | a.endObject(obj, cgn, v) |
| 850 | |
| 851 | case *ssa.MakeInterface: |
| 852 | tConc := v.X.Type() |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 853 | obj = a.makeTagged(tConc, cgn, v) |
| 854 | |
| 855 | // Copy the value into it, if nontrivial. |
| 856 | if x := a.valueNode(v.X); x != 0 { |
| 857 | a.copy(obj+1, x, a.sizeof(tConc)) |
| 858 | } |
| 859 | |
| 860 | case *ssa.FieldAddr: |
| 861 | if xobj := a.objectNode(cgn, v.X); xobj != 0 { |
| 862 | obj = xobj + nodeid(a.offsetOf(mustDeref(v.X.Type()), v.Field)) |
| 863 | } |
| 864 | |
| 865 | case *ssa.IndexAddr: |
| 866 | if xobj := a.objectNode(cgn, v.X); xobj != 0 { |
| 867 | obj = xobj + 1 |
| 868 | } |
| 869 | |
| 870 | case *ssa.Slice: |
| 871 | obj = a.objectNode(cgn, v.X) |
| 872 | |
Tim King | 03a91dd | 2021-08-04 15:10:29 -0700 | [diff] [blame] | 873 | case *ssa.SliceToArrayPointer: |
| 874 | // Going from a []T to a *[k]T for some k. |
| 875 | // A slice []T is treated as if it were a *T pointer. |
| 876 | obj = a.objectNode(cgn, v.X) |
| 877 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 878 | case *ssa.Convert: |
| 879 | // TODO(adonovan): opt: handle these cases too: |
| 880 | // - unsafe.Pointer->*T conversion acts like Alloc |
| 881 | // - string->[]byte/[]rune conversion acts like MakeSlice |
| 882 | } |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 883 | |
| 884 | if a.log != nil { |
Alan Donovan | d7a9805 | 2013-10-14 13:53:41 -0400 | [diff] [blame] | 885 | fmt.Fprintf(a.log, "\tlocalobj[%s] = n%d\n", v.Name(), obj) |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 886 | } |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 887 | a.localobj[v] = obj |
| 888 | } |
| 889 | return obj |
| 890 | } |
| 891 | |
| 892 | // genLoad generates constraints for result = *(ptr + val). |
| 893 | func (a *analysis) genLoad(cgn *cgnode, result nodeid, ptr ssa.Value, offset, sizeof uint32) { |
| 894 | if obj := a.objectNode(cgn, ptr); obj != 0 { |
| 895 | // Pre-apply loadConstraint.solve(). |
| 896 | a.copy(result, obj+nodeid(offset), sizeof) |
| 897 | } else { |
| 898 | a.load(result, a.valueNode(ptr), offset, sizeof) |
| 899 | } |
| 900 | } |
| 901 | |
| 902 | // genOffsetAddr generates constraints for a 'v=ptr.field' (FieldAddr) |
| 903 | // or 'v=ptr[*]' (IndexAddr) instruction v. |
| 904 | func (a *analysis) genOffsetAddr(cgn *cgnode, v ssa.Value, ptr nodeid, offset uint32) { |
| 905 | dst := a.valueNode(v) |
| 906 | if obj := a.objectNode(cgn, v); obj != 0 { |
| 907 | // Pre-apply offsetAddrConstraint.solve(). |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 908 | a.addressOf(v.Type(), dst, obj) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 909 | } else { |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 910 | a.offsetAddr(v.Type(), dst, ptr, offset) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 911 | } |
| 912 | } |
| 913 | |
| 914 | // genStore generates constraints for *(ptr + offset) = val. |
| 915 | func (a *analysis) genStore(cgn *cgnode, ptr ssa.Value, val nodeid, offset, sizeof uint32) { |
| 916 | if obj := a.objectNode(cgn, ptr); obj != 0 { |
| 917 | // Pre-apply storeConstraint.solve(). |
| 918 | a.copy(obj+nodeid(offset), val, sizeof) |
| 919 | } else { |
| 920 | a.store(a.valueNode(ptr), val, offset, sizeof) |
| 921 | } |
| 922 | } |
| 923 | |
Robert Griesemer | 30b1abe | 2014-05-02 14:38:08 -0700 | [diff] [blame] | 924 | // genInstr generates constraints for instruction instr in context cgn. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 925 | func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) { |
| 926 | if a.log != nil { |
| 927 | var prefix string |
| 928 | if val, ok := instr.(ssa.Value); ok { |
| 929 | prefix = val.Name() + " = " |
| 930 | } |
| 931 | fmt.Fprintf(a.log, "; %s%s\n", prefix, instr) |
| 932 | } |
| 933 | |
| 934 | switch instr := instr.(type) { |
| 935 | case *ssa.DebugRef: |
| 936 | // no-op. |
| 937 | |
| 938 | case *ssa.UnOp: |
| 939 | switch instr.Op { |
| 940 | case token.ARROW: // <-x |
| 941 | // We can ignore instr.CommaOk because the node we're |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 942 | // altering is always at zero offset relative to instr |
Alan Donovan | 1145155 | 2014-10-27 13:55:52 -0400 | [diff] [blame] | 943 | tElem := instr.X.Type().Underlying().(*types.Chan).Elem() |
| 944 | a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(tElem)) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 945 | |
| 946 | case token.MUL: // *x |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 947 | a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(instr.Type())) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 948 | |
| 949 | default: |
| 950 | // NOT, SUB, XOR: no-op. |
| 951 | } |
| 952 | |
| 953 | case *ssa.BinOp: |
| 954 | // All no-ops. |
| 955 | |
| 956 | case ssa.CallInstruction: // *ssa.Call, *ssa.Go, *ssa.Defer |
| 957 | a.genCall(cgn, instr) |
| 958 | |
| 959 | case *ssa.ChangeType: |
| 960 | a.copy(a.valueNode(instr), a.valueNode(instr.X), 1) |
| 961 | |
| 962 | case *ssa.Convert: |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 963 | a.genConv(instr, cgn) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 964 | |
| 965 | case *ssa.Extract: |
| 966 | a.copy(a.valueNode(instr), |
| 967 | a.valueOffsetNode(instr.Tuple, instr.Index), |
| 968 | a.sizeof(instr.Type())) |
| 969 | |
| 970 | case *ssa.FieldAddr: |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 971 | a.genOffsetAddr(cgn, instr, a.valueNode(instr.X), |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 972 | a.offsetOf(mustDeref(instr.X.Type()), instr.Field)) |
| 973 | |
| 974 | case *ssa.IndexAddr: |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 975 | a.genOffsetAddr(cgn, instr, a.valueNode(instr.X), 1) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 976 | |
| 977 | case *ssa.Field: |
| 978 | a.copy(a.valueNode(instr), |
| 979 | a.valueOffsetNode(instr.X, instr.Field), |
| 980 | a.sizeof(instr.Type())) |
| 981 | |
| 982 | case *ssa.Index: |
Tim King | 36a5c6a | 2022-08-25 11:09:24 -0400 | [diff] [blame] | 983 | _, isstring := typeparams.CoreType(instr.X.Type()).(*types.Basic) |
| 984 | if !isstring { |
| 985 | a.copy(a.valueNode(instr), 1+a.valueNode(instr.X), a.sizeof(instr.Type())) |
| 986 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 987 | |
| 988 | case *ssa.Select: |
| 989 | recv := a.valueOffsetNode(instr, 2) // instr : (index, recvOk, recv0, ... recv_n-1) |
| 990 | for _, st := range instr.States { |
| 991 | elemSize := a.sizeof(st.Chan.Type().Underlying().(*types.Chan).Elem()) |
| 992 | switch st.Dir { |
Robert Griesemer | 74d33a9 | 2013-12-17 15:45:01 -0800 | [diff] [blame] | 993 | case types.RecvOnly: |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 994 | a.genLoad(cgn, recv, st.Chan, 0, elemSize) |
Alan Donovan | 70692d3 | 2013-11-13 09:13:42 -0500 | [diff] [blame] | 995 | recv += nodeid(elemSize) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 996 | |
Robert Griesemer | 74d33a9 | 2013-12-17 15:45:01 -0800 | [diff] [blame] | 997 | case types.SendOnly: |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 998 | a.genStore(cgn, st.Chan, a.valueNode(st.Send), 0, elemSize) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 999 | } |
| 1000 | } |
| 1001 | |
Alan Donovan | 068f017 | 2013-10-08 12:31:39 -0400 | [diff] [blame] | 1002 | case *ssa.Return: |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1003 | results := a.funcResults(cgn.obj) |
| 1004 | for _, r := range instr.Results { |
| 1005 | sz := a.sizeof(r.Type()) |
| 1006 | a.copy(results, a.valueNode(r), sz) |
| 1007 | results += nodeid(sz) |
| 1008 | } |
| 1009 | |
| 1010 | case *ssa.Send: |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1011 | a.genStore(cgn, instr.Chan, a.valueNode(instr.X), 0, a.sizeof(instr.X.Type())) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1012 | |
| 1013 | case *ssa.Store: |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1014 | a.genStore(cgn, instr.Addr, a.valueNode(instr.Val), 0, a.sizeof(instr.Val.Type())) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1015 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1016 | case *ssa.Alloc, *ssa.MakeSlice, *ssa.MakeChan, *ssa.MakeMap, *ssa.MakeInterface: |
| 1017 | v := instr.(ssa.Value) |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 1018 | a.addressOf(v.Type(), a.valueNode(v), a.objectNode(cgn, v)) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1019 | |
| 1020 | case *ssa.ChangeInterface: |
| 1021 | a.copy(a.valueNode(instr), a.valueNode(instr.X), 1) |
| 1022 | |
| 1023 | case *ssa.TypeAssert: |
Alan Donovan | 94c387c | 2013-10-29 21:57:53 -0400 | [diff] [blame] | 1024 | a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X), true) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1025 | |
| 1026 | case *ssa.Slice: |
| 1027 | a.copy(a.valueNode(instr), a.valueNode(instr.X), 1) |
| 1028 | |
Tim King | 03a91dd | 2021-08-04 15:10:29 -0700 | [diff] [blame] | 1029 | case *ssa.SliceToArrayPointer: |
| 1030 | // Going from a []T to a *[k]T (for some k) is a single `dst = src` constraint. |
| 1031 | // Both []T and *[k]T are modelled as an *IdArrayT where IdArrayT is the identity |
| 1032 | // node for an array of type T, i.e `type IdArrayT struct{elem T}`. |
| 1033 | a.copy(a.valueNode(instr), a.valueNode(instr.X), 1) |
| 1034 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1035 | case *ssa.If, *ssa.Jump: |
| 1036 | // no-op. |
| 1037 | |
| 1038 | case *ssa.Phi: |
| 1039 | sz := a.sizeof(instr.Type()) |
| 1040 | for _, e := range instr.Edges { |
| 1041 | a.copy(a.valueNode(instr), a.valueNode(e), sz) |
| 1042 | } |
| 1043 | |
| 1044 | case *ssa.MakeClosure: |
| 1045 | fn := instr.Fn.(*ssa.Function) |
| 1046 | a.copy(a.valueNode(instr), a.valueNode(fn), 1) |
| 1047 | // Free variables are treated like global variables. |
| 1048 | for i, b := range instr.Bindings { |
| 1049 | a.copy(a.valueNode(fn.FreeVars[i]), a.valueNode(b), a.sizeof(b.Type())) |
| 1050 | } |
| 1051 | |
| 1052 | case *ssa.RunDefers: |
| 1053 | // The analysis is flow insensitive, so we just "call" |
| 1054 | // defers as we encounter them. |
| 1055 | |
| 1056 | case *ssa.Range: |
| 1057 | // Do nothing. Next{Iter: *ssa.Range} handles this case. |
| 1058 | |
| 1059 | case *ssa.Next: |
Scott Cotton | ab1fe72 | 2021-07-23 22:26:53 +0200 | [diff] [blame] | 1060 | if !instr.IsString { |
| 1061 | // Assumes that Next is always directly applied to a Range result |
| 1062 | // for a map. |
| 1063 | |
| 1064 | // Next results in a destination tuple (ok, dk, dv). |
| 1065 | // Recall a map is modeled as type *M where M = struct{sk K; sv V}. |
| 1066 | // Next copies from a src map struct{sk K; sv V} to a dst tuple (ok, dk, dv) |
| 1067 | // |
| 1068 | // When keys or value is a blank identifier in a range statement, e.g |
| 1069 | // for _, v := range m { ... } |
| 1070 | // or |
| 1071 | // for _, _ = range m { ... } |
| 1072 | // we skip copying from sk or dk as there is no use. dk and dv will have |
| 1073 | // Invalid types if they are blank identifiers. This means that the |
| 1074 | // size( (ok, dk, dv) ) may differ from 1 + size(struct{sk K; sv V}). |
| 1075 | // |
| 1076 | // We encode Next using one load of size sz from an offset in src osrc to an |
| 1077 | // offset in dst odst. There are 4 cases to consider: |
| 1078 | // odst | osrc | sz |
| 1079 | // k, v | 1 | 0 | size(sk) + size(sv) |
| 1080 | // k, _ | 1 | 0 | size(sk) |
| 1081 | // _, v | 1+size(dk) | size(sk) | size(sv) |
| 1082 | // _, _ | 1+size(dk) | size(sk) | 0 |
| 1083 | |
| 1084 | // get the source key and value size. Note the source types |
| 1085 | // may be different than the 3-tuple types, but if this is the |
| 1086 | // case then the source is assignable to the destination. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1087 | theMap := instr.Iter.(*ssa.Range).X |
| 1088 | tMap := theMap.Type().Underlying().(*types.Map) |
Alan Donovan | 0f9d71c | 2015-10-26 09:55:02 -0400 | [diff] [blame] | 1089 | |
Scott Cotton | ab1fe72 | 2021-07-23 22:26:53 +0200 | [diff] [blame] | 1090 | sksize := a.sizeof(tMap.Key()) |
| 1091 | svsize := a.sizeof(tMap.Elem()) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1092 | |
Scott Cotton | ab1fe72 | 2021-07-23 22:26:53 +0200 | [diff] [blame] | 1093 | // get the key size of the destination tuple. |
Alan Donovan | 0f9d71c | 2015-10-26 09:55:02 -0400 | [diff] [blame] | 1094 | tTuple := instr.Type().(*types.Tuple) |
Scott Cotton | ab1fe72 | 2021-07-23 22:26:53 +0200 | [diff] [blame] | 1095 | dksize := a.sizeof(tTuple.At(1).Type()) |
Alan Donovan | 0f9d71c | 2015-10-26 09:55:02 -0400 | [diff] [blame] | 1096 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1097 | // Load from the map's (k,v) into the tuple's (ok, k, v). |
Alan Donovan | 0f9d71c | 2015-10-26 09:55:02 -0400 | [diff] [blame] | 1098 | osrc := uint32(0) // offset within map object |
| 1099 | odst := uint32(1) // offset within tuple (initially just after 'ok bool') |
| 1100 | sz := uint32(0) // amount to copy |
| 1101 | |
| 1102 | // Is key valid? |
| 1103 | if tTuple.At(1).Type() != tInvalid { |
Scott Cotton | ab1fe72 | 2021-07-23 22:26:53 +0200 | [diff] [blame] | 1104 | sz += sksize |
Alan Donovan | 0f9d71c | 2015-10-26 09:55:02 -0400 | [diff] [blame] | 1105 | } else { |
Scott Cotton | ab1fe72 | 2021-07-23 22:26:53 +0200 | [diff] [blame] | 1106 | odst += dksize |
| 1107 | osrc += sksize |
Alan Donovan | 0f9d71c | 2015-10-26 09:55:02 -0400 | [diff] [blame] | 1108 | } |
| 1109 | |
| 1110 | // Is value valid? |
| 1111 | if tTuple.At(2).Type() != tInvalid { |
Scott Cotton | ab1fe72 | 2021-07-23 22:26:53 +0200 | [diff] [blame] | 1112 | sz += svsize |
Alan Donovan | 0f9d71c | 2015-10-26 09:55:02 -0400 | [diff] [blame] | 1113 | } |
| 1114 | |
| 1115 | a.genLoad(cgn, a.valueNode(instr)+nodeid(odst), theMap, osrc, sz) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1116 | } |
| 1117 | |
| 1118 | case *ssa.Lookup: |
| 1119 | if tMap, ok := instr.X.Type().Underlying().(*types.Map); ok { |
| 1120 | // CommaOk can be ignored: field 0 is a no-op. |
| 1121 | ksize := a.sizeof(tMap.Key()) |
| 1122 | vsize := a.sizeof(tMap.Elem()) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1123 | a.genLoad(cgn, a.valueNode(instr), instr.X, ksize, vsize) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1124 | } |
| 1125 | |
| 1126 | case *ssa.MapUpdate: |
| 1127 | tmap := instr.Map.Type().Underlying().(*types.Map) |
| 1128 | ksize := a.sizeof(tmap.Key()) |
| 1129 | vsize := a.sizeof(tmap.Elem()) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1130 | a.genStore(cgn, instr.Map, a.valueNode(instr.Key), 0, ksize) |
| 1131 | a.genStore(cgn, instr.Map, a.valueNode(instr.Value), ksize, vsize) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1132 | |
| 1133 | case *ssa.Panic: |
| 1134 | a.copy(a.panicNode, a.valueNode(instr.X), 1) |
| 1135 | |
| 1136 | default: |
| 1137 | panic(fmt.Sprintf("unimplemented: %T", instr)) |
| 1138 | } |
| 1139 | } |
| 1140 | |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 1141 | func (a *analysis) makeCGNode(fn *ssa.Function, obj nodeid, callersite *callsite) *cgnode { |
| 1142 | cgn := &cgnode{fn: fn, obj: obj, callersite: callersite} |
Alan Donovan | 785cfaa | 2013-09-25 17:17:42 -0400 | [diff] [blame] | 1143 | a.cgnodes = append(a.cgnodes, cgn) |
| 1144 | return cgn |
| 1145 | } |
| 1146 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1147 | // genRootCalls generates the synthetic root of the callgraph and the |
| 1148 | // initial calls from it to the analysis scope, such as main, a test |
| 1149 | // or a library. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1150 | func (a *analysis) genRootCalls() *cgnode { |
Alan Donovan | 1edc750 | 2014-06-11 14:03:40 -0400 | [diff] [blame] | 1151 | r := a.prog.NewFunction("<root>", new(types.Signature), "root of callgraph") |
Alan Donovan | 2299ac6 | 2013-10-09 12:41:55 -0400 | [diff] [blame] | 1152 | root := a.makeCGNode(r, 0, nil) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1153 | |
Alan Donovan | ba9c801 | 2014-03-11 18:24:39 -0400 | [diff] [blame] | 1154 | // TODO(adonovan): make an ssa utility to construct an actual |
| 1155 | // root function so we don't need to special-case site-less |
| 1156 | // call edges. |
| 1157 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1158 | // For each main package, call main.init(), main.main(). |
| 1159 | for _, mainPkg := range a.config.Mains { |
| 1160 | main := mainPkg.Func("main") |
| 1161 | if main == nil { |
| 1162 | panic(fmt.Sprintf("%s has no main function", mainPkg)) |
| 1163 | } |
| 1164 | |
| 1165 | targets := a.addOneNode(main.Signature, "root.targets", nil) |
Alan Donovan | 785cfaa | 2013-09-25 17:17:42 -0400 | [diff] [blame] | 1166 | site := &callsite{targets: targets} |
| 1167 | root.sites = append(root.sites, site) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1168 | for _, fn := range [2]*ssa.Function{mainPkg.Func("init"), main} { |
| 1169 | if a.log != nil { |
| 1170 | fmt.Fprintf(a.log, "\troot call to %s:\n", fn) |
| 1171 | } |
| 1172 | a.copy(targets, a.valueNode(fn), 1) |
| 1173 | } |
| 1174 | } |
| 1175 | |
| 1176 | return root |
| 1177 | } |
| 1178 | |
| 1179 | // genFunc generates constraints for function fn. |
| 1180 | func (a *analysis) genFunc(cgn *cgnode) { |
| 1181 | fn := cgn.fn |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 1182 | |
| 1183 | impl := a.findIntrinsic(fn) |
| 1184 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1185 | if a.log != nil { |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 1186 | fmt.Fprintf(a.log, "\n\n==== Generating constraints for %s, %s\n", cgn, cgn.contour()) |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 1187 | |
| 1188 | // Hack: don't display body if intrinsic. |
| 1189 | if impl != nil { |
| 1190 | fn2 := *cgn.fn // copy |
| 1191 | fn2.Locals = nil |
| 1192 | fn2.Blocks = nil |
Alan Donovan | 744d7e6 | 2014-01-28 17:48:10 -0500 | [diff] [blame] | 1193 | fn2.WriteTo(a.log) |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 1194 | } else { |
Alan Donovan | 744d7e6 | 2014-01-28 17:48:10 -0500 | [diff] [blame] | 1195 | cgn.fn.WriteTo(a.log) |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 1196 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1197 | } |
| 1198 | |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 1199 | if impl != nil { |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1200 | impl(a, cgn) |
| 1201 | return |
| 1202 | } |
| 1203 | |
| 1204 | if fn.Blocks == nil { |
| 1205 | // External function with no intrinsic treatment. |
| 1206 | // We'll warn about calls to such functions at the end. |
| 1207 | return |
| 1208 | } |
| 1209 | |
Tim King | 36a5c6a | 2022-08-25 11:09:24 -0400 | [diff] [blame] | 1210 | if fn.TypeParams().Len() > 0 && len(fn.TypeArgs()) == 0 { |
| 1211 | // Body of generic function. |
| 1212 | // We'll warn about calls to such functions at the end. |
| 1213 | return |
| 1214 | } |
| 1215 | |
| 1216 | if strings.HasPrefix(fn.Synthetic, "instantiation wrapper ") { |
| 1217 | // instantiation wrapper of a generic function. |
| 1218 | // These may contain type coercions which are not currently supported. |
| 1219 | // We'll warn about calls to such functions at the end. |
| 1220 | return |
| 1221 | } |
| 1222 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1223 | if a.log != nil { |
| 1224 | fmt.Fprintln(a.log, "; Creating nodes for local values") |
| 1225 | } |
| 1226 | |
| 1227 | a.localval = make(map[ssa.Value]nodeid) |
| 1228 | a.localobj = make(map[ssa.Value]nodeid) |
| 1229 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1230 | // The value nodes for the params are in the func object block. |
| 1231 | params := a.funcParams(cgn.obj) |
| 1232 | for _, p := range fn.Params { |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1233 | a.setValueNode(p, params, cgn) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1234 | params += nodeid(a.sizeof(p.Type())) |
| 1235 | } |
| 1236 | |
Alan Donovan | 94d1589 | 2014-06-11 13:19:52 -0400 | [diff] [blame] | 1237 | // Free variables have global cardinality: |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1238 | // the outer function sets them with MakeClosure; |
Alan Donovan | cc02c5b | 2014-06-11 14:04:45 -0400 | [diff] [blame] | 1239 | // the inner function accesses them with FreeVar. |
Alan Donovan | 94d1589 | 2014-06-11 13:19:52 -0400 | [diff] [blame] | 1240 | // |
Alan Donovan | cc02c5b | 2014-06-11 14:04:45 -0400 | [diff] [blame] | 1241 | // TODO(adonovan): treat free vars context-sensitively. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1242 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1243 | // Create value nodes for all value instructions |
| 1244 | // since SSA may contain forward references. |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 1245 | var space [10]*ssa.Value |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1246 | for _, b := range fn.Blocks { |
| 1247 | for _, instr := range b.Instrs { |
| 1248 | switch instr := instr.(type) { |
| 1249 | case *ssa.Range: |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1250 | // do nothing: it has a funky type, |
| 1251 | // and *ssa.Next does all the work. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1252 | |
| 1253 | case ssa.Value: |
| 1254 | var comment string |
| 1255 | if a.log != nil { |
| 1256 | comment = instr.Name() |
| 1257 | } |
| 1258 | id := a.addNodes(instr.Type(), comment) |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1259 | a.setValueNode(instr, id, cgn) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1260 | } |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 1261 | |
| 1262 | // Record all address-taken functions (for presolver). |
| 1263 | rands := instr.Operands(space[:0]) |
| 1264 | if call, ok := instr.(ssa.CallInstruction); ok && !call.Common().IsInvoke() { |
| 1265 | // Skip CallCommon.Value in "call" mode. |
| 1266 | // TODO(adonovan): fix: relies on unspecified ordering. Specify it. |
| 1267 | rands = rands[1:] |
| 1268 | } |
| 1269 | for _, rand := range rands { |
| 1270 | if atf, ok := (*rand).(*ssa.Function); ok { |
| 1271 | a.atFuncs[atf] = true |
| 1272 | } |
| 1273 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1274 | } |
| 1275 | } |
| 1276 | |
| 1277 | // Generate constraints for instructions. |
| 1278 | for _, b := range fn.Blocks { |
| 1279 | for _, instr := range b.Instrs { |
| 1280 | a.genInstr(cgn, instr) |
| 1281 | } |
| 1282 | } |
| 1283 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 1284 | a.localval = nil |
| 1285 | a.localobj = nil |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1286 | } |
| 1287 | |
Alan Donovan | 87ced82 | 2013-10-23 17:07:52 -0400 | [diff] [blame] | 1288 | // genMethodsOf generates nodes and constraints for all methods of type T. |
| 1289 | func (a *analysis) genMethodsOf(T types.Type) { |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 1290 | itf := isInterface(T) |
| 1291 | |
| 1292 | // TODO(adonovan): can we skip this entirely if itf is true? |
| 1293 | // I think so, but the answer may depend on reflection. |
Alan Donovan | 1f29e74 | 2014-02-11 16:49:27 -0500 | [diff] [blame] | 1294 | mset := a.prog.MethodSets.MethodSet(T) |
Alan Donovan | 87ced82 | 2013-10-23 17:07:52 -0400 | [diff] [blame] | 1295 | for i, n := 0, mset.Len(); i < n; i++ { |
Alan Donovan | afcda55 | 2015-08-31 17:50:03 -0400 | [diff] [blame] | 1296 | m := a.prog.MethodValue(mset.At(i)) |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 1297 | a.valueNode(m) |
| 1298 | |
| 1299 | if !itf { |
| 1300 | // Methods of concrete types are address-taken functions. |
| 1301 | a.atFuncs[m] = true |
| 1302 | } |
Alan Donovan | 87ced82 | 2013-10-23 17:07:52 -0400 | [diff] [blame] | 1303 | } |
| 1304 | } |
| 1305 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1306 | // generate generates offline constraints for the entire program. |
Alan Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 1307 | func (a *analysis) generate() { |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 1308 | start("Constraint generation") |
| 1309 | if a.log != nil { |
| 1310 | fmt.Fprintln(a.log, "==== Generating constraints") |
| 1311 | } |
| 1312 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1313 | // Create a dummy node since we use the nodeid 0 for |
| 1314 | // non-pointerlike variables. |
| 1315 | a.addNodes(tInvalid, "(zero)") |
| 1316 | |
| 1317 | // Create the global node for panic values. |
| 1318 | a.panicNode = a.addNodes(tEface, "panic") |
| 1319 | |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 1320 | // Create nodes and constraints for all methods of reflect.rtype. |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 1321 | // (Shared contours are used by dynamic calls to reflect.Type |
| 1322 | // methods---typically just String().) |
| 1323 | if rtype := a.reflectRtypePtr; rtype != nil { |
Alan Donovan | 87ced82 | 2013-10-23 17:07:52 -0400 | [diff] [blame] | 1324 | a.genMethodsOf(rtype) |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 1325 | } |
| 1326 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1327 | root := a.genRootCalls() |
| 1328 | |
Alan Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 1329 | if a.config.BuildCallGraph { |
| 1330 | a.result.CallGraph = callgraph.New(root.fn) |
| 1331 | } |
| 1332 | |
Alan Donovan | 87ced82 | 2013-10-23 17:07:52 -0400 | [diff] [blame] | 1333 | // Create nodes and constraints for all methods of all types |
| 1334 | // that are dynamically accessible via reflection or interfaces. |
Alan Donovan | f011631 | 2014-12-29 13:20:22 -0500 | [diff] [blame] | 1335 | for _, T := range a.prog.RuntimeTypes() { |
Alan Donovan | 87ced82 | 2013-10-23 17:07:52 -0400 | [diff] [blame] | 1336 | a.genMethodsOf(T) |
| 1337 | } |
| 1338 | |
Alan Donovan | 6361b57 | 2016-04-01 10:32:03 -0400 | [diff] [blame] | 1339 | // Generate constraints for functions as they become reachable |
| 1340 | // from the roots. (No constraints are generated for functions |
| 1341 | // that are dead in this analysis scope.) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1342 | for len(a.genq) > 0 { |
| 1343 | cgn := a.genq[0] |
| 1344 | a.genq = a.genq[1:] |
| 1345 | a.genFunc(cgn) |
| 1346 | } |
| 1347 | |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 1348 | // The runtime magically allocates os.Args; so should we. |
| 1349 | if os := a.prog.ImportedPackage("os"); os != nil { |
| 1350 | // In effect: os.Args = new([1]string)[:] |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 1351 | T := types.NewSlice(types.Typ[types.String]) |
| 1352 | obj := a.addNodes(sliceToArray(T), "<command-line args>") |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 1353 | a.endObject(obj, nil, "<command-line args>") |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 1354 | a.addressOf(T, a.objectNode(nil, os.Var("Args")), obj) |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 1355 | } |
Alan Donovan | 98ed3d3 | 2014-03-11 18:37:19 -0400 | [diff] [blame] | 1356 | |
| 1357 | // Discard generation state, to avoid confusion after node renumbering. |
| 1358 | a.panicNode = 0 |
| 1359 | a.globalval = nil |
| 1360 | a.localval = nil |
| 1361 | a.localobj = nil |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 1362 | |
| 1363 | stop("Constraint generation") |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 1364 | } |