"go/format"
"io/ioutil"
"log"
+ "os"
"path"
"regexp"
+ "runtime"
+ "runtime/pprof"
"sort"
"strings"
+ "sync"
)
type arch struct {
var archs []arch
+var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
+var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
+
func main() {
flag.Parse()
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ log.Fatal("could not create CPU profile: ", err)
+ }
+ defer f.Close()
+ if err := pprof.StartCPUProfile(f); err != nil {
+ log.Fatal("could not start CPU profile: ", err)
+ }
+ defer pprof.StopCPUProfile()
+ }
sort.Sort(ArchsByName(archs))
genOp()
genLower()
+ if *memprofile != "" {
+ f, err := os.Create(*memprofile)
+ if err != nil {
+ log.Fatal("could not create memory profile: ", err)
+ }
+ defer f.Close()
+ runtime.GC() // get up-to-date statistics
+ if err := pprof.WriteHeapProfile(f); err != nil {
+ log.Fatal("could not write memory profile: ", err)
+ }
+ }
}
func genOp() {
return s
}
+// genLower generates all arch-specific rewrite Go source files. The files are
+// generated and written concurrently, since it's a CPU-intensive task that can
+// easily make use of many cores on a machine.
+//
+// Note that there is no limit on the concurrency at the moment. On a four-core
+// laptop at the time of writing, peak RSS usually reached ~230MiB, which seems
+// doable by practially any machine nowadays. If that stops being the case, we
+// can cap this func to a fixed number of architectures being generated at once.
func genLower() {
+ var wg sync.WaitGroup
for _, a := range archs {
- genRules(a)
- genSplitLoadRules(a)
+ a := a
+ wg.Add(1)
+ go func() {
+ genRules(a)
+ genSplitLoadRules(a)
+ wg.Done()
+ }()
}
+ wg.Wait()
}
// countRegs returns the number of set bits in the register mask.
"go/parser"
"go/printer"
"go/token"
+ "go/types"
"io"
- "io/ioutil"
"log"
- "math"
"os"
"regexp"
"sort"
"strings"
+
+ "golang.org/x/tools/go/ast/astutil"
)
// rule syntax:
}
sort.Strings(ops)
- file := &File{arch: arch, suffix: suff}
+ genFile := &File{arch: arch, suffix: suff}
const chunkSize = 10
// Main rewrite routine is a switch on v.Op.
fn := &Func{kind: "Value"}
}
fn.add(sw)
fn.add(stmtf("return false"))
- file.add(fn)
+ genFile.add(fn)
// Generate a routine per op. Note that we don't make one giant routine
// because it is too big for some compilers.
kind: "Value",
suffix: fmt.Sprintf("_%s_%d", op, chunk),
}
- var rewrites bodyBase
+ fn.add(declf("b", "v.Block"))
+ fn.add(declf("config", "b.Func.Config"))
+ fn.add(declf("fe", "b.Func.fe"))
+ fn.add(declf("typ", "&b.Func.Config.Types"))
for _, rule := range rules[chunk:endchunk] {
if rr != nil && !rr.canFail {
log.Fatalf("unconditional rule %s is followed by other rules", rr.match)
if *genLog {
rr.add(stmtf("logRule(%q)", rule.loc))
}
- rewrites.add(rr)
- }
-
- // TODO(mvdan): remove unused vars later instead
- uses := make(map[string]int)
- walk(&rewrites, func(node Node) {
- switch node := node.(type) {
- case *Declare:
- // work around var shadowing
- // TODO(mvdan): forbid it instead.
- uses[node.name] = math.MinInt32
- case *ast.Ident:
- uses[node.Name]++
- }
- })
- if uses["b"]+uses["config"]+uses["fe"]+uses["typ"] > 0 {
- fn.add(declf("b", "v.Block"))
- }
- if uses["config"] > 0 {
- fn.add(declf("config", "b.Func.Config"))
+ fn.add(rr)
}
- if uses["fe"] > 0 {
- fn.add(declf("fe", "b.Func.fe"))
- }
- if uses["typ"] > 0 {
- fn.add(declf("typ", "&b.Func.Config.Types"))
- }
- fn.add(rewrites.list...)
if rr.canFail {
fn.add(stmtf("return false"))
}
- file.add(fn)
+ genFile.add(fn)
}
}
// so we can make this one function with a switch.
fn = &Func{kind: "Block"}
fn.add(declf("config", "b.Func.Config"))
- // TODO(mvdan): declare these only if needed
fn.add(declf("typ", "&config.Types"))
- fn.add(stmtf("_ = typ"))
fn.add(declf("v", "b.Control"))
- fn.add(stmtf("_ = v"))
sw = &Switch{expr: exprf("b.Kind")}
ops = ops[:0]
}
fn.add(sw)
fn.add(stmtf("return false"))
- file.add(fn)
+ genFile.add(fn)
- // gofmt result
+ // Remove unused imports and variables.
buf := new(bytes.Buffer)
- fprint(buf, file)
- b := buf.Bytes()
- src, err := format.Source(b)
+ fprint(buf, genFile)
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "", buf, parser.ParseComments)
if err != nil {
- fmt.Printf("%s\n", b)
- panic(err)
+ log.Fatal(err)
}
- // Write to file
- if err := ioutil.WriteFile("../rewrite"+arch.name+suff+".go", src, 0666); err != nil {
- log.Fatalf("can't write output: %v\n", err)
- }
-}
+ tfile := fset.File(file.Pos())
-func walk(node Node, fn func(Node)) {
- fn(node)
- switch node := node.(type) {
- case *bodyBase:
- case *File:
- case *Func:
- case *Switch:
- walk(node.expr, fn)
- case *Case:
- walk(node.expr, fn)
- case *RuleRewrite:
- case *Declare:
- walk(node.value, fn)
- case *CondBreak:
- walk(node.expr, fn)
- case ast.Node:
- ast.Inspect(node, func(node ast.Node) bool {
- fn(node)
+ for n := 0; n < 3; n++ {
+ unused := make(map[token.Pos]bool)
+ conf := types.Config{Error: func(err error) {
+ if terr, ok := err.(types.Error); ok && strings.Contains(terr.Msg, "not used") {
+ unused[terr.Pos] = true
+ }
+ }}
+ _, _ = conf.Check("ssa", fset, []*ast.File{file}, nil)
+ if len(unused) == 0 {
+ break
+ }
+ pre := func(c *astutil.Cursor) bool {
+ if node := c.Node(); node != nil && unused[node.Pos()] {
+ c.Delete()
+ // Unused imports and declarations use exactly
+ // one line. Prevent leaving an empty line.
+ tfile.MergeLine(tfile.Position(node.Pos()).Line)
+ return false
+ }
+ return true
+ }
+ post := func(c *astutil.Cursor) bool {
+ switch node := c.Node().(type) {
+ case *ast.GenDecl:
+ if len(node.Specs) == 0 {
+ c.Delete()
+ }
+ }
return true
- })
- default:
- log.Fatalf("cannot walk %T", node)
- }
- if wb, ok := node.(interface{ body() []Statement }); ok {
- for _, node := range wb.body() {
- walk(node, fn)
}
+ file = astutil.Apply(file, pre, post).(*ast.File)
+ }
+
+ // Write the well-formatted source to file
+ f, err := os.Create("../rewrite" + arch.name + suff + ".go")
+ if err != nil {
+ log.Fatalf("can't write output: %v", err)
+ }
+ defer f.Close()
+ // gofmt result; use a buffered writer, as otherwise go/format spends
+ // far too much time in syscalls.
+ bw := bufio.NewWriter(f)
+ if err := format.Node(bw, fset, file); err != nil {
+ log.Fatalf("can't format output: %v", err)
+ }
+ if err := bw.Flush(); err != nil {
+ log.Fatalf("can't write output: %v", err)
+ }
+ if err := f.Close(); err != nil {
+ log.Fatalf("can't write output: %v", err)
}
- fn(nil)
}
func fprint(w io.Writer, n Node) {
fmt.Fprintf(w, "// Code generated from gen/%s%s.rules; DO NOT EDIT.\n", n.arch.name, n.suffix)
fmt.Fprintf(w, "// generated with: cd gen; go run *.go\n")
fmt.Fprintf(w, "\npackage ssa\n")
- // TODO(mvdan): keep the needed imports only
- fmt.Fprintln(w, "import \"fmt\"")
- fmt.Fprintln(w, "import \"math\"")
- fmt.Fprintln(w, "import \"cmd/internal/obj\"")
- fmt.Fprintln(w, "import \"cmd/internal/objabi\"")
- fmt.Fprintln(w, "import \"cmd/compile/internal/types\"")
- fmt.Fprintln(w, "var _ = fmt.Println // in case not otherwise used")
- fmt.Fprintln(w, "var _ = math.MinInt8 // in case not otherwise used")
- fmt.Fprintln(w, "var _ = obj.ANOP // in case not otherwise used")
- fmt.Fprintln(w, "var _ = objabi.GOROOT // in case not otherwise used")
- fmt.Fprintln(w, "var _ = types.TypeMem // in case not otherwise used")
- fmt.Fprintln(w)
+ for _, path := range []string{
+ "fmt", "math",
+ "cmd/internal/obj", "cmd/internal/objabi",
+ "cmd/compile/internal/types",
+ } {
+ fmt.Fprintf(w, "import %q\n", path)
+ }
for _, f := range n.list {
f := f.(*Func)
fmt.Fprintf(w, "func rewrite%s%s%s%s(", f.kind, n.arch.name, n.suffix, f.suffix)
package ssa
-import "fmt"
import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValue386(v *Value) bool {
switch v.Op {
case Op386ADCL:
}
}
func rewriteBlock386(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case Block386EQ:
// match: (EQ (InvertFlags cmp) yes no)
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
-import "cmd/compile/internal/types"
-
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValue386splitload(v *Value) bool {
switch v.Op {
case Op386CMPBconstload:
}
}
func rewriteBlock386splitload(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
- v := b.Control
- _ = v
switch b.Kind {
}
return false
package ssa
-import "fmt"
import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValueAMD64(v *Value) bool {
switch v.Op {
case OpAMD64ADCQ:
}
func rewriteBlockAMD64(b *Block) bool {
config := b.Func.Config
- typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case BlockAMD64EQ:
// match: (EQ (TESTL (SHLL (MOVLconst [1]) x) y))
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
-import "cmd/compile/internal/types"
-
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValueAMD64splitload(v *Value) bool {
switch v.Op {
case OpAMD64CMPBconstload:
}
}
func rewriteBlockAMD64splitload(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
- v := b.Control
- _ = v
switch b.Kind {
}
return false
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValueARM(v *Value) bool {
switch v.Op {
case OpARMADC:
}
}
func rewriteBlockARM(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case BlockARMEQ:
// match: (EQ (FlagEQ) yes no)
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValueARM64(v *Value) bool {
switch v.Op {
case OpARM64ADCSflags:
}
}
func rewriteBlockARM64(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case BlockARM64EQ:
// match: (EQ (CMPWconst [0] x:(ANDconst [c] y)) yes no)
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValueMIPS(v *Value) bool {
switch v.Op {
case OpAdd16:
}
}
func rewriteBlockMIPS(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case BlockMIPSEQ:
// match: (EQ (FPFlagTrue cmp) yes no)
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValueMIPS64(v *Value) bool {
switch v.Op {
case OpAdd16:
}
}
func rewriteBlockMIPS64(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case BlockMIPS64EQ:
// match: (EQ (FPFlagTrue cmp) yes no)
package ssa
-import "fmt"
import "math"
-import "cmd/internal/obj"
import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValuePPC64(v *Value) bool {
switch v.Op {
case OpAbs:
}
}
func rewriteBlockPPC64(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case BlockPPC64EQ:
// match: (EQ (CMPconst [0] (ANDconst [c] x)) yes no)
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValueS390X(v *Value) bool {
switch v.Op {
case OpAdd16:
func rewriteBlockS390X(b *Block) bool {
config := b.Func.Config
typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case BlockS390XEQ:
// match: (EQ (InvertFlags cmp) yes no)
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValueWasm(v *Value) bool {
switch v.Op {
case OpAbs:
}
}
func rewriteBlockWasm(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
- v := b.Control
- _ = v
switch b.Kind {
}
return false
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValuedec(v *Value) bool {
switch v.Op {
case OpComplexImag:
return false
}
func rewriteBlockdec(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
- v := b.Control
- _ = v
switch b.Kind {
}
return false
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValuedec64(v *Value) bool {
switch v.Op {
case OpAdd64:
}
}
func rewriteBlockdec64(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
- v := b.Control
- _ = v
switch b.Kind {
}
return false
package ssa
-import "fmt"
-import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
-import "cmd/compile/internal/types"
-
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValuedecArgs(v *Value) bool {
switch v.Op {
case OpArg:
return false
}
func rewriteBlockdecArgs(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
- v := b.Control
- _ = v
switch b.Kind {
}
return false
package ssa
-import "fmt"
import "math"
-import "cmd/internal/obj"
-import "cmd/internal/objabi"
import "cmd/compile/internal/types"
-var _ = fmt.Println // in case not otherwise used
-var _ = math.MinInt8 // in case not otherwise used
-var _ = obj.ANOP // in case not otherwise used
-var _ = objabi.GOROOT // in case not otherwise used
-var _ = types.TypeMem // in case not otherwise used
-
func rewriteValuegeneric(v *Value) bool {
switch v.Op {
case OpAdd16:
return false
}
func rewriteBlockgeneric(b *Block) bool {
- config := b.Func.Config
- typ := &config.Types
- _ = typ
v := b.Control
- _ = v
switch b.Kind {
case BlockIf:
// match: (If (Not cond) yes no)