2013-08-27 16:49:13 -06:00
|
|
|
// 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.
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
package pointer
|
|
|
|
|
|
|
|
import (
|
2013-12-05 20:30:42 -07:00
|
|
|
"bytes"
|
2013-08-22 10:27:55 -06:00
|
|
|
"fmt"
|
|
|
|
"go/token"
|
|
|
|
"io"
|
|
|
|
|
2013-09-25 15:17:42 -06:00
|
|
|
"code.google.com/p/go.tools/call"
|
2013-08-22 10:27:55 -06:00
|
|
|
"code.google.com/p/go.tools/go/types/typemap"
|
|
|
|
"code.google.com/p/go.tools/ssa"
|
|
|
|
)
|
|
|
|
|
2013-09-30 10:39:54 -06:00
|
|
|
// A Config formulates a pointer analysis problem for Analyze().
|
2013-08-22 10:27:55 -06:00
|
|
|
type Config struct {
|
2013-09-30 10:39:54 -06:00
|
|
|
// Mains contains the set of 'main' packages to analyze
|
|
|
|
// Clients must provide the analysis with at least one
|
|
|
|
// package defining a main() function.
|
|
|
|
Mains []*ssa.Package
|
2013-08-22 10:27:55 -06:00
|
|
|
|
2013-09-23 14:13:01 -06:00
|
|
|
// Reflection determines whether to handle reflection
|
|
|
|
// operators soundly, which is currently rather slow since it
|
|
|
|
// causes constraint to be generated during solving
|
|
|
|
// proportional to the number of constraint variables, which
|
|
|
|
// has not yet been reduced by presolver optimisation.
|
|
|
|
Reflection bool
|
|
|
|
|
2013-09-25 15:17:42 -06:00
|
|
|
// BuildCallGraph determines whether to construct a callgraph.
|
|
|
|
// If enabled, the graph will be available in Result.CallGraph.
|
|
|
|
BuildCallGraph bool
|
2013-08-22 10:27:55 -06:00
|
|
|
|
|
|
|
// Print is invoked during the analysis for each discovered
|
2013-09-30 10:39:54 -06:00
|
|
|
// call to the built-in print(x), providing a convenient way
|
|
|
|
// to identify arbitrary expressions of interest in the tests.
|
2013-08-22 10:27:55 -06:00
|
|
|
//
|
|
|
|
// Pointer p may be saved until the analysis is complete, at
|
|
|
|
// which point its methods provide access to the analysis
|
|
|
|
// (The result of callings its methods within the Print
|
|
|
|
// callback is undefined.) p is nil if x is non-pointerlike.
|
|
|
|
//
|
|
|
|
Print func(site *ssa.CallCommon, p Pointer)
|
|
|
|
|
2013-12-05 20:30:42 -07:00
|
|
|
// The client populates Queries[v] or IndirectQueries[v]
|
|
|
|
// for each ssa.Value v of interest, to request that the
|
|
|
|
// points-to sets pts(v) or pts(*v) be computed. If the
|
|
|
|
// client needs both points-to sets, v may appear in both
|
|
|
|
// maps.
|
2013-09-09 19:06:25 -06:00
|
|
|
//
|
2013-12-05 20:30:42 -07:00
|
|
|
// (IndirectQueries is typically used for Values corresponding
|
|
|
|
// to source-level lvalues, e.g. an *ssa.Global.)
|
2013-09-09 19:06:25 -06:00
|
|
|
//
|
2013-12-05 20:30:42 -07:00
|
|
|
// The analysis populates the corresponding
|
|
|
|
// Results.{Indirect,}Queries map when it creates the pointer
|
|
|
|
// variable for v or *v. Upon completion the client can
|
|
|
|
// inspect that map for the results.
|
2013-08-22 10:27:55 -06:00
|
|
|
//
|
|
|
|
// If a Value belongs to a function that the analysis treats
|
2013-12-05 20:30:42 -07:00
|
|
|
// context-sensitively, the corresponding Results.{Indirect,}Queries
|
|
|
|
// slice may have multiple Pointers, one per distinct context.
|
|
|
|
// Use PointsToCombined to merge them.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): this API doesn't scale well for batch tools
|
|
|
|
// that want to dump the entire solution.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): need we distinguish contexts? Current
|
|
|
|
// clients always combine them.
|
2013-08-22 10:27:55 -06:00
|
|
|
//
|
2013-12-05 20:30:42 -07:00
|
|
|
Queries map[ssa.Value]struct{}
|
|
|
|
IndirectQueries map[ssa.Value]struct{}
|
2013-08-22 10:27:55 -06:00
|
|
|
|
2013-09-30 10:39:54 -06:00
|
|
|
// If Log is non-nil, log messages are written to it.
|
2013-08-22 10:27:55 -06:00
|
|
|
// Logging is extremely verbose.
|
|
|
|
Log io.Writer
|
|
|
|
}
|
|
|
|
|
2013-12-05 20:30:42 -07:00
|
|
|
// AddQuery adds v to Config.Queries.
|
|
|
|
func (c *Config) AddQuery(v ssa.Value) {
|
|
|
|
if c.Queries == nil {
|
|
|
|
c.Queries = make(map[ssa.Value]struct{})
|
|
|
|
}
|
|
|
|
c.Queries[v] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddQuery adds v to Config.IndirectQueries.
|
|
|
|
func (c *Config) AddIndirectQuery(v ssa.Value) {
|
|
|
|
if c.IndirectQueries == nil {
|
|
|
|
c.IndirectQueries = make(map[ssa.Value]struct{})
|
|
|
|
}
|
|
|
|
c.IndirectQueries[v] = struct{}{}
|
|
|
|
}
|
2013-09-09 19:06:25 -06:00
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
func (c *Config) prog() *ssa.Program {
|
|
|
|
for _, main := range c.Mains {
|
|
|
|
return main.Prog
|
|
|
|
}
|
|
|
|
panic("empty scope")
|
|
|
|
}
|
|
|
|
|
2013-09-30 10:39:54 -06:00
|
|
|
type Warning struct {
|
|
|
|
Pos token.Pos
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
2013-09-25 15:17:42 -06:00
|
|
|
// A Result contains the results of a pointer analysis.
|
|
|
|
//
|
|
|
|
// See Config for how to request the various Result components.
|
|
|
|
//
|
|
|
|
type Result struct {
|
2013-12-05 20:30:42 -07:00
|
|
|
CallGraph call.Graph // discovered call graph
|
|
|
|
Queries map[ssa.Value][]Pointer // pts(v) for each v in Config.Queries.
|
|
|
|
IndirectQueries map[ssa.Value][]Pointer // pts(*v) for each v in Config.IndirectQueries.
|
|
|
|
Warnings []Warning // warnings of unsoundness
|
2013-09-25 15:17:42 -06:00
|
|
|
}
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
// A Pointer is an equivalence class of pointerlike values.
|
|
|
|
type Pointer interface {
|
|
|
|
// PointsTo returns the points-to set of this pointer.
|
|
|
|
PointsTo() PointsToSet
|
|
|
|
|
|
|
|
// MayAlias reports whether the receiver pointer may alias
|
|
|
|
// the argument pointer.
|
|
|
|
MayAlias(Pointer) bool
|
|
|
|
|
go.tools/pointer: strength reduction during constraint generation.
Motivation: simple constraints---copy and addr---are more
amenable to pre-solver optimizations (forthcoming) than
complex constraints: load, store, and all others.
In code such as the following:
t0 = new struct { x, y int }
t1 = &t0.y
t2 = *t1
there's no need for the full generality of a (complex)
load constraint for t2=*t1 since t1 can only point to t0.y.
All we need is a (simple) copy constraint t2 = (t0.y)
where (t0.y) is the object node label for that field.
For all "addressable" SSA instructions, we tabulate
whether their points-to set is necessarily a singleton. For
some (e.g. Alloc, MakeSlice, etc) this is always true by
design. For others (e.g. FieldAddr) it depends on their
operands.
We exploit this information when generating constraints:
all load-form and store-form constraints are reduced to copy
constraints if the pointer's PTS is a singleton.
Similarly all FieldAddr (y=&x.f) and IndexAddr (y=&x[0])
constraints are reduced to offset addition, for singleton
operands.
Here's the constraint mix when running on the oracle itself.
The total number of constraints is unchanged but the fraction
that are complex has gone down to 21% from 53%.
before after
--simple--
addr 20682 46949
copy 61454 91211
--complex--
offsetAddr 41621 15325
load 18769 12925
store 30758 6908
invoke 758 760
typeAssert 1688 1689
total 175832 175869
Also:
- Add Pointer.Context() for local variables,
since we now plumb cgnodes throughout. Nice.
- Refactor all load-form (load, receive, lookup) and
store-form (Store, send, MapUpdate) constraints to use
genLoad and genStore.
- Log counts of constraints by type.
- valNodes split into localval and globalval maps;
localval is purged after each function.
- analogous maps localobj[v] and globalobj[v] hold sole label
for pts(v), if singleton.
- fnObj map subsumed by globalobj.
- make{Function/Global/Constant} inlined into objectValue.
Much cleaner.
R=crawshaw
CC=golang-dev
https://golang.org/cl/13979043
2013-09-27 09:33:01 -06:00
|
|
|
// Context returns the context of this pointer,
|
|
|
|
// if it corresponds to a local variable.
|
|
|
|
Context() call.GraphNode
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
String() string
|
|
|
|
}
|
|
|
|
|
|
|
|
// A PointsToSet is a set of labels (locations or allocations).
|
|
|
|
//
|
|
|
|
type PointsToSet interface {
|
|
|
|
// PointsTo returns the set of labels that this points-to set
|
|
|
|
// contains.
|
|
|
|
Labels() []*Label
|
|
|
|
|
|
|
|
// Intersects reports whether this points-to set and the
|
|
|
|
// argument points-to set contain common members.
|
|
|
|
Intersects(PointsToSet) bool
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// If this PointsToSet came from a Pointer of interface kind
|
|
|
|
// or a reflect.Value, DynamicTypes returns the set of dynamic
|
|
|
|
// types that it may contain. (For an interface, they will
|
|
|
|
// always be concrete types.)
|
|
|
|
//
|
|
|
|
// The result is a mapping whose keys are the dynamic types to
|
|
|
|
// which it may point. For each pointer-like key type, the
|
|
|
|
// corresponding map value is a set of pointer abstractions of
|
|
|
|
// that dynamic type, represented as a []Pointer slice. Use
|
|
|
|
// PointsToCombined to merge them.
|
|
|
|
//
|
|
|
|
// The result is empty unless CanHaveDynamicTypes(T).
|
|
|
|
//
|
|
|
|
DynamicTypes() *typemap.M
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Union returns the set containing all the elements of each set in sets.
|
|
|
|
func Union(sets ...PointsToSet) PointsToSet {
|
|
|
|
var union ptset
|
|
|
|
for _, set := range sets {
|
|
|
|
set := set.(ptset)
|
|
|
|
union.a = set.a
|
|
|
|
union.pts.addAll(set.pts)
|
|
|
|
}
|
|
|
|
return union
|
|
|
|
}
|
|
|
|
|
|
|
|
// PointsToCombined returns the combined points-to set of all the
|
|
|
|
// specified pointers.
|
|
|
|
func PointsToCombined(ptrs []Pointer) PointsToSet {
|
|
|
|
var ptsets []PointsToSet
|
|
|
|
for _, ptr := range ptrs {
|
|
|
|
ptsets = append(ptsets, ptr.PointsTo())
|
|
|
|
}
|
|
|
|
return Union(ptsets...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---- PointsToSet public interface
|
|
|
|
|
|
|
|
type ptset struct {
|
|
|
|
a *analysis // may be nil if pts is nil
|
|
|
|
pts nodeset
|
|
|
|
}
|
|
|
|
|
2013-12-05 20:30:42 -07:00
|
|
|
func (s ptset) String() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprintf(&buf, "[")
|
|
|
|
sep := ""
|
|
|
|
for l := range s.pts {
|
|
|
|
fmt.Fprintf(&buf, "%s%s", sep, s.a.labelFor(l))
|
|
|
|
sep = ", "
|
|
|
|
}
|
|
|
|
fmt.Fprintf(&buf, "]")
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
func (s ptset) Labels() []*Label {
|
|
|
|
var labels []*Label
|
|
|
|
for l := range s.pts {
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
labels = append(labels, s.a.labelFor(l))
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
return labels
|
|
|
|
}
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
func (s ptset) DynamicTypes() *typemap.M {
|
|
|
|
var tmap typemap.M
|
|
|
|
tmap.SetHasher(s.a.hasher)
|
2013-08-22 10:27:55 -06:00
|
|
|
for ifaceObjId := range s.pts {
|
2013-10-29 19:57:53 -06:00
|
|
|
if !s.a.isTaggedObject(ifaceObjId) {
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
continue // !CanHaveDynamicTypes(tDyn)
|
|
|
|
}
|
2013-10-29 19:57:53 -06:00
|
|
|
tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
if indirect {
|
|
|
|
panic("indirect tagged object") // implement later
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
prev, _ := tmap.At(tDyn).([]Pointer)
|
go.tools/pointer: strength reduction during constraint generation.
Motivation: simple constraints---copy and addr---are more
amenable to pre-solver optimizations (forthcoming) than
complex constraints: load, store, and all others.
In code such as the following:
t0 = new struct { x, y int }
t1 = &t0.y
t2 = *t1
there's no need for the full generality of a (complex)
load constraint for t2=*t1 since t1 can only point to t0.y.
All we need is a (simple) copy constraint t2 = (t0.y)
where (t0.y) is the object node label for that field.
For all "addressable" SSA instructions, we tabulate
whether their points-to set is necessarily a singleton. For
some (e.g. Alloc, MakeSlice, etc) this is always true by
design. For others (e.g. FieldAddr) it depends on their
operands.
We exploit this information when generating constraints:
all load-form and store-form constraints are reduced to copy
constraints if the pointer's PTS is a singleton.
Similarly all FieldAddr (y=&x.f) and IndexAddr (y=&x[0])
constraints are reduced to offset addition, for singleton
operands.
Here's the constraint mix when running on the oracle itself.
The total number of constraints is unchanged but the fraction
that are complex has gone down to 21% from 53%.
before after
--simple--
addr 20682 46949
copy 61454 91211
--complex--
offsetAddr 41621 15325
load 18769 12925
store 30758 6908
invoke 758 760
typeAssert 1688 1689
total 175832 175869
Also:
- Add Pointer.Context() for local variables,
since we now plumb cgnodes throughout. Nice.
- Refactor all load-form (load, receive, lookup) and
store-form (Store, send, MapUpdate) constraints to use
genLoad and genStore.
- Log counts of constraints by type.
- valNodes split into localval and globalval maps;
localval is purged after each function.
- analogous maps localobj[v] and globalobj[v] hold sole label
for pts(v), if singleton.
- fnObj map subsumed by globalobj.
- make{Function/Global/Constant} inlined into objectValue.
Much cleaner.
R=crawshaw
CC=golang-dev
https://golang.org/cl/13979043
2013-09-27 09:33:01 -06:00
|
|
|
tmap.Set(tDyn, append(prev, ptr{s.a, nil, v}))
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
return &tmap
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x ptset) Intersects(y_ PointsToSet) bool {
|
|
|
|
y := y_.(ptset)
|
|
|
|
for l := range x.pts {
|
|
|
|
if _, ok := y.pts[l]; ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---- Pointer public interface
|
|
|
|
|
|
|
|
// ptr adapts a node to the Pointer interface.
|
|
|
|
type ptr struct {
|
go.tools/pointer: strength reduction during constraint generation.
Motivation: simple constraints---copy and addr---are more
amenable to pre-solver optimizations (forthcoming) than
complex constraints: load, store, and all others.
In code such as the following:
t0 = new struct { x, y int }
t1 = &t0.y
t2 = *t1
there's no need for the full generality of a (complex)
load constraint for t2=*t1 since t1 can only point to t0.y.
All we need is a (simple) copy constraint t2 = (t0.y)
where (t0.y) is the object node label for that field.
For all "addressable" SSA instructions, we tabulate
whether their points-to set is necessarily a singleton. For
some (e.g. Alloc, MakeSlice, etc) this is always true by
design. For others (e.g. FieldAddr) it depends on their
operands.
We exploit this information when generating constraints:
all load-form and store-form constraints are reduced to copy
constraints if the pointer's PTS is a singleton.
Similarly all FieldAddr (y=&x.f) and IndexAddr (y=&x[0])
constraints are reduced to offset addition, for singleton
operands.
Here's the constraint mix when running on the oracle itself.
The total number of constraints is unchanged but the fraction
that are complex has gone down to 21% from 53%.
before after
--simple--
addr 20682 46949
copy 61454 91211
--complex--
offsetAddr 41621 15325
load 18769 12925
store 30758 6908
invoke 758 760
typeAssert 1688 1689
total 175832 175869
Also:
- Add Pointer.Context() for local variables,
since we now plumb cgnodes throughout. Nice.
- Refactor all load-form (load, receive, lookup) and
store-form (Store, send, MapUpdate) constraints to use
genLoad and genStore.
- Log counts of constraints by type.
- valNodes split into localval and globalval maps;
localval is purged after each function.
- analogous maps localobj[v] and globalobj[v] hold sole label
for pts(v), if singleton.
- fnObj map subsumed by globalobj.
- make{Function/Global/Constant} inlined into objectValue.
Much cleaner.
R=crawshaw
CC=golang-dev
https://golang.org/cl/13979043
2013-09-27 09:33:01 -06:00
|
|
|
a *analysis
|
|
|
|
cgn *cgnode
|
|
|
|
n nodeid // non-zero
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p ptr) String() string {
|
|
|
|
return fmt.Sprintf("n%d", p.n)
|
|
|
|
}
|
|
|
|
|
go.tools/pointer: strength reduction during constraint generation.
Motivation: simple constraints---copy and addr---are more
amenable to pre-solver optimizations (forthcoming) than
complex constraints: load, store, and all others.
In code such as the following:
t0 = new struct { x, y int }
t1 = &t0.y
t2 = *t1
there's no need for the full generality of a (complex)
load constraint for t2=*t1 since t1 can only point to t0.y.
All we need is a (simple) copy constraint t2 = (t0.y)
where (t0.y) is the object node label for that field.
For all "addressable" SSA instructions, we tabulate
whether their points-to set is necessarily a singleton. For
some (e.g. Alloc, MakeSlice, etc) this is always true by
design. For others (e.g. FieldAddr) it depends on their
operands.
We exploit this information when generating constraints:
all load-form and store-form constraints are reduced to copy
constraints if the pointer's PTS is a singleton.
Similarly all FieldAddr (y=&x.f) and IndexAddr (y=&x[0])
constraints are reduced to offset addition, for singleton
operands.
Here's the constraint mix when running on the oracle itself.
The total number of constraints is unchanged but the fraction
that are complex has gone down to 21% from 53%.
before after
--simple--
addr 20682 46949
copy 61454 91211
--complex--
offsetAddr 41621 15325
load 18769 12925
store 30758 6908
invoke 758 760
typeAssert 1688 1689
total 175832 175869
Also:
- Add Pointer.Context() for local variables,
since we now plumb cgnodes throughout. Nice.
- Refactor all load-form (load, receive, lookup) and
store-form (Store, send, MapUpdate) constraints to use
genLoad and genStore.
- Log counts of constraints by type.
- valNodes split into localval and globalval maps;
localval is purged after each function.
- analogous maps localobj[v] and globalobj[v] hold sole label
for pts(v), if singleton.
- fnObj map subsumed by globalobj.
- make{Function/Global/Constant} inlined into objectValue.
Much cleaner.
R=crawshaw
CC=golang-dev
https://golang.org/cl/13979043
2013-09-27 09:33:01 -06:00
|
|
|
func (p ptr) Context() call.GraphNode {
|
|
|
|
return p.cgn
|
|
|
|
}
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
func (p ptr) PointsTo() PointsToSet {
|
|
|
|
return ptset{p.a, p.a.nodes[p.n].pts}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p ptr) MayAlias(q Pointer) bool {
|
|
|
|
return p.PointsTo().Intersects(q.PointsTo())
|
|
|
|
}
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
func (p ptr) DynamicTypes() *typemap.M {
|
|
|
|
return p.PointsTo().DynamicTypes()
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|