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"
|
2016-03-28 22:27:36 +13:00
|
|
|
"crypto/md5"
|
2015-02-13 14:40:36 -05:00
|
|
|
"fmt"
|
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.
|
|
|
|
|
|
2018-09-07 14:55:09 -07:00
|
|
|
// TODO: get rid of OpVarKill here. It's useful for stack frame allocation
|
|
|
|
|
// so the compiler can allocate two temps to the same location. Here it's now
|
|
|
|
|
// useless, since the implementation of stack objects.
|
|
|
|
|
|
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)
|
2020-10-21 20:15:48 -04:00
|
|
|
uevar bvec
|
|
|
|
|
varkill 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
|
2020-10-21 20:15:48 -04:00
|
|
|
livein bvec
|
|
|
|
|
liveout 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
|
|
|
|
|
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
// allUnsafe indicates that all points in this function are
|
|
|
|
|
// unsafe-points.
|
|
|
|
|
allUnsafe bool
|
|
|
|
|
// unsafePoints bit i is set if Value ID i is an unsafe-point
|
|
|
|
|
// (preemption is not allowed). Only valid if !allUnsafe.
|
2018-02-26 20:48:53 -05:00
|
|
|
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
|
2020-10-21 20:15:48 -04:00
|
|
|
// these to stackMaps.
|
|
|
|
|
livevars []bvec
|
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-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 {
|
2020-04-27 19:18:48 -04:00
|
|
|
vals map[ssa.ID]LivenessIndex
|
2020-08-08 07:58:04 -07:00
|
|
|
// The set of live, pointer-containing variables at the deferreturn
|
|
|
|
|
// call (only set when open-coded defers are used).
|
|
|
|
|
deferreturn LivenessIndex
|
2018-04-21 15:40:56 -04:00
|
|
|
}
|
|
|
|
|
|
2020-04-27 19:18:48 -04:00
|
|
|
func (m *LivenessMap) reset() {
|
|
|
|
|
if m.vals == nil {
|
|
|
|
|
m.vals = make(map[ssa.ID]LivenessIndex)
|
2018-04-21 15:40:56 -04:00
|
|
|
} else {
|
2020-04-27 19:18:48 -04:00
|
|
|
for k := range m.vals {
|
|
|
|
|
delete(m.vals, k)
|
|
|
|
|
}
|
2018-04-21 15:40:56 -04:00
|
|
|
}
|
2020-10-21 20:15:48 -04:00
|
|
|
m.deferreturn = LivenessDontCare
|
2018-04-21 15:40:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
|
2020-04-27 19:18:48 -04:00
|
|
|
m.vals[v.ID] = i
|
2018-03-22 12:04:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m LivenessMap) Get(v *ssa.Value) LivenessIndex {
|
2020-04-16 08:13:58 -04:00
|
|
|
// If v isn't in the map, then it's a "don't care" and not an
|
|
|
|
|
// unsafe-point.
|
2020-04-27 19:18:48 -04:00
|
|
|
if idx, ok := m.vals[v.ID]; ok {
|
|
|
|
|
return idx
|
2018-03-22 12:04:51 -04:00
|
|
|
}
|
2020-10-21 20:15:48 -04:00
|
|
|
return LivenessIndex{StackMapDontCare, false}
|
2018-03-22 12:04:51 -04:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
// LivenessIndex stores the liveness map information for a Value.
|
2018-03-22 12:04:51 -04:00
|
|
|
type LivenessIndex struct {
|
|
|
|
|
stackMapIndex int
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
|
|
|
|
|
// isUnsafePoint indicates that this is an unsafe-point.
|
|
|
|
|
//
|
|
|
|
|
// Note that it's possible for a call Value to have a stack
|
|
|
|
|
// map while also being an unsafe-point. This means it cannot
|
|
|
|
|
// be preempted at this instruction, but that a preemption or
|
|
|
|
|
// stack growth may happen in the called function.
|
|
|
|
|
isUnsafePoint bool
|
2018-03-22 12:04:51 -04:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 20:15:48 -04:00
|
|
|
// LivenessDontCare indicates that the liveness information doesn't
|
|
|
|
|
// matter. Currently it is used in deferreturn liveness when we don't
|
|
|
|
|
// actually need it. It should never be emitted to the PCDATA stream.
|
|
|
|
|
var LivenessDontCare = LivenessIndex{StackMapDontCare, true}
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
|
|
|
|
|
// StackMapDontCare indicates that the stack map index at a Value
|
|
|
|
|
// doesn't matter.
|
2018-03-27 16:11:10 -04:00
|
|
|
//
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
// This is a sentinel value that should never be emitted to the PCDATA
|
|
|
|
|
// stream. We use -1000 because that's obviously never a valid stack
|
|
|
|
|
// index (but -1 is).
|
|
|
|
|
const StackMapDontCare = -1000
|
|
|
|
|
|
|
|
|
|
func (idx LivenessIndex) StackMapValid() bool {
|
|
|
|
|
return idx.stackMapIndex != StackMapDontCare
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 23:43:26 -08:00
|
|
|
type progeffectscache struct {
|
2018-09-07 14:55:09 -07:00
|
|
|
retuevar []int32
|
|
|
|
|
tailuevar []int32
|
|
|
|
|
initialized bool
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
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 {
|
2020-08-22 14:07:30 -07:00
|
|
|
return n.Op == ONAME && (n.Class() == PAUTO || n.Class() == PPARAM || n.Class() == PPARAMOUT) && n.Type.HasPointers()
|
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.
|
|
|
|
|
lv.cache.tailuevar = append(lv.cache.tailuevar, int32(i))
|
|
|
|
|
|
|
|
|
|
case PPARAMOUT:
|
2018-09-07 14:55:09 -07:00
|
|
|
// All results are live at every return point.
|
|
|
|
|
// Note that this point is after escaping return values
|
|
|
|
|
// are copied back to the stack using their PAUTOHEAP references.
|
|
|
|
|
lv.cache.retuevar = append(lv.cache.retuevar, int32(i))
|
2017-01-14 23:43:26 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
2018-09-07 14:55:09 -07:00
|
|
|
// varkill - killed by the instruction (set)
|
|
|
|
|
// A kill happens after the use (for an instruction that updates a value, for example).
|
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
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 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
|
2018-09-07 14:55:09 -07:00
|
|
|
// Read is a read, obviously.
|
|
|
|
|
//
|
2019-09-04 11:11:22 +07:00
|
|
|
// Addr is a read also, as any subsequent holder of the pointer must be able
|
2018-09-07 14:55:09 -07:00
|
|
|
// to see all the values (including initialization) written so far.
|
2018-12-11 16:12:57 -08:00
|
|
|
// This also prevents a variable from "coming back from the dead" and presenting
|
|
|
|
|
// stale pointers to the garbage collector. See issue 28445.
|
2018-09-07 14:55:09 -07:00
|
|
|
if e&(ssa.SymRead|ssa.SymAddr) != 0 {
|
|
|
|
|
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-04-21 16:15:41 -04:00
|
|
|
type livenessFuncCache struct {
|
|
|
|
|
be []BlockEffects
|
|
|
|
|
livenessMap LivenessMap
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
2016-03-13 10:23:18 +09:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2018-04-21 16:15:41 -04:00
|
|
|
// Significant sources of allocation are kept in the ssa.Cache
|
|
|
|
|
// and reused. Surprisingly, the bit vectors themselves aren't
|
2020-04-27 19:18:48 -04:00
|
|
|
// a major source of allocation, but the liveness maps are.
|
2018-04-21 16:15:41 -04:00
|
|
|
if lc, _ := f.Cache.Liveness.(*livenessFuncCache); lc == nil {
|
|
|
|
|
// Prep the cache so liveness can fill it later.
|
|
|
|
|
f.Cache.Liveness = new(livenessFuncCache)
|
|
|
|
|
} else {
|
|
|
|
|
if cap(lc.be) >= f.NumBlocks() {
|
|
|
|
|
lv.be = lc.be[:f.NumBlocks()]
|
|
|
|
|
}
|
2020-10-21 20:15:48 -04:00
|
|
|
lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessDontCare}
|
2020-04-27 19:18:48 -04:00
|
|
|
lc.livenessMap.vals = nil
|
2018-04-21 16:15:41 -04:00
|
|
|
}
|
|
|
|
|
if lv.be == nil {
|
|
|
|
|
lv.be = make([]BlockEffects, f.NumBlocks())
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
|
2020-10-21 20:15:48 -04:00
|
|
|
be.uevar = bulk.next()
|
|
|
|
|
be.varkill = bulk.next()
|
|
|
|
|
be.livein = bulk.next()
|
|
|
|
|
be.liveout = bulk.next()
|
2017-03-09 18:32:17 -08:00
|
|
|
}
|
2020-04-27 19:18:48 -04:00
|
|
|
lv.livenessMap.reset()
|
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
|
|
|
}
|
2020-08-21 20:20:12 -07:00
|
|
|
if !t.HasPointers() {
|
|
|
|
|
// Note: this case ensures that pointers to go:notinheap types
|
|
|
|
|
// are not considered pointers by garbage collection and stack copying.
|
|
|
|
|
return
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
switch t.Etype {
|
2017-11-06 14:50:30 -08:00
|
|
|
case TPTR, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
|
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
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 15:41:45 -04:00
|
|
|
// allUnsafe indicates that all points in this function are
|
|
|
|
|
// unsafe-points.
|
|
|
|
|
func allUnsafe(f *ssa.Func) bool {
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
// The runtime assumes the only safe-points are function
|
|
|
|
|
// prologues (because that's how it used to be). We could and
|
|
|
|
|
// should improve that, but for now keep consider all points
|
|
|
|
|
// in the runtime unsafe. obj will add prologues and their
|
|
|
|
|
// safe-points.
|
|
|
|
|
//
|
|
|
|
|
// 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".
|
2020-06-04 15:41:45 -04:00
|
|
|
return compiling_runtime || f.NoSplit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// markUnsafePoints finds unsafe points and computes lv.unsafePoints.
|
|
|
|
|
func (lv *Liveness) markUnsafePoints() {
|
|
|
|
|
if allUnsafe(lv.f) {
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
// No complex analysis necessary.
|
|
|
|
|
lv.allUnsafe = true
|
2018-02-26 20:48:53 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lv.unsafePoints = bvalloc(int32(lv.f.NumValues()))
|
|
|
|
|
|
2019-11-15 19:49:30 +00:00
|
|
|
// Mark architecture-specific unsafe points.
|
2019-10-25 00:51:10 -04:00
|
|
|
for _, b := range lv.f.Blocks {
|
|
|
|
|
for _, v := range b.Values {
|
|
|
|
|
if v.Op.UnsafePoint() {
|
|
|
|
|
lv.unsafePoints.Set(int32(v.ID))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-26 20:48:53 -05:00
|
|
|
// 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()
|
2018-07-12 11:44:05 -07:00
|
|
|
if s0 == s1 {
|
|
|
|
|
// There's no difference between write barrier on and off.
|
|
|
|
|
// Thus there's no unsafe locations. See issue 26024.
|
|
|
|
|
continue
|
|
|
|
|
}
|
2018-02-26 20:48:53 -05:00
|
|
|
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
|
2019-08-12 20:19:58 +01:00
|
|
|
v := wbBlock.Controls[0]
|
2018-02-26 20:48:53 -05:00
|
|
|
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-24 04:11:05 +02:00
|
|
|
case ssa.Op386MOVLload, ssa.OpARM64MOVWUload, ssa.OpPPC64MOVWZload, ssa.OpWasmI64Load32U:
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 17:14:33 -04:00
|
|
|
// Returns true for instructions that must have a stack map.
|
|
|
|
|
//
|
|
|
|
|
// This does not necessarily mean the instruction is a safe-point. In
|
|
|
|
|
// particular, call Values can have a stack map in case the callee
|
|
|
|
|
// grows the stack, but not themselves be a safe-point.
|
|
|
|
|
func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
|
2020-10-21 20:15:48 -04:00
|
|
|
if !v.Op.IsCall() {
|
|
|
|
|
return false
|
2018-02-26 20:48:53 -05:00
|
|
|
}
|
2020-10-21 20:15:48 -04:00
|
|
|
// typedmemclr and typedmemmove are write barriers and
|
|
|
|
|
// deeply non-preemptible. They are unsafe points and
|
|
|
|
|
// hence should not have liveness maps.
|
|
|
|
|
if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) {
|
2018-02-26 20:48:53 -05:00
|
|
|
return false
|
|
|
|
|
}
|
2020-10-21 20:15:48 -04:00
|
|
|
return true
|
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])
|
|
|
|
|
if e&varkill != 0 {
|
2020-10-21 20:15:48 -04:00
|
|
|
be.varkill.Set(pos)
|
|
|
|
|
be.uevar.Unset(pos)
|
2017-01-14 23:43:26 -08:00
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
if e&uevar != 0 {
|
2020-10-21 20:15:48 -04:00
|
|
|
be.uevar.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))
|
2020-10-21 20:15:48 -04:00
|
|
|
newlivein := bvalloc(nvars)
|
|
|
|
|
newliveout := bvalloc(nvars)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2018-09-07 14:55:09 -07:00
|
|
|
// Walk blocks in postorder ordering. This improves convergence.
|
2017-03-09 18:32:17 -08:00
|
|
|
po := lv.f.Postorder()
|
|
|
|
|
|
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 {
|
2020-10-21 20:15:48 -04:00
|
|
|
newliveout.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 {
|
2020-10-21 20:15:48 -04:00
|
|
|
newliveout.Set(pos)
|
2017-03-09 18:32:17 -08:00
|
|
|
}
|
|
|
|
|
case ssa.BlockExit:
|
2019-11-01 14:04:08 -07:00
|
|
|
// panic exit - nothing to do
|
2017-03-09 18:32:17 -08:00
|
|
|
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
|
2018-09-07 14:55:09 -07:00
|
|
|
// if it is used by this block, or live on output from this block and
|
2015-02-13 14:40:36 -05:00
|
|
|
// 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))
|
2020-10-21 20:15:48 -04:00
|
|
|
liveout := 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 {
|
cmd/compile: move some ONAME-specific flags from Node to Name
The IsClosureVar, IsOutputParamHeapAddr, Assigned, Addrtaken,
InlFormal, and InlLocal flags are only interesting for ONAME nodes, so
it's better to set these flags on Name.flags instead of Node.flags.
Two caveats though:
1. Previously, we would set Assigned and Addrtaken on the entire
expression tree involved in an assignment or addressing operation.
However, the rest of the compiler only actually cares about knowing
whether the underlying ONAME (if any) was assigned/addressed.
2. This actually requires bumping Name.flags from bitset8 to bitset16,
whereas it doesn't allow shrinking Node.flags any. However, Name has
some trailing padding bytes, so expanding Name.flags doesn't cost any
memory.
Passes toolstash-check.
Change-Id: I7775d713566a38d5b9723360b1659b79391744c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/200898
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2019-10-12 16:21:55 -07:00
|
|
|
if n.Name.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
|
|
|
}
|
cmd/compile: move some ONAME-specific flags from Node to Name
The IsClosureVar, IsOutputParamHeapAddr, Assigned, Addrtaken,
InlFormal, and InlLocal flags are only interesting for ONAME nodes, so
it's better to set these flags on Name.flags instead of Node.flags.
Two caveats though:
1. Previously, we would set Assigned and Addrtaken on the entire
expression tree involved in an assignment or addressing operation.
However, the rest of the compiler only actually cares about knowing
whether the underlying ONAME (if any) was assigned/addressed.
2. This actually requires bumping Name.flags from bitset8 to bitset16,
whereas it doesn't allow shrinking Node.flags any. However, Name has
some trailing padding bytes, so expanding Name.flags doesn't cost any
memory.
Passes toolstash-check.
Change-Id: I7775d713566a38d5b9723360b1659b79391744c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/200898
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2019-10-12 16:21:55 -07:00
|
|
|
if n.Name.IsOutputParamHeapAddr() {
|
2018-09-07 14:55:09 -07:00
|
|
|
// This variable will be overwritten early in the function
|
|
|
|
|
// prologue (from the result of a mallocgc) but we need to
|
|
|
|
|
// zero it in case that malloc causes a stack scan.
|
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
|
|
|
}
|
2020-08-08 07:58:04 -07:00
|
|
|
if n.Name.OpenDeferSlot() {
|
|
|
|
|
// Open-coded defer args slots must be live
|
|
|
|
|
// everywhere in a function, since a panic can
|
|
|
|
|
// occur (almost) anywhere. Because it is live
|
|
|
|
|
// everywhere, it must be zeroed on entry.
|
|
|
|
|
livedefer.Set(int32(i))
|
|
|
|
|
// It was already marked as Needzero when created.
|
|
|
|
|
if !n.Name.Needzero() {
|
|
|
|
|
Fatalf("all pointer-containing defer arg slots should have Needzero set")
|
|
|
|
|
}
|
|
|
|
|
}
|
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)
|
2020-10-21 20:15:48 -04:00
|
|
|
lv.livevars = append(lv.livevars, live)
|
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 forward through the basic block instructions and
|
|
|
|
|
// allocate liveness maps for those instructions that need them.
|
2017-03-09 18:32:17 -08:00
|
|
|
for _, v := range b.Values {
|
2020-04-17 17:14:33 -04:00
|
|
|
if !lv.hasStackMap(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
|
|
|
live := bvalloc(nvars)
|
2020-10-21 20:15:48 -04:00
|
|
|
lv.livevars = append(lv.livevars, 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
|
|
|
|
2020-04-17 17:14:33 -04:00
|
|
|
if lv.hasStackMap(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)
|
2020-10-21 20:15:48 -04:00
|
|
|
live.Or(*live, 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)
|
|
|
|
|
if e&varkill != 0 {
|
2020-10-21 20:15:48 -04:00
|
|
|
liveout.Unset(pos)
|
2017-03-22 11:21:35 -07:00
|
|
|
}
|
|
|
|
|
if e&uevar != 0 {
|
2020-10-21 20:15:48 -04:00
|
|
|
liveout.Set(pos)
|
2017-03-22 11:21:35 -07:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
2018-09-07 14:55:09 -07:00
|
|
|
// Check to make sure only input variables are live.
|
|
|
|
|
for i, n := range lv.vars {
|
2020-10-21 20:15:48 -04:00
|
|
|
if !liveout.Get(int32(i)) {
|
2018-09-07 14:55:09 -07:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if n.Class() == PPARAM {
|
|
|
|
|
continue // ok
|
|
|
|
|
}
|
|
|
|
|
Fatalf("bad live variable at entry of %v: %L", lv.fn.Func.Nname, n)
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
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
|
|
|
}
|
2020-08-08 07:58:04 -07:00
|
|
|
|
|
|
|
|
// If we have an open-coded deferreturn call, make a liveness map for it.
|
|
|
|
|
if lv.fn.Func.OpenCodedDeferDisallowed() {
|
2020-10-21 20:15:48 -04:00
|
|
|
lv.livenessMap.deferreturn = LivenessDontCare
|
2020-08-08 07:58:04 -07:00
|
|
|
} else {
|
|
|
|
|
lv.livenessMap.deferreturn = LivenessIndex{
|
|
|
|
|
stackMapIndex: lv.stackMapSet.add(livedefer),
|
|
|
|
|
isUnsafePoint: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
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)) {
|
2018-11-03 09:33:31 -07:00
|
|
|
lv.f.Fatalf("%v %L recorded as live on entry", lv.fn.Func.Nname, n)
|
2017-03-09 12:15:41 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:48:46 -04:00
|
|
|
// Compact coalesces identical bitmaps from lv.livevars into the sets
|
2020-10-21 20:15:48 -04:00
|
|
|
// lv.stackMapSet.
|
2018-04-20 15:48:46 -04:00
|
|
|
//
|
|
|
|
|
// 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) {
|
|
|
|
|
pos := 0
|
|
|
|
|
if b == lv.f.Entry {
|
|
|
|
|
// Handle entry stack map.
|
2020-10-21 20:15:48 -04:00
|
|
|
lv.stackMapSet.add(lv.livevars[0])
|
2018-04-20 15:48:46 -04:00
|
|
|
pos++
|
|
|
|
|
}
|
|
|
|
|
for _, v := range b.Values {
|
2020-10-21 20:15:48 -04:00
|
|
|
hasStackMap := lv.hasStackMap(v)
|
|
|
|
|
isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID))
|
|
|
|
|
idx := LivenessIndex{StackMapDontCare, isUnsafePoint}
|
|
|
|
|
if hasStackMap {
|
|
|
|
|
idx.stackMapIndex = lv.stackMapSet.add(lv.livevars[pos])
|
2018-04-20 15:48:46 -04:00
|
|
|
pos++
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2020-10-21 20:15:48 -04:00
|
|
|
if hasStackMap || isUnsafePoint {
|
|
|
|
|
lv.livenessMap.set(v, idx)
|
|
|
|
|
}
|
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())
|
2020-06-12 13:48:26 -04:00
|
|
|
} else if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil {
|
|
|
|
|
fn := sym.Fn.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)
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-21 20:15:48 -04:00
|
|
|
func (lv *Liveness) printbvec(printed bool, name string, live bvec) bool {
|
|
|
|
|
if live.IsEmpty() {
|
2018-02-26 20:56:58 -05:00
|
|
|
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 {
|
2020-10-21 20:15:48 -04:00
|
|
|
if !live.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
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 20:15:48 -04:00
|
|
|
// printeffect is like printbvec, but for valueEffects.
|
|
|
|
|
func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
|
|
|
|
|
if !x {
|
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)
|
|
|
|
|
}
|
2020-10-21 20:15:48 -04:00
|
|
|
|
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
|
|
|
|
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 {
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
live := lv.stackMaps[0]
|
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())
|
|
|
|
|
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
pcdata := lv.livenessMap.Get(v)
|
2017-03-09 18:32:17 -08:00
|
|
|
|
|
|
|
|
pos, effect := lv.valueEffects(v)
|
2016-03-13 10:23:18 +09:00
|
|
|
printed = false
|
2020-10-21 20:15:48 -04:00
|
|
|
printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0)
|
|
|
|
|
printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 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
|
|
|
|
2020-10-21 20:15:48 -04:00
|
|
|
if pcdata.StackMapValid() {
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
fmt.Printf("\tlive=")
|
|
|
|
|
printed = false
|
|
|
|
|
if pcdata.StackMapValid() {
|
|
|
|
|
live := lv.stackMaps[pcdata.stackMapIndex]
|
|
|
|
|
for j, n := range lv.vars {
|
|
|
|
|
if !live.Get(int32(j)) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if printed {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%v", n)
|
|
|
|
|
printed = true
|
|
|
|
|
}
|
2017-03-09 18:32:17 -08:00
|
|
|
}
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
fmt.Printf("\n")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
|
|
|
|
|
if pcdata.isUnsafePoint {
|
|
|
|
|
fmt.Printf("\tunsafe-point\n")
|
2018-02-26 20:56:58 -05:00
|
|
|
}
|
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)
|
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.
|
2020-10-21 20:15:48 -04:00
|
|
|
func (lv *Liveness) emit() (argsSym, liveSym *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
|
|
|
|
|
|
2018-10-29 18:21:00 -04:00
|
|
|
// Temporary symbols for encoding bitmaps.
|
2020-10-21 20:15:48 -04:00
|
|
|
var argsSymTmp, liveSymTmp obj.LSym
|
2018-10-29 18:21:00 -04:00
|
|
|
|
2018-04-02 14:21:27 -07:00
|
|
|
args := bvalloc(int32(maxArgs / int64(Widthptr)))
|
2018-10-29 18:21:00 -04:00
|
|
|
aoff := duint32(&argsSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
|
|
|
|
aoff = duint32(&argsSymTmp, 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-10-29 18:21:00 -04:00
|
|
|
loff := duint32(&liveSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
|
|
|
|
loff = duint32(&liveSymTmp, 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
|
|
|
|
2018-10-29 18:21:00 -04:00
|
|
|
aoff = dbvec(&argsSymTmp, aoff, args)
|
|
|
|
|
loff = dbvec(&liveSymTmp, loff, locals)
|
2016-03-28 22:27:36 +13:00
|
|
|
}
|
2017-02-24 16:02:31 -08:00
|
|
|
|
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.
|
2018-10-29 18:21:00 -04:00
|
|
|
//
|
|
|
|
|
// These symbols will be added to Ctxt.Data by addGCLocals
|
|
|
|
|
// after parallel compilation is done.
|
|
|
|
|
makeSym := func(tmpSym *obj.LSym) *obj.LSym {
|
|
|
|
|
return Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) {
|
|
|
|
|
lsym.P = tmpSym.P
|
2020-07-12 17:15:35 -04:00
|
|
|
lsym.Set(obj.AttrContentAddressable, true)
|
2018-10-29 18:21:00 -04:00
|
|
|
})
|
|
|
|
|
}
|
2020-10-21 20:15:48 -04:00
|
|
|
return makeSym(&argsSymTmp), makeSym(&liveSymTmp)
|
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-10-29 18:21:00 -04:00
|
|
|
func liveness(e *ssafn, f *ssa.Func, pp *Progs) 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()
|
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 {
|
cmd/compile: fix unsafe-points with stack maps
The compiler currently conflates whether a Value has a stack map with
whether it's an unsafe point. For the most part, unsafe-points don't
have stack maps, so this is mostly fine, but call instructions can be
both an unsafe-point *and* have a stack map. For example, none of the
instructions in a nosplit function should be preemptible, but calls
must still have stack maps in case the called function grows the stack
or get preempted.
Currently, the compiler can't distinguish this case, so calls in
nosplit functions are marked as safe-points just because they have
stack maps. This is particularly problematic if a nosplit function
calls another nosplit function, since this can introduce a preemption
point where there should be none.
We realized this was a problem for split-stack prologues a while back,
and CL 207349 changed the encoding of unsafe-points to use the
register map index instead of the stack map index so we could record
both a stack map and an unsafe-point at the same instruction. But this
was never extended into the compiler.
This CL fixes this problem in the compiler. We make LivenessIndex
slightly more abstract by separating unsafe-point marks from stack and
register map indexes. We map this to the PCDATA encoding later when
producing Progs. This isn't enough to fix the whole problem for
nosplit functions, because obj still adds prologues and marks those as
preemptible, but it's a step in the right direction.
I checked this CL by comparing maps before and after this change in
the runtime and net/http. In net/http, unsafe-points match exactly; at
anything that isn't an unsafe-point, both the stack and register maps
are unchanged by this CL. In the runtime, at every point that was a
safe-point before this change, the stack maps agree (and mostly the
runtime doesn't have register maps at all now). In both, all CALLs
(except write barrier calls) have stack maps.
For #36365.
Change-Id: I066628938b02e78be5c81a6614295bcf7cc566c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/230541
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-04-21 14:23:04 -04:00
|
|
|
if idx := lv.livenessMap.Get(val); idx.StackMapValid() {
|
2018-04-21 15:40:56 -04:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2018-04-21 16:15:41 -04:00
|
|
|
// Update the function cache.
|
|
|
|
|
{
|
|
|
|
|
cache := f.Cache.Liveness.(*livenessFuncCache)
|
|
|
|
|
if cap(lv.be) < 2000 { // Threshold from ssa.Cache slices.
|
|
|
|
|
for i := range lv.be {
|
|
|
|
|
lv.be[i] = BlockEffects{}
|
|
|
|
|
}
|
|
|
|
|
cache.be = lv.be
|
|
|
|
|
}
|
2020-04-27 19:18:48 -04:00
|
|
|
if len(lv.livenessMap.vals) < 2000 {
|
2018-04-21 16:15:41 -04:00
|
|
|
cache.livenessMap = lv.livenessMap
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
// Emit the live pointer map data structures
|
2019-01-22 10:08:10 -08:00
|
|
|
ls := e.curfn.Func.lsym
|
2020-07-19 00:30:12 -04:00
|
|
|
fninfo := ls.Func()
|
2020-10-21 20:15:48 -04:00
|
|
|
fninfo.GCArgs, fninfo.GCLocals = lv.emit()
|
2019-01-22 10:08:10 -08:00
|
|
|
|
|
|
|
|
p := pp.Prog(obj.AFUNCDATA)
|
|
|
|
|
Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Name = obj.NAME_EXTERN
|
2020-07-19 00:30:12 -04:00
|
|
|
p.To.Sym = fninfo.GCArgs
|
2019-01-22 10:08:10 -08:00
|
|
|
|
|
|
|
|
p = pp.Prog(obj.AFUNCDATA)
|
|
|
|
|
Addrconst(&p.From, objabi.FUNCDATA_LocalsPointerMaps)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Name = obj.NAME_EXTERN
|
2020-07-19 00:30:12 -04:00
|
|
|
p.To.Sym = fninfo.GCLocals
|
2019-01-22 10:08:10 -08:00
|
|
|
|
2018-03-22 12:04:51 -04:00
|
|
|
return lv.livenessMap
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2019-05-27 15:58:24 +07:00
|
|
|
|
2019-09-19 17:07:00 +02:00
|
|
|
// isfat reports whether a variable of type t needs multiple assignments to initialize.
|
|
|
|
|
// For example:
|
|
|
|
|
//
|
|
|
|
|
// type T struct { x, y int }
|
|
|
|
|
// x := T{x: 0, y: 1}
|
|
|
|
|
//
|
|
|
|
|
// Then we need:
|
|
|
|
|
//
|
|
|
|
|
// var t T
|
|
|
|
|
// t.x = 0
|
|
|
|
|
// t.y = 1
|
|
|
|
|
//
|
|
|
|
|
// to fully initialize t.
|
2019-05-27 15:58:24 +07:00
|
|
|
func isfat(t *types.Type) bool {
|
|
|
|
|
if t != nil {
|
|
|
|
|
switch t.Etype {
|
2019-09-04 11:11:22 +07:00
|
|
|
case TSLICE, TSTRING,
|
2019-05-27 15:58:24 +07:00
|
|
|
TINTER: // maybe remove later
|
|
|
|
|
return true
|
2019-09-04 11:11:22 +07:00
|
|
|
case TARRAY:
|
|
|
|
|
// Array of 1 element, check if element is fat
|
|
|
|
|
if t.NumElem() == 1 {
|
|
|
|
|
return isfat(t.Elem())
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
case TSTRUCT:
|
|
|
|
|
// Struct with 1 field, check if field is fat
|
|
|
|
|
if t.NumFields() == 1 {
|
|
|
|
|
return isfat(t.Field(0).Type)
|
|
|
|
|
}
|
|
|
|
|
return true
|
2019-05-27 15:58:24 +07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|