2015-03-23 17:02:11 -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.
|
|
|
|
|
|
2016-04-05 15:11:08 +10:00
|
|
|
// +build gen
|
|
|
|
|
|
2015-03-23 17:02:11 -07:00
|
|
|
// This program generates Go code that applies rewrite rules to a Value.
|
|
|
|
|
// The generated code implements a function of type func (v *Value) bool
|
|
|
|
|
// which returns true iff if did something.
|
|
|
|
|
// Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html
|
|
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"bytes"
|
2015-08-12 15:39:16 -07:00
|
|
|
"flag"
|
2015-03-23 17:02:11 -07:00
|
|
|
"fmt"
|
|
|
|
|
"go/format"
|
|
|
|
|
"io"
|
2015-03-26 10:49:03 -07:00
|
|
|
"io/ioutil"
|
2015-03-23 17:02:11 -07:00
|
|
|
"log"
|
|
|
|
|
"os"
|
2015-06-11 21:29:25 -07:00
|
|
|
"regexp"
|
2015-03-23 17:02:11 -07:00
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// rule syntax:
|
2015-10-13 11:08:08 -07:00
|
|
|
// sexpr [&& extra conditions] -> [@block] sexpr
|
2015-03-23 17:02:11 -07:00
|
|
|
//
|
|
|
|
|
// sexpr are s-expressions (lisp-like parenthesized groupings)
|
|
|
|
|
// sexpr ::= (opcode sexpr*)
|
|
|
|
|
// | variable
|
|
|
|
|
// | <type>
|
2015-06-11 21:29:25 -07:00
|
|
|
// | [auxint]
|
|
|
|
|
// | {aux}
|
2015-03-23 17:02:11 -07:00
|
|
|
//
|
|
|
|
|
// aux ::= variable | {code}
|
|
|
|
|
// type ::= variable | {code}
|
|
|
|
|
// variable ::= some token
|
|
|
|
|
// opcode ::= one of the opcodes from ../op.go (without the Op prefix)
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// extra conditions is just a chunk of Go that evaluates to a boolean. It may use
|
|
|
|
|
// variables declared in the matching sexpr. The variable "v" is predefined to be
|
2015-03-23 17:02:11 -07:00
|
|
|
// the value matched by the entire rule.
|
|
|
|
|
|
|
|
|
|
// If multiple rules match, the first one in file order is selected.
|
|
|
|
|
|
2015-08-12 15:39:16 -07:00
|
|
|
var (
|
|
|
|
|
genLog = flag.Bool("log", false, "generate code that logs; for debugging only")
|
|
|
|
|
)
|
|
|
|
|
|
2015-08-12 13:54:04 -07:00
|
|
|
type Rule struct {
|
2016-04-20 11:17:41 -07:00
|
|
|
rule string
|
|
|
|
|
loc string // file name & line number
|
2015-08-12 13:54:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r Rule) String() string {
|
2016-04-20 11:17:41 -07:00
|
|
|
return fmt.Sprintf("rule %q at %s", r.rule, r.loc)
|
2015-08-12 13:54:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse returns the matching part of the rule, additional conditions, and the result.
|
|
|
|
|
func (r Rule) parse() (match, cond, result string) {
|
|
|
|
|
s := strings.Split(r.rule, "->")
|
|
|
|
|
if len(s) != 2 {
|
|
|
|
|
log.Fatalf("no arrow in %s", r)
|
|
|
|
|
}
|
|
|
|
|
match = strings.TrimSpace(s[0])
|
|
|
|
|
result = strings.TrimSpace(s[1])
|
|
|
|
|
cond = ""
|
|
|
|
|
if i := strings.Index(match, "&&"); i >= 0 {
|
|
|
|
|
cond = strings.TrimSpace(match[i+2:])
|
|
|
|
|
match = strings.TrimSpace(match[:i])
|
|
|
|
|
}
|
|
|
|
|
return match, cond, result
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-06 16:03:33 -07:00
|
|
|
func genRules(arch arch) {
|
2015-03-23 17:02:11 -07:00
|
|
|
// Open input file.
|
2015-06-06 16:03:33 -07:00
|
|
|
text, err := os.Open(arch.name + ".rules")
|
2015-03-23 17:02:11 -07:00
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("can't read rule file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 16:45:33 -07:00
|
|
|
// oprules contains a list of rules for each block and opcode
|
2015-08-12 13:54:04 -07:00
|
|
|
blockrules := map[string][]Rule{}
|
|
|
|
|
oprules := map[string][]Rule{}
|
2015-03-23 17:02:11 -07:00
|
|
|
|
|
|
|
|
// read rule file
|
|
|
|
|
scanner := bufio.NewScanner(text)
|
2015-06-10 10:39:57 -07:00
|
|
|
rule := ""
|
2015-08-12 13:54:04 -07:00
|
|
|
var lineno int
|
2016-04-26 14:09:58 -07:00
|
|
|
var ruleLineno int // line number of "->"
|
2015-03-23 17:02:11 -07:00
|
|
|
for scanner.Scan() {
|
2015-08-12 13:54:04 -07:00
|
|
|
lineno++
|
2015-03-23 17:02:11 -07:00
|
|
|
line := scanner.Text()
|
|
|
|
|
if i := strings.Index(line, "//"); i >= 0 {
|
2016-03-01 23:21:55 +00:00
|
|
|
// Remove comments. Note that this isn't string safe, so
|
|
|
|
|
// it will truncate lines with // inside strings. Oh well.
|
2015-03-23 17:02:11 -07:00
|
|
|
line = line[:i]
|
|
|
|
|
}
|
2015-06-10 10:39:57 -07:00
|
|
|
rule += " " + line
|
|
|
|
|
rule = strings.TrimSpace(rule)
|
|
|
|
|
if rule == "" {
|
2015-03-23 17:02:11 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2015-06-10 10:39:57 -07:00
|
|
|
if !strings.Contains(rule, "->") {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2016-04-26 14:09:58 -07:00
|
|
|
if ruleLineno == 0 {
|
|
|
|
|
ruleLineno = lineno
|
|
|
|
|
}
|
2015-06-10 10:39:57 -07:00
|
|
|
if strings.HasSuffix(rule, "->") {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if unbalanced(rule) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
op := strings.Split(rule, " ")[0][1:]
|
2015-07-28 14:19:20 -07:00
|
|
|
if op[len(op)-1] == ')' {
|
|
|
|
|
op = op[:len(op)-1] // rule has only opcode, e.g. (ConstNil) -> ...
|
|
|
|
|
}
|
2016-04-26 14:09:58 -07:00
|
|
|
loc := fmt.Sprintf("%s.rules:%d", arch.name, ruleLineno)
|
2015-06-06 16:03:33 -07:00
|
|
|
if isBlock(op, arch) {
|
2016-04-20 11:17:41 -07:00
|
|
|
blockrules[op] = append(blockrules[op], Rule{rule: rule, loc: loc})
|
2015-05-28 16:45:33 -07:00
|
|
|
} else {
|
2016-04-20 11:17:41 -07:00
|
|
|
oprules[op] = append(oprules[op], Rule{rule: rule, loc: loc})
|
2015-05-28 16:45:33 -07:00
|
|
|
}
|
2015-06-10 10:39:57 -07:00
|
|
|
rule = ""
|
2016-04-26 14:09:58 -07:00
|
|
|
ruleLineno = 0
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
|
log.Fatalf("scanner failed: %v\n", err)
|
|
|
|
|
}
|
2015-08-12 13:54:04 -07:00
|
|
|
if unbalanced(rule) {
|
2016-04-20 11:17:41 -07:00
|
|
|
log.Fatalf("%s.rules:%d: unbalanced rule: %v\n", arch.name, lineno, rule)
|
2015-08-12 13:54:04 -07:00
|
|
|
}
|
2015-03-23 17:02:11 -07:00
|
|
|
|
2015-10-26 21:49:31 -07:00
|
|
|
// Order all the ops.
|
|
|
|
|
var ops []string
|
|
|
|
|
for op := range oprules {
|
|
|
|
|
ops = append(ops, op)
|
|
|
|
|
}
|
|
|
|
|
sort.Strings(ops)
|
|
|
|
|
|
2015-03-23 17:02:11 -07:00
|
|
|
// Start output buffer, write header.
|
|
|
|
|
w := new(bytes.Buffer)
|
2015-06-06 16:03:33 -07:00
|
|
|
fmt.Fprintf(w, "// autogenerated from gen/%s.rules: do not edit!\n", arch.name)
|
|
|
|
|
fmt.Fprintln(w, "// generated with: cd gen; go run *.go")
|
2016-03-01 10:58:06 -08:00
|
|
|
fmt.Fprintln(w)
|
2015-03-23 17:02:11 -07:00
|
|
|
fmt.Fprintln(w, "package ssa")
|
2015-08-12 15:39:16 -07:00
|
|
|
if *genLog {
|
|
|
|
|
fmt.Fprintln(w, "import \"fmt\"")
|
|
|
|
|
}
|
2015-08-28 14:24:10 -04:00
|
|
|
fmt.Fprintln(w, "import \"math\"")
|
|
|
|
|
fmt.Fprintln(w, "var _ = math.MinInt8 // in case not otherwise used")
|
|
|
|
|
|
2015-10-26 21:49:31 -07:00
|
|
|
// Main rewrite routine is a switch on v.Op.
|
2015-06-06 16:03:33 -07:00
|
|
|
fmt.Fprintf(w, "func rewriteValue%s(v *Value, config *Config) bool {\n", arch.name)
|
2015-03-23 17:02:11 -07:00
|
|
|
fmt.Fprintf(w, "switch v.Op {\n")
|
|
|
|
|
for _, op := range ops {
|
2015-06-06 16:03:33 -07:00
|
|
|
fmt.Fprintf(w, "case %s:\n", opName(op, arch))
|
2015-10-26 21:49:31 -07:00
|
|
|
fmt.Fprintf(w, "return rewriteValue%s_%s(v, config)\n", arch.name, opName(op, arch))
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
fmt.Fprintf(w, "return false\n")
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Generate a routine per op. Note that we don't make one giant routine
|
2015-10-26 21:49:31 -07:00
|
|
|
// because it is too big for some compilers.
|
|
|
|
|
for _, op := range ops {
|
|
|
|
|
fmt.Fprintf(w, "func rewriteValue%s_%s(v *Value, config *Config) bool {\n", arch.name, opName(op, arch))
|
|
|
|
|
fmt.Fprintln(w, "b := v.Block")
|
|
|
|
|
fmt.Fprintln(w, "_ = b")
|
2015-03-23 17:02:11 -07:00
|
|
|
for _, rule := range oprules[op] {
|
2015-08-12 13:54:04 -07:00
|
|
|
match, cond, result := rule.parse()
|
2015-03-23 17:02:11 -07:00
|
|
|
fmt.Fprintf(w, "// match: %s\n", match)
|
|
|
|
|
fmt.Fprintf(w, "// cond: %s\n", cond)
|
|
|
|
|
fmt.Fprintf(w, "// result: %s\n", result)
|
|
|
|
|
|
2016-02-04 19:52:10 +01:00
|
|
|
fmt.Fprintf(w, "for {\n")
|
2016-04-20 11:17:41 -07:00
|
|
|
genMatch(w, arch, match, rule.loc)
|
2015-03-23 17:02:11 -07:00
|
|
|
|
|
|
|
|
if cond != "" {
|
2016-02-04 19:52:10 +01:00
|
|
|
fmt.Fprintf(w, "if !(%s) {\nbreak\n}\n", cond)
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
2016-04-20 11:17:41 -07:00
|
|
|
genResult(w, arch, result, rule.loc)
|
2015-08-12 15:39:16 -07:00
|
|
|
if *genLog {
|
2016-04-20 11:17:41 -07:00
|
|
|
fmt.Fprintf(w, "fmt.Println(\"rewrite %s\")\n", rule.loc)
|
2015-08-12 15:39:16 -07:00
|
|
|
}
|
2015-03-23 17:02:11 -07:00
|
|
|
fmt.Fprintf(w, "return true\n")
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
}
|
2015-10-26 21:49:31 -07:00
|
|
|
fmt.Fprintf(w, "return false\n")
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Generate block rewrite function. There are only a few block types
|
2015-10-26 21:49:31 -07:00
|
|
|
// so we can make this one function with a switch.
|
2015-06-06 16:03:33 -07:00
|
|
|
fmt.Fprintf(w, "func rewriteBlock%s(b *Block) bool {\n", arch.name)
|
2015-05-28 16:45:33 -07:00
|
|
|
fmt.Fprintf(w, "switch b.Kind {\n")
|
|
|
|
|
ops = nil
|
|
|
|
|
for op := range blockrules {
|
|
|
|
|
ops = append(ops, op)
|
|
|
|
|
}
|
|
|
|
|
sort.Strings(ops)
|
|
|
|
|
for _, op := range ops {
|
2015-06-06 16:03:33 -07:00
|
|
|
fmt.Fprintf(w, "case %s:\n", blockName(op, arch))
|
2015-05-28 16:45:33 -07:00
|
|
|
for _, rule := range blockrules[op] {
|
2015-08-12 13:54:04 -07:00
|
|
|
match, cond, result := rule.parse()
|
2015-05-28 16:45:33 -07:00
|
|
|
fmt.Fprintf(w, "// match: %s\n", match)
|
|
|
|
|
fmt.Fprintf(w, "// cond: %s\n", cond)
|
|
|
|
|
fmt.Fprintf(w, "// result: %s\n", result)
|
|
|
|
|
|
2016-02-04 19:52:10 +01:00
|
|
|
fmt.Fprintf(w, "for {\n")
|
2015-08-12 13:54:04 -07:00
|
|
|
|
|
|
|
|
s := split(match[1 : len(match)-1]) // remove parens, then split
|
2015-05-28 16:45:33 -07:00
|
|
|
|
|
|
|
|
// check match of control value
|
|
|
|
|
if s[1] != "nil" {
|
|
|
|
|
fmt.Fprintf(w, "v := b.Control\n")
|
2016-03-21 16:18:45 -07:00
|
|
|
if strings.Contains(s[1], "(") {
|
2016-04-20 11:17:41 -07:00
|
|
|
genMatch0(w, arch, s[1], "v", map[string]struct{}{}, false, rule.loc)
|
2016-03-21 16:18:45 -07:00
|
|
|
} else {
|
|
|
|
|
fmt.Fprintf(w, "%s := b.Control\n", s[1])
|
|
|
|
|
}
|
2015-05-28 16:45:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// assign successor names
|
|
|
|
|
succs := s[2:]
|
|
|
|
|
for i, a := range succs {
|
|
|
|
|
if a != "_" {
|
|
|
|
|
fmt.Fprintf(w, "%s := b.Succs[%d]\n", a, i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cond != "" {
|
2016-02-04 19:52:10 +01:00
|
|
|
fmt.Fprintf(w, "if !(%s) {\nbreak\n}\n", cond)
|
2015-05-28 16:45:33 -07:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Rule matches. Generate result.
|
2015-05-28 16:45:33 -07:00
|
|
|
t := split(result[1 : len(result)-1]) // remove parens, then split
|
|
|
|
|
newsuccs := t[2:]
|
|
|
|
|
|
2015-08-28 16:45:17 -07:00
|
|
|
// Check if newsuccs is the same set as succs.
|
2015-05-28 16:45:33 -07:00
|
|
|
m := map[string]bool{}
|
|
|
|
|
for _, succ := range succs {
|
|
|
|
|
if m[succ] {
|
|
|
|
|
log.Fatalf("can't have a repeat successor name %s in %s", succ, rule)
|
|
|
|
|
}
|
|
|
|
|
m[succ] = true
|
|
|
|
|
}
|
|
|
|
|
for _, succ := range newsuccs {
|
|
|
|
|
if !m[succ] {
|
|
|
|
|
log.Fatalf("unknown successor %s in %s", succ, rule)
|
|
|
|
|
}
|
|
|
|
|
delete(m, succ)
|
|
|
|
|
}
|
2015-08-28 16:45:17 -07:00
|
|
|
if len(m) != 0 {
|
|
|
|
|
log.Fatalf("unmatched successors %v in %s", m, rule)
|
|
|
|
|
}
|
2015-05-28 16:45:33 -07:00
|
|
|
|
|
|
|
|
// Modify predecessor lists for no-longer-reachable blocks
|
|
|
|
|
for succ := range m {
|
2015-08-04 12:24:23 -07:00
|
|
|
fmt.Fprintf(w, "b.Func.removePredecessor(b, %s)\n", succ)
|
2015-05-28 16:45:33 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-06 16:03:33 -07:00
|
|
|
fmt.Fprintf(w, "b.Kind = %s\n", blockName(t[0], arch))
|
2015-05-28 16:45:33 -07:00
|
|
|
if t[1] == "nil" {
|
2016-03-15 20:45:50 -07:00
|
|
|
fmt.Fprintf(w, "b.SetControl(nil)\n")
|
2015-05-28 16:45:33 -07:00
|
|
|
} else {
|
2016-04-20 11:17:41 -07:00
|
|
|
fmt.Fprintf(w, "b.SetControl(%s)\n", genResult0(w, arch, t[1], new(int), false, false, rule.loc))
|
2015-05-28 16:45:33 -07:00
|
|
|
}
|
|
|
|
|
if len(newsuccs) < len(succs) {
|
|
|
|
|
fmt.Fprintf(w, "b.Succs = b.Succs[:%d]\n", len(newsuccs))
|
|
|
|
|
}
|
|
|
|
|
for i, a := range newsuccs {
|
|
|
|
|
fmt.Fprintf(w, "b.Succs[%d] = %s\n", i, a)
|
|
|
|
|
}
|
2015-08-11 17:28:56 -07:00
|
|
|
// Update branch prediction
|
|
|
|
|
switch {
|
|
|
|
|
case len(newsuccs) != 2:
|
|
|
|
|
fmt.Fprintln(w, "b.Likely = BranchUnknown")
|
|
|
|
|
case newsuccs[0] == succs[0] && newsuccs[1] == succs[1]:
|
|
|
|
|
// unchanged
|
|
|
|
|
case newsuccs[0] == succs[1] && newsuccs[1] == succs[0]:
|
|
|
|
|
// flipped
|
|
|
|
|
fmt.Fprintln(w, "b.Likely *= -1")
|
|
|
|
|
default:
|
|
|
|
|
// unknown
|
|
|
|
|
fmt.Fprintln(w, "b.Likely = BranchUnknown")
|
|
|
|
|
}
|
2015-05-28 16:45:33 -07:00
|
|
|
|
2015-08-12 15:39:16 -07:00
|
|
|
if *genLog {
|
2016-04-20 11:17:41 -07:00
|
|
|
fmt.Fprintf(w, "fmt.Println(\"rewrite %s\")\n", rule.loc)
|
2015-08-12 15:39:16 -07:00
|
|
|
}
|
2015-05-28 16:45:33 -07:00
|
|
|
fmt.Fprintf(w, "return true\n")
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
fmt.Fprintf(w, "return false\n")
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
|
2015-03-23 17:02:11 -07:00
|
|
|
// gofmt result
|
|
|
|
|
b := w.Bytes()
|
2015-08-12 15:39:16 -07:00
|
|
|
src, err := format.Source(b)
|
2015-03-23 17:02:11 -07:00
|
|
|
if err != nil {
|
2015-08-12 15:39:16 -07:00
|
|
|
fmt.Printf("%s\n", b)
|
2015-03-23 17:02:11 -07:00
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-06 16:03:33 -07:00
|
|
|
// Write to file
|
2015-08-12 15:39:16 -07:00
|
|
|
err = ioutil.WriteFile("../rewrite"+arch.name+".go", src, 0666)
|
2015-03-26 10:49:03 -07:00
|
|
|
if err != nil {
|
2015-03-23 17:02:11 -07:00
|
|
|
log.Fatalf("can't write output: %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-20 11:17:41 -07:00
|
|
|
func genMatch(w io.Writer, arch arch, match string, loc string) {
|
|
|
|
|
genMatch0(w, arch, match, "v", map[string]struct{}{}, true, loc)
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
2016-04-20 11:17:41 -07:00
|
|
|
func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, top bool, loc string) {
|
2016-03-21 16:18:45 -07:00
|
|
|
if match[0] != '(' || match[len(match)-1] != ')' {
|
|
|
|
|
panic("non-compound expr in genMatch0: " + match)
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// split body up into regions. Split by spaces/tabs, except those
|
2015-03-23 17:02:11 -07:00
|
|
|
// contained in () or {}.
|
2015-05-28 16:45:33 -07:00
|
|
|
s := split(match[1 : len(match)-1]) // remove parens, then split
|
2015-03-23 17:02:11 -07:00
|
|
|
|
2016-04-20 11:17:41 -07:00
|
|
|
// Find op record
|
|
|
|
|
var op opData
|
|
|
|
|
for _, x := range genericOps {
|
|
|
|
|
if x.name == s[0] {
|
|
|
|
|
op = x
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, x := range arch.ops {
|
|
|
|
|
if x.name == s[0] {
|
|
|
|
|
op = x
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if op.name == "" {
|
|
|
|
|
log.Fatalf("%s: unknown op %s", loc, s[0])
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-23 17:02:11 -07:00
|
|
|
// check op
|
|
|
|
|
if !top {
|
2016-02-04 19:52:10 +01:00
|
|
|
fmt.Fprintf(w, "if %s.Op != %s {\nbreak\n}\n", v, opName(s[0], arch))
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check type/aux/args
|
|
|
|
|
argnum := 0
|
|
|
|
|
for _, a := range s[1:] {
|
|
|
|
|
if a[0] == '<' {
|
|
|
|
|
// type restriction
|
2015-05-28 16:45:33 -07:00
|
|
|
t := a[1 : len(a)-1] // remove <>
|
2015-06-11 21:29:25 -07:00
|
|
|
if !isVariable(t) {
|
2016-03-01 23:21:55 +00:00
|
|
|
// code. We must match the results of this code.
|
2016-02-04 19:52:10 +01:00
|
|
|
fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, t)
|
2015-03-23 17:02:11 -07:00
|
|
|
} else {
|
|
|
|
|
// variable
|
2016-03-04 10:26:57 -08:00
|
|
|
if _, ok := m[t]; ok {
|
2015-03-23 17:02:11 -07:00
|
|
|
// must match previous variable
|
2016-03-04 10:26:57 -08:00
|
|
|
fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, t)
|
2015-03-23 17:02:11 -07:00
|
|
|
} else {
|
2016-03-21 16:18:45 -07:00
|
|
|
m[t] = struct{}{}
|
2015-03-23 17:02:11 -07:00
|
|
|
fmt.Fprintf(w, "%s := %s.Type\n", t, v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if a[0] == '[' {
|
2015-06-11 21:29:25 -07:00
|
|
|
// auxint restriction
|
2016-04-20 11:17:41 -07:00
|
|
|
switch op.aux {
|
|
|
|
|
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32":
|
|
|
|
|
default:
|
|
|
|
|
log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
|
|
|
|
|
}
|
2015-05-28 16:45:33 -07:00
|
|
|
x := a[1 : len(a)-1] // remove []
|
2015-06-11 21:29:25 -07:00
|
|
|
if !isVariable(x) {
|
2015-03-23 17:02:11 -07:00
|
|
|
// code
|
2016-02-04 19:52:10 +01:00
|
|
|
fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, x)
|
2015-06-11 21:29:25 -07:00
|
|
|
} else {
|
|
|
|
|
// variable
|
2016-03-04 10:26:57 -08:00
|
|
|
if _, ok := m[x]; ok {
|
|
|
|
|
fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, x)
|
2015-06-11 21:29:25 -07:00
|
|
|
} else {
|
2016-03-21 16:18:45 -07:00
|
|
|
m[x] = struct{}{}
|
2015-06-11 21:29:25 -07:00
|
|
|
fmt.Fprintf(w, "%s := %s.AuxInt\n", x, v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if a[0] == '{' {
|
2016-04-20 11:17:41 -07:00
|
|
|
// aux restriction
|
|
|
|
|
switch op.aux {
|
|
|
|
|
case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32":
|
|
|
|
|
default:
|
|
|
|
|
log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
|
|
|
|
|
}
|
2015-06-11 21:29:25 -07:00
|
|
|
x := a[1 : len(a)-1] // remove {}
|
|
|
|
|
if !isVariable(x) {
|
|
|
|
|
// code
|
2016-02-04 19:52:10 +01:00
|
|
|
fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, x)
|
2015-03-23 17:02:11 -07:00
|
|
|
} else {
|
|
|
|
|
// variable
|
2016-03-04 10:26:57 -08:00
|
|
|
if _, ok := m[x]; ok {
|
|
|
|
|
fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, x)
|
2015-03-23 17:02:11 -07:00
|
|
|
} else {
|
2016-03-21 16:18:45 -07:00
|
|
|
m[x] = struct{}{}
|
2015-03-23 17:02:11 -07:00
|
|
|
fmt.Fprintf(w, "%s := %s.Aux\n", x, v)
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-21 16:18:45 -07:00
|
|
|
} else if a == "_" {
|
|
|
|
|
argnum++
|
|
|
|
|
} else if !strings.Contains(a, "(") {
|
|
|
|
|
// leaf variable
|
|
|
|
|
if _, ok := m[a]; ok {
|
|
|
|
|
// variable already has a definition. Check whether
|
|
|
|
|
// the old definition and the new definition match.
|
|
|
|
|
// For example, (add x x). Equality is just pointer equality
|
|
|
|
|
// on Values (so cse is important to do before lowering).
|
|
|
|
|
fmt.Fprintf(w, "if %s != %s.Args[%d] {\nbreak\n}\n", a, v, argnum)
|
|
|
|
|
} else {
|
|
|
|
|
// remember that this variable references the given value
|
|
|
|
|
m[a] = struct{}{}
|
|
|
|
|
fmt.Fprintf(w, "%s := %s.Args[%d]\n", a, v, argnum)
|
|
|
|
|
}
|
|
|
|
|
argnum++
|
2015-03-23 17:02:11 -07:00
|
|
|
} else {
|
2016-03-21 16:18:45 -07:00
|
|
|
// compound sexpr
|
|
|
|
|
var argname string
|
|
|
|
|
colon := strings.Index(a, ":")
|
|
|
|
|
openparen := strings.Index(a, "(")
|
|
|
|
|
if colon >= 0 && openparen >= 0 && colon < openparen {
|
|
|
|
|
// rule-specified name
|
|
|
|
|
argname = a[:colon]
|
|
|
|
|
a = a[colon+1:]
|
|
|
|
|
} else {
|
|
|
|
|
// autogenerated name
|
|
|
|
|
argname = fmt.Sprintf("%s_%d", v, argnum)
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, "%s := %s.Args[%d]\n", argname, v, argnum)
|
2016-04-20 11:17:41 -07:00
|
|
|
genMatch0(w, arch, a, argname, m, false, loc)
|
2015-03-23 17:02:11 -07:00
|
|
|
argnum++
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-20 11:17:41 -07:00
|
|
|
if op.argLength == -1 {
|
2016-02-09 19:46:26 +01:00
|
|
|
fmt.Fprintf(w, "if len(%s.Args) != %d {\nbreak\n}\n", v, argnum)
|
2016-04-20 11:17:41 -07:00
|
|
|
} else if int(op.argLength) != argnum {
|
|
|
|
|
log.Fatalf("%s: op %s should have %d args, has %d", loc, op.name, op.argLength, argnum)
|
2016-02-09 19:46:26 +01:00
|
|
|
}
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
2016-04-20 11:17:41 -07:00
|
|
|
func genResult(w io.Writer, arch arch, result string, loc string) {
|
2016-02-24 10:29:27 -08:00
|
|
|
move := false
|
2015-10-13 11:08:08 -07:00
|
|
|
if result[0] == '@' {
|
|
|
|
|
// parse @block directive
|
|
|
|
|
s := strings.SplitN(result[1:], " ", 2)
|
2016-02-24 10:29:27 -08:00
|
|
|
fmt.Fprintf(w, "b = %s\n", s[0])
|
2015-10-13 11:08:08 -07:00
|
|
|
result = s[1]
|
2016-02-24 10:29:27 -08:00
|
|
|
move = true
|
2015-10-13 11:08:08 -07:00
|
|
|
}
|
2016-04-20 11:17:41 -07:00
|
|
|
genResult0(w, arch, result, new(int), true, move, loc)
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
2016-04-20 11:17:41 -07:00
|
|
|
func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move bool, loc string) string {
|
2016-02-28 15:51:11 -08:00
|
|
|
// TODO: when generating a constant result, use f.constVal to avoid
|
|
|
|
|
// introducing copies just to clean them up again.
|
2015-03-23 17:02:11 -07:00
|
|
|
if result[0] != '(' {
|
|
|
|
|
// variable
|
2015-05-18 16:44:20 -07:00
|
|
|
if top {
|
2015-08-05 10:33:09 -07:00
|
|
|
// It in not safe in general to move a variable between blocks
|
|
|
|
|
// (and particularly not a phi node).
|
|
|
|
|
// Introduce a copy.
|
2016-02-04 17:21:57 +01:00
|
|
|
fmt.Fprintf(w, "v.reset(OpCopy)\n")
|
2015-08-05 10:33:09 -07:00
|
|
|
fmt.Fprintf(w, "v.Type = %s.Type\n", result)
|
|
|
|
|
fmt.Fprintf(w, "v.AddArg(%s)\n", result)
|
2015-05-18 16:44:20 -07:00
|
|
|
}
|
2015-03-23 17:02:11 -07:00
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 16:45:33 -07:00
|
|
|
s := split(result[1 : len(result)-1]) // remove parens, then split
|
2016-02-03 20:50:12 +01:00
|
|
|
|
2016-04-20 11:17:41 -07:00
|
|
|
// Find op record
|
|
|
|
|
var op opData
|
|
|
|
|
for _, x := range genericOps {
|
|
|
|
|
if x.name == s[0] {
|
|
|
|
|
op = x
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, x := range arch.ops {
|
|
|
|
|
if x.name == s[0] {
|
|
|
|
|
op = x
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if op.name == "" {
|
|
|
|
|
log.Fatalf("%s: unknown op %s", loc, s[0])
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-03 20:50:12 +01:00
|
|
|
// Find the type of the variable.
|
|
|
|
|
var opType string
|
|
|
|
|
var typeOverride bool
|
|
|
|
|
for _, a := range s[1:] {
|
|
|
|
|
if a[0] == '<' {
|
|
|
|
|
// type restriction
|
|
|
|
|
opType = a[1 : len(a)-1] // remove <>
|
|
|
|
|
typeOverride = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if opType == "" {
|
|
|
|
|
// find default type, if any
|
|
|
|
|
for _, op := range arch.ops {
|
|
|
|
|
if op.name == s[0] && op.typ != "" {
|
|
|
|
|
opType = typeName(op.typ)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if opType == "" {
|
|
|
|
|
for _, op := range genericOps {
|
|
|
|
|
if op.name == s[0] && op.typ != "" {
|
|
|
|
|
opType = typeName(op.typ)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-23 17:02:11 -07:00
|
|
|
var v string
|
2016-02-24 10:29:27 -08:00
|
|
|
if top && !move {
|
2015-03-23 17:02:11 -07:00
|
|
|
v = "v"
|
2016-02-04 17:21:57 +01:00
|
|
|
fmt.Fprintf(w, "v.reset(%s)\n", opName(s[0], arch))
|
2016-02-03 20:50:12 +01:00
|
|
|
if typeOverride {
|
|
|
|
|
fmt.Fprintf(w, "v.Type = %s\n", opType)
|
|
|
|
|
}
|
2015-03-23 17:02:11 -07:00
|
|
|
} else {
|
2016-02-03 20:50:12 +01:00
|
|
|
if opType == "" {
|
|
|
|
|
log.Fatalf("sub-expression %s (op=%s) must have a type", result, s[0])
|
|
|
|
|
}
|
2015-03-23 17:02:11 -07:00
|
|
|
v = fmt.Sprintf("v%d", *alloc)
|
|
|
|
|
*alloc++
|
2016-02-24 10:29:27 -08:00
|
|
|
fmt.Fprintf(w, "%s := b.NewValue0(v.Line, %s, %s)\n", v, opName(s[0], arch), opType)
|
2016-03-15 20:45:50 -07:00
|
|
|
if move && top {
|
2015-10-13 11:08:08 -07:00
|
|
|
// Rewrite original into a copy
|
2016-02-04 17:21:57 +01:00
|
|
|
fmt.Fprintf(w, "v.reset(OpCopy)\n")
|
2015-10-13 11:08:08 -07:00
|
|
|
fmt.Fprintf(w, "v.AddArg(%s)\n", v)
|
|
|
|
|
}
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
2016-04-20 11:17:41 -07:00
|
|
|
argnum := 0
|
2015-03-23 17:02:11 -07:00
|
|
|
for _, a := range s[1:] {
|
|
|
|
|
if a[0] == '<' {
|
2016-02-03 20:50:12 +01:00
|
|
|
// type restriction, handled above
|
2015-03-23 17:02:11 -07:00
|
|
|
} else if a[0] == '[' {
|
2015-06-11 21:29:25 -07:00
|
|
|
// auxint restriction
|
2016-04-20 11:17:41 -07:00
|
|
|
switch op.aux {
|
|
|
|
|
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32":
|
|
|
|
|
default:
|
|
|
|
|
log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
|
|
|
|
|
}
|
2015-05-28 16:45:33 -07:00
|
|
|
x := a[1 : len(a)-1] // remove []
|
2015-06-11 21:29:25 -07:00
|
|
|
fmt.Fprintf(w, "%s.AuxInt = %s\n", v, x)
|
2015-03-23 17:02:11 -07:00
|
|
|
} else if a[0] == '{' {
|
2015-06-11 21:29:25 -07:00
|
|
|
// aux restriction
|
2016-04-20 11:17:41 -07:00
|
|
|
switch op.aux {
|
|
|
|
|
case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32":
|
|
|
|
|
default:
|
|
|
|
|
log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
|
|
|
|
|
}
|
2015-06-11 21:29:25 -07:00
|
|
|
x := a[1 : len(a)-1] // remove {}
|
|
|
|
|
fmt.Fprintf(w, "%s.Aux = %s\n", v, x)
|
2015-03-23 17:02:11 -07:00
|
|
|
} else {
|
|
|
|
|
// regular argument (sexpr or variable)
|
2016-04-20 11:17:41 -07:00
|
|
|
x := genResult0(w, arch, a, alloc, false, move, loc)
|
2015-03-23 17:02:11 -07:00
|
|
|
fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x)
|
2016-04-20 11:17:41 -07:00
|
|
|
argnum++
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
}
|
2016-04-20 11:17:41 -07:00
|
|
|
if op.argLength != -1 && int(op.argLength) != argnum {
|
|
|
|
|
log.Fatalf("%s: op %s should have %d args, has %d", loc, op.name, op.argLength, argnum)
|
|
|
|
|
}
|
2016-02-03 20:50:12 +01:00
|
|
|
|
2015-03-23 17:02:11 -07:00
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func split(s string) []string {
|
|
|
|
|
var r []string
|
|
|
|
|
|
|
|
|
|
outer:
|
|
|
|
|
for s != "" {
|
2015-05-18 16:44:20 -07:00
|
|
|
d := 0 // depth of ({[<
|
|
|
|
|
var open, close byte // opening and closing markers ({[< or )}]>
|
|
|
|
|
nonsp := false // found a non-space char so far
|
2015-03-23 17:02:11 -07:00
|
|
|
for i := 0; i < len(s); i++ {
|
2015-05-18 16:44:20 -07:00
|
|
|
switch {
|
|
|
|
|
case d == 0 && s[i] == '(':
|
|
|
|
|
open, close = '(', ')'
|
2015-03-23 17:02:11 -07:00
|
|
|
d++
|
2015-05-18 16:44:20 -07:00
|
|
|
case d == 0 && s[i] == '<':
|
|
|
|
|
open, close = '<', '>'
|
|
|
|
|
d++
|
|
|
|
|
case d == 0 && s[i] == '[':
|
|
|
|
|
open, close = '[', ']'
|
|
|
|
|
d++
|
|
|
|
|
case d == 0 && s[i] == '{':
|
|
|
|
|
open, close = '{', '}'
|
|
|
|
|
d++
|
|
|
|
|
case d == 0 && (s[i] == ' ' || s[i] == '\t'):
|
|
|
|
|
if nonsp {
|
2015-03-23 17:02:11 -07:00
|
|
|
r = append(r, strings.TrimSpace(s[:i]))
|
|
|
|
|
s = s[i:]
|
|
|
|
|
continue outer
|
|
|
|
|
}
|
2015-05-18 16:44:20 -07:00
|
|
|
case d > 0 && s[i] == open:
|
|
|
|
|
d++
|
|
|
|
|
case d > 0 && s[i] == close:
|
|
|
|
|
d--
|
2015-03-23 17:02:11 -07:00
|
|
|
default:
|
|
|
|
|
nonsp = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if d != 0 {
|
|
|
|
|
panic("imbalanced expression: " + s)
|
|
|
|
|
}
|
|
|
|
|
if nonsp {
|
|
|
|
|
r = append(r, strings.TrimSpace(s))
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
return r
|
|
|
|
|
}
|
2015-06-06 16:03:33 -07:00
|
|
|
|
|
|
|
|
// isBlock returns true if this op is a block opcode.
|
|
|
|
|
func isBlock(name string, arch arch) bool {
|
|
|
|
|
for _, b := range genericBlocks {
|
|
|
|
|
if b.name == name {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, b := range arch.blocks {
|
|
|
|
|
if b.name == name {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// opName converts from an op name specified in a rule file to an Op enum.
|
|
|
|
|
// if the name matches a generic op, returns "Op" plus the specified name.
|
|
|
|
|
// Otherwise, returns "Op" plus arch name plus op name.
|
|
|
|
|
func opName(name string, arch arch) string {
|
|
|
|
|
for _, op := range genericOps {
|
|
|
|
|
if op.name == name {
|
|
|
|
|
return "Op" + name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "Op" + arch.name + name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func blockName(name string, arch arch) string {
|
|
|
|
|
for _, b := range genericBlocks {
|
|
|
|
|
if b.name == name {
|
|
|
|
|
return "Block" + name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "Block" + arch.name + name
|
|
|
|
|
}
|
2015-06-10 10:39:57 -07:00
|
|
|
|
2015-09-01 09:16:58 -07:00
|
|
|
// typeName returns the string to use to generate a type.
|
|
|
|
|
func typeName(typ string) string {
|
|
|
|
|
switch typ {
|
2015-10-21 17:18:07 -07:00
|
|
|
case "Flags", "Mem", "Void", "Int128":
|
2015-09-01 09:16:58 -07:00
|
|
|
return "Type" + typ
|
|
|
|
|
default:
|
|
|
|
|
return "config.fe.Type" + typ + "()"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-10 10:39:57 -07:00
|
|
|
// unbalanced returns true if there aren't the same number of ( and ) in the string.
|
|
|
|
|
func unbalanced(s string) bool {
|
|
|
|
|
var left, right int
|
|
|
|
|
for _, c := range s {
|
|
|
|
|
if c == '(' {
|
|
|
|
|
left++
|
|
|
|
|
}
|
|
|
|
|
if c == ')' {
|
|
|
|
|
right++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return left != right
|
|
|
|
|
}
|
2015-06-11 21:29:25 -07:00
|
|
|
|
|
|
|
|
// isVariable reports whether s is a single Go alphanumeric identifier.
|
|
|
|
|
func isVariable(s string) bool {
|
2015-08-18 10:28:58 -07:00
|
|
|
b, err := regexp.MatchString("^[A-Za-z_][A-Za-z_0-9]*$", s)
|
2015-06-11 21:29:25 -07:00
|
|
|
if err != nil {
|
|
|
|
|
panic("bad variable regexp")
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|