blob: 5f60e68ea9ec576b6ff7686bd1ae30b580d03962 [file] [log] [blame]
Peter Collingbourne56109b72015-01-13 20:45:08 +00001// 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
21 "llvm.org/llgo/third_party/gotools/go/ssa"
22 "llvm.org/llgo/third_party/gotools/go/types"
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 *analysis, cgn *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 name, fn := 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 "runtime.BlockProfile": ext۰NoEffect,
110 "runtime.Breakpoint": ext۰NoEffect,
111 "runtime.CPUProfile": ext۰NoEffect, // good enough
112 "runtime.Caller": ext۰NoEffect,
113 "runtime.Callers": ext۰NoEffect, // good enough
114 "runtime.FuncForPC": ext۰NoEffect,
115 "runtime.GC": ext۰NoEffect,
116 "runtime.GOMAXPROCS": ext۰NoEffect,
117 "runtime.Goexit": ext۰NoEffect,
118 "runtime.GoroutineProfile": ext۰NoEffect,
119 "runtime.Gosched": ext۰NoEffect,
120 "runtime.MemProfile": ext۰NoEffect,
121 "runtime.NumCPU": ext۰NoEffect,
122 "runtime.NumGoroutine": ext۰NoEffect,
123 "runtime.ReadMemStats": ext۰NoEffect,
124 "runtime.SetBlockProfileRate": ext۰NoEffect,
125 "runtime.SetCPUProfileRate": ext۰NoEffect,
126 "runtime.SetFinalizer": ext۰runtime۰SetFinalizer,
127 "runtime.Stack": ext۰NoEffect,
128 "runtime.ThreadCreateProfile": ext۰NoEffect,
129 "runtime.cstringToGo": ext۰NoEffect,
130 "runtime.funcentry_go": ext۰NoEffect,
131 "runtime.funcline_go": ext۰NoEffect,
132 "runtime.funcname_go": ext۰NoEffect,
133 "runtime.getgoroot": ext۰NoEffect,
134 "runtime/pprof.runtime_cyclesPerSecond": ext۰NoEffect,
135 "strings.IndexByte": ext۰NoEffect,
136 "sync.runtime_Semacquire": ext۰NoEffect,
137 "sync.runtime_Semrelease": ext۰NoEffect,
138 "sync.runtime_Syncsemacquire": ext۰NoEffect,
139 "sync.runtime_Syncsemcheck": ext۰NoEffect,
140 "sync.runtime_Syncsemrelease": ext۰NoEffect,
141 "sync.runtime_procPin": ext۰NoEffect,
142 "sync.runtime_procUnpin": ext۰NoEffect,
143 "sync.runtime_registerPool": ext۰NoEffect,
144 "sync/atomic.AddInt32": ext۰NoEffect,
145 "sync/atomic.AddInt64": ext۰NoEffect,
146 "sync/atomic.AddUint32": ext۰NoEffect,
147 "sync/atomic.AddUint64": ext۰NoEffect,
148 "sync/atomic.AddUintptr": ext۰NoEffect,
149 "sync/atomic.CompareAndSwapInt32": ext۰NoEffect,
150 "sync/atomic.CompareAndSwapUint32": ext۰NoEffect,
151 "sync/atomic.CompareAndSwapUint64": ext۰NoEffect,
152 "sync/atomic.CompareAndSwapUintptr": ext۰NoEffect,
153 "sync/atomic.LoadInt32": ext۰NoEffect,
154 "sync/atomic.LoadInt64": ext۰NoEffect,
155 "sync/atomic.LoadPointer": ext۰NoEffect, // ignore unsafe.Pointers
156 "sync/atomic.LoadUint32": ext۰NoEffect,
157 "sync/atomic.LoadUint64": ext۰NoEffect,
158 "sync/atomic.LoadUintptr": ext۰NoEffect,
159 "sync/atomic.StoreInt32": ext۰NoEffect,
160 "sync/atomic.StorePointer": ext۰NoEffect, // ignore unsafe.Pointers
161 "sync/atomic.StoreUint32": ext۰NoEffect,
162 "sync/atomic.StoreUintptr": ext۰NoEffect,
163 "syscall.Close": ext۰NoEffect,
164 "syscall.Exit": ext۰NoEffect,
165 "syscall.Getpid": ext۰NoEffect,
166 "syscall.Getwd": ext۰NoEffect,
167 "syscall.Kill": ext۰NoEffect,
168 "syscall.RawSyscall": ext۰NoEffect,
169 "syscall.RawSyscall6": ext۰NoEffect,
170 "syscall.Syscall": ext۰NoEffect,
171 "syscall.Syscall6": ext۰NoEffect,
172 "syscall.runtime_AfterFork": ext۰NoEffect,
173 "syscall.runtime_BeforeFork": ext۰NoEffect,
174 "syscall.setenv_c": ext۰NoEffect,
175 "time.Sleep": ext۰NoEffect,
176 "time.now": ext۰NoEffect,
177 "time.startTimer": ext۰time۰startTimer,
178 "time.stopTimer": ext۰NoEffect,
179 } {
180 intrinsicsByName[name] = fn
181 }
182}
183
184// findIntrinsic returns the constraint generation function for an
185// intrinsic function fn, or nil if the function should be handled normally.
186//
187func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic {
188 // Consult the *Function-keyed cache.
189 // A cached nil indicates a normal non-intrinsic function.
190 impl, ok := a.intrinsics[fn]
191 if !ok {
192 impl = intrinsicsByName[fn.String()] // may be nil
193
194 if a.isReflect(fn) {
195 if !a.config.Reflection {
196 impl = ext۰NoEffect // reflection disabled
197 } else if impl == nil {
198 // Ensure all "reflect" code is treated intrinsically.
199 impl = ext۰NotYetImplemented
200 }
201 }
202
203 a.intrinsics[fn] = impl
204 }
205 return impl
206}
207
208// isReflect reports whether fn belongs to the "reflect" package.
209func (a *analysis) isReflect(fn *ssa.Function) bool {
210 if a.reflectValueObj == nil {
211 return false // "reflect" package not loaded
212 }
213 reflectPackage := a.reflectValueObj.Pkg()
214 if fn.Pkg != nil && fn.Pkg.Object == reflectPackage {
215 return true
216 }
217 // Synthetic wrappers have a nil Pkg, so they slip through the
218 // previous check. Check the receiver package.
219 // TODO(adonovan): should synthetic wrappers have a non-nil Pkg?
220 if recv := fn.Signature.Recv(); recv != nil {
221 if named, ok := deref(recv.Type()).(*types.Named); ok {
222 if named.Obj().Pkg() == reflectPackage {
223 return true // e.g. wrapper of (reflect.Value).f
224 }
225 }
226 }
227 return false
228}
229
230// A trivial intrinsic suitable for any function that does not:
231// 1) induce aliases between its arguments or any global variables;
232// 2) call any functions; or
233// 3) create any labels.
234//
235// Many intrinsics (such as CompareAndSwapInt32) have a fourth kind of
236// effect: loading or storing through a pointer. Though these could
237// be significant, we deliberately ignore them because they are
238// generally not worth the effort.
239//
240// We sometimes violate condition #3 if the function creates only
241// non-function labels, as the control-flow graph is still sound.
242//
243func ext۰NoEffect(a *analysis, cgn *cgnode) {}
244
245func ext۰NotYetImplemented(a *analysis, cgn *cgnode) {
246 fn := cgn.fn
247 a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented", fn)
248}
249
250// ---------- func runtime.SetFinalizer(x, f interface{}) ----------
251
252// runtime.SetFinalizer(x, f)
253type runtimeSetFinalizerConstraint struct {
254 targets nodeid // (indirect)
255 f nodeid // (ptr)
256 x nodeid
257}
258
259func (c *runtimeSetFinalizerConstraint) ptr() nodeid { return c.f }
260func (c *runtimeSetFinalizerConstraint) presolve(h *hvn) {
261 h.markIndirect(onodeid(c.targets), "SetFinalizer.targets")
262}
263func (c *runtimeSetFinalizerConstraint) renumber(mapping []nodeid) {
264 c.targets = mapping[c.targets]
265 c.f = mapping[c.f]
266 c.x = mapping[c.x]
267}
268
269func (c *runtimeSetFinalizerConstraint) String() string {
270 return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f)
271}
272
273func (c *runtimeSetFinalizerConstraint) solve(a *analysis, delta *nodeset) {
274 for _, fObj := range delta.AppendTo(a.deltaSpace) {
275 tDyn, f, indirect := a.taggedValue(nodeid(fObj))
276 if indirect {
277 // TODO(adonovan): we'll need to implement this
278 // when we start creating indirect tagged objects.
279 panic("indirect tagged object")
280 }
281
282 tSig, ok := tDyn.Underlying().(*types.Signature)
283 if !ok {
284 continue // not a function
285 }
286 if tSig.Recv() != nil {
287 panic(tSig)
288 }
289 if tSig.Params().Len() != 1 {
290 continue // not a unary function
291 }
292
293 // Extract x to tmp.
294 tx := tSig.Params().At(0).Type()
295 tmp := a.addNodes(tx, "SetFinalizer.tmp")
296 a.typeAssert(tx, tmp, c.x, false)
297
298 // Call f(tmp).
299 a.store(f, tmp, 1, a.sizeof(tx))
300
301 // Add dynamic call target.
302 if a.onlineCopy(c.targets, f) {
303 a.addWork(c.targets)
304 }
305 }
306}
307
308func ext۰runtime۰SetFinalizer(a *analysis, cgn *cgnode) {
309 // This is the shared contour, used for dynamic calls.
310 targets := a.addOneNode(tInvalid, "SetFinalizer.targets", nil)
311 cgn.sites = append(cgn.sites, &callsite{targets: targets})
312 params := a.funcParams(cgn.obj)
313 a.addConstraint(&runtimeSetFinalizerConstraint{
314 targets: targets,
315 x: params,
316 f: params + 1,
317 })
318}
319
320// ---------- func time.startTimer(t *runtimeTimer) ----------
321
322// time.StartTimer(t)
323type timeStartTimerConstraint struct {
324 targets nodeid // (indirect)
325 t nodeid // (ptr)
326}
327
328func (c *timeStartTimerConstraint) ptr() nodeid { return c.t }
329func (c *timeStartTimerConstraint) presolve(h *hvn) {
330 h.markIndirect(onodeid(c.targets), "StartTimer.targets")
331}
332func (c *timeStartTimerConstraint) renumber(mapping []nodeid) {
333 c.targets = mapping[c.targets]
334 c.t = mapping[c.t]
335}
336
337func (c *timeStartTimerConstraint) String() string {
338 return fmt.Sprintf("time.startTimer(n%d)", c.t)
339}
340
341func (c *timeStartTimerConstraint) solve(a *analysis, delta *nodeset) {
342 for _, tObj := range delta.AppendTo(a.deltaSpace) {
343 t := nodeid(tObj)
344
345 // We model startTimer as if it was defined thus:
346 // func startTimer(t *runtimeTimer) { t.f(t.arg) }
347
348 // We hard-code the field offsets of time.runtimeTimer:
349 // type runtimeTimer struct {
350 // 0 __identity__
351 // 1 i int32
352 // 2 when int64
353 // 3 period int64
354 // 4 f func(int64, interface{})
355 // 5 arg interface{}
356 // }
357 f := t + 4
358 arg := t + 5
359
360 // store t.arg to t.f.params[0]
361 // (offset 1 => skip identity)
362 a.store(f, arg, 1, 1)
363
364 // Add dynamic call target.
365 if a.onlineCopy(c.targets, f) {
366 a.addWork(c.targets)
367 }
368 }
369}
370
371func ext۰time۰startTimer(a *analysis, cgn *cgnode) {
372 // This is the shared contour, used for dynamic calls.
373 targets := a.addOneNode(tInvalid, "startTimer.targets", nil)
374 cgn.sites = append(cgn.sites, &callsite{targets: targets})
375 params := a.funcParams(cgn.obj)
376 a.addConstraint(&timeStartTimerConstraint{
377 targets: targets,
378 t: params,
379 })
380}