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