mirror of
https://github.com/golang/go
synced 2024-11-11 21:30:23 -07:00
cmd/compile: assign and preserve statement boundaries.
A new pass run after ssa building (before any other optimization) identifies the "first" ssa node for each statement. Other "noise" nodes are tagged as being never appropriate for a statement boundary (e.g., VarKill, VarDef, Phi). Rewrite, deadcode, cse, and nilcheck are modified to move the statement boundaries forward whenever possible if a boundary-tagged ssa value is removed; never-boundary nodes are ignored in this search (some operations involving constants are also tagged as never-boundary and also ignored because they are likely to be moved or removed during optimization). Code generation treats all nodes except those explicitly marked as statement boundaries as "not statement" nodes, and floats statement boundaries to the beginning of each same-line run of instructions found within a basic block. Line number html conversion was modified to make statement boundary nodes a bit more obvious by prepending a "+". The code in fuse.go that glued together the value slices of two blocks produced a result that depended on the former capacities (not lengths) of the two slices. This causes differences in the 386 bootstrap, and also can sometimes put values into an order that does a worse job of preserving statement boundaries when values are removed. Portions of two delve tests that had caught problems were incorporated into ssa/debug_test.go. There are some opportunities to do better with optimized code, but the next-ing is not lying or overly jumpy. Over 4 CLs, compilebench geomean measured binary size increase of 3.5% and compile user time increase of 3.8% (this is after optimization to reuse a sparse map instead of creating multiple maps.) This CL worsens the optimized-debugging experience with Delve; we need to work with the delve team so that they can use the is_stmt marks that we're emitting now. The reference output changes from time to time depending on other changes in the compiler, sometimes better, sometimes worse. This CL now includes a test ensuring that 99+% of the lines in the Go command itself (a handy optimized binary) include is_stmt markers. Change-Id: I359c94e06843f1eb41f9da437bd614885aa9644a Reviewed-on: https://go-review.googlesource.com/102435 Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
c06f027520
commit
c2c1822b12
@ -113,6 +113,17 @@ func (pp *Progs) Prog(as obj.As) *obj.Prog {
|
||||
|
||||
p.As = as
|
||||
p.Pos = pp.pos
|
||||
if pp.pos.IsStmt() == src.PosIsStmt {
|
||||
// Clear IsStmt for later Progs at this pos provided that as generates executable code.
|
||||
switch as {
|
||||
// TODO: this is an artifact of how funcpctab combines information for instructions at a single PC.
|
||||
// Should try to fix it there. There is a similar workaround in *SSAGenState.Prog in gc/ssa.go.
|
||||
case obj.APCDATA, obj.AFUNCDATA:
|
||||
// is_stmt does not work for these; it DOES for ANOP
|
||||
return p
|
||||
}
|
||||
pp.pos = pp.pos.WithNotStmt()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
|
@ -787,7 +787,7 @@ func (s *state) stmt(n *Node) {
|
||||
}
|
||||
|
||||
b := s.endBlock()
|
||||
b.Pos = s.lastPos // Do this even if b is an empty block.
|
||||
b.Pos = s.lastPos.WithIsStmt() // Do this even if b is an empty block.
|
||||
b.AddEdgeTo(lab.target)
|
||||
|
||||
case OAS:
|
||||
@ -935,7 +935,7 @@ func (s *state) stmt(n *Node) {
|
||||
case ORETURN:
|
||||
s.stmtList(n.List)
|
||||
b := s.exit()
|
||||
b.Pos = s.lastPos
|
||||
b.Pos = s.lastPos.WithIsStmt()
|
||||
|
||||
case ORETJMP:
|
||||
s.stmtList(n.List)
|
||||
@ -966,7 +966,7 @@ func (s *state) stmt(n *Node) {
|
||||
}
|
||||
|
||||
b := s.endBlock()
|
||||
b.Pos = s.lastPos // Do this even if b is an empty block.
|
||||
b.Pos = s.lastPos.WithIsStmt() // Do this even if b is an empty block.
|
||||
b.AddEdgeTo(to)
|
||||
|
||||
case OFOR, OFORUNTIL:
|
||||
@ -4696,11 +4696,30 @@ type SSAGenState struct {
|
||||
// Map from GC safe points to stack map index, generated by
|
||||
// liveness analysis.
|
||||
stackMapIndex map[*ssa.Value]int
|
||||
|
||||
// lineRunStart records the beginning of the current run of instructions
|
||||
// within a single block sharing the same line number
|
||||
// Used to move statement marks to the beginning of such runs.
|
||||
lineRunStart *obj.Prog
|
||||
}
|
||||
|
||||
// Prog appends a new Prog.
|
||||
func (s *SSAGenState) Prog(as obj.As) *obj.Prog {
|
||||
return s.pp.Prog(as)
|
||||
p := s.pp.Prog(as)
|
||||
switch as {
|
||||
case obj.APCDATA, obj.AFUNCDATA:
|
||||
// is_stmt does not work for these; it DOES for ANOP
|
||||
return p
|
||||
}
|
||||
// Float a statement start to the beginning of any same-line run.
|
||||
// lineRunStart is reset at block boundaries, which appears to work well.
|
||||
if s.lineRunStart == nil || s.lineRunStart.Pos.Line() != p.Pos.Line() {
|
||||
s.lineRunStart = p
|
||||
} else if p.Pos.IsStmt() == src.PosIsStmt {
|
||||
s.lineRunStart.Pos = s.lineRunStart.Pos.WithIsStmt()
|
||||
p.Pos = p.Pos.WithNotStmt()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Pc returns the current Prog.
|
||||
@ -4723,25 +4742,27 @@ func (s *SSAGenState) Br(op obj.As, target *ssa.Block) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
// DebugFriendlySetPos sets the position subject to heuristics
|
||||
// DebugFriendlySetPos adjusts Pos.IsStmt subject to heuristics
|
||||
// that reduce "jumpy" line number churn when debugging.
|
||||
// Spill/fill/copy instructions from the register allocator,
|
||||
// phi functions, and instructions with a no-pos position
|
||||
// are examples of instructions that can cause churn.
|
||||
func (s *SSAGenState) DebugFriendlySetPosFrom(v *ssa.Value) {
|
||||
// The two choices here are either to leave lineno unchanged,
|
||||
// or to explicitly set it to src.NoXPos. Leaving it unchanged
|
||||
// (reusing the preceding line number) produces slightly better-
|
||||
// looking assembly language output from the compiler, and is
|
||||
// expected by some already-existing tests.
|
||||
// The debug information appears to be the same in either case
|
||||
switch v.Op {
|
||||
case ssa.OpPhi, ssa.OpCopy, ssa.OpLoadReg, ssa.OpStoreReg:
|
||||
// leave the position unchanged from beginning of block
|
||||
// or previous line number.
|
||||
// These are not statements
|
||||
s.SetPos(v.Pos.WithNotStmt())
|
||||
default:
|
||||
if v.Pos != src.NoXPos {
|
||||
s.SetPos(v.Pos)
|
||||
p := v.Pos
|
||||
if p != src.NoXPos {
|
||||
// If the position is defined, update the position.
|
||||
// Also convert default IsStmt to NotStmt; only
|
||||
// explicit statement boundaries should appear
|
||||
// in the generated code.
|
||||
if p.IsStmt() != src.PosIsStmt {
|
||||
p = p.WithNotStmt()
|
||||
}
|
||||
s.SetPos(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4788,8 +4809,9 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
// debuggers may attribute it to previous function in program.
|
||||
firstPos := src.NoXPos
|
||||
for _, v := range f.Entry.Values {
|
||||
if v.Op != ssa.OpArg && v.Op != ssa.OpVarDef && v.Pos.IsStmt() != src.PosNotStmt { // TODO will be == src.PosIsStmt in pending CL, more accurate
|
||||
firstPos = v.Pos.WithIsStmt()
|
||||
if v.Pos.IsStmt() == src.PosIsStmt {
|
||||
firstPos = v.Pos
|
||||
v.Pos = firstPos.WithDefaultStmt()
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -4797,7 +4819,7 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
// Emit basic blocks
|
||||
for i, b := range f.Blocks {
|
||||
s.bstart[b.ID] = s.pp.next
|
||||
|
||||
s.lineRunStart = nil
|
||||
// Emit values in block
|
||||
thearch.SSAMarkMoves(&s, b)
|
||||
for _, v := range b.Values {
|
||||
@ -4898,9 +4920,12 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve branches
|
||||
// Resolove branchers, and relax DefaultStmt into NotStmt
|
||||
for _, br := range s.Branches {
|
||||
br.P.To.Val = s.bstart[br.B.ID]
|
||||
if br.P.Pos.IsStmt() != src.PosIsStmt {
|
||||
br.P.Pos = br.P.Pos.WithNotStmt()
|
||||
}
|
||||
}
|
||||
|
||||
if logProgs {
|
||||
|
112
src/cmd/compile/internal/ssa/biasedsparsemap.go
Normal file
112
src/cmd/compile/internal/ssa/biasedsparsemap.go
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"cmd/internal/src"
|
||||
"math"
|
||||
)
|
||||
|
||||
// A biasedSparseMap is a sparseMap for integers between J and K inclusive,
|
||||
// where J might be somewhat larger than zero (and K-J is probably much smaller than J).
|
||||
// (The motivating use case is the line numbers of statements for a single function.)
|
||||
// Not all features of a SparseMap are exported, and it is also easy to treat a
|
||||
// biasedSparseMap like a SparseSet.
|
||||
type biasedSparseMap struct {
|
||||
s *sparseMap
|
||||
first int
|
||||
}
|
||||
|
||||
// newBiasedSparseMap returns a new biasedSparseMap for values between first and last, inclusive.
|
||||
func newBiasedSparseMap(first, last int) *biasedSparseMap {
|
||||
if first > last {
|
||||
return &biasedSparseMap{first: math.MaxInt32, s: nil}
|
||||
}
|
||||
return &biasedSparseMap{first: first, s: newSparseMap(1 + last - first)}
|
||||
}
|
||||
|
||||
// cap returns one more than the largest key valid for s
|
||||
func (s *biasedSparseMap) cap() int {
|
||||
if s.s == nil {
|
||||
return 0
|
||||
}
|
||||
return s.s.cap() + int(s.first)
|
||||
}
|
||||
|
||||
// size returns the number of entries stored in s
|
||||
func (s *biasedSparseMap) size() int {
|
||||
if s.s == nil {
|
||||
return 0
|
||||
}
|
||||
return s.s.size()
|
||||
}
|
||||
|
||||
// contains returns whether x is a key in s
|
||||
func (s *biasedSparseMap) contains(x uint) bool {
|
||||
if s.s == nil {
|
||||
return false
|
||||
}
|
||||
if int(x) < s.first {
|
||||
return false
|
||||
}
|
||||
if int(x) >= s.cap() {
|
||||
return false
|
||||
}
|
||||
return s.s.contains(ID(int(x) - s.first))
|
||||
}
|
||||
|
||||
// get returns the value s maps for key x, or -1 if
|
||||
// x is not mapped or is out of range for s.
|
||||
func (s *biasedSparseMap) get(x uint) int32 {
|
||||
if s.s == nil {
|
||||
return -1
|
||||
}
|
||||
if int(x) < s.first {
|
||||
return -1
|
||||
}
|
||||
if int(x) >= s.cap() {
|
||||
return -1
|
||||
}
|
||||
return s.s.get(ID(int(x) - s.first))
|
||||
}
|
||||
|
||||
// getEntry returns the i'th key and value stored in s,
|
||||
// where 0 <= i < s.size()
|
||||
func (s *biasedSparseMap) getEntry(i int) (x uint, v int32) {
|
||||
e := s.s.contents()[i]
|
||||
x = uint(int(e.key) + s.first)
|
||||
v = e.val
|
||||
return
|
||||
}
|
||||
|
||||
// add inserts x->0 into s, provided that x is in the range of keys stored in s.
|
||||
func (s *biasedSparseMap) add(x uint) {
|
||||
if int(x) < s.first || int(x) >= s.cap() {
|
||||
return
|
||||
}
|
||||
s.s.set(ID(int(x)-s.first), 0, src.NoXPos)
|
||||
}
|
||||
|
||||
// add inserts x->v into s, provided that x is in the range of keys stored in s.
|
||||
func (s *biasedSparseMap) set(x uint, v int32) {
|
||||
if int(x) < s.first || int(x) >= s.cap() {
|
||||
return
|
||||
}
|
||||
s.s.set(ID(int(x)-s.first), v, src.NoXPos)
|
||||
}
|
||||
|
||||
// remove removes key x from s.
|
||||
func (s *biasedSparseMap) remove(x uint) {
|
||||
if int(x) < s.first || int(x) >= s.cap() {
|
||||
return
|
||||
}
|
||||
s.s.remove(ID(int(x) - s.first))
|
||||
}
|
||||
|
||||
func (s *biasedSparseMap) clear() {
|
||||
if s.s != nil {
|
||||
s.s.clear()
|
||||
}
|
||||
}
|
@ -356,6 +356,7 @@ commas. For example:
|
||||
// list of passes for the compiler
|
||||
var passes = [...]pass{
|
||||
// TODO: combine phielim and copyelim into a single pass?
|
||||
{name: "number lines", fn: numberLines, required: true},
|
||||
{name: "early phielim", fn: phielim},
|
||||
{name: "early copyelim", fn: copyelim},
|
||||
{name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt
|
||||
|
@ -6,6 +6,7 @@ package ssa
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
@ -233,6 +234,15 @@ func cse(f *Func) {
|
||||
for _, v := range b.Values {
|
||||
for i, w := range v.Args {
|
||||
if x := rewrite[w.ID]; x != nil {
|
||||
if w.Pos.IsStmt() == src.PosIsStmt {
|
||||
// about to lose a statement marker, w
|
||||
// w is an input to v; if they're in the same block
|
||||
// and the same line, v is a good-enough new statement boundary.
|
||||
if w.Block == v.Block && w.Pos.Line() == v.Pos.Line() {
|
||||
v.Pos = v.Pos.WithIsStmt()
|
||||
w.Pos = w.Pos.WithNotStmt()
|
||||
} // TODO and if this fails?
|
||||
}
|
||||
v.SetArg(i, x)
|
||||
rewrites++
|
||||
}
|
||||
|
@ -4,10 +4,14 @@
|
||||
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
// findlive returns the reachable blocks and live values in f.
|
||||
func findlive(f *Func) (reachable []bool, live []bool) {
|
||||
reachable = ReachableBlocks(f)
|
||||
live = liveValues(f, reachable)
|
||||
live, _ = liveValues(f, reachable)
|
||||
return
|
||||
}
|
||||
|
||||
@ -40,10 +44,12 @@ func ReachableBlocks(f *Func) []bool {
|
||||
return reachable
|
||||
}
|
||||
|
||||
// liveValues returns the live values in f.
|
||||
// liveValues returns the live values in f and a list of values that are eligible
|
||||
// to be statements in reversed data flow order.
|
||||
// The second result is used to help conserve statement boundaries for debugging.
|
||||
// reachable is a map from block ID to whether the block is reachable.
|
||||
func liveValues(f *Func, reachable []bool) []bool {
|
||||
live := make([]bool, f.NumValues())
|
||||
func liveValues(f *Func, reachable []bool) (live []bool, liveOrderStmts []*Value) {
|
||||
live = make([]bool, f.NumValues())
|
||||
|
||||
// After regalloc, consider all values to be live.
|
||||
// See the comment at the top of regalloc.go and in deadcode for details.
|
||||
@ -51,7 +57,7 @@ func liveValues(f *Func, reachable []bool) []bool {
|
||||
for i := range live {
|
||||
live[i] = true
|
||||
}
|
||||
return live
|
||||
return
|
||||
}
|
||||
|
||||
// Find all live values
|
||||
@ -66,16 +72,25 @@ func liveValues(f *Func, reachable []bool) []bool {
|
||||
if v := b.Control; v != nil && !live[v.ID] {
|
||||
live[v.ID] = true
|
||||
q = append(q, v)
|
||||
if v.Pos.IsStmt() != src.PosNotStmt {
|
||||
liveOrderStmts = append(liveOrderStmts, v)
|
||||
}
|
||||
}
|
||||
for _, v := range b.Values {
|
||||
if (opcodeTable[v.Op].call || opcodeTable[v.Op].hasSideEffects) && !live[v.ID] {
|
||||
live[v.ID] = true
|
||||
q = append(q, v)
|
||||
if v.Pos.IsStmt() != src.PosNotStmt {
|
||||
liveOrderStmts = append(liveOrderStmts, v)
|
||||
}
|
||||
}
|
||||
if v.Type.IsVoid() && !live[v.ID] {
|
||||
// The only Void ops are nil checks. We must keep these.
|
||||
live[v.ID] = true
|
||||
q = append(q, v)
|
||||
if v.Pos.IsStmt() != src.PosNotStmt {
|
||||
liveOrderStmts = append(liveOrderStmts, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,11 +107,14 @@ func liveValues(f *Func, reachable []bool) []bool {
|
||||
if !live[x.ID] {
|
||||
live[x.ID] = true
|
||||
q = append(q, x) // push
|
||||
if x.Pos.IsStmt() != src.PosNotStmt {
|
||||
liveOrderStmts = append(liveOrderStmts, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return live
|
||||
return
|
||||
}
|
||||
|
||||
// deadcode removes dead code from f.
|
||||
@ -144,7 +162,7 @@ func deadcode(f *Func) {
|
||||
copyelim(f)
|
||||
|
||||
// Find live values.
|
||||
live := liveValues(f, reachable)
|
||||
live, order := liveValues(f, reachable)
|
||||
|
||||
// Remove dead & duplicate entries from namedValues map.
|
||||
s := f.newSparseSet(f.NumValues())
|
||||
@ -177,18 +195,43 @@ func deadcode(f *Func) {
|
||||
}
|
||||
f.Names = f.Names[:i]
|
||||
|
||||
// Unlink values.
|
||||
for _, b := range f.Blocks {
|
||||
pendingLines := f.cachedLineStarts // Holds statement boundaries that need to be moved to a new value/block
|
||||
pendingLines.clear()
|
||||
|
||||
// Unlink values and conserve statement boundaries
|
||||
for i, b := range f.Blocks {
|
||||
if !reachable[b.ID] {
|
||||
// TODO what if control is statement boundary? Too late here.
|
||||
b.SetControl(nil)
|
||||
}
|
||||
for _, v := range b.Values {
|
||||
if !live[v.ID] {
|
||||
v.resetArgs()
|
||||
if v.Pos.IsStmt() == src.PosIsStmt && reachable[b.ID] {
|
||||
pendingLines.set(v.Pos.Line(), int32(i)) // TODO could be more than one pos for a line
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find new homes for lost lines -- require earliest in data flow with same line that is also in same block
|
||||
for i := len(order) - 1; i >= 0; i-- {
|
||||
w := order[i]
|
||||
if j := pendingLines.get(w.Pos.Line()); j > -1 && f.Blocks[j] == w.Block {
|
||||
w.Pos = w.Pos.WithIsStmt()
|
||||
pendingLines.remove(w.Pos.Line())
|
||||
}
|
||||
}
|
||||
|
||||
// Any boundary that failed to match a live value can move to a block end
|
||||
for i := 0; i < pendingLines.size(); i++ {
|
||||
l, bi := pendingLines.getEntry(i)
|
||||
b := f.Blocks[bi]
|
||||
if b.Pos.Line() == l {
|
||||
b.Pos = b.Pos.WithIsStmt()
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dead values from blocks' value list. Return dead
|
||||
// values to the allocator.
|
||||
for _, b := range f.Blocks {
|
||||
|
@ -56,13 +56,13 @@ type Func struct {
|
||||
freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil.
|
||||
freeBlocks *Block // free Blocks linked by succstorage[0].b. All other fields except ID are 0/nil.
|
||||
|
||||
cachedPostorder []*Block // cached postorder traversal
|
||||
cachedIdom []*Block // cached immediate dominators
|
||||
cachedSdom SparseTree // cached dominator tree
|
||||
cachedLoopnest *loopnest // cached loop nest information
|
||||
|
||||
auxmap auxmap // map from aux values to opaque ids used by CSE
|
||||
cachedPostorder []*Block // cached postorder traversal
|
||||
cachedIdom []*Block // cached immediate dominators
|
||||
cachedSdom SparseTree // cached dominator tree
|
||||
cachedLoopnest *loopnest // cached loop nest information
|
||||
cachedLineStarts *biasedSparseMap // cached map/set of line numbers to integers
|
||||
|
||||
auxmap auxmap // map from aux values to opaque ids used by CSE
|
||||
constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
|
||||
}
|
||||
|
||||
@ -149,6 +149,9 @@ func (f *Func) newValue(op Op, t *types.Type, b *Block, pos src.XPos) *Value {
|
||||
v.Op = op
|
||||
v.Type = t
|
||||
v.Block = b
|
||||
if notStmtBoundary(op) {
|
||||
pos = pos.WithNotStmt()
|
||||
}
|
||||
v.Pos = pos
|
||||
b.Values = append(b.Values, v)
|
||||
return v
|
||||
@ -176,6 +179,9 @@ func (f *Func) newValueNoBlock(op Op, t *types.Type, pos src.XPos) *Value {
|
||||
v.Op = op
|
||||
v.Type = t
|
||||
v.Block = nil // caller must fix this.
|
||||
if notStmtBoundary(op) {
|
||||
pos = pos.WithNotStmt()
|
||||
}
|
||||
v.Pos = pos
|
||||
return v
|
||||
}
|
||||
|
@ -152,6 +152,7 @@ func (c *Conf) Fun(entry string, blocs ...bloc) fun {
|
||||
// But not both.
|
||||
f.Cache = new(Cache)
|
||||
f.pass = &emptyPass
|
||||
f.cachedLineStarts = newBiasedSparseMap(0, 100)
|
||||
|
||||
blocks := make(map[string]*Block)
|
||||
values := make(map[string]*Value)
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
// fuse simplifies control flow by joining basic blocks.
|
||||
func fuse(f *Func) {
|
||||
for changed := true; changed; {
|
||||
@ -121,6 +125,25 @@ func fuseBlockPlain(b *Block) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// If a block happened to end in a statement marker,
|
||||
// try to preserve it.
|
||||
if b.Pos.IsStmt() == src.PosIsStmt {
|
||||
l := b.Pos.Line()
|
||||
for _, v := range c.Values {
|
||||
if v.Pos.IsStmt() == src.PosNotStmt {
|
||||
continue
|
||||
}
|
||||
if l == v.Pos.Line() {
|
||||
v.Pos = v.Pos.WithIsStmt()
|
||||
l = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
if l != 0 && c.Pos.Line() == l {
|
||||
c.Pos = c.Pos.WithIsStmt()
|
||||
}
|
||||
}
|
||||
|
||||
// move all of b's values to c.
|
||||
for _, v := range b.Values {
|
||||
v.Block = c
|
||||
@ -128,8 +151,25 @@ func fuseBlockPlain(b *Block) bool {
|
||||
// Use whichever value slice is larger, in the hopes of avoiding growth.
|
||||
// However, take care to avoid c.Values pointing to b.valstorage.
|
||||
// See golang.org/issue/18602.
|
||||
// It's important to keep the elements in the same order; maintenance of
|
||||
// debugging information depends on the order of *Values in Blocks.
|
||||
// This can also cause changes in the order (which may affect other
|
||||
// optimizations and possibly compiler output) for 32-vs-64 bit compilation
|
||||
// platforms (word size affects allocation bucket size affects slice size).
|
||||
if cap(c.Values) >= cap(b.Values) || len(b.Values) <= len(b.valstorage) {
|
||||
c.Values = append(c.Values, b.Values...)
|
||||
bl := len(b.Values)
|
||||
cl := len(c.Values)
|
||||
if cap(c.Values) < bl+cl {
|
||||
// reallocate
|
||||
t := make([]*Value, 0, bl+cl)
|
||||
t = append(t, b.Values...)
|
||||
c.Values = append(t, c.Values...)
|
||||
} else {
|
||||
// in place.
|
||||
c.Values = c.Values[0 : bl+cl]
|
||||
copy(c.Values[bl:], c.Values)
|
||||
copy(c.Values, b.Values)
|
||||
}
|
||||
} else {
|
||||
c.Values = append(b.Values, c.Values...)
|
||||
}
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
// nilcheckelim eliminates unnecessary nil checks.
|
||||
// runs on machine-independent code.
|
||||
func nilcheckelim(f *Func) {
|
||||
@ -103,6 +107,9 @@ func nilcheckelim(f *Func) {
|
||||
// Next, order values in the current block w.r.t. stores.
|
||||
b.Values = storeOrder(b.Values, sset, storeNumber)
|
||||
|
||||
pendingLines := f.cachedLineStarts // Holds statement boundaries that need to be moved to a new value/block
|
||||
pendingLines.clear()
|
||||
|
||||
// Next, process values in the block.
|
||||
i := 0
|
||||
for _, v := range b.Values {
|
||||
@ -112,6 +119,10 @@ func nilcheckelim(f *Func) {
|
||||
case OpIsNonNil:
|
||||
ptr := v.Args[0]
|
||||
if nonNilValues[ptr.ID] {
|
||||
if v.Pos.IsStmt() == src.PosIsStmt { // Boolean true is a terrible statement boundary.
|
||||
pendingLines.add(v.Pos.Line())
|
||||
v.Pos = v.Pos.WithNotStmt()
|
||||
}
|
||||
// This is a redundant explicit nil check.
|
||||
v.reset(OpConstBool)
|
||||
v.AuxInt = 1 // true
|
||||
@ -125,6 +136,9 @@ func nilcheckelim(f *Func) {
|
||||
if f.fe.Debug_checknil() && v.Pos.Line() > 1 {
|
||||
f.Warnl(v.Pos, "removed nil check")
|
||||
}
|
||||
if v.Pos.IsStmt() == src.PosIsStmt { // About to lose a statement boundary
|
||||
pendingLines.add(v.Pos.Line())
|
||||
}
|
||||
v.reset(OpUnknown)
|
||||
f.freeValue(v)
|
||||
i--
|
||||
@ -134,8 +148,18 @@ func nilcheckelim(f *Func) {
|
||||
// undo that information when this dominator subtree is done.
|
||||
nonNilValues[ptr.ID] = true
|
||||
work = append(work, bp{op: ClearPtr, ptr: ptr})
|
||||
fallthrough // a non-eliminated nil check might be a good place for a statement boundary.
|
||||
default:
|
||||
if pendingLines.contains(v.Pos.Line()) && v.Pos.IsStmt() != src.PosNotStmt {
|
||||
v.Pos = v.Pos.WithIsStmt()
|
||||
pendingLines.remove(v.Pos.Line())
|
||||
}
|
||||
}
|
||||
}
|
||||
if pendingLines.contains(b.Pos.Line()) {
|
||||
b.Pos = b.Pos.WithIsStmt()
|
||||
pendingLines.remove(b.Pos.Line())
|
||||
}
|
||||
for j := i; j < len(b.Values); j++ {
|
||||
b.Values[j] = nil
|
||||
}
|
||||
@ -163,11 +187,15 @@ const minZeroPage = 4096
|
||||
func nilcheckelim2(f *Func) {
|
||||
unnecessary := f.newSparseSet(f.NumValues())
|
||||
defer f.retSparseSet(unnecessary)
|
||||
|
||||
pendingLines := f.cachedLineStarts // Holds statement boundaries that need to be moved to a new value/block
|
||||
|
||||
for _, b := range f.Blocks {
|
||||
// Walk the block backwards. Find instructions that will fault if their
|
||||
// input pointer is nil. Remove nil checks on those pointers, as the
|
||||
// faulting instruction effectively does the nil check for free.
|
||||
unnecessary.clear()
|
||||
pendingLines.clear()
|
||||
// Optimization: keep track of removed nilcheck with smallest index
|
||||
firstToRemove := len(b.Values)
|
||||
for i := len(b.Values) - 1; i >= 0; i-- {
|
||||
@ -176,6 +204,9 @@ func nilcheckelim2(f *Func) {
|
||||
if f.fe.Debug_checknil() && v.Pos.Line() > 1 {
|
||||
f.Warnl(v.Pos, "removed nil check")
|
||||
}
|
||||
if v.Pos.IsStmt() == src.PosIsStmt {
|
||||
pendingLines.add(v.Pos.Line())
|
||||
}
|
||||
v.reset(OpUnknown)
|
||||
firstToRemove = i
|
||||
continue
|
||||
@ -231,10 +262,19 @@ func nilcheckelim2(f *Func) {
|
||||
for j := i; j < len(b.Values); j++ {
|
||||
v := b.Values[j]
|
||||
if v.Op != OpUnknown {
|
||||
if v.Pos.IsStmt() != src.PosNotStmt && pendingLines.contains(v.Pos.Line()) {
|
||||
v.Pos = v.Pos.WithIsStmt()
|
||||
pendingLines.remove(v.Pos.Line())
|
||||
}
|
||||
b.Values[i] = v
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
if pendingLines.contains(b.Pos.Line()) {
|
||||
b.Pos = b.Pos.WithIsStmt()
|
||||
}
|
||||
|
||||
for j := i; j < len(b.Values); j++ {
|
||||
b.Values[j] = nil
|
||||
}
|
||||
|
162
src/cmd/compile/internal/ssa/numberlines.go
Normal file
162
src/cmd/compile/internal/ssa/numberlines.go
Normal file
@ -0,0 +1,162 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"cmd/internal/src"
|
||||
"math"
|
||||
)
|
||||
|
||||
func isPoorStatementOp(op Op) bool {
|
||||
switch op {
|
||||
// Note that Nilcheck often vanishes, but when it doesn't, you'd love to start the statement there
|
||||
// so that a debugger-user sees the stop before the panic, and can examine the value.
|
||||
case OpAddr, OpOffPtr, OpStructSelect, OpConstBool, OpConst8, OpConst16, OpConst32, OpConst64, OpConst32F, OpConst64F:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// nextGoodStatementIndex returns an index at i or later that is believed
|
||||
// to be a good place to start the statement for b. This decision is
|
||||
// based on v's Op, the possibility of a better later operation, and
|
||||
// whether the values following i are the same line as v.
|
||||
// If a better statement index isn't found, then i is returned.
|
||||
func nextGoodStatementIndex(v *Value, i int, b *Block) int {
|
||||
// If the value is the last one in the block, too bad, it will have to do
|
||||
// (this assumes that the value ordering vaguely corresponds to the source
|
||||
// program execution order, which tends to be true directly after ssa is
|
||||
// first built.
|
||||
if i >= len(b.Values)-1 {
|
||||
return i
|
||||
}
|
||||
// Only consider the likely-ephemeral/fragile opcodes expected to vanish in a rewrite.
|
||||
if !isPoorStatementOp(v.Op) {
|
||||
return i
|
||||
}
|
||||
// Look ahead to see what the line number is on the next thing that could be a boundary.
|
||||
for j := i + 1; j < len(b.Values); j++ {
|
||||
if b.Values[j].Pos.IsStmt() == src.PosNotStmt { // ignore non-statements
|
||||
continue
|
||||
}
|
||||
if b.Values[j].Pos.Line() == v.Pos.Line() {
|
||||
return j
|
||||
}
|
||||
return i
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// notStmtBoundary indicates which value opcodes can never be a statement
|
||||
// boundary because they don't correspond to a user's understanding of a
|
||||
// statement boundary. Called from *Value.reset(), and *Func.newValue(),
|
||||
// located here to keep all the statement boundary heuristics in one place.
|
||||
// Note: *Value.reset() filters out OpCopy because of how that is used in
|
||||
// rewrite.
|
||||
func notStmtBoundary(op Op) bool {
|
||||
switch op {
|
||||
case OpCopy, OpPhi, OpVarKill, OpVarDef, OpUnknown, OpFwdRef, OpArg:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func numberLines(f *Func) {
|
||||
po := f.Postorder()
|
||||
endlines := make(map[ID]src.XPos)
|
||||
last := uint(0) // uint follows type of XPos.Line()
|
||||
first := uint(math.MaxInt32) // unsigned, but large valid int when cast
|
||||
note := func(line uint) {
|
||||
if line < first {
|
||||
first = line
|
||||
}
|
||||
if line > last {
|
||||
last = line
|
||||
}
|
||||
}
|
||||
|
||||
// Visit in reverse post order so that all non-loop predecessors come first.
|
||||
for j := len(po) - 1; j >= 0; j-- {
|
||||
b := po[j]
|
||||
// Find the first interesting position and check to see if it differs from any predecessor
|
||||
firstPos := src.NoXPos
|
||||
firstPosIndex := -1
|
||||
if b.Pos.IsStmt() != src.PosNotStmt {
|
||||
note(b.Pos.Line())
|
||||
}
|
||||
for i := 0; i < len(b.Values); i++ {
|
||||
v := b.Values[i]
|
||||
if v.Pos.IsStmt() != src.PosNotStmt {
|
||||
note(v.Pos.Line())
|
||||
// skip ahead to better instruction for this line if possible
|
||||
i = nextGoodStatementIndex(v, i, b)
|
||||
v = b.Values[i]
|
||||
firstPosIndex = i
|
||||
firstPos = v.Pos
|
||||
v.Pos = firstPos.WithDefaultStmt() // default to default
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if firstPosIndex == -1 { // Effectively empty block, check block's own Pos, consider preds.
|
||||
if b.Pos.IsStmt() != src.PosNotStmt {
|
||||
b.Pos = b.Pos.WithIsStmt()
|
||||
endlines[b.ID] = b.Pos
|
||||
continue
|
||||
}
|
||||
line := src.NoXPos
|
||||
for _, p := range b.Preds {
|
||||
pbi := p.Block().ID
|
||||
if endlines[pbi] != line {
|
||||
if line == src.NoXPos {
|
||||
line = endlines[pbi]
|
||||
continue
|
||||
} else {
|
||||
line = src.NoXPos
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
endlines[b.ID] = line
|
||||
continue
|
||||
}
|
||||
// check predecessors for any difference; if firstPos differs, then it is a boundary.
|
||||
if len(b.Preds) == 0 { // Don't forget the entry block
|
||||
b.Values[firstPosIndex].Pos = firstPos.WithIsStmt()
|
||||
} else {
|
||||
for _, p := range b.Preds {
|
||||
pbi := p.Block().ID
|
||||
if endlines[pbi] != firstPos {
|
||||
b.Values[firstPosIndex].Pos = firstPos.WithIsStmt()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// iterate forward setting each new (interesting) position as a statement boundary.
|
||||
for i := firstPosIndex + 1; i < len(b.Values); i++ {
|
||||
v := b.Values[i]
|
||||
if v.Pos.IsStmt() == src.PosNotStmt {
|
||||
continue
|
||||
}
|
||||
note(v.Pos.Line())
|
||||
// skip ahead if possible
|
||||
i = nextGoodStatementIndex(v, i, b)
|
||||
v = b.Values[i]
|
||||
if v.Pos.Line() != firstPos.Line() || !v.Pos.SameFile(firstPos) {
|
||||
firstPos = v.Pos
|
||||
v.Pos = v.Pos.WithIsStmt()
|
||||
} else {
|
||||
v.Pos = v.Pos.WithDefaultStmt()
|
||||
}
|
||||
}
|
||||
if b.Pos.IsStmt() != src.PosNotStmt && (b.Pos.Line() != firstPos.Line() || !b.Pos.SameFile(firstPos)) {
|
||||
b.Pos = b.Pos.WithIsStmt()
|
||||
firstPos = b.Pos
|
||||
}
|
||||
endlines[b.ID] = firstPos
|
||||
}
|
||||
f.cachedLineStarts = newBiasedSparseMap(int(first), int(last))
|
||||
}
|
@ -2006,7 +2006,7 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
|
||||
e.s.f.Fatalf("can't find source for %s->%s: %s\n", e.p, e.b, v.LongString())
|
||||
}
|
||||
if dstReg {
|
||||
x = v.copyIntoNoXPos(e.p)
|
||||
x = v.copyInto(e.p)
|
||||
} else {
|
||||
// Rematerialize into stack slot. Need a free
|
||||
// register to accomplish this.
|
||||
|
@ -7,6 +7,7 @@ package ssa
|
||||
import (
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/src"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
@ -16,6 +17,8 @@ import (
|
||||
|
||||
func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) {
|
||||
// repeat rewrites until we find no more rewrites
|
||||
pendingLines := f.cachedLineStarts // Holds statement boundaries that need to be moved to a new value/block
|
||||
pendingLines.clear()
|
||||
for {
|
||||
change := false
|
||||
for _, b := range f.Blocks {
|
||||
@ -27,7 +30,7 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) {
|
||||
if rb(b) {
|
||||
change = true
|
||||
}
|
||||
for _, v := range b.Values {
|
||||
for j, v := range b.Values {
|
||||
change = phielimValue(v) || change
|
||||
|
||||
// Eliminate copy inputs.
|
||||
@ -41,7 +44,27 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) {
|
||||
if a.Op != OpCopy {
|
||||
continue
|
||||
}
|
||||
v.SetArg(i, copySource(a))
|
||||
aa := copySource(a)
|
||||
v.SetArg(i, aa)
|
||||
// If a, a copy, has a line boundary indicator, attempt to find a new value
|
||||
// to hold it. The first candidate is the value that will replace a (aa),
|
||||
// if it shares the same block and line and is eligible.
|
||||
// The second option is v, which has a as an input. Because aa is earlier in
|
||||
// the data flow, it is the better choice.
|
||||
if a.Pos.IsStmt() == src.PosIsStmt {
|
||||
if aa.Block == a.Block && aa.Pos.Line() == a.Pos.Line() && aa.Pos.IsStmt() != src.PosNotStmt {
|
||||
aa.Pos = aa.Pos.WithIsStmt()
|
||||
} else if v.Block == a.Block && v.Pos.Line() == a.Pos.Line() && v.Pos.IsStmt() != src.PosNotStmt {
|
||||
v.Pos = v.Pos.WithIsStmt()
|
||||
} else {
|
||||
// Record the lost line and look for a new home after all rewrites are complete.
|
||||
// TODO: it's possible (in FOR loops, in particular) for statement boundaries for the same
|
||||
// line to appear in more than one block, but only one block is stored, so if both end
|
||||
// up here, then one will be lost.
|
||||
pendingLines.set(a.Pos.Line(), int32(a.Block.ID))
|
||||
}
|
||||
a.Pos = a.Pos.WithNotStmt()
|
||||
}
|
||||
change = true
|
||||
for a.Uses == 0 {
|
||||
b := a.Args[0]
|
||||
@ -53,6 +76,13 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) {
|
||||
// apply rewrite function
|
||||
if rv(v) {
|
||||
change = true
|
||||
// If value changed to a poor choice for a statement boundary, move the boundary
|
||||
if v.Pos.IsStmt() == src.PosIsStmt {
|
||||
if k := nextGoodStatementIndex(v, j, b); k != j {
|
||||
v.Pos = v.Pos.WithNotStmt()
|
||||
b.Values[k].Pos = b.Values[k].Pos.WithIsStmt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,15 +94,27 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) {
|
||||
for _, b := range f.Blocks {
|
||||
j := 0
|
||||
for i, v := range b.Values {
|
||||
vl := v.Pos.Line()
|
||||
if v.Op == OpInvalid {
|
||||
if v.Pos.IsStmt() == src.PosIsStmt {
|
||||
pendingLines.set(vl, int32(b.ID))
|
||||
}
|
||||
f.freeValue(v)
|
||||
continue
|
||||
}
|
||||
if v.Pos.IsStmt() != src.PosNotStmt && pendingLines.get(vl) == int32(b.ID) {
|
||||
pendingLines.remove(vl)
|
||||
v.Pos = v.Pos.WithIsStmt()
|
||||
}
|
||||
if i != j {
|
||||
b.Values[j] = v
|
||||
}
|
||||
j++
|
||||
}
|
||||
if pendingLines.get(b.Pos.Line()) == int32(b.ID) {
|
||||
b.Pos = b.Pos.WithIsStmt()
|
||||
pendingLines.remove(b.Pos.Line())
|
||||
}
|
||||
if j != len(b.Values) {
|
||||
tail := b.Values[j:]
|
||||
for j := range tail {
|
||||
|
107
src/cmd/compile/internal/ssa/stmtlines_test.go
Normal file
107
src/cmd/compile/internal/ssa/stmtlines_test.go
Normal file
@ -0,0 +1,107 @@
|
||||
package ssa_test
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func open(path string) (*dwarf.Data, error) {
|
||||
if fh, err := elf.Open(path); err == nil {
|
||||
return fh.DWARF()
|
||||
}
|
||||
|
||||
if fh, err := pe.Open(path); err == nil {
|
||||
return fh.DWARF()
|
||||
}
|
||||
|
||||
if fh, err := macho.Open(path); err == nil {
|
||||
return fh.DWARF()
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unrecognized executable format")
|
||||
}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Line struct {
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
|
||||
type File struct {
|
||||
lines []string
|
||||
}
|
||||
|
||||
var fileCache = map[string]*File{}
|
||||
|
||||
func (f *File) Get(lineno int) (string, bool) {
|
||||
if f == nil {
|
||||
return "", false
|
||||
}
|
||||
if lineno-1 < 0 || lineno-1 >= len(f.lines) {
|
||||
return "", false
|
||||
}
|
||||
return f.lines[lineno-1], true
|
||||
}
|
||||
|
||||
func TestStmtLines(t *testing.T) {
|
||||
lines := map[Line]bool{}
|
||||
dw, err := open(testenv.GoToolPath(t))
|
||||
must(err)
|
||||
rdr := dw.Reader()
|
||||
rdr.Seek(0)
|
||||
for {
|
||||
e, err := rdr.Next()
|
||||
must(err)
|
||||
if e == nil {
|
||||
break
|
||||
}
|
||||
if e.Tag != dwarf.TagCompileUnit {
|
||||
continue
|
||||
}
|
||||
pkgname, _ := e.Val(dwarf.AttrName).(string)
|
||||
if pkgname == "runtime" {
|
||||
continue
|
||||
}
|
||||
lrdr, err := dw.LineReader(e)
|
||||
must(err)
|
||||
|
||||
var le dwarf.LineEntry
|
||||
|
||||
for {
|
||||
err := lrdr.Next(&le)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
must(err)
|
||||
fl := Line{le.File.Name, le.Line}
|
||||
lines[fl] = lines[fl] || le.IsStmt
|
||||
}
|
||||
}
|
||||
|
||||
nonStmtLines := []Line{}
|
||||
for line, isstmt := range lines {
|
||||
if !isstmt {
|
||||
nonStmtLines = append(nonStmtLines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOARCH == "amd64" && len(nonStmtLines)*100 > len(lines) { // > 99% obtained on amd64, no backsliding
|
||||
t.Errorf("Saw too many (amd64, > 1%%) lines without statement marks, total=%d, nostmt=%d\n", len(lines), len(nonStmtLines))
|
||||
}
|
||||
if len(nonStmtLines)*100 > 2*len(lines) { // expect 98% elsewhere.
|
||||
t.Errorf("Saw too many (not amd64, > 2%%) lines without statement marks, total=%d, nostmt=%d\n", len(lines), len(nonStmtLines))
|
||||
}
|
||||
t.Logf("total=%d, nostmt=%d\n", len(lines), len(nonStmtLines))
|
||||
}
|
@ -96,5 +96,4 @@
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
88: continue
|
||||
86: for i, a := range hist {
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
98: }
|
||||
|
@ -9,54 +9,81 @@
|
||||
63: hist := make([]int, 7) //gdb-opt=(dx/O,dy/O) // TODO sink is missing if this code is in 'test' instead of 'main'
|
||||
64: var reader io.Reader = strings.NewReader(cannedInput) //gdb-dbg=(hist/A) // TODO cannedInput/A is missing if this code is in 'test' instead of 'main'
|
||||
65: if len(os.Args) > 1 {
|
||||
73: scanner := bufio.NewScanner(reader)
|
||||
63: hist := make([]int, 7) //gdb-opt=(dx/O,dy/O) // TODO sink is missing if this code is in 'test' instead of 'main'
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
77: if err != nil { //gdb-dbg=(i) //gdb-opt=(err,hist,i)
|
||||
76: i, err := strconv.ParseInt(s, 10, 64)
|
||||
81: hist = ensure(int(i), hist)
|
||||
82: hist[int(i)]++
|
||||
81: hist = ensure(int(i), hist)
|
||||
74: for scanner.Scan() { //gdb-opt=(scanner/A)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
86: for i, a := range hist {
|
||||
91: n += a
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
91: n += a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
@ -65,8 +92,14 @@
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
90: t += i * a
|
||||
91: n += a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
86: for i, a := range hist {
|
||||
91: n += a
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
91: n += a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
@ -75,8 +108,16 @@
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
90: t += i * a
|
||||
91: n += a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
86: for i, a := range hist {
|
||||
91: n += a
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
91: n += a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
@ -85,8 +126,14 @@
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
90: t += i * a
|
||||
91: n += a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
86: for i, a := range hist {
|
||||
91: n += a
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
91: n += a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
@ -95,7 +142,10 @@
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
90: t += i * a
|
||||
91: n += a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
98: }
|
||||
|
@ -2,7 +2,6 @@
|
||||
55: func test() {
|
||||
57: l := line{point{1 + zero, 2 + zero}, point{3 + zero, 4 + zero}}
|
||||
58: tinycall() // this forces l etc to stack
|
||||
57: l := line{point{1 + zero, 2 + zero}, point{3 + zero, 4 + zero}}
|
||||
59: dx := l.end.x - l.begin.x //gdb-dbg=(l.begin.x,l.end.y)//gdb-opt=(l,dx/O,dy/O)
|
||||
l = {begin = {x = 1, y = 2}, end = {x = 3, y = 4}}
|
||||
dx = <Optimized out, as expected>
|
||||
@ -116,9 +115,15 @@ scanner = (struct bufio.Scanner *) <A>
|
||||
a = 0
|
||||
n = 0
|
||||
t = 0
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
a = 3
|
||||
n = 0
|
||||
t = 0
|
||||
91: n += a
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
a = 3
|
||||
n = 3
|
||||
@ -126,13 +131,20 @@ t = 3
|
||||
91: n += a
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
a = 0
|
||||
n = 6
|
||||
t = 9
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
a = 2
|
||||
n = 6
|
||||
t = 9
|
||||
91: n += a
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
a = 1
|
||||
n = 8
|
||||
@ -140,8 +152,10 @@ t = 17
|
||||
91: n += a
|
||||
90: t += i * a
|
||||
92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t)
|
||||
86: for i, a := range hist {
|
||||
87: if a == 0 { //gdb-opt=(a,n,t)
|
||||
a = 0
|
||||
n = 9
|
||||
t = 22
|
||||
86: for i, a := range hist {
|
||||
98: }
|
||||
|
@ -2,10 +2,3 @@
|
||||
19: func test(t *thing, u *thing) {
|
||||
20: if t.next != nil {
|
||||
23: fmt.Fprintf(os.Stderr, "%s\n", t.name)
|
||||
24: u.self = u
|
||||
25: t.self = t
|
||||
26: t.next = u
|
||||
27: for _, p := range t.stuff {
|
||||
28: if isFoo(t, p) {
|
||||
29: return
|
||||
43: }
|
||||
|
@ -1,19 +1,56 @@
|
||||
./testdata/scopes.go
|
||||
18: func test() {
|
||||
19: x := id(0)
|
||||
20: y := id(0)
|
||||
21: fmt.Println(x)
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
26: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
27: fmt.Println(x, y)
|
||||
28: }
|
||||
11: }
|
||||
21: func test() {
|
||||
22: x := id(0)
|
||||
23: y := id(0)
|
||||
24: fmt.Println(x)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
30: fmt.Println(x, y)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
33: a := y
|
||||
34: f1(a)
|
||||
36: b := 0
|
||||
37: f2(b)
|
||||
38: if gretbool() {
|
||||
39: c := 0
|
||||
40: f3(c)
|
||||
45: f5(b)
|
||||
47: f6(a)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
33: a := y
|
||||
34: f1(a)
|
||||
36: b := 0
|
||||
37: f2(b)
|
||||
38: if gretbool() {
|
||||
42: c := 1.1
|
||||
43: f4(int(c))
|
||||
45: f5(b)
|
||||
47: f6(a)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
52: j = id(1)
|
||||
53: f = id(2)
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
56: j += j * (j ^ 3) / 100
|
||||
57: if i == f {
|
||||
61: sleepytime()
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
56: j += j * (j ^ 3) / 100
|
||||
57: if i == f {
|
||||
61: sleepytime()
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
56: j += j * (j ^ 3) / 100
|
||||
57: if i == f {
|
||||
58: fmt.Println("foo")
|
||||
59: break
|
||||
63: helloworld()
|
||||
65: }
|
||||
14: }
|
||||
|
@ -1,27 +1,63 @@
|
||||
./testdata/scopes.go
|
||||
18: func test() {
|
||||
19: x := id(0)
|
||||
20: y := id(0)
|
||||
21: fmt.Println(x)
|
||||
18: func test() {
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
22: for i := x; i < 3; i++ {
|
||||
27: fmt.Println(x, y)
|
||||
26: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
27: fmt.Println(x, y)
|
||||
28: }
|
||||
11: }
|
||||
21: func test() {
|
||||
22: x := id(0)
|
||||
23: y := id(0)
|
||||
24: fmt.Println(x)
|
||||
25: for i := x; i < 3; i++ {
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
30: fmt.Println(x, y)
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
30: fmt.Println(x, y)
|
||||
21: func test() {
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
34: f1(a)
|
||||
37: f2(b)
|
||||
38: if gretbool() {
|
||||
40: f3(c)
|
||||
45: f5(b)
|
||||
47: f6(a)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
34: f1(a)
|
||||
37: f2(b)
|
||||
38: if gretbool() {
|
||||
43: f4(int(c))
|
||||
45: f5(b)
|
||||
47: f6(a)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
52: j = id(1)
|
||||
53: f = id(2)
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
57: if i == f {
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
61: sleepytime()
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
57: if i == f {
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
61: sleepytime()
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
57: if i == f {
|
||||
58: fmt.Println("foo")
|
||||
63: helloworld()
|
||||
65: }
|
||||
14: }
|
||||
|
@ -1,27 +1,64 @@
|
||||
src/cmd/compile/internal/ssa/testdata/scopes.go
|
||||
18: func test() {
|
||||
19: x := id(0)
|
||||
20: y := id(0)
|
||||
21: fmt.Println(x)
|
||||
21: func test() {
|
||||
22: x := id(0)
|
||||
23: y := id(0)
|
||||
24: fmt.Println(x)
|
||||
0:
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = 0
|
||||
y = 0
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = 1
|
||||
y = 0
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = 4
|
||||
y = 1
|
||||
22: for i := x; i < 3; i++ {
|
||||
26: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = 0
|
||||
y = 5
|
||||
27: fmt.Println(x, y)
|
||||
30: fmt.Println(x, y)
|
||||
0: 5
|
||||
11: }
|
||||
33: a := y
|
||||
34: f1(a)
|
||||
36: b := 0
|
||||
37: f2(b)
|
||||
38: if gretbool() {
|
||||
39: c := 0
|
||||
40: f3(c)
|
||||
45: f5(b)
|
||||
47: f6(a)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
33: a := y
|
||||
34: f1(a)
|
||||
36: b := 0
|
||||
37: f2(b)
|
||||
38: if gretbool() {
|
||||
42: c := 1.1
|
||||
43: f4(int(c))
|
||||
45: f5(b)
|
||||
47: f6(a)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
52: j = id(1)
|
||||
53: f = id(2)
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
56: j += j * (j ^ 3) / 100
|
||||
57: if i == f {
|
||||
61: sleepytime()
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
56: j += j * (j ^ 3) / 100
|
||||
57: if i == f {
|
||||
61: sleepytime()
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
56: j += j * (j ^ 3) / 100
|
||||
57: if i == f {
|
||||
58: fmt.Println("foo")
|
||||
59: break
|
||||
63: helloworld()
|
||||
65: }
|
||||
14: }
|
||||
|
@ -1,39 +1,54 @@
|
||||
src/cmd/compile/internal/ssa/testdata/scopes.go
|
||||
18: func test() {
|
||||
19: x := id(0)
|
||||
20: y := id(0)
|
||||
21: fmt.Println(x)
|
||||
21: func test() {
|
||||
22: x := id(0)
|
||||
23: y := id(0)
|
||||
24: fmt.Println(x)
|
||||
0:
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = 0
|
||||
y = 0
|
||||
22: for i := x; i < 3; i++ {
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = <optimized out>
|
||||
y = 0
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = 1
|
||||
y = 0
|
||||
22: for i := x; i < 3; i++ {
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = <optimized out>
|
||||
y = 0
|
||||
22: for i := x; i < 3; i++ {
|
||||
23: x := i * i
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
26: x := i * i
|
||||
27: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = 4
|
||||
y = 1
|
||||
22: for i := x; i < 3; i++ {
|
||||
24: y += id(x) //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = <optimized out>
|
||||
y = 1
|
||||
22: for i := x; i < 3; i++ {
|
||||
26: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
25: for i := x; i < 3; i++ {
|
||||
30: fmt.Println(x, y)
|
||||
29: y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
x = 0
|
||||
y = 1
|
||||
27: fmt.Println(x, y)
|
||||
y = 5
|
||||
0: 5
|
||||
11: }
|
||||
34: f1(a)
|
||||
37: f2(b)
|
||||
38: if gretbool() {
|
||||
40: f3(c)
|
||||
45: f5(b)
|
||||
47: f6(a)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
34: f1(a)
|
||||
37: f2(b)
|
||||
38: if gretbool() {
|
||||
43: f4(int(c))
|
||||
45: f5(b)
|
||||
47: f6(a)
|
||||
32: for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
52: j = id(1)
|
||||
53: f = id(2)
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
57: if i == f {
|
||||
61: sleepytime()
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
57: if i == f {
|
||||
61: sleepytime()
|
||||
55: for i := 0; i <= 5; i++ {
|
||||
57: if i == f {
|
||||
58: fmt.Println("foo")
|
||||
63: helloworld()
|
||||
65: }
|
||||
14: }
|
||||
|
73
src/cmd/compile/internal/ssa/testdata/scopes.go
vendored
73
src/cmd/compile/internal/ssa/testdata/scopes.go
vendored
@ -4,7 +4,10 @@
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
test()
|
||||
@ -25,4 +28,72 @@ func test() {
|
||||
}
|
||||
y = x + y //gdb-dbg=(x,y)//gdb-opt=(x,y)
|
||||
fmt.Println(x, y)
|
||||
|
||||
for x := 0; x <= 1; x++ { // From delve scopetest.go
|
||||
a := y
|
||||
f1(a)
|
||||
{
|
||||
b := 0
|
||||
f2(b)
|
||||
if gretbool() {
|
||||
c := 0
|
||||
f3(c)
|
||||
} else {
|
||||
c := 1.1
|
||||
f4(int(c))
|
||||
}
|
||||
f5(b)
|
||||
}
|
||||
f6(a)
|
||||
}
|
||||
|
||||
{ // From delve testnextprog.go
|
||||
var (
|
||||
j = id(1)
|
||||
f = id(2)
|
||||
)
|
||||
for i := 0; i <= 5; i++ {
|
||||
j += j * (j ^ 3) / 100
|
||||
if i == f {
|
||||
fmt.Println("foo")
|
||||
break
|
||||
}
|
||||
sleepytime()
|
||||
}
|
||||
helloworld()
|
||||
}
|
||||
}
|
||||
|
||||
func sleepytime() {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
|
||||
func helloworld() {
|
||||
fmt.Println("Hello, World!")
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func f1(x int) {}
|
||||
|
||||
//go:noinline
|
||||
func f2(x int) {}
|
||||
|
||||
//go:noinline
|
||||
func f3(x int) {}
|
||||
|
||||
//go:noinline
|
||||
func f4(x int) {}
|
||||
|
||||
//go:noinline
|
||||
func f5(x int) {}
|
||||
|
||||
//go:noinline
|
||||
func f6(x int) {}
|
||||
|
||||
var boolvar = true
|
||||
|
||||
func gretbool() bool {
|
||||
x := boolvar
|
||||
boolvar = !boolvar
|
||||
return x
|
||||
}
|
||||
|
@ -130,16 +130,21 @@ func (v *Value) LongString() string {
|
||||
for _, a := range v.Args {
|
||||
s += fmt.Sprintf(" %v", a)
|
||||
}
|
||||
r := v.Block.Func.RegAlloc
|
||||
var r []Location
|
||||
if v.Block != nil {
|
||||
r = v.Block.Func.RegAlloc
|
||||
}
|
||||
if int(v.ID) < len(r) && r[v.ID] != nil {
|
||||
s += " : " + r[v.ID].String()
|
||||
}
|
||||
var names []string
|
||||
for name, values := range v.Block.Func.NamedValues {
|
||||
for _, value := range values {
|
||||
if value == v {
|
||||
names = append(names, name.String())
|
||||
break // drop duplicates.
|
||||
if v.Block != nil {
|
||||
for name, values := range v.Block.Func.NamedValues {
|
||||
for _, value := range values {
|
||||
if value == v {
|
||||
names = append(names, name.String())
|
||||
break // drop duplicates.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -244,6 +249,10 @@ func (v *Value) resetArgs() {
|
||||
|
||||
func (v *Value) reset(op Op) {
|
||||
v.Op = op
|
||||
if op != OpCopy && notStmtBoundary(op) {
|
||||
// Special case for OpCopy because of how it is used in rewrite
|
||||
v.Pos = v.Pos.WithNotStmt()
|
||||
}
|
||||
v.resetArgs()
|
||||
v.AuxInt = 0
|
||||
v.Aux = nil
|
||||
@ -251,7 +260,7 @@ func (v *Value) reset(op Op) {
|
||||
|
||||
// copyInto makes a new value identical to v and adds it to the end of b.
|
||||
func (v *Value) copyInto(b *Block) *Value {
|
||||
c := b.NewValue0(v.Pos, v.Op, v.Type) // Lose the position, this causes line number churn otherwise.
|
||||
c := b.NewValue0(v.Pos.WithNotStmt(), v.Op, v.Type) // Lose the position, this causes line number churn otherwise.
|
||||
c.Aux = v.Aux
|
||||
c.AuxInt = v.AuxInt
|
||||
c.AddArgs(v.Args...)
|
||||
@ -263,13 +272,6 @@ func (v *Value) copyInto(b *Block) *Value {
|
||||
return c
|
||||
}
|
||||
|
||||
// copyIntoNoXPos makes a new value identical to v and adds it to the end of b.
|
||||
// The copied value receives no source code position to avoid confusing changes
|
||||
// in debugger information (the intended user is the register allocator).
|
||||
func (v *Value) copyIntoNoXPos(b *Block) *Value {
|
||||
return v.copyIntoWithXPos(b, src.NoXPos)
|
||||
}
|
||||
|
||||
// copyIntoWithXPos makes a new value identical to v and adds it to the end of b.
|
||||
// The supplied position is used as the position of the new value.
|
||||
func (v *Value) copyIntoWithXPos(b *Block, pos src.XPos) *Value {
|
||||
|
@ -395,9 +395,10 @@ func (x lico) lineNumberHTML() string {
|
||||
if x.IsStmt() == PosDefaultStmt {
|
||||
return fmt.Sprintf("%d", x.Line())
|
||||
}
|
||||
style := "b"
|
||||
style, pfx := "b", "+"
|
||||
if x.IsStmt() == PosNotStmt {
|
||||
style = "s" // /strike not supported in HTML5
|
||||
pfx = ""
|
||||
}
|
||||
return fmt.Sprintf("<%s>%d</%s>", style, x.Line(), style)
|
||||
return fmt.Sprintf("<%s>%s%d</%s>", style, pfx, x.Line(), style)
|
||||
}
|
||||
|
@ -30,6 +30,11 @@ func (p XPos) Before(q XPos) bool {
|
||||
return n < m || n == m && p.lico < q.lico
|
||||
}
|
||||
|
||||
// SameFile reports whether p and q are positions in the same file.
|
||||
func (p XPos) SameFile(q XPos) bool {
|
||||
return p.index == q.index
|
||||
}
|
||||
|
||||
// After reports whether the position p comes after q in the source.
|
||||
// For positions with different bases, ordering is by base index.
|
||||
func (p XPos) After(q XPos) bool {
|
||||
|
@ -87,9 +87,9 @@ func ArrayInit(i, j int) [4]int {
|
||||
// Check that assembly output has matching offset and base register
|
||||
// (issue #21064).
|
||||
|
||||
// amd64:`.*b\+24\(SP\)`
|
||||
// arm:`.*b\+4\(FP\)`
|
||||
func check_asmout(a, b int) int {
|
||||
runtime.GC() // use some frame
|
||||
// amd64:`.*b\+24\(SP\)`
|
||||
// arm:`.*b\+4\(FP\)`
|
||||
return b
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user