]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/ssa/config.go
crypto/tls: gofmt
[gostls13.git] / src / cmd / compile / internal / ssa / config.go
1 // Copyright 2015 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
5 package ssa
6
7 import (
8         "cmd/internal/obj"
9         "crypto/sha1"
10         "fmt"
11         "os"
12         "strconv"
13         "strings"
14 )
15
16 type Config struct {
17         arch            string                     // "amd64", etc.
18         IntSize         int64                      // 4 or 8
19         PtrSize         int64                      // 4 or 8
20         lowerBlock      func(*Block) bool          // lowering function
21         lowerValue      func(*Value, *Config) bool // lowering function
22         registers       []Register                 // machine registers
23         fe              Frontend                   // callbacks into compiler frontend
24         HTML            *HTMLWriter                // html writer, for debugging
25         ctxt            *obj.Link                  // Generic arch information
26         optimize        bool                       // Do optimization
27         noDuffDevice    bool                       // Don't use Duff's device
28         sparsePhiCutoff uint64                     // Sparse phi location algorithm used above this #blocks*#variables score
29         curFunc         *Func
30
31         // TODO: more stuff. Compiler flags of interest, ...
32
33         // Given an environment variable used for debug hash match,
34         // what file (if any) receives the yes/no logging?
35         logfiles map[string]*os.File
36
37         // Storage for low-numbered values and blocks.
38         values [2000]Value
39         blocks [200]Block
40
41         // Reusable stackAllocState.
42         // See stackalloc.go's {new,put}StackAllocState.
43         stackAllocState *stackAllocState
44
45         domblockstore []ID         // scratch space for computing dominators
46         scrSparse     []*sparseSet // scratch sparse sets to be re-used.
47 }
48
49 type TypeSource interface {
50         TypeBool() Type
51         TypeInt8() Type
52         TypeInt16() Type
53         TypeInt32() Type
54         TypeInt64() Type
55         TypeUInt8() Type
56         TypeUInt16() Type
57         TypeUInt32() Type
58         TypeUInt64() Type
59         TypeInt() Type
60         TypeFloat32() Type
61         TypeFloat64() Type
62         TypeUintptr() Type
63         TypeString() Type
64         TypeBytePtr() Type // TODO: use unsafe.Pointer instead?
65
66         CanSSA(t Type) bool
67 }
68
69 type Logger interface {
70         // Logf logs a message from the compiler.
71         Logf(string, ...interface{})
72
73         // Log returns true if logging is not a no-op
74         // some logging calls account for more than a few heap allocations.
75         Log() bool
76
77         // Fatal reports a compiler error and exits.
78         Fatalf(line int32, msg string, args ...interface{})
79
80         // Unimplemented reports that the function cannot be compiled.
81         // It will be removed once SSA work is complete.
82         Unimplementedf(line int32, msg string, args ...interface{})
83
84         // Warnl writes compiler messages in the form expected by "errorcheck" tests
85         Warnl(line int32, fmt_ string, args ...interface{})
86
87         // Fowards the Debug_checknil flag from gc
88         Debug_checknil() bool
89 }
90
91 type Frontend interface {
92         TypeSource
93         Logger
94
95         // StringData returns a symbol pointing to the given string's contents.
96         StringData(string) interface{} // returns *gc.Sym
97
98         // Auto returns a Node for an auto variable of the given type.
99         // The SSA compiler uses this function to allocate space for spills.
100         Auto(Type) GCNode
101
102         // Given the name for a compound type, returns the name we should use
103         // for the parts of that compound type.
104         SplitString(LocalSlot) (LocalSlot, LocalSlot)
105         SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
106         SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
107         SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
108         SplitStruct(LocalSlot, int) LocalSlot
109
110         // Line returns a string describing the given line number.
111         Line(int32) string
112 }
113
114 // interface used to hold *gc.Node. We'd use *gc.Node directly but
115 // that would lead to an import cycle.
116 type GCNode interface {
117         Typ() Type
118         String() string
119 }
120
121 // NewConfig returns a new configuration object for the given architecture.
122 func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config {
123         c := &Config{arch: arch, fe: fe}
124         switch arch {
125         case "amd64":
126                 c.IntSize = 8
127                 c.PtrSize = 8
128                 c.lowerBlock = rewriteBlockAMD64
129                 c.lowerValue = rewriteValueAMD64
130                 c.registers = registersAMD64[:]
131         case "386":
132                 c.IntSize = 4
133                 c.PtrSize = 4
134                 c.lowerBlock = rewriteBlockAMD64
135                 c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
136         case "arm":
137                 c.IntSize = 4
138                 c.PtrSize = 4
139                 c.lowerBlock = rewriteBlockARM
140                 c.lowerValue = rewriteValueARM
141                 c.registers = registersARM[:]
142         default:
143                 fe.Unimplementedf(0, "arch %s not implemented", arch)
144         }
145         c.ctxt = ctxt
146         c.optimize = optimize
147
148         // Don't use Duff's device on Plan 9, because floating
149         // point operations are not allowed in note handler.
150         if obj.Getgoos() == "plan9" {
151                 c.noDuffDevice = true
152         }
153
154         // Assign IDs to preallocated values/blocks.
155         for i := range c.values {
156                 c.values[i].ID = ID(i)
157         }
158         for i := range c.blocks {
159                 c.blocks[i].ID = ID(i)
160         }
161
162         c.logfiles = make(map[string]*os.File)
163
164         // cutoff is compared with product of numblocks and numvalues,
165         // if product is smaller than cutoff, use old non-sparse method.
166         // cutoff == 0 implies all sparse.
167         // cutoff == -1 implies none sparse.
168         // Good cutoff values seem to be O(million) depending on constant factor cost of sparse.
169         // TODO: get this from a flag, not an environment variable
170         c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash
171         ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF")
172         if ev != "" {
173                 v, err := strconv.ParseInt(ev, 10, 64)
174                 if err != nil {
175                         fe.Fatalf(0, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev)
176                 }
177                 c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse
178         }
179
180         return c
181 }
182
183 func (c *Config) Frontend() Frontend      { return c.fe }
184 func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
185
186 // NewFunc returns a new, empty function object.
187 // Caller must call f.Free() before calling NewFunc again.
188 func (c *Config) NewFunc() *Func {
189         // TODO(khr): should this function take name, type, etc. as arguments?
190         if c.curFunc != nil {
191                 c.Fatalf(0, "NewFunc called without previous Free")
192         }
193         f := &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
194         c.curFunc = f
195         return f
196 }
197
198 func (c *Config) Logf(msg string, args ...interface{})               { c.fe.Logf(msg, args...) }
199 func (c *Config) Log() bool                                          { return c.fe.Log() }
200 func (c *Config) Fatalf(line int32, msg string, args ...interface{}) { c.fe.Fatalf(line, msg, args...) }
201 func (c *Config) Unimplementedf(line int32, msg string, args ...interface{}) {
202         c.fe.Unimplementedf(line, msg, args...)
203 }
204 func (c *Config) Warnl(line int32, msg string, args ...interface{}) { c.fe.Warnl(line, msg, args...) }
205 func (c *Config) Debug_checknil() bool                              { return c.fe.Debug_checknil() }
206
207 func (c *Config) logDebugHashMatch(evname, name string) {
208         file := c.logfiles[evname]
209         if file == nil {
210                 file = os.Stdout
211                 tmpfile := os.Getenv("GSHS_LOGFILE")
212                 if tmpfile != "" {
213                         var ok error
214                         file, ok = os.Create(tmpfile)
215                         if ok != nil {
216                                 c.Fatalf(0, "Could not open hash-testing logfile %s", tmpfile)
217                         }
218                 }
219                 c.logfiles[evname] = file
220         }
221         s := fmt.Sprintf("%s triggered %s\n", evname, name)
222         file.WriteString(s)
223         file.Sync()
224 }
225
226 // DebugHashMatch returns true if environment variable evname
227 // 1) is empty (this is a special more-quickly implemented case of 3)
228 // 2) is "y" or "Y"
229 // 3) is a suffix of the sha1 hash of name
230 // 4) is a suffix of the environment variable
231 //    fmt.Sprintf("%s%d", evname, n)
232 //    provided that all such variables are nonempty for 0 <= i <= n
233 // Otherwise it returns false.
234 // When true is returned the message
235 //  "%s triggered %s\n", evname, name
236 // is printed on the file named in environment variable
237 //  GSHS_LOGFILE
238 // or standard out if that is empty or there is an error
239 // opening the file.
240
241 func (c *Config) DebugHashMatch(evname, name string) bool {
242         evhash := os.Getenv(evname)
243         if evhash == "" {
244                 return true // default behavior with no EV is "on"
245         }
246         if evhash == "y" || evhash == "Y" {
247                 c.logDebugHashMatch(evname, name)
248                 return true
249         }
250         if evhash == "n" || evhash == "N" {
251                 return false
252         }
253         // Check the hash of the name against a partial input hash.
254         // We use this feature to do a binary search to
255         // find a function that is incorrectly compiled.
256         hstr := ""
257         for _, b := range sha1.Sum([]byte(name)) {
258                 hstr += fmt.Sprintf("%08b", b)
259         }
260
261         if strings.HasSuffix(hstr, evhash) {
262                 c.logDebugHashMatch(evname, name)
263                 return true
264         }
265
266         // Iteratively try additional hashes to allow tests for multi-point
267         // failure.
268         for i := 0; true; i++ {
269                 ev := fmt.Sprintf("%s%d", evname, i)
270                 evv := os.Getenv(ev)
271                 if evv == "" {
272                         break
273                 }
274                 if strings.HasSuffix(hstr, evv) {
275                         c.logDebugHashMatch(ev, name)
276                         return true
277                 }
278         }
279         return false
280 }
281
282 func (c *Config) DebugNameMatch(evname, name string) bool {
283         return os.Getenv(evname) == name
284 }