mirror of
https://github.com/golang/go.git
synced 2025-05-06 08:03:03 +00:00
[dev.ssa] cmd/compile: enhance SSA filtering, add OpConvert
Modified GOSSA{HASH.PKG} environment variable filters to make it easier to make/run with all SSA for testing. Disable attempts at SSA for architectures that are not amd64 (avoid spurious errors/unimplementeds.) Removed easy out for unimplemented features. Add convert op for proper liveness in presence of uintptr to/from unsafe.Pointer conversions. Tweaked stack sizes to get a pass on windows; 1024 instead 768, was observed to pass at least once. Change-Id: Ida3800afcda67d529e3b1cf48ca4a3f0fa48b2c5 Reviewed-on: https://go-review.googlesource.com/16201 Reviewed-by: Keith Randall <khr@golang.org> Run-TryBot: David Chase <drchase@google.com>
This commit is contained in:
parent
3abb844108
commit
e99dd52066
@ -414,7 +414,9 @@ func compile(fn *Node) {
|
|||||||
|
|
||||||
// Build an SSA backend function.
|
// Build an SSA backend function.
|
||||||
// TODO: get rid of usessa.
|
// TODO: get rid of usessa.
|
||||||
ssafn, usessa = buildssa(Curfn)
|
if Thearch.Thestring == "amd64" {
|
||||||
|
ssafn, usessa = buildssa(Curfn)
|
||||||
|
}
|
||||||
|
|
||||||
continpc = nil
|
continpc = nil
|
||||||
breakpc = nil
|
breakpc = nil
|
||||||
|
@ -24,8 +24,32 @@ import (
|
|||||||
// it will never return nil, and the bool can be removed.
|
// it will never return nil, and the bool can be removed.
|
||||||
func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
|
func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
|
||||||
name := fn.Func.Nname.Sym.Name
|
name := fn.Func.Nname.Sym.Name
|
||||||
|
gossahash := os.Getenv("GOSSAHASH")
|
||||||
usessa = strings.HasSuffix(name, "_ssa") || strings.Contains(name, "_ssa.") || name == os.Getenv("GOSSAFUNC")
|
usessa = strings.HasSuffix(name, "_ssa") || strings.Contains(name, "_ssa.") || name == os.Getenv("GOSSAFUNC")
|
||||||
|
|
||||||
|
// Environment variable control of SSA CG
|
||||||
|
// 1. IF GOSSAFUNC == current function name THEN
|
||||||
|
// compile this function with SSA and log output to ssa.html
|
||||||
|
|
||||||
|
// 2. IF GOSSAHASH == "y" or "Y" THEN
|
||||||
|
// compile this function (and everything else) with SSA
|
||||||
|
|
||||||
|
// 3. IF GOSSAHASH == "" THEN
|
||||||
|
// IF GOSSAPKG == current package name THEN
|
||||||
|
// compile this function (and everything in this package) with SSA
|
||||||
|
// ELSE
|
||||||
|
// use the old back end for this function.
|
||||||
|
// This is for compatibility with existing test harness and should go away.
|
||||||
|
|
||||||
|
// 4. IF GOSSAHASH is a suffix of the binary-rendered SHA1 hash of the function name THEN
|
||||||
|
// compile this function with SSA
|
||||||
|
// ELSE
|
||||||
|
// compile this function with the old back end.
|
||||||
|
|
||||||
|
// Plan is for 3 to be remove, and the 2) dependence on GOSSAHASH changes
|
||||||
|
// from "y"/"Y" to empty -- then SSA is default, and is disabled by setting
|
||||||
|
// GOSSAHASH to a value that is neither 0 nor 1 (e.g., "N" or "X")
|
||||||
|
|
||||||
if usessa {
|
if usessa {
|
||||||
fmt.Println("generating SSA for", name)
|
fmt.Println("generating SSA for", name)
|
||||||
dumplist("buildssa-enter", fn.Func.Enter)
|
dumplist("buildssa-enter", fn.Func.Enter)
|
||||||
@ -58,17 +82,6 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// If SSA support for the function is incomplete,
|
|
||||||
// assume that any panics are due to violated
|
|
||||||
// invariants. Swallow them silently.
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
if !e.unimplemented {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// We construct SSA using an algorithm similar to
|
// We construct SSA using an algorithm similar to
|
||||||
// Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau
|
// Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau
|
||||||
// http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
|
// http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
|
||||||
@ -167,27 +180,17 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
|
|||||||
// Main call to ssa package to compile function
|
// Main call to ssa package to compile function
|
||||||
ssa.Compile(s.f)
|
ssa.Compile(s.f)
|
||||||
|
|
||||||
// Calculate stats about what percentage of functions SSA handles.
|
if usessa || gossahash == "y" || gossahash == "Y" {
|
||||||
if false {
|
|
||||||
fmt.Printf("SSA implemented: %t\n", !e.unimplemented)
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.unimplemented {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: enable codegen more broadly once the codegen stabilizes
|
|
||||||
// and runtime support is in (gc maps, write barriers, etc.)
|
|
||||||
if usessa {
|
|
||||||
return s.f, true
|
return s.f, true
|
||||||
}
|
}
|
||||||
if localpkg.Name != os.Getenv("GOSSAPKG") {
|
if gossahash == "" {
|
||||||
return s.f, false
|
if localpkg.Name != os.Getenv("GOSSAPKG") {
|
||||||
}
|
return s.f, false
|
||||||
if os.Getenv("GOSSAHASH") == "" {
|
}
|
||||||
// Use everything in the package
|
// Use everything in the package
|
||||||
return s.f, true
|
return s.f, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the hash of the name against a partial input hash.
|
// Check the hash of the name against a partial input hash.
|
||||||
// We use this feature to do a binary search within a package to
|
// We use this feature to do a binary search within a package to
|
||||||
// find a function that is incorrectly compiled.
|
// find a function that is incorrectly compiled.
|
||||||
@ -195,10 +198,26 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
|
|||||||
for _, b := range sha1.Sum([]byte(name)) {
|
for _, b := range sha1.Sum([]byte(name)) {
|
||||||
hstr += fmt.Sprintf("%08b", b)
|
hstr += fmt.Sprintf("%08b", b)
|
||||||
}
|
}
|
||||||
if strings.HasSuffix(hstr, os.Getenv("GOSSAHASH")) {
|
|
||||||
|
if strings.HasSuffix(hstr, gossahash) {
|
||||||
fmt.Printf("GOSSAHASH triggered %s\n", name)
|
fmt.Printf("GOSSAHASH triggered %s\n", name)
|
||||||
return s.f, true
|
return s.f, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iteratively try additional hashes to allow tests for multi-point
|
||||||
|
// failure.
|
||||||
|
for i := 0; true; i++ {
|
||||||
|
ev := fmt.Sprintf("GOSSAHASH%d", i)
|
||||||
|
evv := os.Getenv(ev)
|
||||||
|
if evv == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(hstr, evv) {
|
||||||
|
fmt.Printf("%s triggered %s\n", ev, name)
|
||||||
|
return s.f, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return s.f, false
|
return s.f, false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1353,6 +1372,15 @@ func (s *state) expr(n *Node) *ssa.Value {
|
|||||||
// Assume everything will work out, so set up our return value.
|
// Assume everything will work out, so set up our return value.
|
||||||
// Anything interesting that happens from here is a fatal.
|
// Anything interesting that happens from here is a fatal.
|
||||||
x := s.expr(n.Left)
|
x := s.expr(n.Left)
|
||||||
|
|
||||||
|
// Special case for not confusing GC and liveness.
|
||||||
|
// We don't want pointers accidentally classified
|
||||||
|
// as not-pointers or vice-versa because of copy
|
||||||
|
// elision.
|
||||||
|
if to.IsPtr() != from.IsPtr() {
|
||||||
|
return s.newValue1(ssa.OpConvert, to, x)
|
||||||
|
}
|
||||||
|
|
||||||
v := s.newValue1(ssa.OpCopy, to, x) // ensure that v has the right type
|
v := s.newValue1(ssa.OpCopy, to, x) // ensure that v has the right type
|
||||||
|
|
||||||
// CONVNOP closure
|
// CONVNOP closure
|
||||||
@ -1364,6 +1392,7 @@ func (s *state) expr(n *Node) *ssa.Value {
|
|||||||
if from.Etype == to.Etype {
|
if from.Etype == to.Etype {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// unsafe.Pointer <--> *T
|
// unsafe.Pointer <--> *T
|
||||||
if to.Etype == TUNSAFEPTR && from.IsPtr() || from.Etype == TUNSAFEPTR && to.IsPtr() {
|
if to.Etype == TUNSAFEPTR && from.IsPtr() || from.Etype == TUNSAFEPTR && to.IsPtr() {
|
||||||
return v
|
return v
|
||||||
|
@ -281,6 +281,9 @@
|
|||||||
(Store [2] ptr val mem) -> (MOVWstore ptr val mem)
|
(Store [2] ptr val mem) -> (MOVWstore ptr val mem)
|
||||||
(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
|
(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
|
||||||
|
|
||||||
|
// We want this to stick out so the to/from ptr conversion is obvious
|
||||||
|
(Convert <t> x) -> (LEAQ <t> x)
|
||||||
|
|
||||||
// checks
|
// checks
|
||||||
(IsNonNil p) -> (SETNE (TESTQ p p))
|
(IsNonNil p) -> (SETNE (TESTQ p p))
|
||||||
(IsInBounds idx len) -> (SETB (CMPQ idx len))
|
(IsInBounds idx len) -> (SETB (CMPQ idx len))
|
||||||
|
@ -237,8 +237,9 @@ var genericOps = []opData{
|
|||||||
{name: "Sqrt"}, // sqrt(arg0), float64 only
|
{name: "Sqrt"}, // sqrt(arg0), float64 only
|
||||||
|
|
||||||
// Data movement
|
// Data movement
|
||||||
{name: "Phi"}, // select an argument based on which predecessor block we came from
|
{name: "Phi"}, // select an argument based on which predecessor block we came from
|
||||||
{name: "Copy"}, // output = arg0
|
{name: "Copy"}, // output = arg0
|
||||||
|
{name: "Convert"}, // output = arg0 -- a copy that converts to/from a pointer
|
||||||
|
|
||||||
// constants. Constant values are stored in the aux field.
|
// constants. Constant values are stored in the aux field.
|
||||||
// booleans have a bool aux field, strings have a string aux
|
// booleans have a bool aux field, strings have a string aux
|
||||||
|
@ -455,6 +455,7 @@ const (
|
|||||||
OpSqrt
|
OpSqrt
|
||||||
OpPhi
|
OpPhi
|
||||||
OpCopy
|
OpCopy
|
||||||
|
OpConvert
|
||||||
OpConstBool
|
OpConstBool
|
||||||
OpConstString
|
OpConstString
|
||||||
OpConstNil
|
OpConstNil
|
||||||
@ -3866,6 +3867,10 @@ var opcodeTable = [...]opInfo{
|
|||||||
name: "Copy",
|
name: "Copy",
|
||||||
generic: true,
|
generic: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Convert",
|
||||||
|
generic: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ConstBool",
|
name: "ConstBool",
|
||||||
generic: true,
|
generic: true,
|
||||||
|
@ -1670,6 +1670,24 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
|
|||||||
goto endc395c0a53eeccf597e225a07b53047d1
|
goto endc395c0a53eeccf597e225a07b53047d1
|
||||||
endc395c0a53eeccf597e225a07b53047d1:
|
endc395c0a53eeccf597e225a07b53047d1:
|
||||||
;
|
;
|
||||||
|
case OpConvert:
|
||||||
|
// match: (Convert <t> x)
|
||||||
|
// cond:
|
||||||
|
// result: (LEAQ <t> x)
|
||||||
|
{
|
||||||
|
t := v.Type
|
||||||
|
x := v.Args[0]
|
||||||
|
v.Op = OpAMD64LEAQ
|
||||||
|
v.AuxInt = 0
|
||||||
|
v.Aux = nil
|
||||||
|
v.resetArgs()
|
||||||
|
v.Type = t
|
||||||
|
v.AddArg(x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
goto end1cac40a6074914d6ae3d4aa039a625ed
|
||||||
|
end1cac40a6074914d6ae3d4aa039a625ed:
|
||||||
|
;
|
||||||
case OpCvt32Fto32:
|
case OpCvt32Fto32:
|
||||||
// match: (Cvt32Fto32 x)
|
// match: (Cvt32Fto32 x)
|
||||||
// cond:
|
// cond:
|
||||||
|
@ -54,8 +54,12 @@ func tighten(f *Func) {
|
|||||||
for _, b := range f.Blocks {
|
for _, b := range f.Blocks {
|
||||||
for i := 0; i < len(b.Values); i++ {
|
for i := 0; i < len(b.Values); i++ {
|
||||||
v := b.Values[i]
|
v := b.Values[i]
|
||||||
if v.Op == OpPhi || v.Op == OpGetClosurePtr {
|
if v.Op == OpPhi || v.Op == OpGetClosurePtr || v.Op == OpConvert {
|
||||||
// GetClosurePtr must stay in entry block
|
// GetClosurePtr must stay in entry block.
|
||||||
|
// OpConvert must not float over call sites.
|
||||||
|
// TODO do we instead need a dependence edge of some sort for OpConvert?
|
||||||
|
// Would memory do the trick, or do we need something else that relates
|
||||||
|
// to safe point operations?
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(v.Args) > 0 && v.Args[len(v.Args)-1].Type.IsMemory() {
|
if len(v.Args) > 0 && v.Args[len(v.Args)-1].Type.IsMemory() {
|
||||||
|
5
src/cmd/dist/test.go
vendored
5
src/cmd/dist/test.go
vendored
@ -278,11 +278,6 @@ func (t *tester) registerStdTest(pkg string) {
|
|||||||
|
|
||||||
// TODO: Remove when SSA codegen is used by default.
|
// TODO: Remove when SSA codegen is used by default.
|
||||||
func (t *tester) registerSSATest(pkg string) {
|
func (t *tester) registerSSATest(pkg string) {
|
||||||
switch pkg {
|
|
||||||
// known failures
|
|
||||||
case "runtime":
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.tests = append(t.tests, distTest{
|
t.tests = append(t.tests, distTest{
|
||||||
name: "go_test_ssa:" + pkg,
|
name: "go_test_ssa:" + pkg,
|
||||||
heading: "Testing packages with SSA codegen.",
|
heading: "Testing packages with SSA codegen.",
|
||||||
|
@ -41,7 +41,7 @@ const (
|
|||||||
STACKSYSTEM = 0
|
STACKSYSTEM = 0
|
||||||
StackSystem = STACKSYSTEM
|
StackSystem = STACKSYSTEM
|
||||||
StackBig = 4096
|
StackBig = 4096
|
||||||
StackGuard = 640*stackGuardMultiplier + StackSystem
|
StackGuard = 1024*stackGuardMultiplier + StackSystem
|
||||||
StackSmall = 128
|
StackSmall = 128
|
||||||
StackLimit = StackGuard - StackSystem - StackSmall
|
StackLimit = StackGuard - StackSystem - StackSmall
|
||||||
)
|
)
|
||||||
|
@ -385,6 +385,9 @@ func Dconv(p *Prog, a *Addr) string {
|
|||||||
if a.Index != REG_NONE {
|
if a.Index != REG_NONE {
|
||||||
str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
|
str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
|
||||||
}
|
}
|
||||||
|
if p.As == ATYPE && a.Gotype != nil {
|
||||||
|
str += fmt.Sprintf("%s", a.Gotype.Name)
|
||||||
|
}
|
||||||
|
|
||||||
case TYPE_CONST:
|
case TYPE_CONST:
|
||||||
if a.Reg != 0 {
|
if a.Reg != 0 {
|
||||||
|
@ -86,7 +86,7 @@ const (
|
|||||||
|
|
||||||
// The stack guard is a pointer this many bytes above the
|
// The stack guard is a pointer this many bytes above the
|
||||||
// bottom of the stack.
|
// bottom of the stack.
|
||||||
_StackGuard = 640*stackGuardMultiplier + _StackSystem
|
_StackGuard = 1024*stackGuardMultiplier + _StackSystem
|
||||||
|
|
||||||
// After a stack split check the SP is allowed to be this
|
// After a stack split check the SP is allowed to be this
|
||||||
// many bytes below the stack guard. This saves an instruction
|
// many bytes below the stack guard. This saves an instruction
|
||||||
|
@ -9,6 +9,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"cmd/internal/obj"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
@ -285,12 +286,13 @@ TestCases:
|
|||||||
// Instead of rewriting the test cases above, adjust
|
// Instead of rewriting the test cases above, adjust
|
||||||
// the first stack frame to use up the extra bytes.
|
// the first stack frame to use up the extra bytes.
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
size += 512 - 128
|
size += (obj.StackGuard - 128) - 128
|
||||||
// Noopt builds have a larger stackguard.
|
// Noopt builds have a larger stackguard.
|
||||||
// See ../cmd/dist/buildruntime.go:stackGuardMultiplier
|
// See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier
|
||||||
|
// This increase is included in obj.StackGuard
|
||||||
for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") {
|
for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") {
|
||||||
if s == "-N" {
|
if s == "-N" {
|
||||||
size += 640
|
size += obj.StackGuard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user