]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/ssa/config.go
all: merge dev.typealias into master
[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         RegSize         int64                      // 4 or 8
21         lowerBlock      func(*Block, *Config) bool // lowering function
22         lowerValue      func(*Value, *Config) bool // lowering function
23         registers       []Register                 // machine registers
24         gpRegMask       regMask                    // general purpose integer register mask
25         fpRegMask       regMask                    // floating point register mask
26         specialRegMask  regMask                    // special register mask
27         FPReg           int8                       // register number of frame pointer, -1 if not used
28         LinkReg         int8                       // register number of link register if it is a general purpose register, -1 if not used
29         hasGReg         bool                       // has hardware g register
30         fe              Frontend                   // callbacks into compiler frontend
31         HTML            *HTMLWriter                // html writer, for debugging
32         ctxt            *obj.Link                  // Generic arch information
33         optimize        bool                       // Do optimization
34         noDuffDevice    bool                       // Don't use Duff's device
35         nacl            bool                       // GOOS=nacl
36         use387          bool                       // GO386=387
37         OldArch         bool                       // True for older versions of architecture, e.g. true for PPC64BE, false for PPC64LE
38         NeedsFpScratch  bool                       // No direct move between GP and FP register sets
39         BigEndian       bool                       //
40         DebugTest       bool                       // default true unless $GOSSAHASH != ""; as a debugging aid, make new code conditional on this and use GOSSAHASH to binary search for failing cases
41         sparsePhiCutoff uint64                     // Sparse phi location algorithm used above this #blocks*#variables score
42         curFunc         *Func
43
44         // TODO: more stuff. Compiler flags of interest, ...
45
46         // Given an environment variable used for debug hash match,
47         // what file (if any) receives the yes/no logging?
48         logfiles map[string]*os.File
49
50         // Storage for low-numbered values and blocks.
51         values [2000]Value
52         blocks [200]Block
53
54         // Reusable stackAllocState.
55         // See stackalloc.go's {new,put}StackAllocState.
56         stackAllocState *stackAllocState
57
58         domblockstore []ID         // scratch space for computing dominators
59         scrSparse     []*sparseSet // scratch sparse sets to be re-used.
60 }
61
62 type TypeSource interface {
63         TypeBool() Type
64         TypeInt8() Type
65         TypeInt16() Type
66         TypeInt32() Type
67         TypeInt64() Type
68         TypeUInt8() Type
69         TypeUInt16() Type
70         TypeUInt32() Type
71         TypeUInt64() Type
72         TypeInt() Type
73         TypeFloat32() Type
74         TypeFloat64() Type
75         TypeUintptr() Type
76         TypeString() Type
77         TypeBytePtr() Type // TODO: use unsafe.Pointer instead?
78
79         CanSSA(t Type) bool
80 }
81
82 type Logger interface {
83         // Logf logs a message from the compiler.
84         Logf(string, ...interface{})
85
86         // Log returns true if logging is not a no-op
87         // some logging calls account for more than a few heap allocations.
88         Log() bool
89
90         // Fatal reports a compiler error and exits.
91         Fatalf(line int32, msg string, args ...interface{})
92
93         // Warnl writes compiler messages in the form expected by "errorcheck" tests
94         Warnl(line int32, fmt_ string, args ...interface{})
95
96         // Forwards the Debug flags from gc
97         Debug_checknil() bool
98         Debug_wb() bool
99 }
100
101 type Frontend interface {
102         TypeSource
103         Logger
104
105         // StringData returns a symbol pointing to the given string's contents.
106         StringData(string) interface{} // returns *gc.Sym
107
108         // Auto returns a Node for an auto variable of the given type.
109         // The SSA compiler uses this function to allocate space for spills.
110         Auto(Type) GCNode
111
112         // Given the name for a compound type, returns the name we should use
113         // for the parts of that compound type.
114         SplitString(LocalSlot) (LocalSlot, LocalSlot)
115         SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
116         SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
117         SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
118         SplitStruct(LocalSlot, int) LocalSlot
119         SplitArray(LocalSlot) LocalSlot              // array must be length 1
120         SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
121
122         // Line returns a string describing the given line number.
123         Line(int32) string
124
125         // AllocFrame assigns frame offsets to all live auto variables.
126         AllocFrame(f *Func)
127
128         // Syslook returns a symbol of the runtime function/variable with the
129         // given name.
130         Syslook(string) interface{} // returns *gc.Sym
131 }
132
133 // interface used to hold *gc.Node. We'd use *gc.Node directly but
134 // that would lead to an import cycle.
135 type GCNode interface {
136         Typ() Type
137         String() string
138 }
139
140 // NewConfig returns a new configuration object for the given architecture.
141 func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config {
142         c := &Config{arch: arch, fe: fe}
143         switch arch {
144         case "amd64":
145                 c.IntSize = 8
146                 c.PtrSize = 8
147                 c.RegSize = 8
148                 c.lowerBlock = rewriteBlockAMD64
149                 c.lowerValue = rewriteValueAMD64
150                 c.registers = registersAMD64[:]
151                 c.gpRegMask = gpRegMaskAMD64
152                 c.fpRegMask = fpRegMaskAMD64
153                 c.FPReg = framepointerRegAMD64
154                 c.LinkReg = linkRegAMD64
155                 c.hasGReg = false
156         case "amd64p32":
157                 c.IntSize = 4
158                 c.PtrSize = 4
159                 c.RegSize = 8
160                 c.lowerBlock = rewriteBlockAMD64
161                 c.lowerValue = rewriteValueAMD64
162                 c.registers = registersAMD64[:]
163                 c.gpRegMask = gpRegMaskAMD64
164                 c.fpRegMask = fpRegMaskAMD64
165                 c.FPReg = framepointerRegAMD64
166                 c.LinkReg = linkRegAMD64
167                 c.hasGReg = false
168                 c.noDuffDevice = true
169         case "386":
170                 c.IntSize = 4
171                 c.PtrSize = 4
172                 c.RegSize = 4
173                 c.lowerBlock = rewriteBlock386
174                 c.lowerValue = rewriteValue386
175                 c.registers = registers386[:]
176                 c.gpRegMask = gpRegMask386
177                 c.fpRegMask = fpRegMask386
178                 c.FPReg = framepointerReg386
179                 c.LinkReg = linkReg386
180                 c.hasGReg = false
181         case "arm":
182                 c.IntSize = 4
183                 c.PtrSize = 4
184                 c.RegSize = 4
185                 c.lowerBlock = rewriteBlockARM
186                 c.lowerValue = rewriteValueARM
187                 c.registers = registersARM[:]
188                 c.gpRegMask = gpRegMaskARM
189                 c.fpRegMask = fpRegMaskARM
190                 c.FPReg = framepointerRegARM
191                 c.LinkReg = linkRegARM
192                 c.hasGReg = true
193         case "arm64":
194                 c.IntSize = 8
195                 c.PtrSize = 8
196                 c.RegSize = 8
197                 c.lowerBlock = rewriteBlockARM64
198                 c.lowerValue = rewriteValueARM64
199                 c.registers = registersARM64[:]
200                 c.gpRegMask = gpRegMaskARM64
201                 c.fpRegMask = fpRegMaskARM64
202                 c.FPReg = framepointerRegARM64
203                 c.LinkReg = linkRegARM64
204                 c.hasGReg = true
205                 c.noDuffDevice = obj.GOOS == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend
206         case "ppc64":
207                 c.OldArch = true
208                 c.BigEndian = true
209                 fallthrough
210         case "ppc64le":
211                 c.IntSize = 8
212                 c.PtrSize = 8
213                 c.RegSize = 8
214                 c.lowerBlock = rewriteBlockPPC64
215                 c.lowerValue = rewriteValuePPC64
216                 c.registers = registersPPC64[:]
217                 c.gpRegMask = gpRegMaskPPC64
218                 c.fpRegMask = fpRegMaskPPC64
219                 c.FPReg = framepointerRegPPC64
220                 c.LinkReg = linkRegPPC64
221                 c.noDuffDevice = true // TODO: Resolve PPC64 DuffDevice (has zero, but not copy)
222                 c.NeedsFpScratch = true
223                 c.hasGReg = true
224         case "mips64":
225                 c.BigEndian = true
226                 fallthrough
227         case "mips64le":
228                 c.IntSize = 8
229                 c.PtrSize = 8
230                 c.RegSize = 8
231                 c.lowerBlock = rewriteBlockMIPS64
232                 c.lowerValue = rewriteValueMIPS64
233                 c.registers = registersMIPS64[:]
234                 c.gpRegMask = gpRegMaskMIPS64
235                 c.fpRegMask = fpRegMaskMIPS64
236                 c.specialRegMask = specialRegMaskMIPS64
237                 c.FPReg = framepointerRegMIPS64
238                 c.LinkReg = linkRegMIPS64
239                 c.hasGReg = true
240         case "s390x":
241                 c.IntSize = 8
242                 c.PtrSize = 8
243                 c.RegSize = 8
244                 c.lowerBlock = rewriteBlockS390X
245                 c.lowerValue = rewriteValueS390X
246                 c.registers = registersS390X[:]
247                 c.gpRegMask = gpRegMaskS390X
248                 c.fpRegMask = fpRegMaskS390X
249                 c.FPReg = framepointerRegS390X
250                 c.LinkReg = linkRegS390X
251                 c.hasGReg = true
252                 c.noDuffDevice = true
253                 c.BigEndian = true
254         case "mips":
255                 c.BigEndian = true
256                 fallthrough
257         case "mipsle":
258                 c.IntSize = 4
259                 c.PtrSize = 4
260                 c.RegSize = 4
261                 c.lowerBlock = rewriteBlockMIPS
262                 c.lowerValue = rewriteValueMIPS
263                 c.registers = registersMIPS[:]
264                 c.gpRegMask = gpRegMaskMIPS
265                 c.fpRegMask = fpRegMaskMIPS
266                 c.specialRegMask = specialRegMaskMIPS
267                 c.FPReg = framepointerRegMIPS
268                 c.LinkReg = linkRegMIPS
269                 c.hasGReg = true
270                 c.noDuffDevice = true
271         default:
272                 fe.Fatalf(0, "arch %s not implemented", arch)
273         }
274         c.ctxt = ctxt
275         c.optimize = optimize
276         c.nacl = obj.GOOS == "nacl"
277
278         // Don't use Duff's device on Plan 9 AMD64, because floating
279         // point operations are not allowed in note handler.
280         if obj.GOOS == "plan9" && arch == "amd64" {
281                 c.noDuffDevice = true
282         }
283
284         if c.nacl {
285                 c.noDuffDevice = true // Don't use Duff's device on NaCl
286
287                 // runtime call clobber R12 on nacl
288                 opcodeTable[OpARMUDIVrtcall].reg.clobbers |= 1 << 12 // R12
289         }
290
291         // Assign IDs to preallocated values/blocks.
292         for i := range c.values {
293                 c.values[i].ID = ID(i)
294         }
295         for i := range c.blocks {
296                 c.blocks[i].ID = ID(i)
297         }
298
299         c.logfiles = make(map[string]*os.File)
300
301         // cutoff is compared with product of numblocks and numvalues,
302         // if product is smaller than cutoff, use old non-sparse method.
303         // cutoff == 0 implies all sparse.
304         // cutoff == -1 implies none sparse.
305         // Good cutoff values seem to be O(million) depending on constant factor cost of sparse.
306         // TODO: get this from a flag, not an environment variable
307         c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash
308         ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF")
309         if ev != "" {
310                 v, err := strconv.ParseInt(ev, 10, 64)
311                 if err != nil {
312                         fe.Fatalf(0, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev)
313                 }
314                 c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse
315         }
316
317         return c
318 }
319
320 func (c *Config) Set387(b bool) {
321         c.NeedsFpScratch = b
322         c.use387 = b
323 }
324
325 func (c *Config) Frontend() Frontend      { return c.fe }
326 func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
327 func (c *Config) Ctxt() *obj.Link         { return c.ctxt }
328
329 // NewFunc returns a new, empty function object.
330 // Caller must call f.Free() before calling NewFunc again.
331 func (c *Config) NewFunc() *Func {
332         // TODO(khr): should this function take name, type, etc. as arguments?
333         if c.curFunc != nil {
334                 c.Fatalf(0, "NewFunc called without previous Free")
335         }
336         f := &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
337         c.curFunc = f
338         return f
339 }
340
341 func (c *Config) Logf(msg string, args ...interface{})               { c.fe.Logf(msg, args...) }
342 func (c *Config) Log() bool                                          { return c.fe.Log() }
343 func (c *Config) Fatalf(line int32, msg string, args ...interface{}) { c.fe.Fatalf(line, msg, args...) }
344 func (c *Config) Warnl(line int32, msg string, args ...interface{})  { c.fe.Warnl(line, msg, args...) }
345 func (c *Config) Debug_checknil() bool                               { return c.fe.Debug_checknil() }
346 func (c *Config) Debug_wb() bool                                     { return c.fe.Debug_wb() }
347
348 func (c *Config) logDebugHashMatch(evname, name string) {
349         file := c.logfiles[evname]
350         if file == nil {
351                 file = os.Stdout
352                 tmpfile := os.Getenv("GSHS_LOGFILE")
353                 if tmpfile != "" {
354                         var ok error
355                         file, ok = os.Create(tmpfile)
356                         if ok != nil {
357                                 c.Fatalf(0, "Could not open hash-testing logfile %s", tmpfile)
358                         }
359                 }
360                 c.logfiles[evname] = file
361         }
362         s := fmt.Sprintf("%s triggered %s\n", evname, name)
363         file.WriteString(s)
364         file.Sync()
365 }
366
367 // DebugHashMatch returns true if environment variable evname
368 // 1) is empty (this is a special more-quickly implemented case of 3)
369 // 2) is "y" or "Y"
370 // 3) is a suffix of the sha1 hash of name
371 // 4) is a suffix of the environment variable
372 //    fmt.Sprintf("%s%d", evname, n)
373 //    provided that all such variables are nonempty for 0 <= i <= n
374 // Otherwise it returns false.
375 // When true is returned the message
376 //  "%s triggered %s\n", evname, name
377 // is printed on the file named in environment variable
378 //  GSHS_LOGFILE
379 // or standard out if that is empty or there is an error
380 // opening the file.
381
382 func (c *Config) DebugHashMatch(evname, name string) bool {
383         evhash := os.Getenv(evname)
384         if evhash == "" {
385                 return true // default behavior with no EV is "on"
386         }
387         if evhash == "y" || evhash == "Y" {
388                 c.logDebugHashMatch(evname, name)
389                 return true
390         }
391         if evhash == "n" || evhash == "N" {
392                 return false
393         }
394         // Check the hash of the name against a partial input hash.
395         // We use this feature to do a binary search to
396         // find a function that is incorrectly compiled.
397         hstr := ""
398         for _, b := range sha1.Sum([]byte(name)) {
399                 hstr += fmt.Sprintf("%08b", b)
400         }
401
402         if strings.HasSuffix(hstr, evhash) {
403                 c.logDebugHashMatch(evname, name)
404                 return true
405         }
406
407         // Iteratively try additional hashes to allow tests for multi-point
408         // failure.
409         for i := 0; true; i++ {
410                 ev := fmt.Sprintf("%s%d", evname, i)
411                 evv := os.Getenv(ev)
412                 if evv == "" {
413                         break
414                 }
415                 if strings.HasSuffix(hstr, evv) {
416                         c.logDebugHashMatch(ev, name)
417                         return true
418                 }
419         }
420         return false
421 }
422
423 func (c *Config) DebugNameMatch(evname, name string) bool {
424         return os.Getenv(evname) == name
425 }