Commit fc7b83d1 authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/compile: break up large value rewrite functions

This makes the cmd/compile/internal/ssa package
compile much faster, and has no impact
on the speed of the compiler.

The chunk size was selected empirically,
in that at chunk size 10, the object
file was smaller than at chunk size 5 or 20.

name  old time/op       new time/op       delta
SSA         7.33s ± 5%        5.64s ± 1%  -23.10%  (p=0.000 n=10+10)

name  old user-time/op  new user-time/op  delta
SSA         9.70s ± 1%        8.04s ± 2%  -17.17%  (p=0.000 n=9+10)

name  old obj-bytes     new obj-bytes     delta
SSA         9.82M ± 0%        8.28M ± 0%  -15.67%  (p=0.000 n=10+10)

Change-Id: Iab472905da3f0e82f3db2c93d06e2759abc9dd44
Reviewed-on: https://go-review.googlesource.com/41296Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent eaa198f3
...@@ -162,12 +162,20 @@ func genRules(arch arch) { ...@@ -162,12 +162,20 @@ func genRules(arch arch) {
fmt.Fprintln(w, "var _ = objabi.GOROOT // in case not otherwise used") fmt.Fprintln(w, "var _ = objabi.GOROOT // in case not otherwise used")
fmt.Fprintln(w) fmt.Fprintln(w)
const chunkSize = 10
// Main rewrite routine is a switch on v.Op. // Main rewrite routine is a switch on v.Op.
fmt.Fprintf(w, "func rewriteValue%s(v *Value) bool {\n", arch.name) fmt.Fprintf(w, "func rewriteValue%s(v *Value) bool {\n", arch.name)
fmt.Fprintf(w, "switch v.Op {\n") fmt.Fprintf(w, "switch v.Op {\n")
for _, op := range ops { for _, op := range ops {
fmt.Fprintf(w, "case %s:\n", op) fmt.Fprintf(w, "case %s:\n", op)
fmt.Fprintf(w, "return rewriteValue%s_%s(v)\n", arch.name, op) fmt.Fprint(w, "return ")
for chunk := 0; chunk < len(oprules[op]); chunk += chunkSize {
if chunk > 0 {
fmt.Fprint(w, " || ")
}
fmt.Fprintf(w, "rewriteValue%s_%s_%d(v)", arch.name, op, chunk)
}
fmt.Fprintln(w)
} }
fmt.Fprintf(w, "}\n") fmt.Fprintf(w, "}\n")
fmt.Fprintf(w, "return false\n") fmt.Fprintf(w, "return false\n")
...@@ -176,67 +184,73 @@ func genRules(arch arch) { ...@@ -176,67 +184,73 @@ func genRules(arch arch) {
// Generate a routine per op. Note that we don't make one giant routine // Generate a routine per op. Note that we don't make one giant routine
// because it is too big for some compilers. // because it is too big for some compilers.
for _, op := range ops { for _, op := range ops {
buf := new(bytes.Buffer) for chunk := 0; chunk < len(oprules[op]); chunk += chunkSize {
var canFail bool buf := new(bytes.Buffer)
for i, rule := range oprules[op] { var canFail bool
match, cond, result := rule.parse() endchunk := chunk + chunkSize
fmt.Fprintf(buf, "// match: %s\n", match) if endchunk > len(oprules[op]) {
fmt.Fprintf(buf, "// cond: %s\n", cond) endchunk = len(oprules[op])
fmt.Fprintf(buf, "// result: %s\n", result)
canFail = false
fmt.Fprintf(buf, "for {\n")
if genMatch(buf, arch, match, rule.loc) {
canFail = true
} }
for i, rule := range oprules[op][chunk:endchunk] {
match, cond, result := rule.parse()
fmt.Fprintf(buf, "// match: %s\n", match)
fmt.Fprintf(buf, "// cond: %s\n", cond)
fmt.Fprintf(buf, "// result: %s\n", result)
canFail = false
fmt.Fprintf(buf, "for {\n")
if genMatch(buf, arch, match, rule.loc) {
canFail = true
}
if cond != "" { if cond != "" {
fmt.Fprintf(buf, "if !(%s) {\nbreak\n}\n", cond) fmt.Fprintf(buf, "if !(%s) {\nbreak\n}\n", cond)
canFail = true canFail = true
}
if !canFail && i+chunk != len(oprules[op])-1 {
log.Fatalf("unconditional rule %s is followed by other rules", match)
}
genResult(buf, arch, result, rule.loc)
if *genLog {
fmt.Fprintf(buf, "logRule(\"%s\")\n", rule.loc)
}
fmt.Fprintf(buf, "return true\n")
fmt.Fprintf(buf, "}\n")
} }
if !canFail && i != len(oprules[op])-1 { if canFail {
log.Fatalf("unconditional rule %s is followed by other rules", match) fmt.Fprintf(buf, "return false\n")
} }
genResult(buf, arch, result, rule.loc) body := buf.String()
if *genLog { // Do a rough match to predict whether we need b, config, fe, and/or types.
fmt.Fprintf(buf, "logRule(\"%s\")\n", rule.loc) // It's not precise--thus the blank assignments--but it's good enough
// to avoid generating needless code and doing pointless nil checks.
hasb := strings.Contains(body, "b.")
hasconfig := strings.Contains(body, "config.") || strings.Contains(body, "config)")
hasfe := strings.Contains(body, "fe.")
hasts := strings.Contains(body, "types.")
fmt.Fprintf(w, "func rewriteValue%s_%s_%d(v *Value) bool {\n", arch.name, op, chunk)
if hasb || hasconfig || hasfe {
fmt.Fprintln(w, "b := v.Block")
fmt.Fprintln(w, "_ = b")
} }
fmt.Fprintf(buf, "return true\n") if hasconfig {
fmt.Fprintln(w, "config := b.Func.Config")
fmt.Fprintf(buf, "}\n") fmt.Fprintln(w, "_ = config")
} }
if canFail { if hasfe {
fmt.Fprintf(buf, "return false\n") fmt.Fprintln(w, "fe := b.Func.fe")
} fmt.Fprintln(w, "_ = fe")
}
body := buf.String() if hasts {
// Do a rough match to predict whether we need b, config, fe, and/or types. fmt.Fprintln(w, "types := &b.Func.Config.Types")
// It's not precise--thus the blank assignments--but it's good enough fmt.Fprintln(w, "_ = types")
// to avoid generating needless code and doing pointless nil checks. }
hasb := strings.Contains(body, "b.") fmt.Fprint(w, body)
hasconfig := strings.Contains(body, "config.") || strings.Contains(body, "config)") fmt.Fprintf(w, "}\n")
hasfe := strings.Contains(body, "fe.")
hasts := strings.Contains(body, "types.")
fmt.Fprintf(w, "func rewriteValue%s_%s(v *Value) bool {\n", arch.name, op)
if hasb || hasconfig || hasfe {
fmt.Fprintln(w, "b := v.Block")
fmt.Fprintln(w, "_ = b")
}
if hasconfig {
fmt.Fprintln(w, "config := b.Func.Config")
fmt.Fprintln(w, "_ = config")
}
if hasfe {
fmt.Fprintln(w, "fe := b.Func.fe")
fmt.Fprintln(w, "_ = fe")
}
if hasts {
fmt.Fprintln(w, "types := &b.Func.Config.Types")
fmt.Fprintln(w, "_ = types")
} }
fmt.Fprint(w, body)
fmt.Fprintf(w, "}\n")
} }
// Generate block rewrite function. There are only a few block types // Generate block rewrite function. There are only a few block types
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
...@@ -14,31 +14,31 @@ var _ = objabi.GOROOT // in case not otherwise used ...@@ -14,31 +14,31 @@ var _ = objabi.GOROOT // in case not otherwise used
func rewriteValuedec(v *Value) bool { func rewriteValuedec(v *Value) bool {
switch v.Op { switch v.Op {
case OpComplexImag: case OpComplexImag:
return rewriteValuedec_OpComplexImag(v) return rewriteValuedec_OpComplexImag_0(v)
case OpComplexReal: case OpComplexReal:
return rewriteValuedec_OpComplexReal(v) return rewriteValuedec_OpComplexReal_0(v)
case OpIData: case OpIData:
return rewriteValuedec_OpIData(v) return rewriteValuedec_OpIData_0(v)
case OpITab: case OpITab:
return rewriteValuedec_OpITab(v) return rewriteValuedec_OpITab_0(v)
case OpLoad: case OpLoad:
return rewriteValuedec_OpLoad(v) return rewriteValuedec_OpLoad_0(v)
case OpSliceCap: case OpSliceCap:
return rewriteValuedec_OpSliceCap(v) return rewriteValuedec_OpSliceCap_0(v)
case OpSliceLen: case OpSliceLen:
return rewriteValuedec_OpSliceLen(v) return rewriteValuedec_OpSliceLen_0(v)
case OpSlicePtr: case OpSlicePtr:
return rewriteValuedec_OpSlicePtr(v) return rewriteValuedec_OpSlicePtr_0(v)
case OpStore: case OpStore:
return rewriteValuedec_OpStore(v) return rewriteValuedec_OpStore_0(v)
case OpStringLen: case OpStringLen:
return rewriteValuedec_OpStringLen(v) return rewriteValuedec_OpStringLen_0(v)
case OpStringPtr: case OpStringPtr:
return rewriteValuedec_OpStringPtr(v) return rewriteValuedec_OpStringPtr_0(v)
} }
return false return false
} }
func rewriteValuedec_OpComplexImag(v *Value) bool { func rewriteValuedec_OpComplexImag_0(v *Value) bool {
// match: (ComplexImag (ComplexMake _ imag)) // match: (ComplexImag (ComplexMake _ imag))
// cond: // cond:
// result: imag // result: imag
...@@ -55,7 +55,7 @@ func rewriteValuedec_OpComplexImag(v *Value) bool { ...@@ -55,7 +55,7 @@ func rewriteValuedec_OpComplexImag(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpComplexReal(v *Value) bool { func rewriteValuedec_OpComplexReal_0(v *Value) bool {
// match: (ComplexReal (ComplexMake real _)) // match: (ComplexReal (ComplexMake real _))
// cond: // cond:
// result: real // result: real
...@@ -72,7 +72,7 @@ func rewriteValuedec_OpComplexReal(v *Value) bool { ...@@ -72,7 +72,7 @@ func rewriteValuedec_OpComplexReal(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpIData(v *Value) bool { func rewriteValuedec_OpIData_0(v *Value) bool {
// match: (IData (IMake _ data)) // match: (IData (IMake _ data))
// cond: // cond:
// result: data // result: data
...@@ -89,7 +89,7 @@ func rewriteValuedec_OpIData(v *Value) bool { ...@@ -89,7 +89,7 @@ func rewriteValuedec_OpIData(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpITab(v *Value) bool { func rewriteValuedec_OpITab_0(v *Value) bool {
b := v.Block b := v.Block
_ = b _ = b
// match: (ITab (IMake itab _)) // match: (ITab (IMake itab _))
...@@ -108,7 +108,7 @@ func rewriteValuedec_OpITab(v *Value) bool { ...@@ -108,7 +108,7 @@ func rewriteValuedec_OpITab(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpLoad(v *Value) bool { func rewriteValuedec_OpLoad_0(v *Value) bool {
b := v.Block b := v.Block
_ = b _ = b
config := b.Func.Config config := b.Func.Config
...@@ -244,7 +244,7 @@ func rewriteValuedec_OpLoad(v *Value) bool { ...@@ -244,7 +244,7 @@ func rewriteValuedec_OpLoad(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpSliceCap(v *Value) bool { func rewriteValuedec_OpSliceCap_0(v *Value) bool {
// match: (SliceCap (SliceMake _ _ cap)) // match: (SliceCap (SliceMake _ _ cap))
// cond: // cond:
// result: cap // result: cap
...@@ -261,7 +261,7 @@ func rewriteValuedec_OpSliceCap(v *Value) bool { ...@@ -261,7 +261,7 @@ func rewriteValuedec_OpSliceCap(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpSliceLen(v *Value) bool { func rewriteValuedec_OpSliceLen_0(v *Value) bool {
// match: (SliceLen (SliceMake _ len _)) // match: (SliceLen (SliceMake _ len _))
// cond: // cond:
// result: len // result: len
...@@ -278,7 +278,7 @@ func rewriteValuedec_OpSliceLen(v *Value) bool { ...@@ -278,7 +278,7 @@ func rewriteValuedec_OpSliceLen(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpSlicePtr(v *Value) bool { func rewriteValuedec_OpSlicePtr_0(v *Value) bool {
// match: (SlicePtr (SliceMake ptr _ _)) // match: (SlicePtr (SliceMake ptr _ _))
// cond: // cond:
// result: ptr // result: ptr
...@@ -295,7 +295,7 @@ func rewriteValuedec_OpSlicePtr(v *Value) bool { ...@@ -295,7 +295,7 @@ func rewriteValuedec_OpSlicePtr(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpStore(v *Value) bool { func rewriteValuedec_OpStore_0(v *Value) bool {
b := v.Block b := v.Block
_ = b _ = b
config := b.Func.Config config := b.Func.Config
...@@ -456,7 +456,7 @@ func rewriteValuedec_OpStore(v *Value) bool { ...@@ -456,7 +456,7 @@ func rewriteValuedec_OpStore(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpStringLen(v *Value) bool { func rewriteValuedec_OpStringLen_0(v *Value) bool {
// match: (StringLen (StringMake _ len)) // match: (StringLen (StringMake _ len))
// cond: // cond:
// result: len // result: len
...@@ -473,7 +473,7 @@ func rewriteValuedec_OpStringLen(v *Value) bool { ...@@ -473,7 +473,7 @@ func rewriteValuedec_OpStringLen(v *Value) bool {
} }
return false return false
} }
func rewriteValuedec_OpStringPtr(v *Value) bool { func rewriteValuedec_OpStringPtr_0(v *Value) bool {
// match: (StringPtr (StringMake ptr _)) // match: (StringPtr (StringMake ptr _))
// cond: // cond:
// result: ptr // result: ptr
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment