GoPLS Viewer

Home|gopls/go/pointer/intrinsics.go
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
5package pointer
6
7// This package defines the treatment of intrinsics, i.e. library
8// functions requiring special analytical treatment.
9//
10// Most of these are C or assembly functions, but even some Go
11// functions require may special treatment if the analysis completely
12// replaces the implementation of an API such as reflection.
13
14// TODO(adonovan): support a means of writing analytic summaries in
15// the target code, so that users can summarise the effects of their
16// own C functions using a snippet of Go.
17
18import (
19    "fmt"
20    "go/types"
21
22    "golang.org/x/tools/go/ssa"
23)
24
25// Instances of 'intrinsic' generate analysis constraints for calls to
26// intrinsic functions.
27// Implementations may exploit information from the calling site
28// via cgn.callersite; for shared contours this is nil.
29type intrinsic func(a *analysiscgn *cgnode)
30
31// Initialized in explicit init() to defeat (spurious) initialization
32// cycle error.
33var intrinsicsByName = make(map[string]intrinsic)
34
35func init() {
36    // Key strings are from Function.String().
37    // That little dot ۰ is an Arabic zero numeral (U+06F0),
38    // categories [Nd].
39    for namefn := range map[string]intrinsic{
40        // Other packages.
41        "bytes.Equal":                  ext۰NoEffect,
42        "bytes.IndexByte":              ext۰NoEffect,
43        "crypto/aes.decryptBlockAsm":   ext۰NoEffect,
44        "crypto/aes.encryptBlockAsm":   ext۰NoEffect,
45        "crypto/aes.expandKeyAsm":      ext۰NoEffect,
46        "crypto/aes.hasAsm":            ext۰NoEffect,
47        "crypto/md5.block":             ext۰NoEffect,
48        "crypto/rc4.xorKeyStream":      ext۰NoEffect,
49        "crypto/sha1.block":            ext۰NoEffect,
50        "crypto/sha256.block":          ext۰NoEffect,
51        "hash/crc32.castagnoliSSE42":   ext۰NoEffect,
52        "hash/crc32.haveSSE42":         ext۰NoEffect,
53        "math.Abs":                     ext۰NoEffect,
54        "math.Acos":                    ext۰NoEffect,
55        "math.Asin":                    ext۰NoEffect,
56        "math.Atan":                    ext۰NoEffect,
57        "math.Atan2":                   ext۰NoEffect,
58        "math.Ceil":                    ext۰NoEffect,
59        "math.Cos":                     ext۰NoEffect,
60        "math.Dim":                     ext۰NoEffect,
61        "math.Exp":                     ext۰NoEffect,
62        "math.Exp2":                    ext۰NoEffect,
63        "math.Expm1":                   ext۰NoEffect,
64        "math.Float32bits":             ext۰NoEffect,
65        "math.Float32frombits":         ext۰NoEffect,
66        "math.Float64bits":             ext۰NoEffect,
67        "math.Float64frombits":         ext۰NoEffect,
68        "math.Floor":                   ext۰NoEffect,
69        "math.Frexp":                   ext۰NoEffect,
70        "math.Hypot":                   ext۰NoEffect,
71        "math.Ldexp":                   ext۰NoEffect,
72        "math.Log":                     ext۰NoEffect,
73        "math.Log10":                   ext۰NoEffect,
74        "math.Log1p":                   ext۰NoEffect,
75        "math.Log2":                    ext۰NoEffect,
76        "math.Max":                     ext۰NoEffect,
77        "math.Min":                     ext۰NoEffect,
78        "math.Mod":                     ext۰NoEffect,
79        "math.Modf":                    ext۰NoEffect,
80        "math.Remainder":               ext۰NoEffect,
81        "math.Sin":                     ext۰NoEffect,
82        "math.Sincos":                  ext۰NoEffect,
83        "math.Sqrt":                    ext۰NoEffect,
84        "math.Tan":                     ext۰NoEffect,
85        "math.Trunc":                   ext۰NoEffect,
86        "math/big.addMulVVW":           ext۰NoEffect,
87        "math/big.addVV":               ext۰NoEffect,
88        "math/big.addVW":               ext۰NoEffect,
89        "math/big.bitLen":              ext۰NoEffect,
90        "math/big.divWVW":              ext۰NoEffect,
91        "math/big.divWW":               ext۰NoEffect,
92        "math/big.mulAddVWW":           ext۰NoEffect,
93        "math/big.mulWW":               ext۰NoEffect,
94        "math/big.shlVU":               ext۰NoEffect,
95        "math/big.shrVU":               ext۰NoEffect,
96        "math/big.subVV":               ext۰NoEffect,
97        "math/big.subVW":               ext۰NoEffect,
98        "net.runtime_Semacquire":       ext۰NoEffect,
99        "net.runtime_Semrelease":       ext۰NoEffect,
100        "net.runtime_pollClose":        ext۰NoEffect,
101        "net.runtime_pollOpen":         ext۰NoEffect,
102        "net.runtime_pollReset":        ext۰NoEffect,
103        "net.runtime_pollServerInit":   ext۰NoEffect,
104        "net.runtime_pollSetDeadline":  ext۰NoEffect,
105        "net.runtime_pollUnblock":      ext۰NoEffect,
106        "net.runtime_pollWait":         ext۰NoEffect,
107        "net.runtime_pollWaitCanceled"ext۰NoEffect,
108        "os.epipecheck":                ext۰NoEffect,
109        // All other runtime functions are treated as NoEffect.
110        "runtime.SetFinalizer":              ext۰runtime۰SetFinalizer,
111        "strings.IndexByte":                 ext۰NoEffect,
112        "sync.runtime_Semacquire":           ext۰NoEffect,
113        "sync.runtime_Semrelease":           ext۰NoEffect,
114        "sync.runtime_Syncsemacquire":       ext۰NoEffect,
115        "sync.runtime_Syncsemcheck":         ext۰NoEffect,
116        "sync.runtime_Syncsemrelease":       ext۰NoEffect,
117        "sync.runtime_procPin":              ext۰NoEffect,
118        "sync.runtime_procUnpin":            ext۰NoEffect,
119        "sync.runtime_registerPool":         ext۰NoEffect,
120        "sync/atomic.AddInt32":              ext۰NoEffect,
121        "sync/atomic.AddInt64":              ext۰NoEffect,
122        "sync/atomic.AddUint32":             ext۰NoEffect,
123        "sync/atomic.AddUint64":             ext۰NoEffect,
124        "sync/atomic.AddUintptr":            ext۰NoEffect,
125        "sync/atomic.CompareAndSwapInt32":   ext۰NoEffect,
126        "sync/atomic.CompareAndSwapUint32":  ext۰NoEffect,
127        "sync/atomic.CompareAndSwapUint64":  ext۰NoEffect,
128        "sync/atomic.CompareAndSwapUintptr"ext۰NoEffect,
129        "sync/atomic.LoadInt32":             ext۰NoEffect,
130        "sync/atomic.LoadInt64":             ext۰NoEffect,
131        "sync/atomic.LoadPointer":           ext۰NoEffect// ignore unsafe.Pointers
132        "sync/atomic.LoadUint32":            ext۰NoEffect,
133        "sync/atomic.LoadUint64":            ext۰NoEffect,
134        "sync/atomic.LoadUintptr":           ext۰NoEffect,
135        "sync/atomic.StoreInt32":            ext۰NoEffect,
136        "sync/atomic.StorePointer":          ext۰NoEffect// ignore unsafe.Pointers
137        "sync/atomic.StoreUint32":           ext۰NoEffect,
138        "sync/atomic.StoreUintptr":          ext۰NoEffect,
139        "syscall.Close":                     ext۰NoEffect,
140        "syscall.Exit":                      ext۰NoEffect,
141        "syscall.Getpid":                    ext۰NoEffect,
142        "syscall.Getwd":                     ext۰NoEffect,
143        "syscall.Kill":                      ext۰NoEffect,
144        "syscall.RawSyscall":                ext۰NoEffect,
145        "syscall.RawSyscall6":               ext۰NoEffect,
146        "syscall.Syscall":                   ext۰NoEffect,
147        "syscall.Syscall6":                  ext۰NoEffect,
148        "syscall.runtime_AfterFork":         ext۰NoEffect,
149        "syscall.runtime_BeforeFork":        ext۰NoEffect,
150        "syscall.setenv_c":                  ext۰NoEffect,
151        "time.Sleep":                        ext۰NoEffect,
152        "time.now":                          ext۰NoEffect,
153        "time.startTimer":                   ext۰time۰startTimer,
154        "time.stopTimer":                    ext۰NoEffect,
155    } {
156        intrinsicsByName[name] = fn
157    }
158}
159
160// findIntrinsic returns the constraint generation function for an
161// intrinsic function fn, or nil if the function should be handled normally.
162func (a *analysisfindIntrinsic(fn *ssa.Functionintrinsic {
163    // Consult the *Function-keyed cache.
164    // A cached nil indicates a normal non-intrinsic function.
165    implok := a.intrinsics[fn]
166    if !ok {
167        impl = intrinsicsByName[fn.String()] // may be nil
168
169        if a.isReflect(fn) {
170            if !a.config.Reflection {
171                impl = ext۰NoEffect // reflection disabled
172            } else if impl == nil {
173                // Ensure all "reflect" code is treated intrinsically.
174                impl = ext۰NotYetImplemented
175            }
176        } else if impl == nil && fn.Pkg != nil && fn.Pkg.Pkg.Path() == "runtime" {
177            // Ignore "runtime" (except SetFinalizer):
178            // it has few interesting effects on aliasing
179            // and is full of unsafe code we can't analyze.
180            impl = ext۰NoEffect
181        }
182
183        a.intrinsics[fn] = impl
184    }
185    return impl
186}
187
188// isReflect reports whether fn belongs to the "reflect" package.
189func (a *analysisisReflect(fn *ssa.Functionbool {
190    if a.reflectValueObj == nil {
191        return false // "reflect" package not loaded
192    }
193    reflectPackage := a.reflectValueObj.Pkg()
194    if fn.Pkg != nil && fn.Pkg.Pkg == reflectPackage {
195        return true
196    }
197    // Synthetic wrappers have a nil Pkg, so they slip through the
198    // previous check.  Check the receiver package.
199    // TODO(adonovan): should synthetic wrappers have a non-nil Pkg?
200    if recv := fn.Signature.Recv(); recv != nil {
201        if namedok := deref(recv.Type()).(*types.Named); ok {
202            if named.Obj().Pkg() == reflectPackage {
203                return true // e.g. wrapper of (reflect.Value).f
204            }
205        }
206    }
207    return false
208}
209
210// A trivial intrinsic suitable for any function that does not:
211// 1) induce aliases between its arguments or any global variables;
212// 2) call any functions; or
213// 3) create any labels.
214//
215// Many intrinsics (such as CompareAndSwapInt32) have a fourth kind of
216// effect: loading or storing through a pointer.  Though these could
217// be significant, we deliberately ignore them because they are
218// generally not worth the effort.
219//
220// We sometimes violate condition #3 if the function creates only
221// non-function labels, as the control-flow graph is still sound.
222func ext۰NoEffect(a *analysiscgn *cgnode) {}
223
224func ext۰NotYetImplemented(a *analysiscgn *cgnode) {
225    fn := cgn.fn
226    a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented"fn)
227}
228
229// ---------- func runtime.SetFinalizer(x, f interface{}) ----------
230
231// runtime.SetFinalizer(x, f)
232type runtimeSetFinalizerConstraint struct {
233    targets nodeid // (indirect)
234    f       nodeid // (ptr)
235    x       nodeid
236}
237
238func (c *runtimeSetFinalizerConstraintptr() nodeid { return c.f }
239func (c *runtimeSetFinalizerConstraintpresolve(h *hvn) {
240    h.markIndirect(onodeid(c.targets), "SetFinalizer.targets")
241}
242func (c *runtimeSetFinalizerConstraintrenumber(mapping []nodeid) {
243    c.targets = mapping[c.targets]
244    c.f = mapping[c.f]
245    c.x = mapping[c.x]
246}
247
248func (c *runtimeSetFinalizerConstraintString() string {
249    return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)"c.xc.f)
250}
251
252func (c *runtimeSetFinalizerConstraintsolve(a *analysisdelta *nodeset) {
253    for _fObj := range delta.AppendTo(a.deltaSpace) {
254        tDynfindirect := a.taggedValue(nodeid(fObj))
255        if indirect {
256            // TODO(adonovan): we'll need to implement this
257            // when we start creating indirect tagged objects.
258            panic("indirect tagged object")
259        }
260
261        tSigok := tDyn.Underlying().(*types.Signature)
262        if !ok {
263            continue // not a function
264        }
265        if tSig.Recv() != nil {
266            panic(tSig)
267        }
268        if tSig.Params().Len() != 1 {
269            continue //  not a unary function
270        }
271
272        // Extract x to tmp.
273        tx := tSig.Params().At(0).Type()
274        tmp := a.addNodes(tx"SetFinalizer.tmp")
275        a.typeAssert(txtmpc.xfalse)
276
277        // Call f(tmp).
278        a.store(ftmp1a.sizeof(tx))
279
280        // Add dynamic call target.
281        if a.onlineCopy(c.targetsf) {
282            a.addWork(c.targets)
283        }
284    }
285}
286
287func ext۰runtime۰SetFinalizer(a *analysiscgn *cgnode) {
288    // This is the shared contour, used for dynamic calls.
289    targets := a.addOneNode(tInvalid"SetFinalizer.targets"nil)
290    cgn.sites = append(cgn.sites, &callsite{targetstargets})
291    params := a.funcParams(cgn.obj)
292    a.addConstraint(&runtimeSetFinalizerConstraint{
293        targetstargets,
294        x:       params,
295        f:       params + 1,
296    })
297}
298
299// ---------- func time.startTimer(t *runtimeTimer) ----------
300
301// time.StartTimer(t)
302type timeStartTimerConstraint struct {
303    targets nodeid // (indirect)
304    t       nodeid // (ptr)
305}
306
307func (c *timeStartTimerConstraintptr() nodeid { return c.t }
308func (c *timeStartTimerConstraintpresolve(h *hvn) {
309    h.markIndirect(onodeid(c.targets), "StartTimer.targets")
310}
311func (c *timeStartTimerConstraintrenumber(mapping []nodeid) {
312    c.targets = mapping[c.targets]
313    c.t = mapping[c.t]
314}
315
316func (c *timeStartTimerConstraintString() string {
317    return fmt.Sprintf("time.startTimer(n%d)"c.t)
318}
319
320func (c *timeStartTimerConstraintsolve(a *analysisdelta *nodeset) {
321    for _tObj := range delta.AppendTo(a.deltaSpace) {
322        t := nodeid(tObj)
323
324        // We model startTimer as if it was defined thus:
325        //     func startTimer(t *runtimeTimer) { t.f(t.arg) }
326
327        // We hard-code the field offsets of time.runtimeTimer:
328        // type runtimeTimer struct {
329        //  0     __identity__
330        //  1    i      int32
331        //  2    when   int64
332        //  3    period int64
333        //  4    f      func(int64, interface{})
334        //  5    arg    interface{}
335        // }
336        f := t + 4
337        arg := t + 5
338
339        // store t.arg to t.f.params[0]
340        // (offset 1 => skip identity)
341        a.store(farg11)
342
343        // Add dynamic call target.
344        if a.onlineCopy(c.targetsf) {
345            a.addWork(c.targets)
346        }
347    }
348}
349
350func ext۰time۰startTimer(a *analysiscgn *cgnode) {
351    // This is the shared contour, used for dynamic calls.
352    targets := a.addOneNode(tInvalid"startTimer.targets"nil)
353    cgn.sites = append(cgn.sites, &callsite{targetstargets})
354    params := a.funcParams(cgn.obj)
355    a.addConstraint(&timeStartTimerConstraint{
356        targetstargets,
357        t:       params,
358    })
359}
360
MembersX
runtimeSetFinalizerConstraint.targets
runtimeSetFinalizerConstraint.ptr
runtimeSetFinalizerConstraint.String.c
timeStartTimerConstraint.solve.delta
ext۰time۰startTimer.cgn
analysis.isReflect.reflectPackage
runtimeSetFinalizerConstraint.String
timeStartTimerConstraint.t
timeStartTimerConstraint.renumber.mapping
timeStartTimerConstraint.String.c
ext۰NoEffect
timeStartTimerConstraint.renumber
analysis.findIntrinsic.fn
ext۰NotYetImplemented.cgn
ext۰NotYetImplemented.fn
runtimeSetFinalizerConstraint.f
runtimeSetFinalizerConstraint.solve.c
runtimeSetFinalizerConstraint.solve.RangeStmt_10439.BlockStmt.tx
timeStartTimerConstraint.renumber.c
timeStartTimerConstraint.solve.c
ext۰NotYetImplemented.a
runtimeSetFinalizerConstraint.solve.RangeStmt_10439.fObj
runtimeSetFinalizerConstraint.solve.RangeStmt_10439.BlockStmt.f
analysis.isReflect.a
runtimeSetFinalizerConstraint.renumber.c
runtimeSetFinalizerConstraint.solve
ext۰runtime۰SetFinalizer
timeStartTimerConstraint.presolve.c
ext۰NotYetImplemented
runtimeSetFinalizerConstraint.renumber.mapping
ext۰time۰startTimer
init.RangeStmt_1275.fn
runtimeSetFinalizerConstraint.presolve.h
ext۰NoEffect.a
runtimeSetFinalizerConstraint.solve.RangeStmt_10439.BlockStmt.tDyn
timeStartTimerConstraint.ptr
timeStartTimerConstraint.presolve.h
ext۰time۰startTimer.params
analysis.isReflect.recv
analysis.findIntrinsic
runtimeSetFinalizerConstraint.solve.RangeStmt_10439.BlockStmt.indirect
ext۰time۰startTimer.targets
init.RangeStmt_1275.name
runtimeSetFinalizerConstraint
ext۰runtime۰SetFinalizer.a
ext۰runtime۰SetFinalizer.targets
timeStartTimerConstraint.targets
timeStartTimerConstraint.presolve
analysis.isReflect
runtimeSetFinalizerConstraint.x
runtimeSetFinalizerConstraint.ptr.c
runtimeSetFinalizerConstraint.solve.RangeStmt_10439.BlockStmt.tmp
timeStartTimerConstraint.ptr.c
timeStartTimerConstraint.String
timeStartTimerConstraint.solve.RangeStmt_12257.tObj
timeStartTimerConstraint.solve.RangeStmt_12257.BlockStmt.t
intrinsic
ext۰NoEffect.cgn
runtimeSetFinalizerConstraint.presolve
ext۰runtime۰SetFinalizer.params
analysis.isReflect.fn
runtimeSetFinalizerConstraint.renumber
ext۰time۰startTimer.a
runtimeSetFinalizerConstraint.presolve.c
analysis.findIntrinsic.a
ext۰runtime۰SetFinalizer.cgn
timeStartTimerConstraint.solve
timeStartTimerConstraint.solve.a
init
runtimeSetFinalizerConstraint.solve.delta
timeStartTimerConstraint
runtimeSetFinalizerConstraint.solve.a
Members
X