When compiling with -race, we insert calls to racefuncentry,
into every function. Add a rule that removes them in leaf functions,
without instrumented loads/stores.
Shaves ~30kb from "-race" version of go tool:
file difference:
go_old
15626192
go_new
15597520 [-28672 bytes]
section differences:
global text (code) = -24513 bytes (-0.358598%)
read-only data = -5849 bytes (-0.167064%)
Total difference -30362 bytes (-0.097928%)
Fixes #24662
Change-Id: Ia63bf1827f4cf2c25e3e28dcd097c150994ade0a
Reviewed-on: https://go-review.googlesource.com/121235
Run-TryBot: Ilya Tocar <ilya.tocar@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
"cmd/internal/sys"
)
-// The racewalk pass is currently handled in two parts.
+// The racewalk pass is currently handled in three parts.
//
// First, for flag_race, it inserts calls to racefuncenter and
// racefuncexit at the start and end (respectively) of each
// the Func.InstrumentBody flag as needed. For background on why this
// is done during SSA construction rather than a separate SSA pass,
// see issue #19054.
+//
+// Third we remove calls to racefuncenter and racefuncexit, for leaf
+// functions without instrumented operations. This is done as part of
+// ssa opt pass via special rule.
// TODO(dvyukov): do not instrument initialization as writes:
// a := make([]int, 10)
s.f.Cache = &ssaCaches[worker]
s.f.Cache.Reset()
s.f.DebugTest = s.f.DebugHashMatch("GOSSAHASH", name)
+ s.f.Config.Race = flag_race
s.f.Name = name
if fn.Func.Pragma&Nosplit != 0 {
s.f.NoSplit = true
nacl bool // GOOS=nacl
use387 bool // GO386=387
SoftFloat bool //
+ Race bool // race detector enabled
NeedsFpScratch bool // No direct move between GP and FP register sets
BigEndian bool //
}
(Store {t4} (OffPtr <tt4> [o4] dst) d3
(Store {t5} (OffPtr <tt5> [o5] dst) d4
(Zero {t1} [n] dst mem)))))
+
+(StaticCall {sym} x) && needRaceCleanup(sym,v) -> x
}
return false
}
+
+// needRaceCleanup reports whether this call to racefuncenter/exit isn't needed.
+func needRaceCleanup(sym interface{}, v *Value) bool {
+ f := v.Block.Func
+ if !f.Config.Race {
+ return false
+ }
+ if !isSameSym(sym, "runtime.racefuncenter") && !isSameSym(sym, "runtime.racefuncexit") {
+ return false
+ }
+ for _, b := range f.Blocks {
+ for _, v := range b.Values {
+ if v.Op == OpStaticCall {
+ switch v.Aux.(fmt.Stringer).String() {
+ case "runtime.racefuncenter", "runtime.racefuncexit", "runtime.panicindex",
+ "runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap":
+ // Check for racefuncenter will encounter racefuncexit and vice versa.
+ // Allow calls to panic*
+ default:
+ // If we encounterd any call, we need to keep racefunc*,
+ // for accurate stacktraces.
+ return false
+ }
+ }
+ }
+ }
+ return true
+}
v.AddArg(mem)
return true
}
+ // match: (StaticCall {sym} x)
+ // cond: needRaceCleanup(sym,v)
+ // result: x
+ for {
+ sym := v.Aux
+ x := v.Args[0]
+ if !(needRaceCleanup(sym, v)) {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpStore_0(v *Value) bool {