mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
The -d compiler flag can also specify ssa phase and flag,
for example -d=ssa/generic_cse/time,ssa/generic_cse/stats
Spaces in the phase names can be specified with an
underscore. Flags currently parsed (not necessarily
recognized by the phases yet) are:
on, off, mem, time, debug, stats, and test
On, off and time are handled in the harness,
debug, stats, and test are interpreted by the phase itself.
The pass is now attached to the Func being compiled, and a
new method logStats(key, ...value) on *Func to encourage a
semi-standardized format for that output. Output fields
are separated by tabs to ease digestion by awk and
spreadsheets. For example,
if f.pass.stats > 0 {
f.logStat("CSE REWRITES", rewrites)
}
Change-Id: I16db2b5af64c50ca9a47efeb51d961147a903abc
Reviewed-on: https://go-review.googlesource.com/19885
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Todd Neal <todd@tneal.org>
235 lines
6.6 KiB
Go
235 lines
6.6 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ssa
|
|
|
|
import (
|
|
"cmd/internal/obj"
|
|
"crypto/sha1"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type Config struct {
|
|
arch string // "amd64", etc.
|
|
IntSize int64 // 4 or 8
|
|
PtrSize int64 // 4 or 8
|
|
lowerBlock func(*Block) bool // lowering function
|
|
lowerValue func(*Value, *Config) bool // lowering function
|
|
fe Frontend // callbacks into compiler frontend
|
|
HTML *HTMLWriter // html writer, for debugging
|
|
ctxt *obj.Link // Generic arch information
|
|
optimize bool // Do optimization
|
|
curFunc *Func
|
|
|
|
// TODO: more stuff. Compiler flags of interest, ...
|
|
|
|
// Given an environment variable used for debug hash match,
|
|
// what file (if any) receives the yes/no logging?
|
|
logfiles map[string]*os.File
|
|
|
|
// Storage for low-numbered values and blocks.
|
|
values [2000]Value
|
|
blocks [200]Block
|
|
|
|
domblockstore []ID // scratch space for computing dominators
|
|
scrSparse []*sparseSet // scratch sparse sets to be re-used.
|
|
}
|
|
|
|
type TypeSource interface {
|
|
TypeBool() Type
|
|
TypeInt8() Type
|
|
TypeInt16() Type
|
|
TypeInt32() Type
|
|
TypeInt64() Type
|
|
TypeUInt8() Type
|
|
TypeUInt16() Type
|
|
TypeUInt32() Type
|
|
TypeUInt64() Type
|
|
TypeInt() Type
|
|
TypeFloat32() Type
|
|
TypeFloat64() Type
|
|
TypeUintptr() Type
|
|
TypeString() Type
|
|
TypeBytePtr() Type // TODO: use unsafe.Pointer instead?
|
|
|
|
CanSSA(t Type) bool
|
|
}
|
|
|
|
type Logger interface {
|
|
// Logf logs a message from the compiler.
|
|
Logf(string, ...interface{})
|
|
|
|
// Log returns true if logging is not a no-op
|
|
// some logging calls account for more than a few heap allocations.
|
|
Log() bool
|
|
|
|
// Fatal reports a compiler error and exits.
|
|
Fatalf(line int32, msg string, args ...interface{})
|
|
|
|
// Unimplemented reports that the function cannot be compiled.
|
|
// It will be removed once SSA work is complete.
|
|
Unimplementedf(line int32, msg string, args ...interface{})
|
|
|
|
// Warnl writes compiler messages in the form expected by "errorcheck" tests
|
|
Warnl(line int, fmt_ string, args ...interface{})
|
|
|
|
// Fowards the Debug_checknil flag from gc
|
|
Debug_checknil() bool
|
|
}
|
|
|
|
type Frontend interface {
|
|
TypeSource
|
|
Logger
|
|
|
|
// StringData returns a symbol pointing to the given string's contents.
|
|
StringData(string) interface{} // returns *gc.Sym
|
|
|
|
// Auto returns a Node for an auto variable of the given type.
|
|
// The SSA compiler uses this function to allocate space for spills.
|
|
Auto(Type) GCNode
|
|
|
|
// Line returns a string describing the given line number.
|
|
Line(int32) string
|
|
}
|
|
|
|
// interface used to hold *gc.Node. We'd use *gc.Node directly but
|
|
// that would lead to an import cycle.
|
|
type GCNode interface {
|
|
Typ() Type
|
|
String() string
|
|
}
|
|
|
|
// NewConfig returns a new configuration object for the given architecture.
|
|
func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config {
|
|
c := &Config{arch: arch, fe: fe}
|
|
switch arch {
|
|
case "amd64":
|
|
c.IntSize = 8
|
|
c.PtrSize = 8
|
|
c.lowerBlock = rewriteBlockAMD64
|
|
c.lowerValue = rewriteValueAMD64
|
|
case "386":
|
|
c.IntSize = 4
|
|
c.PtrSize = 4
|
|
c.lowerBlock = rewriteBlockAMD64
|
|
c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
|
|
default:
|
|
fe.Unimplementedf(0, "arch %s not implemented", arch)
|
|
}
|
|
c.ctxt = ctxt
|
|
c.optimize = optimize
|
|
|
|
// Assign IDs to preallocated values/blocks.
|
|
for i := range c.values {
|
|
c.values[i].ID = ID(i)
|
|
}
|
|
for i := range c.blocks {
|
|
c.blocks[i].ID = ID(i)
|
|
}
|
|
|
|
c.logfiles = make(map[string]*os.File)
|
|
|
|
return c
|
|
}
|
|
|
|
func (c *Config) Frontend() Frontend { return c.fe }
|
|
|
|
// NewFunc returns a new, empty function object.
|
|
// Caller must call f.Free() before calling NewFunc again.
|
|
func (c *Config) NewFunc() *Func {
|
|
// TODO(khr): should this function take name, type, etc. as arguments?
|
|
if c.curFunc != nil {
|
|
c.Fatalf(0, "NewFunc called without previous Free")
|
|
}
|
|
f := &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
|
|
c.curFunc = f
|
|
return f
|
|
}
|
|
|
|
func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) }
|
|
func (c *Config) Log() bool { return c.fe.Log() }
|
|
func (c *Config) Fatalf(line int32, msg string, args ...interface{}) { c.fe.Fatalf(line, msg, args...) }
|
|
func (c *Config) Unimplementedf(line int32, msg string, args ...interface{}) {
|
|
c.fe.Unimplementedf(line, msg, args...)
|
|
}
|
|
func (c *Config) Warnl(line int, msg string, args ...interface{}) { c.fe.Warnl(line, msg, args...) }
|
|
func (c *Config) Debug_checknil() bool { return c.fe.Debug_checknil() }
|
|
|
|
func (c *Config) logDebugHashMatch(evname, name string) {
|
|
var file *os.File
|
|
file = c.logfiles[evname]
|
|
if file == nil {
|
|
file = os.Stdout
|
|
tmpfile := os.Getenv("GSHS_LOGFILE")
|
|
if tmpfile != "" {
|
|
var ok error
|
|
file, ok = os.Create(tmpfile)
|
|
if ok != nil {
|
|
c.Fatalf(0, "Could not open hash-testing logfile %s", tmpfile)
|
|
}
|
|
}
|
|
c.logfiles[evname] = file
|
|
}
|
|
s := fmt.Sprintf("%s triggered %s\n", evname, name)
|
|
file.WriteString(s)
|
|
file.Sync()
|
|
}
|
|
|
|
// DebugHashMatch returns true if environment variable evname
|
|
// 1) is empty (this is a special more-quickly implemented case of 3)
|
|
// 2) is "y" or "Y"
|
|
// 3) is a suffix of the sha1 hash of name
|
|
// 4) is a suffix of the environment variable
|
|
// fmt.Sprintf("%s%d", evname, n)
|
|
// provided that all such variables are nonempty for 0 <= i <= n
|
|
// Otherwise it returns false.
|
|
// When true is returned the message
|
|
// "%s triggered %s\n", evname, name
|
|
// is printed on the file named in environment variable
|
|
// GSHS_LOGFILE
|
|
// or standard out if that is empty or there is an error
|
|
// opening the file.
|
|
|
|
func (c *Config) DebugHashMatch(evname, name string) bool {
|
|
evhash := os.Getenv(evname)
|
|
if evhash == "" {
|
|
return true // default behavior with no EV is "on"
|
|
}
|
|
if evhash == "y" || evhash == "Y" {
|
|
c.logDebugHashMatch(evname, name)
|
|
return true
|
|
}
|
|
if evhash == "n" || evhash == "N" {
|
|
return false
|
|
}
|
|
// Check the hash of the name against a partial input hash.
|
|
// We use this feature to do a binary search to
|
|
// find a function that is incorrectly compiled.
|
|
hstr := ""
|
|
for _, b := range sha1.Sum([]byte(name)) {
|
|
hstr += fmt.Sprintf("%08b", b)
|
|
}
|
|
|
|
if strings.HasSuffix(hstr, evhash) {
|
|
c.logDebugHashMatch(evname, name)
|
|
return true
|
|
}
|
|
|
|
// Iteratively try additional hashes to allow tests for multi-point
|
|
// failure.
|
|
for i := 0; true; i++ {
|
|
ev := fmt.Sprintf("%s%d", evname, i)
|
|
evv := os.Getenv(ev)
|
|
if evv == "" {
|
|
break
|
|
}
|
|
if strings.HasSuffix(hstr, evv) {
|
|
c.logDebugHashMatch(ev, name)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|