2015-02-13 14:40:36 -05:00
|
|
|
// Copyright 2013 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.
|
|
|
|
|
|
2015-03-05 13:57:36 -05:00
|
|
|
// Garbage collector liveness bitmap generation.
|
|
|
|
|
|
|
|
|
|
// The command line flag -live causes this code to print debug information.
|
|
|
|
|
// The levels are:
|
|
|
|
|
//
|
|
|
|
|
// -live (aka -live=1): print liveness lists as code warnings at safe points
|
|
|
|
|
// -live=2: print an assembly listing with liveness annotations
|
|
|
|
|
//
|
|
|
|
|
// Each level includes the earlier output as well.
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
package gc
|
|
|
|
|
|
|
|
|
|
import (
|
2017-03-09 18:32:17 -08:00
|
|
|
"cmd/compile/internal/ssa"
|
cmd/compile: factor out Pkg, Sym, and Type into package types
- created new package cmd/compile/internal/types
- moved Pkg, Sym, Type to new package
- to break cycles, for now we need the (ugly) types/utils.go
file which contains a handful of functions that must be installed
early by the gc frontend
- to break cycles, for now we need two functions to convert between
*gc.Node and *types.Node (the latter is a dummy type)
- adjusted the gc's code to use the new package and the conversion
functions as needed
- made several Pkg, Sym, and Type methods functions as needed
- renamed constructors typ, typPtr, typArray, etc. to types.New,
types.NewPtr, types.NewArray, etc.
Passes toolstash-check -all.
Change-Id: I8adfa5e85c731645d0a7fd2030375ed6ebf54b72
Reviewed-on: https://go-review.googlesource.com/39855
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-04-04 17:54:02 -07:00
|
|
|
"cmd/compile/internal/types"
|
2015-02-13 14:40:36 -05:00
|
|
|
"cmd/internal/obj"
|
2016-06-08 22:02:08 -07:00
|
|
|
"cmd/internal/objabi"
|
|
|
|
|
"cmd/internal/src"
|
2016-03-28 22:27:36 +13:00
|
|
|
"crypto/md5"
|
2016-06-08 22:02:08 -07:00
|
|
|
"crypto/sha1"
|
2015-02-13 14:40:36 -05:00
|
|
|
"fmt"
|
2016-06-08 22:02:08 -07:00
|
|
|
"os"
|
2015-10-28 10:40:47 -07:00
|
|
|
"strings"
|
2015-02-13 14:40:36 -05:00
|
|
|
)
|
|
|
|
|
|
2017-11-06 16:32:33 -08:00
|
|
|
// OpVarDef is an annotation for the liveness analysis, marking a place
|
2017-04-14 06:43:01 -07:00
|
|
|
// where a complete initialization (definition) of a variable begins.
|
|
|
|
|
// Since the liveness analysis can see initialization of single-word
|
2017-11-06 16:32:33 -08:00
|
|
|
// variables quite easy, OpVarDef is only needed for multi-word
|
|
|
|
|
// variables satisfying isfat(n.Type). For simplicity though, buildssa
|
|
|
|
|
// emits OpVarDef regardless of variable width.
|
2017-04-14 06:43:01 -07:00
|
|
|
//
|
2017-11-06 16:32:33 -08:00
|
|
|
// An 'OpVarDef x' annotation in the instruction stream tells the liveness
|
2017-04-14 06:43:01 -07:00
|
|
|
// analysis to behave as though the variable x is being initialized at that
|
2017-11-06 16:32:33 -08:00
|
|
|
// point in the instruction stream. The OpVarDef must appear before the
|
2017-04-14 06:43:01 -07:00
|
|
|
// actual (multi-instruction) initialization, and it must also appear after
|
|
|
|
|
// any uses of the previous value, if any. For example, if compiling:
|
|
|
|
|
//
|
|
|
|
|
// x = x[1:]
|
|
|
|
|
//
|
|
|
|
|
// it is important to generate code like:
|
|
|
|
|
//
|
|
|
|
|
// base, len, cap = pieces of x[1:]
|
2017-11-06 16:32:33 -08:00
|
|
|
// OpVarDef x
|
2017-04-14 06:43:01 -07:00
|
|
|
// x = {base, len, cap}
|
|
|
|
|
//
|
|
|
|
|
// If instead the generated code looked like:
|
|
|
|
|
//
|
2017-11-06 16:32:33 -08:00
|
|
|
// OpVarDef x
|
2017-04-14 06:43:01 -07:00
|
|
|
// base, len, cap = pieces of x[1:]
|
|
|
|
|
// x = {base, len, cap}
|
|
|
|
|
//
|
|
|
|
|
// then the liveness analysis would decide the previous value of x was
|
|
|
|
|
// unnecessary even though it is about to be used by the x[1:] computation.
|
|
|
|
|
// Similarly, if the generated code looked like:
|
|
|
|
|
//
|
|
|
|
|
// base, len, cap = pieces of x[1:]
|
|
|
|
|
// x = {base, len, cap}
|
2017-11-06 16:32:33 -08:00
|
|
|
// OpVarDef x
|
2017-04-14 06:43:01 -07:00
|
|
|
//
|
|
|
|
|
// then the liveness analysis will not preserve the new value of x, because
|
2017-11-06 16:32:33 -08:00
|
|
|
// the OpVarDef appears to have "overwritten" it.
|
2017-04-14 06:43:01 -07:00
|
|
|
//
|
2017-11-06 16:32:33 -08:00
|
|
|
// OpVarDef is a bit of a kludge to work around the fact that the instruction
|
2017-04-14 06:43:01 -07:00
|
|
|
// stream is working on single-word values but the liveness analysis
|
|
|
|
|
// wants to work on individual variables, which might be multi-word
|
|
|
|
|
// aggregates. It might make sense at some point to look into letting
|
|
|
|
|
// the liveness analysis work on single-word values as well, although
|
|
|
|
|
// there are complications around interface values, slices, and strings,
|
|
|
|
|
// all of which cannot be treated as individual words.
|
|
|
|
|
//
|
2017-11-06 16:32:33 -08:00
|
|
|
// OpVarKill is the opposite of OpVarDef: it marks a value as no longer needed,
|
|
|
|
|
// even if its address has been taken. That is, an OpVarKill annotation asserts
|
2017-04-14 06:43:01 -07:00
|
|
|
// that its argument is certainly dead, for use when the liveness analysis
|
|
|
|
|
// would not otherwise be able to deduce that fact.
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
// BlockEffects summarizes the liveness effects on an SSA block.
|
|
|
|
|
type BlockEffects struct {
|
2018-04-05 17:35:13 -04:00
|
|
|
// Computed during Liveness.prologue using only the content of
|
2015-03-05 13:57:36 -05:00
|
|
|
// individual blocks:
|
|
|
|
|
//
|
|
|
|
|
// uevar: upward exposed variables (used before set in block)
|
|
|
|
|
// varkill: killed variables (set in block)
|
|
|
|
|
// avarinit: addrtaken variables set or used (proof of initialization)
|
2018-02-26 20:56:58 -05:00
|
|
|
uevar varRegVec
|
|
|
|
|
varkill varRegVec
|
2016-04-29 14:17:04 +10:00
|
|
|
avarinit bvec
|
2015-03-05 13:57:36 -05:00
|
|
|
|
2018-04-05 17:35:13 -04:00
|
|
|
// Computed during Liveness.solve using control flow information:
|
2015-03-05 13:57:36 -05:00
|
|
|
//
|
|
|
|
|
// livein: variables live at block entry
|
|
|
|
|
// liveout: variables live at block exit
|
|
|
|
|
// avarinitany: addrtaken variables possibly initialized at block exit
|
|
|
|
|
// (initialized in block or at exit from any predecessor block)
|
|
|
|
|
// avarinitall: addrtaken variables certainly initialized at block exit
|
|
|
|
|
// (initialized in block or at exit from all predecessor blocks)
|
2018-02-26 20:56:58 -05:00
|
|
|
livein varRegVec
|
|
|
|
|
liveout varRegVec
|
2016-04-29 14:17:04 +10:00
|
|
|
avarinitany bvec
|
|
|
|
|
avarinitall bvec
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A collection of global state used by liveness analysis.
|
|
|
|
|
type Liveness struct {
|
2017-03-17 09:19:56 -07:00
|
|
|
fn *Node
|
2017-03-09 18:32:17 -08:00
|
|
|
f *ssa.Func
|
2017-03-17 09:19:56 -07:00
|
|
|
vars []*Node
|
2017-04-27 16:27:47 -07:00
|
|
|
idx map[*Node]int32
|
2017-03-17 09:19:56 -07:00
|
|
|
stkptrsize int64
|
2015-03-05 13:57:36 -05:00
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
be []BlockEffects
|
|
|
|
|
|
2018-02-26 20:48:53 -05:00
|
|
|
// unsafePoints bit i is set if Value ID i is not a safe point.
|
|
|
|
|
unsafePoints bvec
|
|
|
|
|
|
2018-04-20 15:48:46 -04:00
|
|
|
// An array with a bit vector for each safe point in the
|
|
|
|
|
// current Block during Liveness.epilogue. Indexed in Value
|
|
|
|
|
// order for that block. Additionally, for the entry block
|
|
|
|
|
// livevars[0] is the entry bitmap. Liveness.compact moves
|
|
|
|
|
// these to stackMaps and regMaps.
|
2018-02-26 20:56:58 -05:00
|
|
|
livevars []varRegVec
|
2017-01-14 23:43:26 -08:00
|
|
|
|
2018-03-22 12:04:51 -04:00
|
|
|
// livenessMap maps from safe points (i.e., CALLs) to their
|
|
|
|
|
// liveness map indexes.
|
|
|
|
|
livenessMap LivenessMap
|
2018-04-20 15:48:46 -04:00
|
|
|
stackMapSet bvecSet
|
2018-03-22 12:04:51 -04:00
|
|
|
stackMaps []bvec
|
2018-04-20 15:48:46 -04:00
|
|
|
regMapSet map[liveRegMask]int
|
2018-02-26 20:56:58 -05:00
|
|
|
regMaps []liveRegMask
|
2018-04-13 15:55:43 -04:00
|
|
|
|
2017-01-14 23:43:26 -08:00
|
|
|
cache progeffectscache
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-22 12:04:51 -04:00
|
|
|
// LivenessMap maps from *ssa.Value to LivenessIndex.
|
|
|
|
|
type LivenessMap struct {
|
2018-04-21 15:40:56 -04:00
|
|
|
m []LivenessIndex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *LivenessMap) reset(ids int) {
|
|
|
|
|
m2 := m.m
|
|
|
|
|
if ids > cap(m2) {
|
|
|
|
|
m2 = make([]LivenessIndex, ids)
|
|
|
|
|
} else {
|
|
|
|
|
m2 = m2[:ids]
|
|
|
|
|
}
|
|
|
|
|
none := LivenessInvalid
|
|
|
|
|
for i := range m2 {
|
|
|
|
|
m2[i] = none
|
|
|
|
|
}
|
|
|
|
|
m.m = m2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
|
|
|
|
|
m.m[v.ID] = i
|
2018-03-22 12:04:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m LivenessMap) Get(v *ssa.Value) LivenessIndex {
|
2018-04-21 15:40:56 -04:00
|
|
|
if int(v.ID) < len(m.m) {
|
|
|
|
|
return m.m[int(v.ID)]
|
2018-03-22 12:04:51 -04:00
|
|
|
}
|
|
|
|
|
// Not a safe point.
|
|
|
|
|
return LivenessInvalid
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LivenessIndex stores the liveness map index for a safe-point.
|
|
|
|
|
type LivenessIndex struct {
|
|
|
|
|
stackMapIndex int
|
2018-02-26 20:56:58 -05:00
|
|
|
regMapIndex int
|
2018-03-22 12:04:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LivenessInvalid indicates an unsafe point.
|
2018-03-27 16:11:10 -04:00
|
|
|
//
|
|
|
|
|
// We use index -2 because PCDATA tables conventionally start at -1,
|
|
|
|
|
// so -1 is used to mean the entry liveness map (which is actually at
|
|
|
|
|
// index 0; sigh). TODO(austin): Maybe we should use PCDATA+1 as the
|
|
|
|
|
// index into the liveness map so -1 uniquely refers to the entry
|
|
|
|
|
// liveness map.
|
2018-02-26 20:56:58 -05:00
|
|
|
var LivenessInvalid = LivenessIndex{-2, -2}
|
2018-03-22 12:04:51 -04:00
|
|
|
|
|
|
|
|
func (idx LivenessIndex) Valid() bool {
|
|
|
|
|
return idx.stackMapIndex >= 0
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 23:43:26 -08:00
|
|
|
type progeffectscache struct {
|
|
|
|
|
textavarinit []int32
|
2017-03-09 12:15:41 -08:00
|
|
|
retuevar []int32
|
|
|
|
|
tailuevar []int32
|
2017-01-14 23:43:26 -08:00
|
|
|
initialized bool
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2018-02-26 20:56:58 -05:00
|
|
|
// varRegVec contains liveness bitmaps for variables and registers.
|
|
|
|
|
type varRegVec struct {
|
|
|
|
|
vars bvec
|
|
|
|
|
regs liveRegMask
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *varRegVec) Eq(v2 varRegVec) bool {
|
|
|
|
|
return v.vars.Eq(v2.vars) && v.regs == v2.regs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *varRegVec) Copy(v2 varRegVec) {
|
|
|
|
|
v.vars.Copy(v2.vars)
|
|
|
|
|
v.regs = v2.regs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *varRegVec) Clear() {
|
|
|
|
|
v.vars.Clear()
|
|
|
|
|
v.regs = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *varRegVec) Or(v1, v2 varRegVec) {
|
|
|
|
|
v.vars.Or(v1.vars, v2.vars)
|
|
|
|
|
v.regs = v1.regs | v2.regs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *varRegVec) AndNot(v1, v2 varRegVec) {
|
|
|
|
|
v.vars.AndNot(v1.vars, v2.vars)
|
|
|
|
|
v.regs = v1.regs &^ v2.regs
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-25 10:01:58 -04:00
|
|
|
// livenessShouldTrack reports whether the liveness analysis
|
|
|
|
|
// should track the variable n.
|
|
|
|
|
// We don't care about variables that have no pointers,
|
|
|
|
|
// nor do we care about non-local variables,
|
|
|
|
|
// nor do we care about empty structs (handled by the pointer check),
|
|
|
|
|
// nor do we care about the fake PAUTOHEAP variables.
|
|
|
|
|
func livenessShouldTrack(n *Node) bool {
|
2017-04-25 18:14:12 -07:00
|
|
|
return n.Op == ONAME && (n.Class() == PAUTO || n.Class() == PPARAM || n.Class() == PPARAMOUT) && types.Haspointers(n.Type)
|
2016-05-25 10:01:58 -04:00
|
|
|
}
|
cmd/compile: fix liveness computation for heap-escaped parameters
The liveness computation of parameters generally was never
correct, but forcing all parameters to be live throughout the
function covered up that problem. The new SSA back end is
too clever: even though it currently keeps the parameter values live
throughout the function, it may find optimizations that mean
the current values are not written back to the original parameter
stack slots immediately or ever (for example if a parameter is set
to nil, SSA constant propagation may replace all later uses of the
parameter with a constant nil, eliminating the need to write the nil
value back to the stack slot), so the liveness code must now
track the actual operations on the stack slots, exposing these
problems.
One small problem in the handling of arguments is that nodarg
can return ONAME PPARAM nodes with adjusted offsets, so that
there are actually multiple *Node pointers for the same parameter
in the instruction stream. This might be possible to correct, but
not in this CL. For now, we fix this by using n.Orig instead of n
when considering PPARAM and PPARAMOUT nodes.
The major problem in the handling of arguments is general
confusion in the liveness code about the meaning of PPARAM|PHEAP
and PPARAMOUT|PHEAP nodes, especially as contrasted with PAUTO|PHEAP.
The difference between these two is that when a local variable "moves"
to the heap, it's really just allocated there to start with; in contrast,
when an argument moves to the heap, the actual data has to be copied
there from the stack at the beginning of the function, and when a
result "moves" to the heap the value in the heap has to be copied
back to the stack when the function returns
This general confusion is also present in the SSA back end.
The PHEAP bit worked decently when I first introduced it 7 years ago (!)
in 391425ae. The back end did nothing sophisticated, and in particular
there was no analysis at all: no escape analysis, no liveness analysis,
and certainly no SSA back end. But the complications caused in the
various downstream consumers suggest that this should be a detail
kept mainly in the front end.
This CL therefore eliminates both the PHEAP bit and even the idea of
"heap variables" from the back ends.
First, it replaces the PPARAM|PHEAP, PPARAMOUT|PHEAP, and PAUTO|PHEAP
variable classes with the single PAUTOHEAP, a pseudo-class indicating
a variable maintained on the heap and available by indirecting a
local variable kept on the stack (a plain PAUTO).
Second, walkexpr replaces all references to PAUTOHEAP variables
with indirections of the corresponding PAUTO variable.
The back ends and the liveness code now just see plain indirected
variables. This may actually produce better code, but the real goal
here is to eliminate these little-used and somewhat suspect code
paths in the back end analyses.
The OPARAM node type goes away too.
A followup CL will do the same to PPARAMREF. I'm not sure that
the back ends (SSA in particular) are handling those right either,
and with the framework established in this CL that change is trivial
and the result clearly more correct.
Fixes #15747.
Change-Id: I2770b1ce3cbc93981bfc7166be66a9da12013d74
Reviewed-on: https://go-review.googlesource.com/23393
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-05-25 01:33:24 -04:00
|
|
|
|
2017-04-27 16:27:47 -07:00
|
|
|
// getvariables returns the list of on-stack variables that we need to track
|
|
|
|
|
// and a map for looking up indices by *Node.
|
|
|
|
|
func getvariables(fn *Node) ([]*Node, map[*Node]int32) {
|
2016-05-25 10:01:58 -04:00
|
|
|
var vars []*Node
|
|
|
|
|
for _, n := range fn.Func.Dcl {
|
|
|
|
|
if livenessShouldTrack(n) {
|
|
|
|
|
vars = append(vars, n)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2017-04-27 16:27:47 -07:00
|
|
|
idx := make(map[*Node]int32, len(vars))
|
|
|
|
|
for i, n := range vars {
|
|
|
|
|
idx[n] = int32(i)
|
|
|
|
|
}
|
|
|
|
|
return vars, idx
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-01-14 23:43:26 -08:00
|
|
|
func (lv *Liveness) initcache() {
|
|
|
|
|
if lv.cache.initialized {
|
|
|
|
|
Fatalf("liveness cache initialized twice")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
lv.cache.initialized = true
|
|
|
|
|
|
|
|
|
|
for i, node := range lv.vars {
|
2017-04-25 18:14:12 -07:00
|
|
|
switch node.Class() {
|
2017-01-14 23:43:26 -08:00
|
|
|
case PPARAM:
|
|
|
|
|
// A return instruction with a p.to is a tail return, which brings
|
|
|
|
|
// the stack pointer back up (if it ever went down) and then jumps
|
|
|
|
|
// to a new function entirely. That form of instruction must read
|
|
|
|
|
// all the parameters for correctness, and similarly it must not
|
|
|
|
|
// read the out arguments - they won't be set until the new
|
|
|
|
|
// function runs.
|
2017-03-09 12:15:41 -08:00
|
|
|
|
2017-01-14 23:43:26 -08:00
|
|
|
lv.cache.tailuevar = append(lv.cache.tailuevar, int32(i))
|
|
|
|
|
|
2017-02-27 19:56:38 +02:00
|
|
|
if node.Addrtaken() {
|
2017-01-14 23:43:26 -08:00
|
|
|
lv.cache.textavarinit = append(lv.cache.textavarinit, int32(i))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case PPARAMOUT:
|
|
|
|
|
// If the result had its address taken, it is being tracked
|
|
|
|
|
// by the avarinit code, which does not use uevar.
|
|
|
|
|
// If we added it to uevar too, we'd not see any kill
|
|
|
|
|
// and decide that the variable was live entry, which it is not.
|
|
|
|
|
// So only use uevar in the non-addrtaken case.
|
|
|
|
|
// The p.to.type == obj.TYPE_NONE limits the bvset to
|
|
|
|
|
// non-tail-call return instructions; see note below for details.
|
2017-02-27 19:56:38 +02:00
|
|
|
if !node.Addrtaken() {
|
2017-01-14 23:43:26 -08:00
|
|
|
lv.cache.retuevar = append(lv.cache.retuevar, int32(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
// A liveEffect is a set of flags that describe an instruction's
|
|
|
|
|
// liveness effects on a variable.
|
2015-02-13 14:40:36 -05:00
|
|
|
//
|
2017-03-09 18:32:17 -08:00
|
|
|
// The possible flags are:
|
|
|
|
|
// uevar - used by the instruction
|
|
|
|
|
// varkill - killed by the instruction
|
2015-02-13 14:40:36 -05:00
|
|
|
// for variables without address taken, means variable was set
|
|
|
|
|
// for variables with address taken, means variable was marked dead
|
2017-03-09 18:32:17 -08:00
|
|
|
// avarinit - initialized or referred to by the instruction,
|
2015-02-13 14:40:36 -05:00
|
|
|
// only for variables with address taken but not escaping to heap
|
|
|
|
|
//
|
|
|
|
|
// The avarinit output serves as a signal that the data has been
|
|
|
|
|
// initialized, because any use of a variable must come after its
|
|
|
|
|
// initialization.
|
2017-03-09 18:32:17 -08:00
|
|
|
type liveEffect int
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
const (
|
|
|
|
|
uevar liveEffect = 1 << iota
|
|
|
|
|
varkill
|
|
|
|
|
avarinit
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// valueEffects returns the index of a variable in lv.vars and the
|
|
|
|
|
// liveness effects v has on that variable.
|
|
|
|
|
// If v does not affect any tracked variables, it returns -1, 0.
|
2017-04-27 16:27:47 -07:00
|
|
|
func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
|
2017-03-09 18:32:17 -08:00
|
|
|
n, e := affectedNode(v)
|
2017-04-27 16:27:47 -07:00
|
|
|
if e == 0 || n == nil || n.Op != ONAME { // cheapest checks first
|
2017-03-09 18:32:17 -08:00
|
|
|
return -1, 0
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-21 10:15:14 -07:00
|
|
|
// AllocFrame has dropped unused variables from
|
|
|
|
|
// lv.fn.Func.Dcl, but they might still be referenced by
|
|
|
|
|
// OpVarFoo pseudo-ops. Ignore them to prevent "lost track of
|
|
|
|
|
// variable" ICEs (issue 19632).
|
|
|
|
|
switch v.Op {
|
|
|
|
|
case ssa.OpVarDef, ssa.OpVarKill, ssa.OpVarLive, ssa.OpKeepAlive:
|
2017-04-27 15:17:57 -07:00
|
|
|
if !n.Name.Used() {
|
2017-03-21 10:15:14 -07:00
|
|
|
return -1, 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-27 16:27:47 -07:00
|
|
|
var effect liveEffect
|
2017-03-09 18:32:17 -08:00
|
|
|
if n.Addrtaken() {
|
|
|
|
|
if v.Op != ssa.OpVarKill {
|
|
|
|
|
effect |= avarinit
|
|
|
|
|
}
|
|
|
|
|
if v.Op == ssa.OpVarDef || v.Op == ssa.OpVarKill {
|
|
|
|
|
effect |= varkill
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Read is a read, obviously.
|
|
|
|
|
// Addr by itself is also implicitly a read.
|
|
|
|
|
//
|
|
|
|
|
// Addr|Write means that the address is being taken
|
|
|
|
|
// but only so that the instruction can write to the value.
|
|
|
|
|
// It is not a read.
|
|
|
|
|
|
|
|
|
|
if e&ssa.SymRead != 0 || e&(ssa.SymAddr|ssa.SymWrite) == ssa.SymAddr {
|
|
|
|
|
effect |= uevar
|
|
|
|
|
}
|
|
|
|
|
if e&ssa.SymWrite != 0 && (!isfat(n.Type) || v.Op == ssa.OpVarDef) {
|
|
|
|
|
effect |= varkill
|
2016-09-16 15:02:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-27 16:27:47 -07:00
|
|
|
if effect == 0 {
|
|
|
|
|
return -1, 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if pos, ok := lv.idx[n]; ok {
|
|
|
|
|
return pos, effect
|
|
|
|
|
}
|
|
|
|
|
return -1, 0
|
2017-03-09 18:32:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// affectedNode returns the *Node affected by v
|
|
|
|
|
func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) {
|
|
|
|
|
// Special cases.
|
|
|
|
|
switch v.Op {
|
|
|
|
|
case ssa.OpLoadReg:
|
|
|
|
|
n, _ := AutoVar(v.Args[0])
|
|
|
|
|
return n, ssa.SymRead
|
|
|
|
|
case ssa.OpStoreReg:
|
|
|
|
|
n, _ := AutoVar(v)
|
|
|
|
|
return n, ssa.SymWrite
|
|
|
|
|
|
|
|
|
|
case ssa.OpVarLive:
|
2017-09-18 14:53:56 -07:00
|
|
|
return v.Aux.(*Node), ssa.SymRead
|
2017-03-09 18:32:17 -08:00
|
|
|
case ssa.OpVarDef, ssa.OpVarKill:
|
|
|
|
|
return v.Aux.(*Node), ssa.SymWrite
|
|
|
|
|
case ssa.OpKeepAlive:
|
|
|
|
|
n, _ := AutoVar(v.Args[0])
|
|
|
|
|
return n, ssa.SymRead
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e := v.Op.SymEffect()
|
|
|
|
|
if e == 0 {
|
|
|
|
|
return nil, 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch a := v.Aux.(type) {
|
2017-09-18 14:53:56 -07:00
|
|
|
case nil, *obj.LSym:
|
2017-03-09 18:32:17 -08:00
|
|
|
// ok, but no node
|
2018-03-23 20:20:50 +00:00
|
|
|
return nil, e
|
2017-09-18 14:53:56 -07:00
|
|
|
case *Node:
|
2018-03-23 20:20:50 +00:00
|
|
|
return a, e
|
2017-03-09 18:32:17 -08:00
|
|
|
default:
|
|
|
|
|
Fatalf("weird aux: %s", v.LongString())
|
2018-03-23 20:20:50 +00:00
|
|
|
return nil, e
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-26 20:56:58 -05:00
|
|
|
// regEffects returns the registers affected by v.
|
|
|
|
|
func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
|
|
|
|
|
if v.Op == ssa.OpPhi {
|
|
|
|
|
// All phi node arguments must come from the same
|
|
|
|
|
// register and the result must also go to that
|
|
|
|
|
// register, so there's no overall effect.
|
|
|
|
|
return 0, 0
|
|
|
|
|
}
|
|
|
|
|
addLocs := func(mask liveRegMask, v *ssa.Value, ptrOnly bool) liveRegMask {
|
|
|
|
|
if int(v.ID) >= len(lv.f.RegAlloc) {
|
|
|
|
|
// v has no allocated registers.
|
|
|
|
|
return mask
|
|
|
|
|
}
|
|
|
|
|
loc := lv.f.RegAlloc[v.ID]
|
|
|
|
|
if loc == nil {
|
|
|
|
|
// v has no allocated registers.
|
|
|
|
|
return mask
|
|
|
|
|
}
|
|
|
|
|
if v.Op == ssa.OpGetG {
|
|
|
|
|
// GetG represents the G register, which is a
|
|
|
|
|
// pointer, but not a valid GC register. The
|
|
|
|
|
// current G is always reachable, so it's okay
|
|
|
|
|
// to ignore this register.
|
|
|
|
|
return mask
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Collect registers and types from v's location.
|
|
|
|
|
var regs [2]*ssa.Register
|
|
|
|
|
nreg := 0
|
|
|
|
|
switch loc := loc.(type) {
|
|
|
|
|
case ssa.LocalSlot:
|
|
|
|
|
return mask
|
|
|
|
|
case *ssa.Register:
|
|
|
|
|
if ptrOnly && !v.Type.HasHeapPointer() {
|
|
|
|
|
return mask
|
|
|
|
|
}
|
|
|
|
|
regs[0] = loc
|
|
|
|
|
nreg = 1
|
|
|
|
|
case ssa.LocPair:
|
|
|
|
|
// The value will have TTUPLE type, and the
|
|
|
|
|
// children are nil or *ssa.Register.
|
|
|
|
|
if v.Type.Etype != types.TTUPLE {
|
|
|
|
|
v.Fatalf("location pair %s has non-tuple type %v", loc, v.Type)
|
|
|
|
|
}
|
|
|
|
|
for i, loc1 := range loc {
|
|
|
|
|
if loc1 == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if ptrOnly && !v.Type.FieldType(i).HasHeapPointer() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
regs[nreg] = loc1.(*ssa.Register)
|
|
|
|
|
nreg++
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
v.Fatalf("weird RegAlloc location: %s (%T)", loc, loc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add register locations to vars.
|
|
|
|
|
for _, reg := range regs[:nreg] {
|
|
|
|
|
if reg.GCNum() == -1 {
|
|
|
|
|
if ptrOnly {
|
|
|
|
|
v.Fatalf("pointer in non-pointer register %v", reg)
|
|
|
|
|
} else {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mask |= 1 << uint(reg.GCNum())
|
|
|
|
|
}
|
|
|
|
|
return mask
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// v clobbers all registers it writes to (whether or not the
|
|
|
|
|
// write is pointer-typed).
|
|
|
|
|
kill = addLocs(0, v, false)
|
|
|
|
|
for _, arg := range v.Args {
|
|
|
|
|
// v uses all registers is reads from, but we only
|
|
|
|
|
// care about marking those containing pointers.
|
|
|
|
|
uevar = addLocs(uevar, arg, true)
|
|
|
|
|
}
|
|
|
|
|
return uevar, kill
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type liveRegMask uint32
|
|
|
|
|
|
|
|
|
|
func (m liveRegMask) niceString(config *ssa.Config) string {
|
|
|
|
|
if m == 0 {
|
|
|
|
|
return "<none>"
|
|
|
|
|
}
|
|
|
|
|
str := ""
|
|
|
|
|
for i, reg := range config.GCRegMap {
|
|
|
|
|
if m&(1<<uint(i)) != 0 {
|
|
|
|
|
if str != "" {
|
|
|
|
|
str += ","
|
|
|
|
|
}
|
|
|
|
|
str += reg.String()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return str
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
// Constructs a new liveness structure used to hold the global state of the
|
2016-03-13 10:23:18 +09:00
|
|
|
// liveness computation. The cfg argument is a slice of *BasicBlocks and the
|
|
|
|
|
// vars argument is a slice of *Nodes.
|
2017-04-27 16:27:47 -07:00
|
|
|
func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkptrsize int64) *Liveness {
|
2017-03-09 18:32:17 -08:00
|
|
|
lv := &Liveness{
|
2017-03-17 09:19:56 -07:00
|
|
|
fn: fn,
|
2017-03-09 18:32:17 -08:00
|
|
|
f: f,
|
2017-03-17 09:19:56 -07:00
|
|
|
vars: vars,
|
2017-04-27 16:27:47 -07:00
|
|
|
idx: idx,
|
2017-03-17 09:19:56 -07:00
|
|
|
stkptrsize: stkptrsize,
|
2017-03-09 18:32:17 -08:00
|
|
|
be: make([]BlockEffects, f.NumBlocks()),
|
2018-04-20 15:48:46 -04:00
|
|
|
|
2018-04-21 15:40:56 -04:00
|
|
|
regMapSet: make(map[liveRegMask]int),
|
2016-03-13 10:23:18 +09:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
nblocks := int32(len(f.Blocks))
|
2015-02-23 16:07:24 -05:00
|
|
|
nvars := int32(len(vars))
|
2015-03-02 21:25:33 -05:00
|
|
|
bulk := bvbulkalloc(nvars, nblocks*7)
|
2017-03-09 18:32:17 -08:00
|
|
|
for _, b := range f.Blocks {
|
|
|
|
|
be := lv.blockEffects(b)
|
|
|
|
|
|
2018-02-26 20:56:58 -05:00
|
|
|
be.uevar = varRegVec{vars: bulk.next()}
|
|
|
|
|
be.varkill = varRegVec{vars: bulk.next()}
|
|
|
|
|
be.livein = varRegVec{vars: bulk.next()}
|
|
|
|
|
be.liveout = varRegVec{vars: bulk.next()}
|
2017-03-09 18:32:17 -08:00
|
|
|
be.avarinit = bulk.next()
|
|
|
|
|
be.avarinitany = bulk.next()
|
|
|
|
|
be.avarinitall = bulk.next()
|
|
|
|
|
}
|
2018-04-21 15:40:56 -04:00
|
|
|
lv.livenessMap.reset(lv.f.NumValues())
|
2018-02-26 20:48:53 -05:00
|
|
|
|
|
|
|
|
lv.markUnsafePoints()
|
2017-03-09 18:32:17 -08:00
|
|
|
return lv
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
func (lv *Liveness) blockEffects(b *ssa.Block) *BlockEffects {
|
|
|
|
|
return &lv.be[b.ID]
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-09-20 15:22:21 -07:00
|
|
|
// NOTE: The bitmap for a specific type t could be cached in t after
|
|
|
|
|
// the first run and then simply copied into bv at the correct offset
|
|
|
|
|
// on future calls with the same type t.
|
2017-09-18 23:29:46 -07:00
|
|
|
func onebitwalktype1(t *types.Type, off int64, bv bvec) {
|
|
|
|
|
if t.Align > 0 && off&int64(t.Align-1) != 0 {
|
2018-04-02 14:21:27 -07:00
|
|
|
Fatalf("onebitwalktype1: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch t.Etype {
|
2017-09-18 23:29:46 -07:00
|
|
|
case TINT8, TUINT8, TINT16, TUINT16,
|
|
|
|
|
TINT32, TUINT32, TINT64, TUINT64,
|
|
|
|
|
TINT, TUINT, TUINTPTR, TBOOL,
|
|
|
|
|
TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128:
|
|
|
|
|
|
|
|
|
|
case TPTR32, TPTR64, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
|
|
|
|
|
if off&int64(Widthptr-1) != 0 {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("onebitwalktype1: invalid alignment, %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2017-09-18 23:29:46 -07:00
|
|
|
bv.Set(int32(off / int64(Widthptr))) // pointer
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case TSTRING:
|
cmd/internal/gc, runtime: use 1-bit bitmap for stack frames, data, bss
The bitmaps were 2 bits per pointer because we needed to distinguish
scalar, pointer, multiword, and we used the leftover value to distinguish
uninitialized from scalar, even though the garbage collector (GC) didn't care.
Now that there are no multiword structures from the GC's point of view,
cut the bitmaps down to 1 bit per pointer, recording just live pointer vs not.
The GC assumes the same layout for stack frames and for the maps
describing the global data and bss sections, so change them all in one CL.
The code still refers to 4-bit heap bitmaps and 2-bit "type bitmaps", since
the 2-bit representation lives (at least for now) in some of the reflect data.
Because these stack frame bitmaps are stored directly in the rodata in
the binary, this CL reduces the size of the 6g binary by about 1.1%.
Performance change is basically a wash, but using less memory,
and smaller binaries, and enables other bitmap reductions.
name old mean new mean delta
BenchmarkBinaryTree17 13.2s × (0.97,1.03) 13.0s × (0.99,1.01) -0.93% (p=0.005)
BenchmarkBinaryTree17-2 9.69s × (0.96,1.05) 9.51s × (0.96,1.03) -1.86% (p=0.001)
BenchmarkBinaryTree17-4 10.1s × (0.97,1.05) 10.0s × (0.96,1.05) ~ (p=0.141)
BenchmarkFannkuch11 4.35s × (0.99,1.01) 4.43s × (0.98,1.04) +1.75% (p=0.001)
BenchmarkFannkuch11-2 4.31s × (0.99,1.03) 4.32s × (1.00,1.00) ~ (p=0.095)
BenchmarkFannkuch11-4 4.32s × (0.99,1.02) 4.38s × (0.98,1.04) +1.38% (p=0.008)
BenchmarkFmtFprintfEmpty 83.5ns × (0.97,1.10) 87.3ns × (0.92,1.11) +4.55% (p=0.014)
BenchmarkFmtFprintfEmpty-2 81.8ns × (0.98,1.04) 82.5ns × (0.97,1.08) ~ (p=0.364)
BenchmarkFmtFprintfEmpty-4 80.9ns × (0.99,1.01) 82.6ns × (0.97,1.08) +2.12% (p=0.010)
BenchmarkFmtFprintfString 320ns × (0.95,1.04) 322ns × (0.97,1.05) ~ (p=0.368)
BenchmarkFmtFprintfString-2 303ns × (0.97,1.04) 304ns × (0.97,1.04) ~ (p=0.484)
BenchmarkFmtFprintfString-4 305ns × (0.97,1.05) 306ns × (0.98,1.05) ~ (p=0.543)
BenchmarkFmtFprintfInt 311ns × (0.98,1.03) 319ns × (0.97,1.03) +2.63% (p=0.000)
BenchmarkFmtFprintfInt-2 297ns × (0.98,1.04) 301ns × (0.97,1.04) +1.19% (p=0.023)
BenchmarkFmtFprintfInt-4 302ns × (0.98,1.02) 304ns × (0.97,1.03) ~ (p=0.126)
BenchmarkFmtFprintfIntInt 554ns × (0.96,1.05) 554ns × (0.97,1.03) ~ (p=0.975)
BenchmarkFmtFprintfIntInt-2 520ns × (0.98,1.03) 517ns × (0.98,1.02) ~ (p=0.153)
BenchmarkFmtFprintfIntInt-4 524ns × (0.98,1.02) 525ns × (0.98,1.03) ~ (p=0.597)
BenchmarkFmtFprintfPrefixedInt 433ns × (0.97,1.06) 434ns × (0.97,1.06) ~ (p=0.804)
BenchmarkFmtFprintfPrefixedInt-2 413ns × (0.98,1.04) 413ns × (0.98,1.03) ~ (p=0.881)
BenchmarkFmtFprintfPrefixedInt-4 420ns × (0.97,1.03) 421ns × (0.97,1.03) ~ (p=0.561)
BenchmarkFmtFprintfFloat 620ns × (0.99,1.03) 636ns × (0.97,1.03) +2.57% (p=0.000)
BenchmarkFmtFprintfFloat-2 601ns × (0.98,1.02) 617ns × (0.98,1.03) +2.58% (p=0.000)
BenchmarkFmtFprintfFloat-4 613ns × (0.98,1.03) 626ns × (0.98,1.02) +2.15% (p=0.000)
BenchmarkFmtManyArgs 2.19µs × (0.96,1.04) 2.23µs × (0.97,1.02) +1.65% (p=0.000)
BenchmarkFmtManyArgs-2 2.08µs × (0.98,1.03) 2.10µs × (0.99,1.02) +0.79% (p=0.019)
BenchmarkFmtManyArgs-4 2.10µs × (0.98,1.02) 2.13µs × (0.98,1.02) +1.72% (p=0.000)
BenchmarkGobDecode 21.3ms × (0.97,1.05) 21.1ms × (0.97,1.04) -1.36% (p=0.025)
BenchmarkGobDecode-2 20.0ms × (0.97,1.03) 19.2ms × (0.97,1.03) -4.00% (p=0.000)
BenchmarkGobDecode-4 19.5ms × (0.99,1.02) 19.0ms × (0.99,1.01) -2.39% (p=0.000)
BenchmarkGobEncode 18.3ms × (0.95,1.07) 18.1ms × (0.96,1.08) ~ (p=0.305)
BenchmarkGobEncode-2 16.8ms × (0.97,1.02) 16.4ms × (0.98,1.02) -2.79% (p=0.000)
BenchmarkGobEncode-4 15.4ms × (0.98,1.02) 15.4ms × (0.98,1.02) ~ (p=0.465)
BenchmarkGzip 650ms × (0.98,1.03) 655ms × (0.97,1.04) ~ (p=0.075)
BenchmarkGzip-2 652ms × (0.98,1.03) 655ms × (0.98,1.02) ~ (p=0.337)
BenchmarkGzip-4 656ms × (0.98,1.04) 653ms × (0.98,1.03) ~ (p=0.291)
BenchmarkGunzip 143ms × (1.00,1.01) 143ms × (1.00,1.01) ~ (p=0.507)
BenchmarkGunzip-2 143ms × (1.00,1.01) 143ms × (1.00,1.01) ~ (p=0.313)
BenchmarkGunzip-4 143ms × (1.00,1.01) 143ms × (1.00,1.01) ~ (p=0.312)
BenchmarkHTTPClientServer 110µs × (0.98,1.03) 109µs × (0.99,1.02) -1.40% (p=0.000)
BenchmarkHTTPClientServer-2 154µs × (0.90,1.08) 149µs × (0.90,1.08) -3.43% (p=0.007)
BenchmarkHTTPClientServer-4 138µs × (0.97,1.04) 138µs × (0.96,1.04) ~ (p=0.670)
BenchmarkJSONEncode 40.2ms × (0.98,1.02) 40.2ms × (0.98,1.05) ~ (p=0.828)
BenchmarkJSONEncode-2 35.1ms × (0.99,1.02) 35.2ms × (0.98,1.03) ~ (p=0.392)
BenchmarkJSONEncode-4 35.3ms × (0.98,1.03) 35.3ms × (0.98,1.02) ~ (p=0.813)
BenchmarkJSONDecode 119ms × (0.97,1.02) 117ms × (0.98,1.02) -1.80% (p=0.000)
BenchmarkJSONDecode-2 115ms × (0.99,1.02) 114ms × (0.98,1.02) -1.18% (p=0.000)
BenchmarkJSONDecode-4 116ms × (0.98,1.02) 114ms × (0.98,1.02) -1.43% (p=0.000)
BenchmarkMandelbrot200 6.03ms × (1.00,1.01) 6.03ms × (1.00,1.01) ~ (p=0.985)
BenchmarkMandelbrot200-2 6.03ms × (1.00,1.01) 6.02ms × (1.00,1.01) ~ (p=0.320)
BenchmarkMandelbrot200-4 6.03ms × (1.00,1.01) 6.03ms × (1.00,1.01) ~ (p=0.799)
BenchmarkGoParse 8.63ms × (0.89,1.10) 8.58ms × (0.93,1.09) ~ (p=0.667)
BenchmarkGoParse-2 8.20ms × (0.97,1.04) 8.37ms × (0.97,1.04) +1.96% (p=0.001)
BenchmarkGoParse-4 8.00ms × (0.98,1.02) 8.14ms × (0.99,1.02) +1.75% (p=0.000)
BenchmarkRegexpMatchEasy0_32 162ns × (1.00,1.01) 164ns × (0.98,1.04) +1.35% (p=0.011)
BenchmarkRegexpMatchEasy0_32-2 161ns × (1.00,1.01) 161ns × (1.00,1.00) ~ (p=0.185)
BenchmarkRegexpMatchEasy0_32-4 161ns × (1.00,1.00) 161ns × (1.00,1.00) -0.19% (p=0.001)
BenchmarkRegexpMatchEasy0_1K 540ns × (0.99,1.02) 566ns × (0.98,1.04) +4.98% (p=0.000)
BenchmarkRegexpMatchEasy0_1K-2 540ns × (0.99,1.01) 557ns × (0.99,1.01) +3.21% (p=0.000)
BenchmarkRegexpMatchEasy0_1K-4 541ns × (0.99,1.01) 559ns × (0.99,1.01) +3.26% (p=0.000)
BenchmarkRegexpMatchEasy1_32 139ns × (0.98,1.04) 139ns × (0.99,1.03) ~ (p=0.979)
BenchmarkRegexpMatchEasy1_32-2 139ns × (0.99,1.04) 139ns × (0.99,1.02) ~ (p=0.777)
BenchmarkRegexpMatchEasy1_32-4 139ns × (0.98,1.04) 139ns × (0.99,1.04) ~ (p=0.771)
BenchmarkRegexpMatchEasy1_1K 890ns × (0.99,1.03) 885ns × (1.00,1.01) -0.50% (p=0.004)
BenchmarkRegexpMatchEasy1_1K-2 888ns × (0.99,1.01) 885ns × (0.99,1.01) -0.37% (p=0.004)
BenchmarkRegexpMatchEasy1_1K-4 890ns × (0.99,1.02) 884ns × (1.00,1.00) -0.70% (p=0.000)
BenchmarkRegexpMatchMedium_32 252ns × (0.99,1.01) 251ns × (0.99,1.01) ~ (p=0.081)
BenchmarkRegexpMatchMedium_32-2 254ns × (0.99,1.04) 252ns × (0.99,1.01) -0.78% (p=0.027)
BenchmarkRegexpMatchMedium_32-4 253ns × (0.99,1.04) 252ns × (0.99,1.01) -0.70% (p=0.022)
BenchmarkRegexpMatchMedium_1K 72.9µs × (0.99,1.01) 72.7µs × (1.00,1.00) ~ (p=0.064)
BenchmarkRegexpMatchMedium_1K-2 74.1µs × (0.98,1.05) 72.9µs × (1.00,1.01) -1.61% (p=0.001)
BenchmarkRegexpMatchMedium_1K-4 73.6µs × (0.99,1.05) 72.8µs × (1.00,1.00) -1.13% (p=0.007)
BenchmarkRegexpMatchHard_32 3.88µs × (0.99,1.03) 3.92µs × (0.98,1.05) ~ (p=0.143)
BenchmarkRegexpMatchHard_32-2 3.89µs × (0.99,1.03) 3.93µs × (0.98,1.09) ~ (p=0.278)
BenchmarkRegexpMatchHard_32-4 3.90µs × (0.99,1.05) 3.93µs × (0.98,1.05) ~ (p=0.252)
BenchmarkRegexpMatchHard_1K 118µs × (0.99,1.01) 117µs × (0.99,1.02) -0.54% (p=0.003)
BenchmarkRegexpMatchHard_1K-2 118µs × (0.99,1.01) 118µs × (0.99,1.03) ~ (p=0.581)
BenchmarkRegexpMatchHard_1K-4 118µs × (0.99,1.02) 117µs × (0.99,1.01) -0.54% (p=0.002)
BenchmarkRevcomp 991ms × (0.95,1.10) 989ms × (0.94,1.08) ~ (p=0.879)
BenchmarkRevcomp-2 978ms × (0.95,1.11) 962ms × (0.96,1.08) ~ (p=0.257)
BenchmarkRevcomp-4 979ms × (0.96,1.07) 974ms × (0.96,1.11) ~ (p=0.678)
BenchmarkTemplate 141ms × (0.99,1.02) 145ms × (0.99,1.02) +2.75% (p=0.000)
BenchmarkTemplate-2 135ms × (0.98,1.02) 138ms × (0.99,1.02) +2.34% (p=0.000)
BenchmarkTemplate-4 136ms × (0.98,1.02) 140ms × (0.99,1.02) +2.71% (p=0.000)
BenchmarkTimeParse 640ns × (0.99,1.01) 622ns × (0.99,1.01) -2.88% (p=0.000)
BenchmarkTimeParse-2 640ns × (0.99,1.01) 622ns × (1.00,1.00) -2.81% (p=0.000)
BenchmarkTimeParse-4 640ns × (1.00,1.01) 622ns × (0.99,1.01) -2.82% (p=0.000)
BenchmarkTimeFormat 730ns × (0.98,1.02) 731ns × (0.98,1.03) ~ (p=0.767)
BenchmarkTimeFormat-2 709ns × (0.99,1.02) 707ns × (0.99,1.02) ~ (p=0.347)
BenchmarkTimeFormat-4 717ns × (0.98,1.01) 718ns × (0.98,1.02) ~ (p=0.793)
Change-Id: Ie779c47e912bf80eb918bafa13638bd8dfd6c2d9
Reviewed-on: https://go-review.googlesource.com/9406
Reviewed-by: Rick Hudson <rlh@golang.org>
2015-04-27 22:45:57 -04:00
|
|
|
// struct { byte *str; intgo len; }
|
2017-09-18 23:29:46 -07:00
|
|
|
if off&int64(Widthptr-1) != 0 {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("onebitwalktype1: invalid alignment, %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2017-09-18 23:29:46 -07:00
|
|
|
bv.Set(int32(off / int64(Widthptr))) //pointer in first slot
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case TINTER:
|
cmd/internal/gc, runtime: use 1-bit bitmap for stack frames, data, bss
The bitmaps were 2 bits per pointer because we needed to distinguish
scalar, pointer, multiword, and we used the leftover value to distinguish
uninitialized from scalar, even though the garbage collector (GC) didn't care.
Now that there are no multiword structures from the GC's point of view,
cut the bitmaps down to 1 bit per pointer, recording just live pointer vs not.
The GC assumes the same layout for stack frames and for the maps
describing the global data and bss sections, so change them all in one CL.
The code still refers to 4-bit heap bitmaps and 2-bit "type bitmaps", since
the 2-bit representation lives (at least for now) in some of the reflect data.
Because these stack frame bitmaps are stored directly in the rodata in
the binary, this CL reduces the size of the 6g binary by about 1.1%.
Performance change is basically a wash, but using less memory,
and smaller binaries, and enables other bitmap reductions.
name old mean new mean delta
BenchmarkBinaryTree17 13.2s × (0.97,1.03) 13.0s × (0.99,1.01) -0.93% (p=0.005)
BenchmarkBinaryTree17-2 9.69s × (0.96,1.05) 9.51s × (0.96,1.03) -1.86% (p=0.001)
BenchmarkBinaryTree17-4 10.1s × (0.97,1.05) 10.0s × (0.96,1.05) ~ (p=0.141)
BenchmarkFannkuch11 4.35s × (0.99,1.01) 4.43s × (0.98,1.04) +1.75% (p=0.001)
BenchmarkFannkuch11-2 4.31s × (0.99,1.03) 4.32s × (1.00,1.00) ~ (p=0.095)
BenchmarkFannkuch11-4 4.32s × (0.99,1.02) 4.38s × (0.98,1.04) +1.38% (p=0.008)
BenchmarkFmtFprintfEmpty 83.5ns × (0.97,1.10) 87.3ns × (0.92,1.11) +4.55% (p=0.014)
BenchmarkFmtFprintfEmpty-2 81.8ns × (0.98,1.04) 82.5ns × (0.97,1.08) ~ (p=0.364)
BenchmarkFmtFprintfEmpty-4 80.9ns × (0.99,1.01) 82.6ns × (0.97,1.08) +2.12% (p=0.010)
BenchmarkFmtFprintfString 320ns × (0.95,1.04) 322ns × (0.97,1.05) ~ (p=0.368)
BenchmarkFmtFprintfString-2 303ns × (0.97,1.04) 304ns × (0.97,1.04) ~ (p=0.484)
BenchmarkFmtFprintfString-4 305ns × (0.97,1.05) 306ns × (0.98,1.05) ~ (p=0.543)
BenchmarkFmtFprintfInt 311ns × (0.98,1.03) 319ns × (0.97,1.03) +2.63% (p=0.000)
BenchmarkFmtFprintfInt-2 297ns × (0.98,1.04) 301ns × (0.97,1.04) +1.19% (p=0.023)
BenchmarkFmtFprintfInt-4 302ns × (0.98,1.02) 304ns × (0.97,1.03) ~ (p=0.126)
BenchmarkFmtFprintfIntInt 554ns × (0.96,1.05) 554ns × (0.97,1.03) ~ (p=0.975)
BenchmarkFmtFprintfIntInt-2 520ns × (0.98,1.03) 517ns × (0.98,1.02) ~ (p=0.153)
BenchmarkFmtFprintfIntInt-4 524ns × (0.98,1.02) 525ns × (0.98,1.03) ~ (p=0.597)
BenchmarkFmtFprintfPrefixedInt 433ns × (0.97,1.06) 434ns × (0.97,1.06) ~ (p=0.804)
BenchmarkFmtFprintfPrefixedInt-2 413ns × (0.98,1.04) 413ns × (0.98,1.03) ~ (p=0.881)
BenchmarkFmtFprintfPrefixedInt-4 420ns × (0.97,1.03) 421ns × (0.97,1.03) ~ (p=0.561)
BenchmarkFmtFprintfFloat 620ns × (0.99,1.03) 636ns × (0.97,1.03) +2.57% (p=0.000)
BenchmarkFmtFprintfFloat-2 601ns × (0.98,1.02) 617ns × (0.98,1.03) +2.58% (p=0.000)
BenchmarkFmtFprintfFloat-4 613ns × (0.98,1.03) 626ns × (0.98,1.02) +2.15% (p=0.000)
BenchmarkFmtManyArgs 2.19µs × (0.96,1.04) 2.23µs × (0.97,1.02) +1.65% (p=0.000)
BenchmarkFmtManyArgs-2 2.08µs × (0.98,1.03) 2.10µs × (0.99,1.02) +0.79% (p=0.019)
BenchmarkFmtManyArgs-4 2.10µs × (0.98,1.02) 2.13µs × (0.98,1.02) +1.72% (p=0.000)
BenchmarkGobDecode 21.3ms × (0.97,1.05) 21.1ms × (0.97,1.04) -1.36% (p=0.025)
BenchmarkGobDecode-2 20.0ms × (0.97,1.03) 19.2ms × (0.97,1.03) -4.00% (p=0.000)
BenchmarkGobDecode-4 19.5ms × (0.99,1.02) 19.0ms × (0.99,1.01) -2.39% (p=0.000)
BenchmarkGobEncode 18.3ms × (0.95,1.07) 18.1ms × (0.96,1.08) ~ (p=0.305)
BenchmarkGobEncode-2 16.8ms × (0.97,1.02) 16.4ms × (0.98,1.02) -2.79% (p=0.000)
BenchmarkGobEncode-4 15.4ms × (0.98,1.02) 15.4ms × (0.98,1.02) ~ (p=0.465)
BenchmarkGzip 650ms × (0.98,1.03) 655ms × (0.97,1.04) ~ (p=0.075)
BenchmarkGzip-2 652ms × (0.98,1.03) 655ms × (0.98,1.02) ~ (p=0.337)
BenchmarkGzip-4 656ms × (0.98,1.04) 653ms × (0.98,1.03) ~ (p=0.291)
BenchmarkGunzip 143ms × (1.00,1.01) 143ms × (1.00,1.01) ~ (p=0.507)
BenchmarkGunzip-2 143ms × (1.00,1.01) 143ms × (1.00,1.01) ~ (p=0.313)
BenchmarkGunzip-4 143ms × (1.00,1.01) 143ms × (1.00,1.01) ~ (p=0.312)
BenchmarkHTTPClientServer 110µs × (0.98,1.03) 109µs × (0.99,1.02) -1.40% (p=0.000)
BenchmarkHTTPClientServer-2 154µs × (0.90,1.08) 149µs × (0.90,1.08) -3.43% (p=0.007)
BenchmarkHTTPClientServer-4 138µs × (0.97,1.04) 138µs × (0.96,1.04) ~ (p=0.670)
BenchmarkJSONEncode 40.2ms × (0.98,1.02) 40.2ms × (0.98,1.05) ~ (p=0.828)
BenchmarkJSONEncode-2 35.1ms × (0.99,1.02) 35.2ms × (0.98,1.03) ~ (p=0.392)
BenchmarkJSONEncode-4 35.3ms × (0.98,1.03) 35.3ms × (0.98,1.02) ~ (p=0.813)
BenchmarkJSONDecode 119ms × (0.97,1.02) 117ms × (0.98,1.02) -1.80% (p=0.000)
BenchmarkJSONDecode-2 115ms × (0.99,1.02) 114ms × (0.98,1.02) -1.18% (p=0.000)
BenchmarkJSONDecode-4 116ms × (0.98,1.02) 114ms × (0.98,1.02) -1.43% (p=0.000)
BenchmarkMandelbrot200 6.03ms × (1.00,1.01) 6.03ms × (1.00,1.01) ~ (p=0.985)
BenchmarkMandelbrot200-2 6.03ms × (1.00,1.01) 6.02ms × (1.00,1.01) ~ (p=0.320)
BenchmarkMandelbrot200-4 6.03ms × (1.00,1.01) 6.03ms × (1.00,1.01) ~ (p=0.799)
BenchmarkGoParse 8.63ms × (0.89,1.10) 8.58ms × (0.93,1.09) ~ (p=0.667)
BenchmarkGoParse-2 8.20ms × (0.97,1.04) 8.37ms × (0.97,1.04) +1.96% (p=0.001)
BenchmarkGoParse-4 8.00ms × (0.98,1.02) 8.14ms × (0.99,1.02) +1.75% (p=0.000)
BenchmarkRegexpMatchEasy0_32 162ns × (1.00,1.01) 164ns × (0.98,1.04) +1.35% (p=0.011)
BenchmarkRegexpMatchEasy0_32-2 161ns × (1.00,1.01) 161ns × (1.00,1.00) ~ (p=0.185)
BenchmarkRegexpMatchEasy0_32-4 161ns × (1.00,1.00) 161ns × (1.00,1.00) -0.19% (p=0.001)
BenchmarkRegexpMatchEasy0_1K 540ns × (0.99,1.02) 566ns × (0.98,1.04) +4.98% (p=0.000)
BenchmarkRegexpMatchEasy0_1K-2 540ns × (0.99,1.01) 557ns × (0.99,1.01) +3.21% (p=0.000)
BenchmarkRegexpMatchEasy0_1K-4 541ns × (0.99,1.01) 559ns × (0.99,1.01) +3.26% (p=0.000)
BenchmarkRegexpMatchEasy1_32 139ns × (0.98,1.04) 139ns × (0.99,1.03) ~ (p=0.979)
BenchmarkRegexpMatchEasy1_32-2 139ns × (0.99,1.04) 139ns × (0.99,1.02) ~ (p=0.777)
BenchmarkRegexpMatchEasy1_32-4 139ns × (0.98,1.04) 139ns × (0.99,1.04) ~ (p=0.771)
BenchmarkRegexpMatchEasy1_1K 890ns × (0.99,1.03) 885ns × (1.00,1.01) -0.50% (p=0.004)
BenchmarkRegexpMatchEasy1_1K-2 888ns × (0.99,1.01) 885ns × (0.99,1.01) -0.37% (p=0.004)
BenchmarkRegexpMatchEasy1_1K-4 890ns × (0.99,1.02) 884ns × (1.00,1.00) -0.70% (p=0.000)
BenchmarkRegexpMatchMedium_32 252ns × (0.99,1.01) 251ns × (0.99,1.01) ~ (p=0.081)
BenchmarkRegexpMatchMedium_32-2 254ns × (0.99,1.04) 252ns × (0.99,1.01) -0.78% (p=0.027)
BenchmarkRegexpMatchMedium_32-4 253ns × (0.99,1.04) 252ns × (0.99,1.01) -0.70% (p=0.022)
BenchmarkRegexpMatchMedium_1K 72.9µs × (0.99,1.01) 72.7µs × (1.00,1.00) ~ (p=0.064)
BenchmarkRegexpMatchMedium_1K-2 74.1µs × (0.98,1.05) 72.9µs × (1.00,1.01) -1.61% (p=0.001)
BenchmarkRegexpMatchMedium_1K-4 73.6µs × (0.99,1.05) 72.8µs × (1.00,1.00) -1.13% (p=0.007)
BenchmarkRegexpMatchHard_32 3.88µs × (0.99,1.03) 3.92µs × (0.98,1.05) ~ (p=0.143)
BenchmarkRegexpMatchHard_32-2 3.89µs × (0.99,1.03) 3.93µs × (0.98,1.09) ~ (p=0.278)
BenchmarkRegexpMatchHard_32-4 3.90µs × (0.99,1.05) 3.93µs × (0.98,1.05) ~ (p=0.252)
BenchmarkRegexpMatchHard_1K 118µs × (0.99,1.01) 117µs × (0.99,1.02) -0.54% (p=0.003)
BenchmarkRegexpMatchHard_1K-2 118µs × (0.99,1.01) 118µs × (0.99,1.03) ~ (p=0.581)
BenchmarkRegexpMatchHard_1K-4 118µs × (0.99,1.02) 117µs × (0.99,1.01) -0.54% (p=0.002)
BenchmarkRevcomp 991ms × (0.95,1.10) 989ms × (0.94,1.08) ~ (p=0.879)
BenchmarkRevcomp-2 978ms × (0.95,1.11) 962ms × (0.96,1.08) ~ (p=0.257)
BenchmarkRevcomp-4 979ms × (0.96,1.07) 974ms × (0.96,1.11) ~ (p=0.678)
BenchmarkTemplate 141ms × (0.99,1.02) 145ms × (0.99,1.02) +2.75% (p=0.000)
BenchmarkTemplate-2 135ms × (0.98,1.02) 138ms × (0.99,1.02) +2.34% (p=0.000)
BenchmarkTemplate-4 136ms × (0.98,1.02) 140ms × (0.99,1.02) +2.71% (p=0.000)
BenchmarkTimeParse 640ns × (0.99,1.01) 622ns × (0.99,1.01) -2.88% (p=0.000)
BenchmarkTimeParse-2 640ns × (0.99,1.01) 622ns × (1.00,1.00) -2.81% (p=0.000)
BenchmarkTimeParse-4 640ns × (1.00,1.01) 622ns × (0.99,1.01) -2.82% (p=0.000)
BenchmarkTimeFormat 730ns × (0.98,1.02) 731ns × (0.98,1.03) ~ (p=0.767)
BenchmarkTimeFormat-2 709ns × (0.99,1.02) 707ns × (0.99,1.02) ~ (p=0.347)
BenchmarkTimeFormat-4 717ns × (0.98,1.01) 718ns × (0.98,1.02) ~ (p=0.793)
Change-Id: Ie779c47e912bf80eb918bafa13638bd8dfd6c2d9
Reviewed-on: https://go-review.googlesource.com/9406
Reviewed-by: Rick Hudson <rlh@golang.org>
2015-04-27 22:45:57 -04:00
|
|
|
// struct { Itab *tab; void *data; }
|
|
|
|
|
// or, when isnilinter(t)==true:
|
|
|
|
|
// struct { Type *type; void *data; }
|
2017-09-18 23:29:46 -07:00
|
|
|
if off&int64(Widthptr-1) != 0 {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("onebitwalktype1: invalid alignment, %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2018-02-27 13:46:03 -08:00
|
|
|
// The first word of an interface is a pointer, but we don't
|
|
|
|
|
// treat it as such.
|
|
|
|
|
// 1. If it is a non-empty interface, the pointer points to an itab
|
|
|
|
|
// which is always in persistentalloc space.
|
|
|
|
|
// 2. If it is an empty interface, the pointer points to a _type.
|
|
|
|
|
// a. If it is a compile-time-allocated type, it points into
|
|
|
|
|
// the read-only data section.
|
|
|
|
|
// b. If it is a reflect-allocated type, it points into the Go heap.
|
|
|
|
|
// Reflect is responsible for keeping a reference to
|
|
|
|
|
// the underlying type so it won't be GCd.
|
|
|
|
|
// If we ever have a moving GC, we need to change this for 2b (as
|
|
|
|
|
// well as scan itabs to update their itab._type fields).
|
2017-09-18 23:29:46 -07:00
|
|
|
bv.Set(int32(off/int64(Widthptr) + 1)) // pointer in second slot
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-18 14:02:08 -07:00
|
|
|
case TSLICE:
|
|
|
|
|
// struct { byte *array; uintgo len; uintgo cap; }
|
2017-09-18 23:29:46 -07:00
|
|
|
if off&int64(Widthptr-1) != 0 {
|
2016-04-18 14:02:08 -07:00
|
|
|
Fatalf("onebitwalktype1: invalid TARRAY alignment, %v", t)
|
|
|
|
|
}
|
2017-09-18 23:29:46 -07:00
|
|
|
bv.Set(int32(off / int64(Widthptr))) // pointer in first slot (BitsPointer)
|
2016-04-18 14:02:08 -07:00
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
case TARRAY:
|
2017-09-18 23:29:46 -07:00
|
|
|
elt := t.Elem()
|
|
|
|
|
if elt.Width == 0 {
|
|
|
|
|
// Short-circuit for #20739.
|
|
|
|
|
break
|
|
|
|
|
}
|
2016-04-18 14:02:08 -07:00
|
|
|
for i := int64(0); i < t.NumElem(); i++ {
|
2017-09-18 23:29:46 -07:00
|
|
|
onebitwalktype1(elt, off, bv)
|
|
|
|
|
off += elt.Width
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TSTRUCT:
|
2017-09-18 23:29:46 -07:00
|
|
|
for _, f := range t.Fields().Slice() {
|
|
|
|
|
onebitwalktype1(f.Type, off+f.Offset, bv)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("onebitwalktype1: unexpected type, %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 15:50:45 -04:00
|
|
|
// usedRegs returns the maximum width of the live register map.
|
|
|
|
|
func (lv *Liveness) usedRegs() int32 {
|
|
|
|
|
var any liveRegMask
|
|
|
|
|
for _, live := range lv.regMaps {
|
|
|
|
|
any |= live
|
|
|
|
|
}
|
|
|
|
|
i := int32(0)
|
|
|
|
|
for any != 0 {
|
|
|
|
|
any >>= 1
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Generates live pointer value maps for arguments and local variables. The
|
|
|
|
|
// this argument and the in arguments are always assumed live. The vars
|
2016-03-13 10:23:18 +09:00
|
|
|
// argument is a slice of *Nodes.
|
2017-09-20 12:58:16 -07:00
|
|
|
func (lv *Liveness) pointerMap(liveout bvec, vars []*Node, args, locals bvec) {
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); ; i++ {
|
2016-10-04 15:57:24 -07:00
|
|
|
i = liveout.Next(i)
|
2015-02-17 22:13:49 -05:00
|
|
|
if i < 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
}
|
2016-03-13 10:23:18 +09:00
|
|
|
node := vars[i]
|
2017-04-25 18:14:12 -07:00
|
|
|
switch node.Class() {
|
2015-02-13 14:40:36 -05:00
|
|
|
case PAUTO:
|
2017-09-18 23:29:46 -07:00
|
|
|
onebitwalktype1(node.Type, node.Xoffset+lv.stkptrsize, locals)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case PPARAM, PPARAMOUT:
|
2017-09-18 23:29:46 -07:00
|
|
|
onebitwalktype1(node.Type, node.Xoffset, args)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-26 20:48:53 -05:00
|
|
|
// markUnsafePoints finds unsafe points and computes lv.unsafePoints.
|
|
|
|
|
func (lv *Liveness) markUnsafePoints() {
|
|
|
|
|
if compiling_runtime || lv.f.NoSplit {
|
|
|
|
|
// No complex analysis necessary. Do this on the fly
|
|
|
|
|
// in issafepoint.
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lv.unsafePoints = bvalloc(int32(lv.f.NumValues()))
|
|
|
|
|
|
|
|
|
|
// Mark write barrier unsafe points.
|
|
|
|
|
for _, wbBlock := range lv.f.WBLoads {
|
2018-05-22 11:07:43 -04:00
|
|
|
if wbBlock.Kind == ssa.BlockPlain && len(wbBlock.Values) == 0 {
|
|
|
|
|
// The write barrier block was optimized away
|
|
|
|
|
// but we haven't done dead block elimination.
|
|
|
|
|
// (This can happen in -N mode.)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2018-02-26 20:48:53 -05:00
|
|
|
// Check that we have the expected diamond shape.
|
|
|
|
|
if len(wbBlock.Succs) != 2 {
|
|
|
|
|
lv.f.Fatalf("expected branch at write barrier block %v", wbBlock)
|
|
|
|
|
}
|
|
|
|
|
s0, s1 := wbBlock.Succs[0].Block(), wbBlock.Succs[1].Block()
|
|
|
|
|
if s0.Kind != ssa.BlockPlain || s1.Kind != ssa.BlockPlain {
|
|
|
|
|
lv.f.Fatalf("expected successors of write barrier block %v to be plain", wbBlock)
|
|
|
|
|
}
|
|
|
|
|
if s0.Succs[0].Block() != s1.Succs[0].Block() {
|
|
|
|
|
lv.f.Fatalf("expected successors of write barrier block %v to converge", wbBlock)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Flow backwards from the control value to find the
|
|
|
|
|
// flag load. We don't know what lowered ops we're
|
|
|
|
|
// looking for, but all current arches produce a
|
|
|
|
|
// single op that does the memory load from the flag
|
|
|
|
|
// address, so we look for that.
|
|
|
|
|
var load *ssa.Value
|
|
|
|
|
v := wbBlock.Control
|
|
|
|
|
for {
|
|
|
|
|
if sym, ok := v.Aux.(*obj.LSym); ok && sym == writeBarrier {
|
|
|
|
|
load = v
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
switch v.Op {
|
|
|
|
|
case ssa.Op386TESTL:
|
|
|
|
|
// 386 lowers Neq32 to (TESTL cond cond),
|
|
|
|
|
if v.Args[0] == v.Args[1] {
|
|
|
|
|
v = v.Args[0]
|
|
|
|
|
continue
|
|
|
|
|
}
|
2018-05-22 11:35:03 -04:00
|
|
|
case ssa.Op386MOVLload, ssa.OpARM64MOVWUload, ssa.OpPPC64MOVWZload:
|
2018-02-26 20:48:53 -05:00
|
|
|
// Args[0] is the address of the write
|
|
|
|
|
// barrier control. Ignore Args[1],
|
|
|
|
|
// which is the mem operand.
|
2018-05-22 11:35:03 -04:00
|
|
|
// TODO: Just ignore mem operands?
|
2018-02-26 20:48:53 -05:00
|
|
|
v = v.Args[0]
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// Common case: just flow backwards.
|
|
|
|
|
if len(v.Args) != 1 {
|
|
|
|
|
v.Fatalf("write barrier control value has more than one argument: %s", v.LongString())
|
|
|
|
|
}
|
|
|
|
|
v = v.Args[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark everything after the load unsafe.
|
|
|
|
|
found := false
|
|
|
|
|
for _, v := range wbBlock.Values {
|
|
|
|
|
found = found || v == load
|
|
|
|
|
if found {
|
|
|
|
|
lv.unsafePoints.Set(int32(v.ID))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark the two successor blocks unsafe. These come
|
|
|
|
|
// back together immediately after the direct write in
|
|
|
|
|
// one successor and the last write barrier call in
|
|
|
|
|
// the other, so there's no need to be more precise.
|
|
|
|
|
for _, succ := range wbBlock.Succs {
|
|
|
|
|
for _, v := range succ.Block().Values {
|
|
|
|
|
lv.unsafePoints.Set(int32(v.ID))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find uintptr -> unsafe.Pointer conversions and flood
|
|
|
|
|
// unsafeness back to a call (which is always a safe point).
|
|
|
|
|
//
|
|
|
|
|
// Looking for the uintptr -> unsafe.Pointer conversion has a
|
|
|
|
|
// few advantages over looking for unsafe.Pointer -> uintptr
|
|
|
|
|
// conversions:
|
|
|
|
|
//
|
|
|
|
|
// 1. We avoid needlessly blocking safe-points for
|
|
|
|
|
// unsafe.Pointer -> uintptr conversions that never go back to
|
|
|
|
|
// a Pointer.
|
|
|
|
|
//
|
|
|
|
|
// 2. We don't have to detect calls to reflect.Value.Pointer,
|
|
|
|
|
// reflect.Value.UnsafeAddr, and reflect.Value.InterfaceData,
|
|
|
|
|
// which are implicit unsafe.Pointer -> uintptr conversions.
|
|
|
|
|
// We can't even reliably detect this if there's an indirect
|
|
|
|
|
// call to one of these methods.
|
|
|
|
|
//
|
|
|
|
|
// TODO: For trivial unsafe.Pointer arithmetic, it would be
|
|
|
|
|
// nice to only flood as far as the unsafe.Pointer -> uintptr
|
|
|
|
|
// conversion, but it's hard to know which argument of an Add
|
|
|
|
|
// or Sub to follow.
|
|
|
|
|
var flooded bvec
|
|
|
|
|
var flood func(b *ssa.Block, vi int)
|
|
|
|
|
flood = func(b *ssa.Block, vi int) {
|
|
|
|
|
if flooded.n == 0 {
|
|
|
|
|
flooded = bvalloc(int32(lv.f.NumBlocks()))
|
|
|
|
|
}
|
|
|
|
|
if flooded.Get(int32(b.ID)) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for i := vi - 1; i >= 0; i-- {
|
|
|
|
|
v := b.Values[i]
|
|
|
|
|
if v.Op.IsCall() {
|
|
|
|
|
// Uintptrs must not contain live
|
|
|
|
|
// pointers across calls, so stop
|
|
|
|
|
// flooding.
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
lv.unsafePoints.Set(int32(v.ID))
|
|
|
|
|
}
|
|
|
|
|
if vi == len(b.Values) {
|
|
|
|
|
// We marked all values in this block, so no
|
|
|
|
|
// need to flood this block again.
|
|
|
|
|
flooded.Set(int32(b.ID))
|
|
|
|
|
}
|
|
|
|
|
for _, pred := range b.Preds {
|
|
|
|
|
flood(pred.Block(), len(pred.Block().Values))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, b := range lv.f.Blocks {
|
|
|
|
|
for i, v := range b.Values {
|
|
|
|
|
if !(v.Op == ssa.OpConvert && v.Type.IsPtrShaped()) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// Flood the unsafe-ness of this backwards
|
|
|
|
|
// until we hit a call.
|
|
|
|
|
flood(b, i+1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
// Returns true for instructions that are safe points that must be annotated
|
|
|
|
|
// with liveness information.
|
2018-02-26 20:48:53 -05:00
|
|
|
func (lv *Liveness) issafepoint(v *ssa.Value) bool {
|
|
|
|
|
// The runtime was written with the assumption that
|
|
|
|
|
// safe-points only appear at call sites (because that's how
|
|
|
|
|
// it used to be). We could and should improve that, but for
|
|
|
|
|
// now keep the old safe-point rules in the runtime.
|
|
|
|
|
//
|
|
|
|
|
// go:nosplit functions are similar. Since safe points used to
|
|
|
|
|
// be coupled with stack checks, go:nosplit often actually
|
|
|
|
|
// means "no safe points in this function".
|
|
|
|
|
if compiling_runtime || lv.f.NoSplit {
|
|
|
|
|
return v.Op.IsCall()
|
|
|
|
|
}
|
|
|
|
|
switch v.Op {
|
|
|
|
|
case ssa.OpInitMem, ssa.OpArg, ssa.OpSP, ssa.OpSB,
|
|
|
|
|
ssa.OpSelect0, ssa.OpSelect1, ssa.OpGetG,
|
|
|
|
|
ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive,
|
|
|
|
|
ssa.OpPhi:
|
|
|
|
|
// These don't produce code (see genssa).
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return !lv.unsafePoints.Get(int32(v.ID))
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Initializes the sets for solving the live variables. Visits all the
|
2015-02-13 14:40:36 -05:00
|
|
|
// instructions in each basic block to summarizes the information at each basic
|
|
|
|
|
// block
|
2017-09-20 12:58:16 -07:00
|
|
|
func (lv *Liveness) prologue() {
|
2017-01-14 23:43:26 -08:00
|
|
|
lv.initcache()
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
for _, b := range lv.f.Blocks {
|
|
|
|
|
be := lv.blockEffects(b)
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
// Walk the block instructions backward and update the block
|
|
|
|
|
// effects with the each prog effects.
|
2017-03-09 18:32:17 -08:00
|
|
|
for j := len(b.Values) - 1; j >= 0; j-- {
|
|
|
|
|
pos, e := lv.valueEffects(b.Values[j])
|
2018-02-26 20:56:58 -05:00
|
|
|
regUevar, regKill := lv.regEffects(b.Values[j])
|
2017-03-09 18:32:17 -08:00
|
|
|
if e&varkill != 0 {
|
2018-02-26 20:56:58 -05:00
|
|
|
be.varkill.vars.Set(pos)
|
|
|
|
|
be.uevar.vars.Unset(pos)
|
2017-01-14 23:43:26 -08:00
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
be.varkill.regs |= regKill
|
|
|
|
|
be.uevar.regs &^= regKill
|
2017-03-09 18:32:17 -08:00
|
|
|
if e&uevar != 0 {
|
2018-02-26 20:56:58 -05:00
|
|
|
be.uevar.vars.Set(pos)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
be.uevar.regs |= regUevar
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Walk the block instructions forward to update avarinit bits.
|
|
|
|
|
// avarinit describes the effect at the end of the block, not the beginning.
|
2018-03-14 10:50:49 +00:00
|
|
|
for _, val := range b.Values {
|
|
|
|
|
pos, e := lv.valueEffects(val)
|
2018-02-26 20:56:58 -05:00
|
|
|
// No need for regEffects because registers never appear in avarinit.
|
2017-03-09 18:32:17 -08:00
|
|
|
if e&varkill != 0 {
|
|
|
|
|
be.avarinit.Unset(pos)
|
2017-01-14 23:43:26 -08:00
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
if e&avarinit != 0 {
|
|
|
|
|
be.avarinit.Set(pos)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Solve the liveness dataflow equations.
|
2017-09-20 12:58:16 -07:00
|
|
|
func (lv *Liveness) solve() {
|
2015-02-13 14:40:36 -05:00
|
|
|
// These temporary bitvectors exist to avoid successive allocations and
|
|
|
|
|
// frees within the loop.
|
2018-02-26 20:56:58 -05:00
|
|
|
nvars := int32(len(lv.vars))
|
|
|
|
|
newlivein := varRegVec{vars: bvalloc(nvars)}
|
|
|
|
|
newliveout := varRegVec{vars: bvalloc(nvars)}
|
|
|
|
|
any := bvalloc(nvars)
|
|
|
|
|
all := bvalloc(nvars)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Push avarinitall, avarinitany forward.
|
|
|
|
|
// avarinitall says the addressed var is initialized along all paths reaching the block exit.
|
|
|
|
|
// avarinitany says the addressed var is initialized along some path reaching the block exit.
|
2017-03-09 18:32:17 -08:00
|
|
|
for _, b := range lv.f.Blocks {
|
|
|
|
|
be := lv.blockEffects(b)
|
|
|
|
|
if b == lv.f.Entry {
|
|
|
|
|
be.avarinitall.Copy(be.avarinit)
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
2017-03-09 18:32:17 -08:00
|
|
|
be.avarinitall.Clear()
|
|
|
|
|
be.avarinitall.Not()
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
be.avarinitany.Copy(be.avarinit)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
// Walk blocks in the general direction of propagation (RPO
|
|
|
|
|
// for avarinit{any,all}, and PO for live{in,out}). This
|
|
|
|
|
// improves convergence.
|
|
|
|
|
po := lv.f.Postorder()
|
|
|
|
|
|
2016-03-15 17:03:10 +11:00
|
|
|
for change := true; change; {
|
|
|
|
|
change = false
|
2017-03-09 18:32:17 -08:00
|
|
|
for i := len(po) - 1; i >= 0; i-- {
|
|
|
|
|
b := po[i]
|
|
|
|
|
be := lv.blockEffects(b)
|
|
|
|
|
lv.avarinitanyall(b, any, all)
|
|
|
|
|
|
2018-02-26 20:56:58 -05:00
|
|
|
any.AndNot(any, be.varkill.vars)
|
|
|
|
|
all.AndNot(all, be.varkill.vars)
|
2017-03-09 18:32:17 -08:00
|
|
|
any.Or(any, be.avarinit)
|
|
|
|
|
all.Or(all, be.avarinit)
|
|
|
|
|
if !any.Eq(be.avarinitany) {
|
2016-03-15 17:03:10 +11:00
|
|
|
change = true
|
2017-03-09 18:32:17 -08:00
|
|
|
be.avarinitany.Copy(any)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
if !all.Eq(be.avarinitall) {
|
2016-03-15 17:03:10 +11:00
|
|
|
change = true
|
2017-03-09 18:32:17 -08:00
|
|
|
be.avarinitall.Copy(all)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Iterate through the blocks in reverse round-robin fashion. A work
|
|
|
|
|
// queue might be slightly faster. As is, the number of iterations is
|
2015-02-13 14:40:36 -05:00
|
|
|
// so low that it hardly seems to be worth the complexity.
|
|
|
|
|
|
2016-03-15 17:03:10 +11:00
|
|
|
for change := true; change; {
|
|
|
|
|
change = false
|
2017-03-09 18:32:17 -08:00
|
|
|
for _, b := range po {
|
|
|
|
|
be := lv.blockEffects(b)
|
2015-03-02 21:25:33 -05:00
|
|
|
|
2016-10-04 15:57:24 -07:00
|
|
|
newliveout.Clear()
|
2017-03-09 18:32:17 -08:00
|
|
|
switch b.Kind {
|
|
|
|
|
case ssa.BlockRet:
|
|
|
|
|
for _, pos := range lv.cache.retuevar {
|
2018-02-26 20:56:58 -05:00
|
|
|
newliveout.vars.Set(pos)
|
2017-03-09 12:15:41 -08:00
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
case ssa.BlockRetJmp:
|
|
|
|
|
for _, pos := range lv.cache.tailuevar {
|
2018-02-26 20:56:58 -05:00
|
|
|
newliveout.vars.Set(pos)
|
2017-03-09 18:32:17 -08:00
|
|
|
}
|
|
|
|
|
case ssa.BlockExit:
|
|
|
|
|
// nothing to do
|
|
|
|
|
default:
|
2017-03-09 12:15:41 -08:00
|
|
|
// A variable is live on output from this block
|
|
|
|
|
// if it is live on input to some successor.
|
|
|
|
|
//
|
|
|
|
|
// out[b] = \bigcup_{s \in succ[b]} in[s]
|
2017-03-09 18:32:17 -08:00
|
|
|
newliveout.Copy(lv.blockEffects(b.Succs[0].Block()).livein)
|
|
|
|
|
for _, succ := range b.Succs[1:] {
|
|
|
|
|
newliveout.Or(newliveout, lv.blockEffects(succ.Block()).livein)
|
2017-03-09 12:15:41 -08:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
if !be.liveout.Eq(newliveout) {
|
2016-03-15 17:03:10 +11:00
|
|
|
change = true
|
2017-03-09 18:32:17 -08:00
|
|
|
be.liveout.Copy(newliveout)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A variable is live on input to this block
|
|
|
|
|
// if it is live on output from this block and
|
|
|
|
|
// not set by the code in this block.
|
|
|
|
|
//
|
|
|
|
|
// in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
|
2017-03-09 18:32:17 -08:00
|
|
|
newlivein.AndNot(be.liveout, be.varkill)
|
|
|
|
|
be.livein.Or(newlivein, be.uevar)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Visits all instructions in a basic block and computes a bit vector of live
|
|
|
|
|
// variables at each safe point locations.
|
2017-09-20 12:58:16 -07:00
|
|
|
func (lv *Liveness) epilogue() {
|
2015-02-23 16:07:24 -05:00
|
|
|
nvars := int32(len(lv.vars))
|
2018-02-26 20:56:58 -05:00
|
|
|
liveout := varRegVec{vars: bvalloc(nvars)}
|
2015-02-23 16:07:24 -05:00
|
|
|
any := bvalloc(nvars)
|
|
|
|
|
all := bvalloc(nvars)
|
2017-02-24 16:02:31 -08:00
|
|
|
livedefer := bvalloc(nvars) // always-live variables
|
2017-01-30 14:55:12 -08:00
|
|
|
|
|
|
|
|
// If there is a defer (that could recover), then all output
|
|
|
|
|
// parameters are live all the time. In addition, any locals
|
|
|
|
|
// that are pointers to heap-allocated output parameters are
|
|
|
|
|
// also always live (post-deferreturn code needs these
|
|
|
|
|
// pointers to copy values back to the stack).
|
|
|
|
|
// TODO: if the output parameter is heap-allocated, then we
|
|
|
|
|
// don't need to keep the stack copy live?
|
2017-03-15 22:55:21 -07:00
|
|
|
if lv.fn.Func.HasDefer() {
|
2017-02-24 16:02:31 -08:00
|
|
|
for i, n := range lv.vars {
|
2017-04-25 18:14:12 -07:00
|
|
|
if n.Class() == PPARAMOUT {
|
2017-01-30 14:55:12 -08:00
|
|
|
if n.IsOutputParamHeapAddr() {
|
2017-03-09 10:38:45 -08:00
|
|
|
// Just to be paranoid. Heap addresses are PAUTOs.
|
2017-01-30 14:55:12 -08:00
|
|
|
Fatalf("variable %v both output param and heap output param", n)
|
|
|
|
|
}
|
2017-03-09 10:38:45 -08:00
|
|
|
if n.Name.Param.Heapaddr != nil {
|
|
|
|
|
// If this variable moved to the heap, then
|
|
|
|
|
// its stack copy is not live.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// Note: zeroing is handled by zeroResults in walk.go.
|
2017-02-24 16:02:31 -08:00
|
|
|
livedefer.Set(int32(i))
|
2017-01-30 14:55:12 -08:00
|
|
|
}
|
2016-06-18 19:40:57 -07:00
|
|
|
if n.IsOutputParamHeapAddr() {
|
2017-02-27 19:56:38 +02:00
|
|
|
n.Name.SetNeedzero(true)
|
2017-02-24 16:02:31 -08:00
|
|
|
livedefer.Set(int32(i))
|
2016-06-18 19:40:57 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:48:46 -04:00
|
|
|
// We must analyze the entry block first. The runtime assumes
|
|
|
|
|
// the function entry map is index 0. Conveniently, layout
|
|
|
|
|
// already ensured that the entry block is first.
|
|
|
|
|
if lv.f.Entry != lv.f.Blocks[0] {
|
|
|
|
|
lv.f.Fatalf("entry block must be first")
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
{
|
|
|
|
|
// Reserve an entry for function entry.
|
|
|
|
|
live := bvalloc(nvars)
|
|
|
|
|
for _, pos := range lv.cache.textavarinit {
|
|
|
|
|
live.Set(pos)
|
|
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
lv.livevars = append(lv.livevars, varRegVec{vars: live})
|
2017-03-09 18:32:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, b := range lv.f.Blocks {
|
|
|
|
|
be := lv.blockEffects(b)
|
2018-04-17 16:33:59 -04:00
|
|
|
firstBitmapIndex := len(lv.livevars)
|
2017-03-09 18:32:17 -08:00
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
// Compute avarinitany and avarinitall for entry to block.
|
2018-04-05 17:35:13 -04:00
|
|
|
// This duplicates information known during Liveness.solve
|
2015-02-13 14:40:36 -05:00
|
|
|
// but avoids storing two more vectors for each block.
|
2017-03-09 18:32:17 -08:00
|
|
|
lv.avarinitanyall(b, any, all)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Walk forward through the basic block instructions and
|
|
|
|
|
// allocate liveness maps for those instructions that need them.
|
|
|
|
|
// Seed the maps with information about the addrtaken variables.
|
2017-03-09 18:32:17 -08:00
|
|
|
for _, v := range b.Values {
|
|
|
|
|
pos, e := lv.valueEffects(v)
|
2018-02-26 20:56:58 -05:00
|
|
|
// No need for regEffects because registers never appear in avarinit.
|
2017-03-09 18:32:17 -08:00
|
|
|
if e&varkill != 0 {
|
2017-01-14 23:43:26 -08:00
|
|
|
any.Unset(pos)
|
|
|
|
|
all.Unset(pos)
|
|
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
if e&avarinit != 0 {
|
2017-01-14 23:43:26 -08:00
|
|
|
any.Set(pos)
|
|
|
|
|
all.Set(pos)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2018-02-26 20:48:53 -05:00
|
|
|
if !lv.issafepoint(v) {
|
2017-03-09 18:32:17 -08:00
|
|
|
continue
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
// Annotate ambiguously live variables so that they can
|
2017-04-19 11:19:53 -07:00
|
|
|
// be zeroed at function entry and at VARKILL points.
|
2017-03-22 11:21:35 -07:00
|
|
|
// liveout is dead here and used as a temporary.
|
2018-02-26 20:56:58 -05:00
|
|
|
liveout.vars.AndNot(any, all)
|
|
|
|
|
if !liveout.vars.IsEmpty() {
|
|
|
|
|
for pos := int32(0); pos < liveout.vars.n; pos++ {
|
|
|
|
|
if !liveout.vars.Get(pos) {
|
2017-03-09 18:32:17 -08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
all.Set(pos) // silence future warnings in this block
|
|
|
|
|
n := lv.vars[pos]
|
|
|
|
|
if !n.Name.Needzero() {
|
|
|
|
|
n.Name.SetNeedzero(true)
|
|
|
|
|
if debuglive >= 1 {
|
2017-03-22 20:28:12 -07:00
|
|
|
Warnl(v.Pos, "%v: %L is ambiguously live", lv.fn.Func.Nname, n)
|
2017-03-09 18:32:17 -08:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
// Live stuff first.
|
|
|
|
|
live := bvalloc(nvars)
|
|
|
|
|
live.Copy(any)
|
2018-02-26 20:56:58 -05:00
|
|
|
lv.livevars = append(lv.livevars, varRegVec{vars: live})
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-10-02 17:29:10 -04:00
|
|
|
// walk backward, construct maps at each safe point
|
2018-04-17 16:33:59 -04:00
|
|
|
index := int32(len(lv.livevars) - 1)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2017-03-22 11:21:35 -07:00
|
|
|
liveout.Copy(be.liveout)
|
2017-03-09 18:32:17 -08:00
|
|
|
for i := len(b.Values) - 1; i >= 0; i-- {
|
|
|
|
|
v := b.Values[i]
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2018-02-26 20:48:53 -05:00
|
|
|
if lv.issafepoint(v) {
|
2017-03-22 11:21:35 -07:00
|
|
|
// Found an interesting instruction, record the
|
|
|
|
|
// corresponding liveness information.
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2018-02-26 20:56:58 -05:00
|
|
|
live := &lv.livevars[index]
|
|
|
|
|
live.Or(*live, liveout)
|
|
|
|
|
live.vars.Or(live.vars, livedefer) // only for non-entry safe points
|
2017-03-22 11:21:35 -07:00
|
|
|
index--
|
2017-03-09 18:32:17 -08:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2017-03-22 11:21:35 -07:00
|
|
|
// Update liveness information.
|
|
|
|
|
pos, e := lv.valueEffects(v)
|
2018-02-26 20:56:58 -05:00
|
|
|
regUevar, regKill := lv.regEffects(v)
|
2017-03-22 11:21:35 -07:00
|
|
|
if e&varkill != 0 {
|
2018-02-26 20:56:58 -05:00
|
|
|
liveout.vars.Unset(pos)
|
2017-03-22 11:21:35 -07:00
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
liveout.regs &^= regKill
|
2017-03-22 11:21:35 -07:00
|
|
|
if e&uevar != 0 {
|
2018-02-26 20:56:58 -05:00
|
|
|
liveout.vars.Set(pos)
|
2017-03-22 11:21:35 -07:00
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
liveout.regs |= regUevar
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
if b == lv.f.Entry {
|
2017-03-22 11:21:35 -07:00
|
|
|
if index != 0 {
|
|
|
|
|
Fatalf("bad index for entry point: %v", index)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
// Record live variables.
|
2018-02-26 20:56:58 -05:00
|
|
|
live := &lv.livevars[index]
|
|
|
|
|
live.Or(*live, liveout)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2018-04-17 16:33:59 -04:00
|
|
|
|
|
|
|
|
// Check that no registers are live across calls.
|
|
|
|
|
// For closure calls, the CALLclosure is the last use
|
|
|
|
|
// of the context register, so it's dead after the call.
|
|
|
|
|
index = int32(firstBitmapIndex)
|
|
|
|
|
for _, v := range b.Values {
|
|
|
|
|
if lv.issafepoint(v) {
|
|
|
|
|
live := lv.livevars[index]
|
|
|
|
|
if v.Op.IsCall() && live.regs != 0 {
|
|
|
|
|
lv.printDebug()
|
|
|
|
|
v.Fatalf("internal error: %v register %s recorded as live at call", lv.fn.Func.Nname, live.regs.niceString(lv.f.Config))
|
|
|
|
|
}
|
|
|
|
|
index++
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-20 15:48:46 -04:00
|
|
|
|
|
|
|
|
// The liveness maps for this block are now complete. Compact them.
|
|
|
|
|
lv.compact(b)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:48:46 -04:00
|
|
|
// Done compacting. Throw out the stack map set.
|
|
|
|
|
lv.stackMaps = lv.stackMapSet.extractUniqe()
|
|
|
|
|
lv.stackMapSet = bvecSet{}
|
|
|
|
|
|
2017-03-09 12:15:41 -08:00
|
|
|
// Useful sanity check: on entry to the function,
|
|
|
|
|
// the only things that can possibly be live are the
|
|
|
|
|
// input parameters.
|
|
|
|
|
for j, n := range lv.vars {
|
2018-04-20 15:48:46 -04:00
|
|
|
if n.Class() != PPARAM && lv.stackMaps[0].Get(int32(j)) {
|
2017-03-22 20:28:12 -07:00
|
|
|
Fatalf("internal error: %v %L recorded as live on entry", lv.fn.Func.Nname, n)
|
2017-03-09 12:15:41 -08:00
|
|
|
}
|
|
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
// Check that no registers are live at function entry.
|
|
|
|
|
// The context register, if any, comes from a
|
|
|
|
|
// LoweredGetClosurePtr operation first thing in the function,
|
|
|
|
|
// so it doesn't appear live at entry.
|
2018-04-20 15:48:46 -04:00
|
|
|
if regs := lv.regMaps[0]; regs != 0 {
|
2018-02-26 20:56:58 -05:00
|
|
|
lv.printDebug()
|
|
|
|
|
lv.f.Fatalf("internal error: %v register %s recorded as live on entry", lv.fn.Func.Nname, regs.niceString(lv.f.Config))
|
|
|
|
|
}
|
2017-03-09 12:15:41 -08:00
|
|
|
}
|
|
|
|
|
|
2016-06-08 22:02:08 -07:00
|
|
|
func (lv *Liveness) clobber() {
|
|
|
|
|
// The clobberdead experiment inserts code to clobber all the dead variables (locals and args)
|
|
|
|
|
// before and after every safepoint. This experiment is useful for debugging the generation
|
|
|
|
|
// of live pointer bitmaps.
|
|
|
|
|
if objabi.Clobberdead_enabled == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var varSize int64
|
|
|
|
|
for _, n := range lv.vars {
|
2017-04-28 00:19:49 +00:00
|
|
|
varSize += n.Type.Size()
|
2016-06-08 22:02:08 -07:00
|
|
|
}
|
2018-04-13 15:55:43 -04:00
|
|
|
if len(lv.stackMaps) > 1000 || varSize > 10000 {
|
2016-06-08 22:02:08 -07:00
|
|
|
// Be careful to avoid doing too much work.
|
|
|
|
|
// Bail if >1000 safepoints or >10000 bytes of variables.
|
|
|
|
|
// Otherwise, giant functions make this experiment generate too much code.
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if h := os.Getenv("GOCLOBBERDEADHASH"); h != "" {
|
|
|
|
|
// Clobber only functions where the hash of the function name matches a pattern.
|
|
|
|
|
// Useful for binary searching for a miscompiled function.
|
|
|
|
|
hstr := ""
|
2017-04-23 05:10:21 -07:00
|
|
|
for _, b := range sha1.Sum([]byte(lv.fn.funcname())) {
|
2016-06-08 22:02:08 -07:00
|
|
|
hstr += fmt.Sprintf("%08b", b)
|
|
|
|
|
}
|
|
|
|
|
if !strings.HasSuffix(hstr, h) {
|
|
|
|
|
return
|
|
|
|
|
}
|
2017-04-23 05:10:21 -07:00
|
|
|
fmt.Printf("\t\t\tCLOBBERDEAD %s\n", lv.fn.funcname())
|
2016-06-08 22:02:08 -07:00
|
|
|
}
|
|
|
|
|
if lv.f.Name == "forkAndExecInChild" {
|
|
|
|
|
// forkAndExecInChild calls vfork (on linux/amd64, anyway).
|
|
|
|
|
// The code we add here clobbers parts of the stack in the child.
|
|
|
|
|
// When the parent resumes, it is using the same stack frame. But the
|
|
|
|
|
// child has clobbered stack variables that the parent needs. Boom!
|
|
|
|
|
// In particular, the sys argument gets clobbered.
|
|
|
|
|
// Note to self: GOCLOBBERDEADHASH=011100101110
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var oldSched []*ssa.Value
|
|
|
|
|
for _, b := range lv.f.Blocks {
|
|
|
|
|
// Copy block's values to a temporary.
|
|
|
|
|
oldSched = append(oldSched[:0], b.Values...)
|
|
|
|
|
b.Values = b.Values[:0]
|
|
|
|
|
|
|
|
|
|
// Clobber all dead variables at entry.
|
|
|
|
|
if b == lv.f.Entry {
|
|
|
|
|
for len(oldSched) > 0 && len(oldSched[0].Args) == 0 {
|
|
|
|
|
// Skip argless ops. We need to skip at least
|
|
|
|
|
// the lowered ClosurePtr op, because it
|
|
|
|
|
// really wants to be first. This will also
|
|
|
|
|
// skip ops like InitMem and SP, which are ok.
|
|
|
|
|
b.Values = append(b.Values, oldSched[0])
|
|
|
|
|
oldSched = oldSched[1:]
|
|
|
|
|
}
|
2018-04-13 15:55:43 -04:00
|
|
|
clobber(lv, b, lv.stackMaps[0])
|
2016-06-08 22:02:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy values into schedule, adding clobbering around safepoints.
|
|
|
|
|
for _, v := range oldSched {
|
2018-02-26 20:48:53 -05:00
|
|
|
if !lv.issafepoint(v) {
|
2016-06-08 22:02:08 -07:00
|
|
|
b.Values = append(b.Values, v)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
before := true
|
|
|
|
|
if v.Op.IsCall() && v.Aux != nil && v.Aux.(*obj.LSym) == typedmemmove {
|
|
|
|
|
// Can't put clobber code before the call to typedmemmove.
|
|
|
|
|
// The variable to-be-copied is marked as dead
|
|
|
|
|
// at the callsite. That is ok, though, as typedmemmove
|
|
|
|
|
// is marked as nosplit, and the first thing it does
|
|
|
|
|
// is to call memmove (also nosplit), after which
|
|
|
|
|
// the source value is dead.
|
|
|
|
|
// See issue 16026.
|
|
|
|
|
before = false
|
|
|
|
|
}
|
|
|
|
|
if before {
|
2018-03-22 12:04:51 -04:00
|
|
|
clobber(lv, b, lv.stackMaps[lv.livenessMap.Get(v).stackMapIndex])
|
2016-06-08 22:02:08 -07:00
|
|
|
}
|
|
|
|
|
b.Values = append(b.Values, v)
|
2018-03-22 12:04:51 -04:00
|
|
|
clobber(lv, b, lv.stackMaps[lv.livenessMap.Get(v).stackMapIndex])
|
2016-06-08 22:02:08 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clobber generates code to clobber all dead variables (those not marked in live).
|
|
|
|
|
// Clobbering instructions are added to the end of b.Values.
|
|
|
|
|
func clobber(lv *Liveness, b *ssa.Block, live bvec) {
|
|
|
|
|
for i, n := range lv.vars {
|
|
|
|
|
if !live.Get(int32(i)) {
|
|
|
|
|
clobberVar(b, n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clobberVar generates code to trash the pointers in v.
|
|
|
|
|
// Clobbering instructions are added to the end of b.Values.
|
|
|
|
|
func clobberVar(b *ssa.Block, v *Node) {
|
|
|
|
|
clobberWalk(b, v, 0, v.Type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// b = block to which we append instructions
|
|
|
|
|
// v = variable
|
|
|
|
|
// offset = offset of (sub-portion of) variable to clobber (in bytes)
|
|
|
|
|
// t = type of sub-portion of v.
|
|
|
|
|
func clobberWalk(b *ssa.Block, v *Node, offset int64, t *types.Type) {
|
|
|
|
|
if !types.Haspointers(t) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
switch t.Etype {
|
|
|
|
|
case TPTR32,
|
|
|
|
|
TPTR64,
|
|
|
|
|
TUNSAFEPTR,
|
|
|
|
|
TFUNC,
|
|
|
|
|
TCHAN,
|
|
|
|
|
TMAP:
|
|
|
|
|
clobberPtr(b, v, offset)
|
|
|
|
|
|
|
|
|
|
case TSTRING:
|
|
|
|
|
// struct { byte *str; int len; }
|
|
|
|
|
clobberPtr(b, v, offset)
|
|
|
|
|
|
|
|
|
|
case TINTER:
|
|
|
|
|
// struct { Itab *tab; void *data; }
|
|
|
|
|
// or, when isnilinter(t)==true:
|
|
|
|
|
// struct { Type *type; void *data; }
|
2018-02-27 13:46:03 -08:00
|
|
|
// Note: the first word isn't a pointer. See comment in plive.go:onebitwalktype1.
|
2016-06-08 22:02:08 -07:00
|
|
|
clobberPtr(b, v, offset+int64(Widthptr))
|
|
|
|
|
|
|
|
|
|
case TSLICE:
|
|
|
|
|
// struct { byte *array; int len; int cap; }
|
|
|
|
|
clobberPtr(b, v, offset)
|
|
|
|
|
|
|
|
|
|
case TARRAY:
|
|
|
|
|
for i := int64(0); i < t.NumElem(); i++ {
|
2017-04-28 00:19:49 +00:00
|
|
|
clobberWalk(b, v, offset+i*t.Elem().Size(), t.Elem())
|
2016-06-08 22:02:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TSTRUCT:
|
|
|
|
|
for _, t1 := range t.Fields().Slice() {
|
|
|
|
|
clobberWalk(b, v, offset+t1.Offset, t1.Type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
Fatalf("clobberWalk: unexpected type, %v", t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clobberPtr generates a clobber of the pointer at offset offset in v.
|
|
|
|
|
// The clobber instruction is added at the end of b.
|
|
|
|
|
func clobberPtr(b *ssa.Block, v *Node, offset int64) {
|
2017-09-18 14:53:56 -07:00
|
|
|
b.NewValue0IA(src.NoXPos, ssa.OpClobber, types.TypeVoid, offset, v)
|
2016-06-08 22:02:08 -07:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
func (lv *Liveness) avarinitanyall(b *ssa.Block, any, all bvec) {
|
|
|
|
|
if len(b.Preds) == 0 {
|
2017-03-09 12:15:41 -08:00
|
|
|
any.Clear()
|
|
|
|
|
all.Clear()
|
|
|
|
|
for _, pos := range lv.cache.textavarinit {
|
|
|
|
|
any.Set(pos)
|
|
|
|
|
all.Set(pos)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
be := lv.blockEffects(b.Preds[0].Block())
|
|
|
|
|
any.Copy(be.avarinitany)
|
|
|
|
|
all.Copy(be.avarinitall)
|
|
|
|
|
|
|
|
|
|
for _, pred := range b.Preds[1:] {
|
|
|
|
|
be := lv.blockEffects(pred.Block())
|
|
|
|
|
any.Or(any, be.avarinitany)
|
|
|
|
|
all.And(all, be.avarinitall)
|
2017-03-09 12:15:41 -08:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:48:46 -04:00
|
|
|
// Compact coalesces identical bitmaps from lv.livevars into the sets
|
|
|
|
|
// lv.stackMapSet and lv.regMaps.
|
|
|
|
|
//
|
|
|
|
|
// Compact clears lv.livevars.
|
2015-02-13 14:40:36 -05:00
|
|
|
//
|
|
|
|
|
// There are actually two lists of bitmaps, one list for the local variables and one
|
|
|
|
|
// list for the function arguments. Both lists are indexed by the same PCDATA
|
|
|
|
|
// index, so the corresponding pairs must be considered together when
|
|
|
|
|
// merging duplicates. The argument bitmaps change much less often during
|
|
|
|
|
// function execution than the local variable bitmaps, so it is possible that
|
|
|
|
|
// we could introduce a separate PCDATA index for arguments vs locals and
|
|
|
|
|
// then compact the set of argument bitmaps separately from the set of
|
|
|
|
|
// local variable bitmaps. As of 2014-04-02, doing this to the godoc binary
|
|
|
|
|
// is actually a net loss: we save about 50k of argument bitmaps but the new
|
|
|
|
|
// PCDATA tables cost about 100k. So for now we keep using a single index for
|
|
|
|
|
// both bitmap lists.
|
2018-04-20 15:48:46 -04:00
|
|
|
func (lv *Liveness) compact(b *ssa.Block) {
|
|
|
|
|
add := func(live varRegVec) LivenessIndex {
|
|
|
|
|
// Deduplicate the stack map.
|
|
|
|
|
stackIndex := lv.stackMapSet.add(live.vars)
|
|
|
|
|
// Deduplicate the register map.
|
|
|
|
|
regIndex, ok := lv.regMapSet[live.regs]
|
2018-02-26 20:56:58 -05:00
|
|
|
if !ok {
|
2018-04-20 15:48:46 -04:00
|
|
|
regIndex = len(lv.regMapSet)
|
|
|
|
|
lv.regMapSet[live.regs] = regIndex
|
2018-02-26 20:56:58 -05:00
|
|
|
lv.regMaps = append(lv.regMaps, live.regs)
|
|
|
|
|
}
|
2018-04-20 15:48:46 -04:00
|
|
|
return LivenessIndex{stackIndex, regIndex}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2018-04-20 15:48:46 -04:00
|
|
|
pos := 0
|
|
|
|
|
if b == lv.f.Entry {
|
|
|
|
|
// Handle entry stack map.
|
|
|
|
|
add(lv.livevars[0])
|
|
|
|
|
pos++
|
|
|
|
|
}
|
|
|
|
|
for _, v := range b.Values {
|
|
|
|
|
if lv.issafepoint(v) {
|
2018-04-21 15:40:56 -04:00
|
|
|
lv.livenessMap.set(v, add(lv.livevars[pos]))
|
2018-04-20 15:48:46 -04:00
|
|
|
pos++
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2018-04-20 15:48:46 -04:00
|
|
|
|
|
|
|
|
// Reset livevars.
|
|
|
|
|
lv.livevars = lv.livevars[:0]
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
|
2017-04-23 05:10:21 -07:00
|
|
|
if debuglive == 0 || lv.fn.funcname() == "init" || strings.HasPrefix(lv.fn.funcname(), ".") {
|
2017-03-09 18:32:17 -08:00
|
|
|
return
|
|
|
|
|
}
|
2018-02-26 20:48:53 -05:00
|
|
|
if !(v == nil || v.Op.IsCall()) {
|
|
|
|
|
// Historically we only printed this information at
|
|
|
|
|
// calls. Keep doing so.
|
|
|
|
|
return
|
|
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
if live.IsEmpty() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-22 20:28:12 -07:00
|
|
|
pos := lv.fn.Func.Nname.Pos
|
2017-03-09 18:32:17 -08:00
|
|
|
if v != nil {
|
|
|
|
|
pos = v.Pos
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s := "live at "
|
|
|
|
|
if v == nil {
|
2017-04-23 05:10:21 -07:00
|
|
|
s += fmt.Sprintf("entry to %s:", lv.fn.funcname())
|
2017-03-09 18:32:17 -08:00
|
|
|
} else if sym, ok := v.Aux.(*obj.LSym); ok {
|
|
|
|
|
fn := sym.Name
|
2017-10-05 15:49:32 +02:00
|
|
|
if pos := strings.Index(fn, "."); pos >= 0 {
|
2017-03-09 18:32:17 -08:00
|
|
|
fn = fn[pos+1:]
|
|
|
|
|
}
|
|
|
|
|
s += fmt.Sprintf("call to %s:", fn)
|
|
|
|
|
} else {
|
|
|
|
|
s += "indirect call:"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for j, n := range lv.vars {
|
|
|
|
|
if live.Get(int32(j)) {
|
|
|
|
|
s += fmt.Sprintf(" %v", n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Warnl(pos, s)
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-26 20:56:58 -05:00
|
|
|
func (lv *Liveness) printbvec(printed bool, name string, live varRegVec) bool {
|
|
|
|
|
if live.vars.IsEmpty() && live.regs == 0 {
|
|
|
|
|
return printed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !printed {
|
|
|
|
|
fmt.Printf("\t")
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf(" ")
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%s=", name)
|
|
|
|
|
|
|
|
|
|
comma := ""
|
2017-03-09 18:32:17 -08:00
|
|
|
for i, n := range lv.vars {
|
2018-02-26 20:56:58 -05:00
|
|
|
if !live.vars.Get(int32(i)) {
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
fmt.Printf("%s%s", comma, n.Sym.Name)
|
|
|
|
|
comma = ","
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
fmt.Printf("%s%s", comma, live.regs.niceString(lv.f.Config))
|
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2018-02-26 20:56:58 -05:00
|
|
|
// printeffect is like printbvec, but for valueEffects and regEffects.
|
|
|
|
|
func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool, regMask liveRegMask) bool {
|
|
|
|
|
if !x && regMask == 0 {
|
2017-03-09 18:32:17 -08:00
|
|
|
return printed
|
|
|
|
|
}
|
|
|
|
|
if !printed {
|
|
|
|
|
fmt.Printf("\t")
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf(" ")
|
|
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
fmt.Printf("%s=", name)
|
|
|
|
|
if x {
|
|
|
|
|
fmt.Printf("%s", lv.vars[pos].Sym.Name)
|
|
|
|
|
}
|
|
|
|
|
for j, reg := range lv.f.Config.GCRegMap {
|
|
|
|
|
if regMask&(1<<uint(j)) != 0 {
|
|
|
|
|
if x {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
x = true
|
|
|
|
|
fmt.Printf("%v", reg)
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
// Prints the computed liveness information and inputs, for debugging.
|
|
|
|
|
// This format synthesizes the information used during the multiple passes
|
|
|
|
|
// into a single presentation.
|
2017-09-20 12:58:16 -07:00
|
|
|
func (lv *Liveness) printDebug() {
|
2017-04-23 05:10:21 -07:00
|
|
|
fmt.Printf("liveness: %s\n", lv.fn.funcname())
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
pcdata := 0
|
2017-03-09 18:32:17 -08:00
|
|
|
for i, b := range lv.f.Blocks {
|
2015-02-13 14:40:36 -05:00
|
|
|
if i > 0 {
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bb#0 pred=1,2 succ=3,4
|
2017-03-09 18:32:17 -08:00
|
|
|
fmt.Printf("bb#%d pred=", b.ID)
|
|
|
|
|
for j, pred := range b.Preds {
|
2015-02-13 14:40:36 -05:00
|
|
|
if j > 0 {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
fmt.Printf("%d", pred.Block().ID)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
fmt.Printf(" succ=")
|
2017-03-09 18:32:17 -08:00
|
|
|
for j, succ := range b.Succs {
|
2015-02-13 14:40:36 -05:00
|
|
|
if j > 0 {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
fmt.Printf("%d", succ.Block().ID)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
be := lv.blockEffects(b)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
// initial settings
|
|
|
|
|
printed := false
|
|
|
|
|
printed = lv.printbvec(printed, "uevar", be.uevar)
|
|
|
|
|
printed = lv.printbvec(printed, "livein", be.livein)
|
2016-03-13 10:23:18 +09:00
|
|
|
if printed {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// program listing, with individual effects listed
|
2017-03-09 18:32:17 -08:00
|
|
|
|
|
|
|
|
if b == lv.f.Entry {
|
2018-04-13 15:55:43 -04:00
|
|
|
live := lv.stackMaps[pcdata]
|
2017-03-22 20:28:12 -07:00
|
|
|
fmt.Printf("(%s) function entry\n", linestr(lv.fn.Func.Nname.Pos))
|
2017-03-09 18:32:17 -08:00
|
|
|
fmt.Printf("\tlive=")
|
|
|
|
|
printed = false
|
|
|
|
|
for j, n := range lv.vars {
|
|
|
|
|
if !live.Get(int32(j)) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if printed {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%v", n)
|
|
|
|
|
printed = true
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, v := range b.Values {
|
|
|
|
|
fmt.Printf("(%s) %v\n", linestr(v.Pos), v.LongString())
|
|
|
|
|
|
2018-03-22 12:04:51 -04:00
|
|
|
if pos := lv.livenessMap.Get(v); pos.Valid() {
|
|
|
|
|
pcdata = pos.stackMapIndex
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
|
|
|
|
|
pos, effect := lv.valueEffects(v)
|
2018-02-26 20:56:58 -05:00
|
|
|
regUevar, regKill := lv.regEffects(v)
|
2016-03-13 10:23:18 +09:00
|
|
|
printed = false
|
2018-02-26 20:56:58 -05:00
|
|
|
printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0, regUevar)
|
|
|
|
|
printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0, regKill)
|
|
|
|
|
printed = lv.printeffect(printed, "avarinit", pos, effect&avarinit != 0, 0)
|
2016-03-13 10:23:18 +09:00
|
|
|
if printed {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
|
2018-02-26 20:48:53 -05:00
|
|
|
if !lv.issafepoint(v) {
|
2017-03-09 18:32:17 -08:00
|
|
|
continue
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2018-04-13 15:55:43 -04:00
|
|
|
live := lv.stackMaps[pcdata]
|
2017-03-09 18:32:17 -08:00
|
|
|
fmt.Printf("\tlive=")
|
|
|
|
|
printed = false
|
|
|
|
|
for j, n := range lv.vars {
|
|
|
|
|
if !live.Get(int32(j)) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if printed {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%v", n)
|
|
|
|
|
printed = true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2018-02-26 20:56:58 -05:00
|
|
|
regLive := lv.regMaps[lv.livenessMap.Get(v).regMapIndex]
|
|
|
|
|
if regLive != 0 {
|
|
|
|
|
if printed {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%s", regLive.niceString(lv.f.Config))
|
|
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
fmt.Printf("\n")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bb bitsets
|
|
|
|
|
fmt.Printf("end\n")
|
2017-03-09 18:32:17 -08:00
|
|
|
printed = false
|
|
|
|
|
printed = lv.printbvec(printed, "varkill", be.varkill)
|
|
|
|
|
printed = lv.printbvec(printed, "liveout", be.liveout)
|
2018-02-26 20:56:58 -05:00
|
|
|
printed = lv.printbvec(printed, "avarinit", varRegVec{vars: be.avarinit})
|
|
|
|
|
printed = lv.printbvec(printed, "avarinitany", varRegVec{vars: be.avarinitany})
|
|
|
|
|
printed = lv.printbvec(printed, "avarinitall", varRegVec{vars: be.avarinitall})
|
2016-03-13 10:23:18 +09:00
|
|
|
if printed {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-24 16:02:31 -08:00
|
|
|
// Dumps a slice of bitmaps to a symbol as a sequence of uint32 values. The
|
|
|
|
|
// first word dumped is the total number of bitmaps. The second word is the
|
|
|
|
|
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
|
|
|
|
|
// remaining bytes are the raw bitmaps.
|
2018-03-27 15:50:45 -04:00
|
|
|
func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) {
|
2018-04-02 14:21:27 -07:00
|
|
|
// Size args bitmaps to be just large enough to hold the largest pointer.
|
|
|
|
|
// First, find the largest Xoffset node we care about.
|
|
|
|
|
// (Nodes without pointers aren't in lv.vars; see livenessShouldTrack.)
|
|
|
|
|
var maxArgNode *Node
|
|
|
|
|
for _, n := range lv.vars {
|
|
|
|
|
switch n.Class() {
|
|
|
|
|
case PPARAM, PPARAMOUT:
|
|
|
|
|
if maxArgNode == nil || n.Xoffset > maxArgNode.Xoffset {
|
|
|
|
|
maxArgNode = n
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Next, find the offset of the largest pointer in the largest node.
|
|
|
|
|
var maxArgs int64
|
|
|
|
|
if maxArgNode != nil {
|
|
|
|
|
maxArgs = maxArgNode.Xoffset + typeptrdata(maxArgNode.Type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Size locals bitmaps to be stkptrsize sized.
|
|
|
|
|
// We cannot shrink them to only hold the largest pointer,
|
|
|
|
|
// because their size is used to calculate the beginning
|
|
|
|
|
// of the local variables frame.
|
|
|
|
|
// Further discussion in https://golang.org/cl/104175.
|
|
|
|
|
// TODO: consider trimming leading zeros.
|
|
|
|
|
// This would require shifting all bitmaps.
|
|
|
|
|
maxLocals := lv.stkptrsize
|
|
|
|
|
|
|
|
|
|
args := bvalloc(int32(maxArgs / int64(Widthptr)))
|
2018-04-13 15:55:43 -04:00
|
|
|
aoff := duint32(argssym, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
|
|
|
|
aoff = duint32(argssym, aoff, uint32(args.n)) // number of bits in each bitmap
|
2017-02-24 16:02:31 -08:00
|
|
|
|
2018-04-02 14:21:27 -07:00
|
|
|
locals := bvalloc(int32(maxLocals / int64(Widthptr)))
|
2018-04-13 15:55:43 -04:00
|
|
|
loff := duint32(livesym, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
|
|
|
|
loff = duint32(livesym, loff, uint32(locals.n)) // number of bits in each bitmap
|
2017-02-24 16:02:31 -08:00
|
|
|
|
2018-04-13 15:55:43 -04:00
|
|
|
for _, live := range lv.stackMaps {
|
2017-02-24 16:02:31 -08:00
|
|
|
args.Clear()
|
|
|
|
|
locals.Clear()
|
|
|
|
|
|
2017-09-20 12:58:16 -07:00
|
|
|
lv.pointerMap(live, lv.vars, args, locals)
|
2017-02-24 16:02:31 -08:00
|
|
|
|
2017-04-21 13:59:51 -07:00
|
|
|
aoff = dbvec(argssym, aoff, args)
|
|
|
|
|
loff = dbvec(livesym, loff, locals)
|
2016-03-28 22:27:36 +13:00
|
|
|
}
|
2017-02-24 16:02:31 -08:00
|
|
|
|
2018-03-27 15:50:45 -04:00
|
|
|
regs := bvalloc(lv.usedRegs())
|
|
|
|
|
roff := duint32(regssym, 0, uint32(len(lv.regMaps))) // number of bitmaps
|
|
|
|
|
roff = duint32(regssym, roff, uint32(regs.n)) // number of bits in each bitmap
|
|
|
|
|
if regs.n > 32 {
|
|
|
|
|
// Our uint32 conversion below won't work.
|
|
|
|
|
Fatalf("GP registers overflow uint32")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if regs.n > 0 {
|
|
|
|
|
for _, live := range lv.regMaps {
|
|
|
|
|
regs.Clear()
|
|
|
|
|
regs.b[0] = uint32(live)
|
|
|
|
|
roff = dbvec(regssym, roff, regs)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-14 06:35:53 -07:00
|
|
|
// Give these LSyms content-addressable names,
|
|
|
|
|
// so that they can be de-duplicated.
|
|
|
|
|
// This provides significant binary size savings.
|
|
|
|
|
// It is safe to rename these LSyms because
|
2017-04-14 06:44:30 -07:00
|
|
|
// they are tracked separately from ctxt.hash.
|
2017-04-14 06:35:53 -07:00
|
|
|
argssym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(argssym.P))
|
|
|
|
|
livesym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(livesym.P))
|
2018-03-27 15:50:45 -04:00
|
|
|
regssym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(regssym.P))
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-09 18:32:17 -08:00
|
|
|
// Entry pointer for liveness analysis. Solves for the liveness of
|
|
|
|
|
// pointer variables in the function and emits a runtime data
|
2015-02-13 14:40:36 -05:00
|
|
|
// structure read by the garbage collector.
|
2017-03-09 18:32:17 -08:00
|
|
|
// Returns a map from GC safe points to their corresponding stack map index.
|
2018-03-22 12:04:51 -04:00
|
|
|
func liveness(e *ssafn, f *ssa.Func) LivenessMap {
|
2015-02-13 14:40:36 -05:00
|
|
|
// Construct the global liveness state.
|
2017-04-27 16:27:47 -07:00
|
|
|
vars, idx := getvariables(e.curfn)
|
|
|
|
|
lv := newliveness(e.curfn, f, vars, idx, e.stkptrsize)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Run the dataflow framework.
|
2017-09-20 12:58:16 -07:00
|
|
|
lv.prologue()
|
|
|
|
|
lv.solve()
|
|
|
|
|
lv.epilogue()
|
2016-06-08 22:02:08 -07:00
|
|
|
lv.clobber()
|
2018-04-20 15:48:46 -04:00
|
|
|
if debuglive > 0 {
|
|
|
|
|
lv.showlive(nil, lv.stackMaps[0])
|
2018-04-21 15:40:56 -04:00
|
|
|
for _, b := range f.Blocks {
|
|
|
|
|
for _, val := range b.Values {
|
|
|
|
|
if idx := lv.livenessMap.Get(val); idx.Valid() {
|
|
|
|
|
lv.showlive(val, lv.stackMaps[idx.stackMapIndex])
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-20 15:48:46 -04:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
if debuglive >= 2 {
|
2017-09-20 12:58:16 -07:00
|
|
|
lv.printDebug()
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit the live pointer map data structures
|
2017-04-14 06:35:53 -07:00
|
|
|
if ls := e.curfn.Func.lsym; ls != nil {
|
2018-03-27 15:50:45 -04:00
|
|
|
lv.emit(&ls.Func.GCArgs, &ls.Func.GCLocals, &ls.Func.GCRegs)
|
2017-04-14 06:35:53 -07:00
|
|
|
}
|
2018-03-22 12:04:51 -04:00
|
|
|
return lv.livenessMap
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|