611 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			611 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2013 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| /*
 | |
| 
 | |
| Package pointer implements Andersen's analysis, an inclusion-based
 | |
| pointer analysis algorithm first described in (Andersen, 1994).
 | |
| 
 | |
| A pointer analysis relates every pointer expression in a whole program
 | |
| to the set of memory locations to which it might point.  This
 | |
| information can be used to construct a call graph of the program that
 | |
| precisely represents the destinations of dynamic function and method
 | |
| calls.  It can also be used to determine, for example, which pairs of
 | |
| channel operations operate on the same channel.
 | |
| 
 | |
| The package allows the client to request a set of expressions of
 | |
| interest for which the points-to information will be returned once the
 | |
| analysis is complete.  In addition, the client may request that a
 | |
| callgraph is constructed.  The example program in example_test.go
 | |
| demonstrates both of these features.  Clients should not request more
 | |
| information than they need since it may increase the cost of the
 | |
| analysis significantly.
 | |
| 
 | |
| 
 | |
| CLASSIFICATION
 | |
| 
 | |
| Our algorithm is INCLUSION-BASED: the points-to sets for x and y will
 | |
| be related by pts(y) ⊇ pts(x) if the program contains the statement
 | |
| y = x.
 | |
| 
 | |
| It is FLOW-INSENSITIVE: it ignores all control flow constructs and the
 | |
| order of statements in a program.  It is therefore a "MAY ALIAS"
 | |
| analysis: its facts are of the form "P may/may not point to L",
 | |
| not "P must point to L".
 | |
| 
 | |
| It is FIELD-SENSITIVE: it builds separate points-to sets for distinct
 | |
| fields, such as x and y in struct { x, y *int }.
 | |
| 
 | |
| It is mostly CONTEXT-INSENSITIVE: most functions are analyzed once,
 | |
| so values can flow in at one call to the function and return out at
 | |
| another.  Only some smaller functions are analyzed with consideration
 | |
| of their calling context.
 | |
| 
 | |
| It has a CONTEXT-SENSITIVE HEAP: objects are named by both allocation
 | |
| site and context, so the objects returned by two distinct calls to f:
 | |
|    func f() *T { return new(T) }
 | |
| are distinguished up to the limits of the calling context.
 | |
| 
 | |
| It is a WHOLE PROGRAM analysis: it requires SSA-form IR for the
 | |
| complete Go program and summaries for native code.
 | |
| 
 | |
| See the (Hind, PASTE'01) survey paper for an explanation of these terms.
 | |
| 
 | |
| 
 | |
| SOUNDNESS
 | |
| 
 | |
| The analysis is fully sound when invoked on pure Go programs that do not
 | |
| use reflection or unsafe.Pointer conversions.  In other words, if there
 | |
| is any possible execution of the program in which pointer P may point to
 | |
| object O, the analysis will report that fact.
 | |
| 
 | |
| 
 | |
| REFLECTION
 | |
| 
 | |
| By default, the "reflect" library is ignored by the analysis, as if all
 | |
| its functions were no-ops, but if the client enables the Reflection flag,
 | |
| the analysis will make a reasonable attempt to model the effects of
 | |
| calls into this library.  However, this comes at a significant
 | |
| performance cost, and not all features of that library are yet
 | |
| implemented.  In addition, some simplifying approximations must be made
 | |
| to ensure that the analysis terminates; for example, reflection can be
 | |
| used to construct an infinite set of types and values of those types,
 | |
| but the analysis arbitrarily bounds the depth of such types.
 | |
| 
 | |
| Most but not all reflection operations are supported.
 | |
| In particular, addressable reflect.Values are not yet implemented, so
 | |
| operations such as (reflect.Value).Set have no analytic effect.
 | |
| 
 | |
| 
 | |
| UNSAFE POINTER CONVERSIONS
 | |
| 
 | |
| The pointer analysis makes no attempt to understand aliasing between the
 | |
| operand x and result y of an unsafe.Pointer conversion:
 | |
|    y = (*T)(unsafe.Pointer(x))
 | |
| It is as if the conversion allocated an entirely new object:
 | |
|    y = new(T)
 | |
| 
 | |
| 
 | |
| NATIVE CODE
 | |
| 
 | |
| The analysis cannot model the aliasing effects of functions written in
 | |
| languages other than Go, such as runtime intrinsics in C or assembly, or
 | |
| code accessed via cgo.  The result is as if such functions are no-ops.
 | |
| However, various important intrinsics are understood by the analysis,
 | |
| along with built-ins such as append.
 | |
| 
 | |
| The analysis currently provides no way for users to specify the aliasing
 | |
| effects of native code.
 | |
| 
 | |
| ------------------------------------------------------------------------
 | |
| 
 | |
| IMPLEMENTATION
 | |
| 
 | |
| The remaining documentation is intended for package maintainers and
 | |
| pointer analysis specialists.  Maintainers should have a solid
 | |
| understanding of the referenced papers (especially those by H&L and PKH)
 | |
| before making making significant changes.
 | |
| 
 | |
| The implementation is similar to that described in (Pearce et al,
 | |
| PASTE'04).  Unlike many algorithms which interleave constraint
 | |
| generation and solving, constructing the callgraph as they go, this
 | |
| implementation for the most part observes a phase ordering (generation
 | |
| before solving), with only simple (copy) constraints being generated
 | |
| during solving.  (The exception is reflection, which creates various
 | |
| constraints during solving as new types flow to reflect.Value
 | |
| operations.)  This improves the traction of presolver optimisations,
 | |
| but imposes certain restrictions, e.g. potential context sensitivity
 | |
| is limited since all variants must be created a priori.
 | |
| 
 | |
| 
 | |
| TERMINOLOGY
 | |
| 
 | |
| A type is said to be "pointer-like" if it is a reference to an object.
 | |
| Pointer-like types include pointers and also interfaces, maps, channels,
 | |
| functions and slices.
 | |
| 
 | |
| We occasionally use C's x->f notation to distinguish the case where x
 | |
| is a struct pointer from x.f where is a struct value.
 | |
| 
 | |
| Pointer analysis literature (and our comments) often uses the notation
 | |
| dst=*src+offset to mean something different than what it means in Go.
 | |
| It means: for each node index p in pts(src), the node index p+offset is
 | |
| in pts(dst).  Similarly *dst+offset=src is used for store constraints
 | |
| and dst=src+offset for offset-address constraints.
 | |
| 
 | |
| 
 | |
| NODES
 | |
| 
 | |
| Nodes are the key datastructure of the analysis, and have a dual role:
 | |
| they represent both constraint variables (equivalence classes of
 | |
| pointers) and members of points-to sets (things that can be pointed
 | |
| at, i.e. "labels").
 | |
| 
 | |
| Nodes are naturally numbered.  The numbering enables compact
 | |
| representations of sets of nodes such as bitvectors (or BDDs); and the
 | |
| ordering enables a very cheap way to group related nodes together.  For
 | |
| example, passing n parameters consists of generating n parallel
 | |
| constraints from caller+i to callee+i for 0<=i<n.
 | |
| 
 | |
| The zero nodeid means "not a pointer".  For simplicity, we generate flow
 | |
| constraints even for non-pointer types such as int.  The pointer
 | |
| equivalence (PE) presolver optimization detects which variables cannot
 | |
| point to anything; this includes not only all variables of non-pointer
 | |
| types (such as int) but also variables of pointer-like types if they are
 | |
| always nil, or are parameters to a function that is never called.
 | |
| 
 | |
| Each node represents a scalar part of a value or object.
 | |
| Aggregate types (structs, tuples, arrays) are recursively flattened
 | |
| out into a sequential list of scalar component types, and all the
 | |
| elements of an array are represented by a single node.  (The
 | |
| flattening of a basic type is a list containing a single node.)
 | |
| 
 | |
| Nodes are connected into a graph with various kinds of labelled edges:
 | |
| simple edges (or copy constraints) represent value flow.  Complex
 | |
| edges (load, store, etc) trigger the creation of new simple edges
 | |
| during the solving phase.
 | |
| 
 | |
| 
 | |
| OBJECTS
 | |
| 
 | |
| Conceptually, an "object" is a contiguous sequence of nodes denoting
 | |
| an addressable location: something that a pointer can point to.  The
 | |
| first node of an object has a non-nil obj field containing information
 | |
| about the allocation: its size, context, and ssa.Value.
 | |
| 
 | |
| Objects include:
 | |
|    - functions and globals;
 | |
|    - variable allocations in the stack frame or heap;
 | |
|    - maps, channels and slices created by calls to make();
 | |
|    - allocations to construct an interface;
 | |
|    - allocations caused by conversions, e.g. []byte(str).
 | |
|    - arrays allocated by calls to append();
 | |
| 
 | |
| Many objects have no Go types.  For example, the func, map and chan type
 | |
| kinds in Go are all varieties of pointers, but their respective objects
 | |
| are actual functions (executable code), maps (hash tables), and channels
 | |
| (synchronized queues).  Given the way we model interfaces, they too are
 | |
| pointers to "tagged" objects with no Go type.  And an *ssa.Global denotes
 | |
| the address of a global variable, but the object for a Global is the
 | |
| actual data.  So, the types of an ssa.Value that creates an object is
 | |
| "off by one indirection": a pointer to the object.
 | |
| 
 | |
| The individual nodes of an object are sometimes referred to as "labels".
 | |
| 
 | |
| For uniformity, all objects have a non-zero number of fields, even those
 | |
| of the empty type struct{}.  (All arrays are treated as if of length 1,
 | |
| so there are no empty arrays.  The empty tuple is never address-taken,
 | |
| so is never an object.)
 | |
| 
 | |
| 
 | |
| TAGGED OBJECTS
 | |
| 
 | |
| An tagged object has the following layout:
 | |
| 
 | |
|     T          -- obj.flags ⊇ {otTagged}
 | |
|     v
 | |
|     ...
 | |
| 
 | |
| The T node's typ field is the dynamic type of the "payload": the value
 | |
| v which follows, flattened out.  The T node's obj has the otTagged
 | |
| flag.
 | |
| 
 | |
| Tagged objects are needed when generalizing across types: interfaces,
 | |
| reflect.Values, reflect.Types.  Each of these three types is modelled
 | |
| as a pointer that exclusively points to tagged objects.
 | |
| 
 | |
| Tagged objects may be indirect (obj.flags ⊇ {otIndirect}) meaning that
 | |
| the value v is not of type T but *T; this is used only for
 | |
| reflect.Values that represent lvalues.  (These are not implemented yet.)
 | |
| 
 | |
| 
 | |
| ANALYSIS ABSTRACTION OF EACH TYPE
 | |
| 
 | |
| Variables of the following "scalar" types may be represented by a
 | |
| single node: basic types, pointers, channels, maps, slices, 'func'
 | |
| pointers, interfaces.
 | |
| 
 | |
| Pointers
 | |
|   Nothing to say here, oddly.
 | |
| 
 | |
| Basic types (bool, string, numbers, unsafe.Pointer)
 | |
|   Currently all fields in the flattening of a type, including
 | |
|   non-pointer basic types such as int, are represented in objects and
 | |
|   values.  Though non-pointer nodes within values are uninteresting,
 | |
|   non-pointer nodes in objects may be useful (if address-taken)
 | |
|   because they permit the analysis to deduce, in this example,
 | |
| 
 | |
|      var s struct{ ...; x int; ... }
 | |
|      p := &s.x
 | |
| 
 | |
|   that p points to s.x.  If we ignored such object fields, we could only
 | |
|   say that p points somewhere within s.
 | |
| 
 | |
|   All other basic types are ignored.  Expressions of these types have
 | |
|   zero nodeid, and fields of these types within aggregate other types
 | |
|   are omitted.
 | |
| 
 | |
|   unsafe.Pointers are not modelled as pointers, so a conversion of an
 | |
|   unsafe.Pointer to *T is (unsoundly) treated equivalent to new(T).
 | |
| 
 | |
| Channels
 | |
|   An expression of type 'chan T' is a kind of pointer that points
 | |
|   exclusively to channel objects, i.e. objects created by MakeChan (or
 | |
|   reflection).
 | |
| 
 | |
|   'chan T' is treated like *T.
 | |
|   *ssa.MakeChan is treated as equivalent to new(T).
 | |
|   *ssa.Send and receive (*ssa.UnOp(ARROW)) and are equivalent to store
 | |
|    and load.
 | |
| 
 | |
| Maps
 | |
|   An expression of type 'map[K]V' is a kind of pointer that points
 | |
|   exclusively to map objects, i.e. objects created by MakeMap (or
 | |
|   reflection).
 | |
| 
 | |
|   map K[V] is treated like *M where M = struct{k K; v V}.
 | |
|   *ssa.MakeMap is equivalent to new(M).
 | |
|   *ssa.MapUpdate is equivalent to *y=x where *y and x have type M.
 | |
|   *ssa.Lookup is equivalent to y=x.v where x has type *M.
 | |
| 
 | |
| Slices
 | |
|   A slice []T, which dynamically resembles a struct{array *T, len, cap int},
 | |
|   is treated as if it were just a *T pointer; the len and cap fields are
 | |
|   ignored.
 | |
| 
 | |
|   *ssa.MakeSlice is treated like new([1]T): an allocation of a
 | |
|    singleton array.
 | |
|   *ssa.Index on a slice is equivalent to a load.
 | |
|   *ssa.IndexAddr on a slice returns the address of the sole element of the
 | |
|   slice, i.e. the same address.
 | |
|   *ssa.Slice is treated as a simple copy.
 | |
| 
 | |
| Functions
 | |
|   An expression of type 'func...' is a kind of pointer that points
 | |
|   exclusively to function objects.
 | |
| 
 | |
|   A function object has the following layout:
 | |
| 
 | |
|      identity         -- typ:*types.Signature; obj.flags ⊇ {otFunction}
 | |
|      params_0         -- (the receiver, if a method)
 | |
|      ...
 | |
|      params_n-1
 | |
|      results_0
 | |
|      ...
 | |
|      results_m-1
 | |
| 
 | |
|   There may be multiple function objects for the same *ssa.Function
 | |
|   due to context-sensitive treatment of some functions.
 | |
| 
 | |
|   The first node is the function's identity node.
 | |
|   Associated with every callsite is a special "targets" variable,
 | |
|   whose pts() contains the identity node of each function to which
 | |
|   the call may dispatch.  Identity words are not otherwise used during
 | |
|   the analysis, but we construct the call graph from the pts()
 | |
|   solution for such nodes.
 | |
| 
 | |
|   The following block of contiguous nodes represents the flattened-out
 | |
|   types of the parameters ("P-block") and results ("R-block") of the
 | |
|   function object.
 | |
| 
 | |
|   The treatment of free variables of closures (*ssa.FreeVar) is like
 | |
|   that of global variables; it is not context-sensitive.
 | |
|   *ssa.MakeClosure instructions create copy edges to Captures.
 | |
| 
 | |
|   A Go value of type 'func' (i.e. a pointer to one or more functions)
 | |
|   is a pointer whose pts() contains function objects.  The valueNode()
 | |
|   for an *ssa.Function returns a singleton for that function.
 | |
| 
 | |
| Interfaces
 | |
|   An expression of type 'interface{...}' is a kind of pointer that
 | |
|   points exclusively to tagged objects.  All tagged objects pointed to
 | |
|   by an interface are direct (the otIndirect flag is clear) and
 | |
|   concrete (the tag type T is not itself an interface type).  The
 | |
|   associated ssa.Value for an interface's tagged objects may be an
 | |
|   *ssa.MakeInterface instruction, or nil if the tagged object was
 | |
|   created by an instrinsic (e.g. reflection).
 | |
| 
 | |
|   Constructing an interface value causes generation of constraints for
 | |
|   all of the concrete type's methods; we can't tell a priori which
 | |
|   ones may be called.
 | |
| 
 | |
|   TypeAssert y = x.(T) is implemented by a dynamic constraint
 | |
|   triggered by each tagged object O added to pts(x): a typeFilter
 | |
|   constraint if T is an interface type, or an untag constraint if T is
 | |
|   a concrete type.  A typeFilter tests whether O.typ implements T; if
 | |
|   so, O is added to pts(y).  An untagFilter tests whether O.typ is
 | |
|   assignable to T,and if so, a copy edge O.v -> y is added.
 | |
| 
 | |
|   ChangeInterface is a simple copy because the representation of
 | |
|   tagged objects is independent of the interface type (in contrast
 | |
|   to the "method tables" approach used by the gc runtime).
 | |
| 
 | |
|   y := Invoke x.m(...) is implemented by allocating contiguous P/R
 | |
|   blocks for the callsite and adding a dynamic rule triggered by each
 | |
|   tagged object added to pts(x).  The rule adds param/results copy
 | |
|   edges to/from each discovered concrete method.
 | |
| 
 | |
|   (Q. Why do we model an interface as a pointer to a pair of type and
 | |
|   value, rather than as a pair of a pointer to type and a pointer to
 | |
|   value?
 | |
|   A. Control-flow joins would merge interfaces ({T1}, {V1}) and ({T2},
 | |
|   {V2}) to make ({T1,T2}, {V1,V2}), leading to the infeasible and
 | |
|   type-unsafe combination (T1,V2).  Treating the value and its concrete
 | |
|   type as inseparable makes the analysis type-safe.)
 | |
| 
 | |
| reflect.Value
 | |
|   A reflect.Value is modelled very similar to an interface{}, i.e. as
 | |
|   a pointer exclusively to tagged objects, but with two generalizations.
 | |
| 
 | |
|   1) a reflect.Value that represents an lvalue points to an indirect
 | |
|      (obj.flags ⊇ {otIndirect}) tagged object, which has a similar
 | |
|      layout to an tagged object except that the value is a pointer to
 | |
|      the dynamic type.  Indirect tagged objects preserve the correct
 | |
|      aliasing so that mutations made by (reflect.Value).Set can be
 | |
|      observed.
 | |
| 
 | |
|      Indirect objects only arise when an lvalue is derived from an
 | |
|      rvalue by indirection, e.g. the following code:
 | |
| 
 | |
|         type S struct { X T }
 | |
|         var s S
 | |
|         var i interface{} = &s    // i points to a *S-tagged object (from MakeInterface)
 | |
|         v1 := reflect.ValueOf(i)  // v1 points to same *S-tagged object as i
 | |
|         v2 := v1.Elem()           // v2 points to an indirect S-tagged object, pointing to s
 | |
|         v3 := v2.FieldByName("X") // v3 points to an indirect int-tagged object, pointing to s.X
 | |
|         v3.Set(y)                 // pts(s.X) ⊇ pts(y)
 | |
| 
 | |
|      Whether indirect or not, the concrete type of the tagged object
 | |
|      corresponds to the user-visible dynamic type, and the existence
 | |
|      of a pointer is an implementation detail.
 | |
| 
 | |
|      (NB: indirect tagged objects are not yet implemented)
 | |
| 
 | |
|   2) The dynamic type tag of a tagged object pointed to by a
 | |
|      reflect.Value may be an interface type; it need not be concrete.
 | |
| 
 | |
|      This arises in code such as this:
 | |
|         tEface := reflect.TypeOf(new(interface{}).Elem() // interface{}
 | |
|         eface := reflect.Zero(tEface)
 | |
|      pts(eface) is a singleton containing an interface{}-tagged
 | |
|      object.  That tagged object's payload is an interface{} value,
 | |
|      i.e. the pts of the payload contains only concrete-tagged
 | |
|      objects, although in this example it's the zero interface{} value,
 | |
|      so its pts is empty.
 | |
| 
 | |
| reflect.Type
 | |
|   Just as in the real "reflect" library, we represent a reflect.Type
 | |
|   as an interface whose sole implementation is the concrete type,
 | |
|   *reflect.rtype.  (This choice is forced on us by go/types: clients
 | |
|   cannot fabricate types with arbitrary method sets.)
 | |
| 
 | |
|   rtype instances are canonical: there is at most one per dynamic
 | |
|   type.  (rtypes are in fact large structs but since identity is all
 | |
|   that matters, we represent them by a single node.)
 | |
| 
 | |
|   The payload of each *rtype-tagged object is an *rtype pointer that
 | |
|   points to exactly one such canonical rtype object.  We exploit this
 | |
|   by setting the node.typ of the payload to the dynamic type, not
 | |
|   '*rtype'.  This saves us an indirection in each resolution rule.  As
 | |
|   an optimisation, *rtype-tagged objects are canonicalized too.
 | |
| 
 | |
| 
 | |
| Aggregate types:
 | |
| 
 | |
| Aggregate types are treated as if all directly contained
 | |
| aggregates are recursively flattened out.
 | |
| 
 | |
| Structs
 | |
|   *ssa.Field y = x.f creates a simple edge to y from x's node at f's offset.
 | |
| 
 | |
|   *ssa.FieldAddr y = &x->f requires a dynamic closure rule to create
 | |
|    simple edges for each struct discovered in pts(x).
 | |
| 
 | |
|   The nodes of a struct consist of a special 'identity' node (whose
 | |
|   type is that of the struct itself), followed by the nodes for all
 | |
|   the struct's fields, recursively flattened out.  A pointer to the
 | |
|   struct is a pointer to its identity node.  That node allows us to
 | |
|   distinguish a pointer to a struct from a pointer to its first field.
 | |
| 
 | |
|   Field offsets are logical field offsets (plus one for the identity
 | |
|   node), so the sizes of the fields can be ignored by the analysis.
 | |
| 
 | |
|   (The identity node is non-traditional but enables the distinction
 | |
|   described above, which is valuable for code comprehension tools.
 | |
|   Typical pointer analyses for C, whose purpose is compiler
 | |
|   optimization, must soundly model unsafe.Pointer (void*) conversions,
 | |
|   and this requires fidelity to the actual memory layout using physical
 | |
|   field offsets.)
 | |
| 
 | |
|   *ssa.Field y = x.f creates a simple edge to y from x's node at f's offset.
 | |
| 
 | |
|   *ssa.FieldAddr y = &x->f requires a dynamic closure rule to create
 | |
|    simple edges for each struct discovered in pts(x).
 | |
| 
 | |
| Arrays
 | |
|   We model an array by an identity node (whose type is that of the
 | |
|   array itself) followed by a node representing all the elements of
 | |
|   the array; the analysis does not distinguish elements with different
 | |
|   indices.  Effectively, an array is treated like struct{elem T}, a
 | |
|   load y=x[i] like y=x.elem, and a store x[i]=y like x.elem=y; the
 | |
|   index i is ignored.
 | |
| 
 | |
|   A pointer to an array is pointer to its identity node.  (A slice is
 | |
|   also a pointer to an array's identity node.)  The identity node
 | |
|   allows us to distinguish a pointer to an array from a pointer to one
 | |
|   of its elements, but it is rather costly because it introduces more
 | |
|   offset constraints into the system.  Furthermore, sound treatment of
 | |
|   unsafe.Pointer would require us to dispense with this node.
 | |
| 
 | |
|   Arrays may be allocated by Alloc, by make([]T), by calls to append,
 | |
|   and via reflection.
 | |
| 
 | |
| Tuples (T, ...)
 | |
|   Tuples are treated like structs with naturally numbered fields.
 | |
|   *ssa.Extract is analogous to *ssa.Field.
 | |
| 
 | |
|   However, tuples have no identity field since by construction, they
 | |
|   cannot be address-taken.
 | |
| 
 | |
| 
 | |
| FUNCTION CALLS
 | |
| 
 | |
|   There are three kinds of function call:
 | |
|   (1) static "call"-mode calls of functions.
 | |
|   (2) dynamic "call"-mode calls of functions.
 | |
|   (3) dynamic "invoke"-mode calls of interface methods.
 | |
|   Cases 1 and 2 apply equally to methods and standalone functions.
 | |
| 
 | |
|   Static calls.
 | |
|     A static call consists three steps:
 | |
|     - finding the function object of the callee;
 | |
|     - creating copy edges from the actual parameter value nodes to the
 | |
|       P-block in the function object (this includes the receiver if
 | |
|       the callee is a method);
 | |
|     - creating copy edges from the R-block in the function object to
 | |
|       the value nodes for the result of the call.
 | |
| 
 | |
|     A static function call is little more than two struct value copies
 | |
|     between the P/R blocks of caller and callee:
 | |
| 
 | |
|        callee.P = caller.P
 | |
|        caller.R = callee.R
 | |
| 
 | |
|     Context sensitivity
 | |
| 
 | |
|       Static calls (alone) may be treated context sensitively,
 | |
|       i.e. each callsite may cause a distinct re-analysis of the
 | |
|       callee, improving precision.  Our current context-sensitivity
 | |
|       policy treats all intrinsics and getter/setter methods in this
 | |
|       manner since such functions are small and seem like an obvious
 | |
|       source of spurious confluences, though this has not yet been
 | |
|       evaluated.
 | |
| 
 | |
|   Dynamic function calls
 | |
| 
 | |
|     Dynamic calls work in a similar manner except that the creation of
 | |
|     copy edges occurs dynamically, in a similar fashion to a pair of
 | |
|     struct copies in which the callee is indirect:
 | |
| 
 | |
|        callee->P = caller.P
 | |
|        caller.R = callee->R
 | |
| 
 | |
|     (Recall that the function object's P- and R-blocks are contiguous.)
 | |
| 
 | |
|   Interface method invocation
 | |
| 
 | |
|     For invoke-mode calls, we create a params/results block for the
 | |
|     callsite and attach a dynamic closure rule to the interface.  For
 | |
|     each new tagged object that flows to the interface, we look up
 | |
|     the concrete method, find its function object, and connect its P/R
 | |
|     blocks to the callsite's P/R blocks, adding copy edges to the graph
 | |
|     during solving.
 | |
| 
 | |
|   Recording call targets
 | |
| 
 | |
|     The analysis notifies its clients of each callsite it encounters,
 | |
|     passing a CallSite interface.  Among other things, the CallSite
 | |
|     contains a synthetic constraint variable ("targets") whose
 | |
|     points-to solution includes the set of all function objects to
 | |
|     which the call may dispatch.
 | |
| 
 | |
|     It is via this mechanism that the callgraph is made available.
 | |
|     Clients may also elect to be notified of callgraph edges directly;
 | |
|     internally this just iterates all "targets" variables' pts(·)s.
 | |
| 
 | |
| 
 | |
| PRESOLVER
 | |
| 
 | |
| We implement Hash-Value Numbering (HVN), a pre-solver constraint
 | |
| optimization described in Hardekopf & Lin, SAS'07.  This is documented
 | |
| in more detail in hvn.go.  We intend to add its cousins HR and HU in
 | |
| future.
 | |
| 
 | |
| 
 | |
| SOLVER
 | |
| 
 | |
| The solver is currently a naive Andersen-style implementation; it does
 | |
| not perform online cycle detection, though we plan to add solver
 | |
| optimisations such as Hybrid- and Lazy- Cycle Detection from (Hardekopf
 | |
| & Lin, PLDI'07).
 | |
| 
 | |
| It uses difference propagation (Pearce et al, SQC'04) to avoid
 | |
| redundant re-triggering of closure rules for values already seen.
 | |
| 
 | |
| Points-to sets are represented using sparse bit vectors (similar to
 | |
| those used in LLVM and gcc), which are more space- and time-efficient
 | |
| than sets based on Go's built-in map type or dense bit vectors.
 | |
| 
 | |
| Nodes are permuted prior to solving so that object nodes (which may
 | |
| appear in points-to sets) are lower numbered than non-object (var)
 | |
| nodes.  This improves the density of the set over which the PTSs
 | |
| range, and thus the efficiency of the representation.
 | |
| 
 | |
| Partly thanks to avoiding map iteration, the execution of the solver is
 | |
| 100% deterministic, a great help during debugging.
 | |
| 
 | |
| 
 | |
| FURTHER READING
 | |
| 
 | |
| Andersen, L. O. 1994. Program analysis and specialization for the C
 | |
| programming language. Ph.D. dissertation. DIKU, University of
 | |
| Copenhagen.
 | |
| 
 | |
| David J. Pearce, Paul H. J. Kelly, and Chris Hankin. 2004.  Efficient
 | |
| field-sensitive pointer analysis for C. In Proceedings of the 5th ACM
 | |
| SIGPLAN-SIGSOFT workshop on Program analysis for software tools and
 | |
| engineering (PASTE '04). ACM, New York, NY, USA, 37-42.
 | |
| http://doi.acm.org/10.1145/996821.996835
 | |
| 
 | |
| David J. Pearce, Paul H. J. Kelly, and Chris Hankin. 2004. Online
 | |
| Cycle Detection and Difference Propagation: Applications to Pointer
 | |
| Analysis. Software Quality Control 12, 4 (December 2004), 311-337.
 | |
| http://dx.doi.org/10.1023/B:SQJO.0000039791.93071.a2
 | |
| 
 | |
| David Grove and Craig Chambers. 2001. A framework for call graph
 | |
| construction algorithms. ACM Trans. Program. Lang. Syst. 23, 6
 | |
| (November 2001), 685-746.
 | |
| http://doi.acm.org/10.1145/506315.506316
 | |
| 
 | |
| Ben Hardekopf and Calvin Lin. 2007. The ant and the grasshopper: fast
 | |
| and accurate pointer analysis for millions of lines of code. In
 | |
| Proceedings of the 2007 ACM SIGPLAN conference on Programming language
 | |
| design and implementation (PLDI '07). ACM, New York, NY, USA, 290-299.
 | |
| http://doi.acm.org/10.1145/1250734.1250767
 | |
| 
 | |
| Ben Hardekopf and Calvin Lin. 2007. Exploiting pointer and location
 | |
| equivalence to optimize pointer analysis. In Proceedings of the 14th
 | |
| international conference on Static Analysis (SAS'07), Hanne Riis
 | |
| Nielson and Gilberto Filé (Eds.). Springer-Verlag, Berlin, Heidelberg,
 | |
| 265-280.
 | |
| 
 | |
| Atanas Rountev and Satish Chandra. 2000. Off-line variable substitution
 | |
| for scaling points-to analysis. In Proceedings of the ACM SIGPLAN 2000
 | |
| conference on Programming language design and implementation (PLDI '00).
 | |
| ACM, New York, NY, USA, 47-56. DOI=10.1145/349299.349310
 | |
| http://doi.acm.org/10.1145/349299.349310
 | |
| 
 | |
| */
 | |
| package pointer // import "golang.org/x/tools/go/pointer"
 |