2015-04-15 15:51:25 -07:00
|
|
|
// 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
|
|
|
|
|
|
[dev.ssa] cmd/compile: enhance command line option processing for SSA
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>
2016-02-25 13:10:51 -05:00
|
|
|
import (
|
|
|
|
|
"cmd/internal/obj"
|
|
|
|
|
"crypto/sha1"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
2015-08-24 02:16:19 -07:00
|
|
|
|
2015-04-15 15:51:25 -07:00
|
|
|
type Config struct {
|
2015-07-30 11:03:05 -07:00
|
|
|
arch string // "amd64", etc.
|
|
|
|
|
IntSize int64 // 4 or 8
|
|
|
|
|
PtrSize int64 // 4 or 8
|
2015-05-27 14:52:22 -07:00
|
|
|
lowerBlock func(*Block) bool // lowering function
|
|
|
|
|
lowerValue func(*Value, *Config) bool // lowering function
|
|
|
|
|
fe Frontend // callbacks into compiler frontend
|
2015-08-10 12:15:52 -07:00
|
|
|
HTML *HTMLWriter // html writer, for debugging
|
2015-10-22 13:07:38 -07:00
|
|
|
ctxt *obj.Link // Generic arch information
|
2016-01-27 16:47:23 -08:00
|
|
|
optimize bool // Do optimization
|
2016-01-28 13:46:30 -08:00
|
|
|
curFunc *Func
|
2015-04-15 15:51:25 -07:00
|
|
|
|
|
|
|
|
// TODO: more stuff. Compiler flags of interest, ...
|
2016-01-28 13:46:30 -08:00
|
|
|
|
[dev.ssa] cmd/compile: enhance command line option processing for SSA
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>
2016-02-25 13:10:51 -05:00
|
|
|
// Given an environment variable used for debug hash match,
|
|
|
|
|
// what file (if any) receives the yes/no logging?
|
|
|
|
|
logfiles map[string]*os.File
|
|
|
|
|
|
2016-01-28 13:46:30 -08:00
|
|
|
// Storage for low-numbered values and blocks.
|
|
|
|
|
values [2000]Value
|
|
|
|
|
blocks [200]Block
|
2016-02-02 06:35:34 -05:00
|
|
|
|
2016-02-10 17:43:31 -05:00
|
|
|
domblockstore []ID // scratch space for computing dominators
|
|
|
|
|
scrSparse []*sparseSet // scratch sparse sets to be re-used.
|
2015-04-15 15:51:25 -07:00
|
|
|
}
|
|
|
|
|
|
2015-07-30 11:03:05 -07:00
|
|
|
type TypeSource interface {
|
|
|
|
|
TypeBool() Type
|
|
|
|
|
TypeInt8() Type
|
|
|
|
|
TypeInt16() Type
|
|
|
|
|
TypeInt32() Type
|
|
|
|
|
TypeInt64() Type
|
|
|
|
|
TypeUInt8() Type
|
|
|
|
|
TypeUInt16() Type
|
|
|
|
|
TypeUInt32() Type
|
|
|
|
|
TypeUInt64() Type
|
|
|
|
|
TypeInt() Type
|
2015-08-28 14:24:10 -04:00
|
|
|
TypeFloat32() Type
|
|
|
|
|
TypeFloat64() Type
|
2015-07-30 11:03:05 -07:00
|
|
|
TypeUintptr() Type
|
|
|
|
|
TypeString() Type
|
|
|
|
|
TypeBytePtr() Type // TODO: use unsafe.Pointer instead?
|
2015-09-18 22:58:10 -07:00
|
|
|
|
|
|
|
|
CanSSA(t Type) bool
|
2015-07-30 11:03:05 -07:00
|
|
|
}
|
|
|
|
|
|
2015-08-10 12:15:52 -07:00
|
|
|
type Logger interface {
|
2016-01-29 14:44:15 -05:00
|
|
|
// Logf logs a message from the compiler.
|
2015-06-24 14:03:39 -07:00
|
|
|
Logf(string, ...interface{})
|
2015-06-12 11:01:13 -07:00
|
|
|
|
2016-01-29 14:44:15 -05:00
|
|
|
// Log returns true if logging is not a no-op
|
|
|
|
|
// some logging calls account for more than a few heap allocations.
|
|
|
|
|
Log() bool
|
|
|
|
|
|
2015-06-12 11:01:13 -07:00
|
|
|
// Fatal reports a compiler error and exits.
|
2016-01-13 11:14:57 -08:00
|
|
|
Fatalf(line int32, msg string, args ...interface{})
|
2015-06-12 11:01:13 -07:00
|
|
|
|
|
|
|
|
// Unimplemented reports that the function cannot be compiled.
|
|
|
|
|
// It will be removed once SSA work is complete.
|
2016-01-13 11:14:57 -08:00
|
|
|
Unimplementedf(line int32, msg string, args ...interface{})
|
2015-10-26 17:34:06 -04:00
|
|
|
|
|
|
|
|
// 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
|
2015-05-27 14:52:22 -07:00
|
|
|
}
|
|
|
|
|
|
2015-08-10 12:15:52 -07:00
|
|
|
type Frontend interface {
|
|
|
|
|
TypeSource
|
|
|
|
|
Logger
|
|
|
|
|
|
|
|
|
|
// StringData returns a symbol pointing to the given string's contents.
|
|
|
|
|
StringData(string) interface{} // returns *gc.Sym
|
2015-08-24 02:16:19 -07:00
|
|
|
|
|
|
|
|
// Auto returns a Node for an auto variable of the given type.
|
|
|
|
|
// The SSA compiler uses this function to allocate space for spills.
|
2015-10-22 14:22:38 -07:00
|
|
|
Auto(Type) GCNode
|
2016-01-14 16:02:23 -08:00
|
|
|
|
|
|
|
|
// Line returns a string describing the given line number.
|
|
|
|
|
Line(int32) string
|
2015-10-22 14:22:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
2015-08-10 12:15:52 -07:00
|
|
|
}
|
|
|
|
|
|
2015-04-15 15:51:25 -07:00
|
|
|
// NewConfig returns a new configuration object for the given architecture.
|
2016-01-27 16:47:23 -08:00
|
|
|
func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config {
|
2015-05-27 14:52:22 -07:00
|
|
|
c := &Config{arch: arch, fe: fe}
|
2015-04-15 15:51:25 -07:00
|
|
|
switch arch {
|
|
|
|
|
case "amd64":
|
[dev.ssa] cmd/compile/internal/ssa: redo how sign extension is handled
For integer types less than a machine register, we have to decide
what the invariants are for the high bits of the register. We used
to set the high bits to the correct extension (sign or zero, as
determined by the type) of the low bits.
This CL makes the compiler ignore the high bits of the register
altogether (they are junk).
On this plus side, this means ops that generate subword results don't
have to worry about correctly extending them. On the minus side,
ops that consume subword arguments have to deal with the input
registers not being correctly extended.
For x86, this tradeoff is probably worth it. Almost all opcodes
have versions that use only the correct subword piece of their
inputs. (The one big exception is array indexing.) Not many opcodes
can correctly sign extend on output.
For other architectures, the tradeoff is probably not so clear, as
they don't have many subword-safe opcodes (e.g. 16-bit compare,
ignoring the high 16/48 bits). Fortunately we can decide whether
we do this per-architecture.
For the machine-independent opcodes, we pretend that the "register"
size is equal to the type width, so sign extension is immaterial.
Opcodes that care about the signedness of the input (e.g. compare,
right shift) have two different variants.
Change-Id: I465484c5734545ee697afe83bc8bf4b53bd9df8d
Reviewed-on: https://go-review.googlesource.com/12600
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2015-07-23 14:35:02 -07:00
|
|
|
c.IntSize = 8
|
2015-07-19 15:48:20 -07:00
|
|
|
c.PtrSize = 8
|
2015-06-06 16:03:33 -07:00
|
|
|
c.lowerBlock = rewriteBlockAMD64
|
|
|
|
|
c.lowerValue = rewriteValueAMD64
|
2015-04-15 15:51:25 -07:00
|
|
|
case "386":
|
[dev.ssa] cmd/compile/internal/ssa: redo how sign extension is handled
For integer types less than a machine register, we have to decide
what the invariants are for the high bits of the register. We used
to set the high bits to the correct extension (sign or zero, as
determined by the type) of the low bits.
This CL makes the compiler ignore the high bits of the register
altogether (they are junk).
On this plus side, this means ops that generate subword results don't
have to worry about correctly extending them. On the minus side,
ops that consume subword arguments have to deal with the input
registers not being correctly extended.
For x86, this tradeoff is probably worth it. Almost all opcodes
have versions that use only the correct subword piece of their
inputs. (The one big exception is array indexing.) Not many opcodes
can correctly sign extend on output.
For other architectures, the tradeoff is probably not so clear, as
they don't have many subword-safe opcodes (e.g. 16-bit compare,
ignoring the high 16/48 bits). Fortunately we can decide whether
we do this per-architecture.
For the machine-independent opcodes, we pretend that the "register"
size is equal to the type width, so sign extension is immaterial.
Opcodes that care about the signedness of the input (e.g. compare,
right shift) have two different variants.
Change-Id: I465484c5734545ee697afe83bc8bf4b53bd9df8d
Reviewed-on: https://go-review.googlesource.com/12600
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2015-07-23 14:35:02 -07:00
|
|
|
c.IntSize = 4
|
2015-07-19 15:48:20 -07:00
|
|
|
c.PtrSize = 4
|
2015-06-06 16:03:33 -07:00
|
|
|
c.lowerBlock = rewriteBlockAMD64
|
|
|
|
|
c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
|
2015-04-15 15:51:25 -07:00
|
|
|
default:
|
2016-01-13 11:14:57 -08:00
|
|
|
fe.Unimplementedf(0, "arch %s not implemented", arch)
|
2015-04-15 15:51:25 -07:00
|
|
|
}
|
2015-10-22 13:07:38 -07:00
|
|
|
c.ctxt = ctxt
|
2016-01-27 16:47:23 -08:00
|
|
|
c.optimize = optimize
|
2015-04-15 15:51:25 -07:00
|
|
|
|
2016-01-28 13:46:30 -08:00
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
[dev.ssa] cmd/compile: enhance command line option processing for SSA
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>
2016-02-25 13:10:51 -05:00
|
|
|
c.logfiles = make(map[string]*os.File)
|
|
|
|
|
|
2015-04-15 15:51:25 -07:00
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-22 13:13:53 -07:00
|
|
|
func (c *Config) Frontend() Frontend { return c.fe }
|
|
|
|
|
|
2016-01-28 13:46:30 -08:00
|
|
|
// NewFunc returns a new, empty function object.
|
|
|
|
|
// Caller must call f.Free() before calling NewFunc again.
|
2015-04-15 15:51:25 -07:00
|
|
|
func (c *Config) NewFunc() *Func {
|
|
|
|
|
// TODO(khr): should this function take name, type, etc. as arguments?
|
2016-01-28 13:46:30 -08:00
|
|
|
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
|
2015-04-15 15:51:25 -07:00
|
|
|
}
|
|
|
|
|
|
2016-01-13 11:14:57 -08:00
|
|
|
func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) }
|
2016-01-29 14:44:15 -05:00
|
|
|
func (c *Config) Log() bool { return c.fe.Log() }
|
2016-01-13 11:14:57 -08:00
|
|
|
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...)
|
|
|
|
|
}
|
2015-10-26 17:34:06 -04:00
|
|
|
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() }
|
[dev.ssa] cmd/compile: enhance command line option processing for SSA
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>
2016-02-25 13:10:51 -05:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|