Nname *Name // ONAME node
OClosure *ClosureExpr // OCLOSURE node
- // Extra entry code for the function. For example, allocate and initialize
- // memory for escaping parameters.
- Enter Nodes
- Exit Nodes
-
// ONAME nodes for all params/locals for this func/closure, does NOT
// include closurevars until transforming closures during walk.
// Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs,
funcNilCheckDisabled // disable nil checks when compiling this function
funcInlinabilityChecked // inliner has already determined whether the function is inlinable
funcNeverReturns // function never returns (in most cases calls panic(), os.Exit(), or equivalent)
- funcInstrumentBody // add race/msan/asan instrumentation during SSA construction
funcOpenCodedDeferDisallowed // can't do open-coded defers
funcClosureResultsLost // closure is called indirectly and we lost track of its results; used by escape analysis
funcPackageInit // compiler emitted .init func for package
func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 }
func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
func (f *Func) NeverReturns() bool { return f.flags&funcNeverReturns != 0 }
-func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 }
func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
func (f *Func) ClosureResultsLost() bool { return f.flags&funcClosureResultsLost != 0 }
func (f *Func) IsPackageInit() bool { return f.flags&funcPackageInit != 0 }
func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) }
func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
func (f *Func) SetNeverReturns(b bool) { f.flags.set(funcNeverReturns, b) }
-func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) }
func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
func (f *Func) SetClosureResultsLost(b bool) { f.flags.set(funcClosureResultsLost, b) }
func (f *Func) SetIsPackageInit(b bool) { f.flags.set(funcPackageInit, b) }
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
+ "cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
ir.Syms.Panicnildottype = typecheck.LookupRuntimeFunc("panicnildottype")
ir.Syms.Panicoverflow = typecheck.LookupRuntimeFunc("panicoverflow")
ir.Syms.Panicshift = typecheck.LookupRuntimeFunc("panicshift")
+ ir.Syms.Racefuncenter = typecheck.LookupRuntimeFunc("racefuncenter")
+ ir.Syms.Racefuncexit = typecheck.LookupRuntimeFunc("racefuncexit")
ir.Syms.Raceread = typecheck.LookupRuntimeFunc("raceread")
ir.Syms.Racereadrange = typecheck.LookupRuntimeFunc("racereadrange")
ir.Syms.Racewrite = typecheck.LookupRuntimeFunc("racewrite")
var astBuf *bytes.Buffer
if printssa {
astBuf = &bytes.Buffer{}
- ir.FDumpList(astBuf, "buildssa-enter", fn.Enter)
ir.FDumpList(astBuf, "buildssa-body", fn.Body)
- ir.FDumpList(astBuf, "buildssa-exit", fn.Exit)
if ssaDumpStdout {
fmt.Println("generating SSA for", name)
fmt.Print(astBuf.String())
}
s.checkPtrEnabled = ir.ShouldCheckPtr(fn, 1)
+ if base.Flag.Cfg.Instrumenting && fn.Pragma&ir.Norace == 0 && !fn.Linksym().ABIWrapper() {
+ if !base.Flag.Race || !objabi.LookupPkgSpecial(fn.Sym().Pkg.Path).NoRaceFunc {
+ s.instrumentMemory = true
+ }
+ if base.Flag.Race {
+ s.instrumentEnterExit = true
+ }
+ }
+
fe := ssafn{
curfn: fn,
log: printssa && ssaDumpStdout,
// preceding the deferreturn/ret code that we don't track correctly.
s.hasOpenDefers = false
}
- if s.hasOpenDefers && len(s.curfn.Exit) > 0 {
- // Skip doing open defers if there is any extra exit code (likely
- // race detection), since we will not generate that code in the
- // case of the extra deferreturn/ret segment.
+ if s.hasOpenDefers && s.instrumentEnterExit {
+ // Skip doing open defers if we need to instrument function
+ // returns for the race detector, since we will not generate that
+ // code in the case of the extra deferreturn/ret segment.
s.hasOpenDefers = false
}
if s.hasOpenDefers {
}
// Convert the AST-based IR to the SSA-based IR
- s.stmtList(fn.Enter)
+ if s.instrumentEnterExit {
+ s.rtcall(ir.Syms.Racefuncenter, true, nil, s.newValue0(ssa.OpGetCallerPC, types.Types[types.TUINTPTR]))
+ }
s.zeroResults()
s.paramsToHeap()
s.stmtList(fn.Body)
// Used to deduplicate panic calls.
panics map[funcLine]*ssa.Block
- cgoUnsafeArgs bool
- hasdefer bool // whether the function contains a defer statement
- softFloat bool
- hasOpenDefers bool // whether we are doing open-coded defers
- checkPtrEnabled bool // whether to insert checkptr instrumentation
+ cgoUnsafeArgs bool
+ hasdefer bool // whether the function contains a defer statement
+ softFloat bool
+ hasOpenDefers bool // whether we are doing open-coded defers
+ checkPtrEnabled bool // whether to insert checkptr instrumentation
+ instrumentEnterExit bool // whether to instrument function enter/exit
+ instrumentMemory bool // whether to instrument memory operations
// If doing open-coded defers, list of info about the defer calls in
// scanning order. Hence, at exit we should run these defers in reverse
}
func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrumentKind) {
- if !s.curfn.InstrumentBody() {
+ if !s.instrumentMemory {
return
}
}
}
- var b *ssa.Block
- var m *ssa.Value
// Do actual return.
// These currently turn into self-copies (in many cases).
resultFields := s.curfn.Type().Results()
results := make([]*ssa.Value, len(resultFields)+1, len(resultFields)+1)
- m = s.newValue0(ssa.OpMakeResult, s.f.OwnAux.LateExpansionResultType())
// Store SSAable and heap-escaped PPARAMOUT variables back to stack locations.
for i, f := range resultFields {
n := f.Nname.(*ir.Name)
}
}
- // Run exit code. Today, this is just racefuncexit, in -race mode.
- // TODO(register args) this seems risky here with a register-ABI, but not clear it is right to do it earlier either.
- // Spills in register allocation might just fix it.
- s.stmtList(s.curfn.Exit)
+ // In -race mode, we need to call racefuncexit.
+ // Note: This has to happen after we load any heap-allocated results,
+ // otherwise races will be attributed to the caller instead.
+ if s.instrumentEnterExit {
+ s.rtcall(ir.Syms.Racefuncexit, true, nil)
+ }
results[len(results)-1] = s.mem()
+ m := s.newValue0(ssa.OpMakeResult, s.f.OwnAux.LateExpansionResultType())
m.AddArgs(results...)
- b = s.endBlock()
+ b := s.endBlock()
b.Kind = ssa.BlockRet
b.SetControl(m)
if s.hasdefer && s.hasOpenDefers {
+++ /dev/null
-// Copyright 2012 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.
-
-package walk
-
-import (
- "cmd/compile/internal/base"
- "cmd/compile/internal/ir"
- "cmd/compile/internal/types"
- "cmd/internal/objabi"
- "cmd/internal/src"
-)
-
-// 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
-// function. This is handled below.
-//
-// Second, during buildssa, it inserts appropriate instrumentation
-// calls immediately before each memory load or store. This is handled
-// by the (*state).instrument method in ssa.go, so here we just set
-// 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)
-
-func instrument(fn *ir.Func) {
- if fn.Pragma&ir.Norace != 0 || (fn.Linksym() != nil && fn.Linksym().ABIWrapper()) {
- return
- }
-
- if !base.Flag.Race || !objabi.LookupPkgSpecial(base.Ctxt.Pkgpath).NoRaceFunc {
- fn.SetInstrumentBody(true)
- }
-
- if base.Flag.Race {
- lno := base.Pos
- base.Pos = src.NoXPos
- var init ir.Nodes
- fn.Enter.Prepend(mkcallstmt("racefuncenter", mkcall("getcallerpc", types.Types[types.TUINTPTR], &init)))
- if len(init) != 0 {
- base.Fatalf("race walk: unexpected init for getcallerpc")
- }
- fn.Exit.Append(mkcallstmt("racefuncexit"))
- base.Pos = lno
- }
-}