2015-03-03 13:38:14 -08:00
|
|
|
// Copyright 2015 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
|
|
|
|
|
|
2016-12-06 17:08:06 -08:00
|
|
|
import (
|
|
|
|
|
"cmd/internal/src"
|
|
|
|
|
"fmt"
|
|
|
|
|
)
|
2015-03-03 13:38:14 -08:00
|
|
|
|
|
|
|
|
// Block represents a basic block in the control flow graph of a function.
|
|
|
|
|
type Block struct {
|
2016-03-01 23:21:55 +00:00
|
|
|
// A unique identifier for the block. The system will attempt to allocate
|
2015-03-03 13:38:14 -08:00
|
|
|
// these IDs densely, but no guarantees.
|
|
|
|
|
ID ID
|
|
|
|
|
|
2016-12-07 18:14:35 -08:00
|
|
|
// Source position for block's control operation
|
2016-12-15 17:17:01 -08:00
|
|
|
Pos src.XPos
|
2016-04-28 16:52:47 -07:00
|
|
|
|
2025-09-05 19:05:18 -04:00
|
|
|
// What cpu features (AVXnnn, SVEyyy) are implied to reach/execute this block?
|
|
|
|
|
CPUfeatures CPUfeatures
|
|
|
|
|
|
2015-03-03 13:38:14 -08:00
|
|
|
// The kind of block this is.
|
|
|
|
|
Kind BlockKind
|
|
|
|
|
|
2016-04-28 16:52:47 -07:00
|
|
|
// Likely direction for branches.
|
|
|
|
|
// If BranchLikely, Succs[0] is the most likely branch taken.
|
|
|
|
|
// If BranchUnlikely, Succs[1] is the most likely branch taken.
|
|
|
|
|
// Ignored if len(Succs) < 2.
|
|
|
|
|
// Fatal if not BranchUnknown and len(Succs) > 2.
|
|
|
|
|
Likely BranchPrediction
|
|
|
|
|
|
|
|
|
|
// After flagalloc, records whether flags are live at the end of the block.
|
|
|
|
|
FlagsLiveAtEnd bool
|
|
|
|
|
|
2024-04-02 11:12:44 -04:00
|
|
|
// A block that would be good to align (according to the optimizer's guesses)
|
|
|
|
|
Hotness Hotness
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Subsequent blocks, if any. The number and order depend on the block kind.
|
2016-04-28 16:52:47 -07:00
|
|
|
Succs []Edge
|
2015-03-03 13:38:14 -08:00
|
|
|
|
|
|
|
|
// Inverse of successors.
|
|
|
|
|
// The order is significant to Phi nodes in the block.
|
2016-03-01 23:21:55 +00:00
|
|
|
// TODO: predecessors is a pain to maintain. Can we somehow order phi
|
2015-03-03 13:38:14 -08:00
|
|
|
// arguments by block id and have this field computed explicitly when needed?
|
2016-04-28 16:52:47 -07:00
|
|
|
Preds []Edge
|
2015-03-03 13:38:14 -08:00
|
|
|
|
2019-08-12 20:19:58 +01:00
|
|
|
// A list of values that determine how the block is exited. The number
|
|
|
|
|
// and type of control values depends on the Kind of the block. For
|
|
|
|
|
// instance, a BlockIf has a single boolean control value and BlockExit
|
|
|
|
|
// has a single memory control value.
|
|
|
|
|
//
|
|
|
|
|
// The ControlValues() method may be used to get a slice with the non-nil
|
|
|
|
|
// control values that can be ranged over.
|
|
|
|
|
//
|
|
|
|
|
// Controls[1] must be nil if Controls[0] is nil.
|
|
|
|
|
Controls [2]*Value
|
2015-03-03 13:38:14 -08:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Auxiliary info for the block. Its value depends on the Kind.
|
2020-12-07 17:15:44 -08:00
|
|
|
Aux Aux
|
2019-09-17 07:29:31 -07:00
|
|
|
AuxInt int64
|
2015-09-08 21:28:44 -07:00
|
|
|
|
2015-05-05 16:19:12 -07:00
|
|
|
// The unordered set of Values that define the operation of this block.
|
|
|
|
|
// After the scheduling pass, this list is ordered.
|
2015-03-03 13:38:14 -08:00
|
|
|
Values []*Value
|
|
|
|
|
|
|
|
|
|
// The containing function
|
|
|
|
|
Func *Func
|
2015-05-30 01:03:06 -04:00
|
|
|
|
2019-08-12 20:19:58 +01:00
|
|
|
// Storage for Succs, Preds and Values.
|
2016-04-28 16:52:47 -07:00
|
|
|
succstorage [2]Edge
|
|
|
|
|
predstorage [4]Edge
|
|
|
|
|
valstorage [9]*Value
|
|
|
|
|
}
|
2016-01-22 13:44:58 -08:00
|
|
|
|
2016-04-28 16:52:47 -07:00
|
|
|
// Edge represents a CFG edge.
|
|
|
|
|
// Example edges for b branching to either c or d.
|
|
|
|
|
// (c and d have other predecessors.)
|
2022-02-03 14:12:08 -05:00
|
|
|
//
|
|
|
|
|
// b.Succs = [{c,3}, {d,1}]
|
|
|
|
|
// c.Preds = [?, ?, ?, {b,0}]
|
|
|
|
|
// d.Preds = [?, {b,1}, ?]
|
|
|
|
|
//
|
2016-04-28 16:52:47 -07:00
|
|
|
// These indexes allow us to edit the CFG in constant time.
|
|
|
|
|
// In addition, it informs phi ops in degenerate cases like:
|
2022-02-03 14:12:08 -05:00
|
|
|
//
|
|
|
|
|
// b:
|
|
|
|
|
// if k then c else c
|
|
|
|
|
// c:
|
|
|
|
|
// v = Phi(x, y)
|
|
|
|
|
//
|
2016-04-28 16:52:47 -07:00
|
|
|
// Then the indexes tell you whether x is chosen from
|
|
|
|
|
// the if or else branch from b.
|
2022-02-03 14:12:08 -05:00
|
|
|
//
|
|
|
|
|
// b.Succs = [{c,0},{c,1}]
|
|
|
|
|
// c.Preds = [{b,0},{b,1}]
|
|
|
|
|
//
|
2016-04-28 16:52:47 -07:00
|
|
|
// means x is chosen if k is true.
|
|
|
|
|
type Edge struct {
|
|
|
|
|
// block edge goes to (in a Succs list) or from (in a Preds list)
|
|
|
|
|
b *Block
|
|
|
|
|
// index of reverse edge. Invariant:
|
|
|
|
|
// e := x.Succs[idx]
|
|
|
|
|
// e.b.Preds[e.i] = Edge{x,idx}
|
|
|
|
|
// and similarly for predecessors.
|
|
|
|
|
i int
|
|
|
|
|
}
|
2016-01-28 15:54:45 -08:00
|
|
|
|
2016-04-28 16:52:47 -07:00
|
|
|
func (e Edge) Block() *Block {
|
|
|
|
|
return e.b
|
2015-03-03 13:38:14 -08:00
|
|
|
}
|
2016-09-30 10:12:32 -07:00
|
|
|
func (e Edge) Index() int {
|
|
|
|
|
return e.i
|
|
|
|
|
}
|
2020-03-04 07:24:56 -08:00
|
|
|
func (e Edge) String() string {
|
|
|
|
|
return fmt.Sprintf("{%v,%d}", e.b, e.i)
|
|
|
|
|
}
|
2015-03-03 13:38:14 -08:00
|
|
|
|
2022-01-29 19:07:27 -05:00
|
|
|
// BlockKind is the kind of SSA block.
|
2024-04-02 11:12:44 -04:00
|
|
|
type BlockKind uint8
|
2015-03-03 13:38:14 -08:00
|
|
|
|
|
|
|
|
// short form print
|
|
|
|
|
func (b *Block) String() string {
|
|
|
|
|
return fmt.Sprintf("b%d", b.ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// long form print
|
|
|
|
|
func (b *Block) LongString() string {
|
2015-06-06 16:03:33 -07:00
|
|
|
s := b.Kind.String()
|
2015-09-08 21:28:44 -07:00
|
|
|
if b.Aux != nil {
|
2019-09-17 07:29:31 -07:00
|
|
|
s += fmt.Sprintf(" {%s}", b.Aux)
|
|
|
|
|
}
|
2020-04-04 22:51:15 +01:00
|
|
|
if t := b.AuxIntString(); t != "" {
|
|
|
|
|
s += fmt.Sprintf(" [%s]", t)
|
2015-09-08 21:28:44 -07:00
|
|
|
}
|
2019-08-12 20:19:58 +01:00
|
|
|
for _, c := range b.ControlValues() {
|
|
|
|
|
s += fmt.Sprintf(" %s", c)
|
2015-03-03 13:38:14 -08:00
|
|
|
}
|
|
|
|
|
if len(b.Succs) > 0 {
|
|
|
|
|
s += " ->"
|
|
|
|
|
for _, c := range b.Succs {
|
2016-04-28 16:52:47 -07:00
|
|
|
s += " " + c.b.String()
|
2015-03-03 13:38:14 -08:00
|
|
|
}
|
|
|
|
|
}
|
2015-08-11 17:28:56 -07:00
|
|
|
switch b.Likely {
|
|
|
|
|
case BranchUnlikely:
|
|
|
|
|
s += " (unlikely)"
|
|
|
|
|
case BranchLikely:
|
|
|
|
|
s += " (likely)"
|
|
|
|
|
}
|
2015-03-03 13:38:14 -08:00
|
|
|
return s
|
|
|
|
|
}
|
2015-06-12 11:01:13 -07:00
|
|
|
|
2019-08-12 20:19:58 +01:00
|
|
|
// NumControls returns the number of non-nil control values the
|
|
|
|
|
// block has.
|
|
|
|
|
func (b *Block) NumControls() int {
|
|
|
|
|
if b.Controls[0] == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
if b.Controls[1] == nil {
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
return 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ControlValues returns a slice containing the non-nil control
|
|
|
|
|
// values of the block. The index of each control value will be
|
|
|
|
|
// the same as it is in the Controls property and can be used
|
|
|
|
|
// in ReplaceControl calls.
|
|
|
|
|
func (b *Block) ControlValues() []*Value {
|
|
|
|
|
if b.Controls[0] == nil {
|
|
|
|
|
return b.Controls[:0]
|
|
|
|
|
}
|
|
|
|
|
if b.Controls[1] == nil {
|
|
|
|
|
return b.Controls[:1]
|
|
|
|
|
}
|
|
|
|
|
return b.Controls[:2]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetControl removes all existing control values and then adds
|
|
|
|
|
// the control value provided. The number of control values after
|
|
|
|
|
// a call to SetControl will always be 1.
|
2016-03-15 20:45:50 -07:00
|
|
|
func (b *Block) SetControl(v *Value) {
|
2019-08-12 20:19:58 +01:00
|
|
|
b.ResetControls()
|
|
|
|
|
b.Controls[0] = v
|
|
|
|
|
v.Uses++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ResetControls sets the number of controls for the block to 0.
|
|
|
|
|
func (b *Block) ResetControls() {
|
|
|
|
|
if b.Controls[0] != nil {
|
|
|
|
|
b.Controls[0].Uses--
|
|
|
|
|
}
|
|
|
|
|
if b.Controls[1] != nil {
|
|
|
|
|
b.Controls[1].Uses--
|
|
|
|
|
}
|
|
|
|
|
b.Controls = [2]*Value{} // reset both controls to nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddControl appends a control value to the existing list of control values.
|
|
|
|
|
func (b *Block) AddControl(v *Value) {
|
|
|
|
|
i := b.NumControls()
|
|
|
|
|
b.Controls[i] = v // panics if array is full
|
|
|
|
|
v.Uses++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReplaceControl exchanges the existing control value at the index provided
|
|
|
|
|
// for the new value. The index must refer to a valid control value.
|
|
|
|
|
func (b *Block) ReplaceControl(i int, v *Value) {
|
|
|
|
|
b.Controls[i].Uses--
|
|
|
|
|
b.Controls[i] = v
|
|
|
|
|
v.Uses++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CopyControls replaces the controls for this block with those from the
|
|
|
|
|
// provided block. The provided block is not modified.
|
|
|
|
|
func (b *Block) CopyControls(from *Block) {
|
|
|
|
|
if b == from {
|
|
|
|
|
return
|
2016-03-15 20:45:50 -07:00
|
|
|
}
|
2019-08-12 20:19:58 +01:00
|
|
|
b.ResetControls()
|
|
|
|
|
for _, c := range from.ControlValues() {
|
|
|
|
|
b.AddControl(c)
|
2016-03-15 20:45:50 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 11:02:41 +01:00
|
|
|
// Reset sets the block to the provided kind and clears all the blocks control
|
2019-11-15 19:49:30 +00:00
|
|
|
// and auxiliary values. Other properties of the block, such as its successors,
|
2019-10-02 11:02:41 +01:00
|
|
|
// predecessors and values are left unmodified.
|
|
|
|
|
func (b *Block) Reset(kind BlockKind) {
|
|
|
|
|
b.Kind = kind
|
|
|
|
|
b.ResetControls()
|
|
|
|
|
b.Aux = nil
|
2019-09-17 07:29:31 -07:00
|
|
|
b.AuxInt = 0
|
2019-10-02 11:02:41 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-01 13:09:09 -08:00
|
|
|
// resetWithControl resets b and adds control v.
|
|
|
|
|
// It is equivalent to b.Reset(kind); b.AddControl(v),
|
|
|
|
|
// except that it is one call instead of two and avoids a bounds check.
|
|
|
|
|
// It is intended for use by rewrite rules, where this matters.
|
|
|
|
|
func (b *Block) resetWithControl(kind BlockKind, v *Value) {
|
|
|
|
|
b.Kind = kind
|
|
|
|
|
b.ResetControls()
|
|
|
|
|
b.Aux = nil
|
|
|
|
|
b.AuxInt = 0
|
|
|
|
|
b.Controls[0] = v
|
|
|
|
|
v.Uses++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// resetWithControl2 resets b and adds controls v and w.
|
|
|
|
|
// It is equivalent to b.Reset(kind); b.AddControl(v); b.AddControl(w),
|
|
|
|
|
// except that it is one call instead of three and avoids two bounds checks.
|
|
|
|
|
// It is intended for use by rewrite rules, where this matters.
|
|
|
|
|
func (b *Block) resetWithControl2(kind BlockKind, v, w *Value) {
|
|
|
|
|
b.Kind = kind
|
|
|
|
|
b.ResetControls()
|
|
|
|
|
b.Aux = nil
|
|
|
|
|
b.AuxInt = 0
|
|
|
|
|
b.Controls[0] = v
|
|
|
|
|
b.Controls[1] = w
|
|
|
|
|
v.Uses++
|
|
|
|
|
w.Uses++
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 21:35:31 -07:00
|
|
|
// truncateValues truncates b.Values at the ith element, zeroing subsequent elements.
|
|
|
|
|
// The values in b.Values after i must already have had their args reset,
|
|
|
|
|
// to maintain correct value uses counts.
|
|
|
|
|
func (b *Block) truncateValues(i int) {
|
2025-04-17 07:49:35 +00:00
|
|
|
clear(b.Values[i:])
|
2020-04-22 21:35:31 -07:00
|
|
|
b.Values = b.Values[:i]
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-31 02:48:34 +00:00
|
|
|
// AddEdgeTo adds an edge from block b to block c.
|
2015-08-28 21:36:29 -05:00
|
|
|
func (b *Block) AddEdgeTo(c *Block) {
|
2016-04-28 16:52:47 -07:00
|
|
|
i := len(b.Succs)
|
|
|
|
|
j := len(c.Preds)
|
|
|
|
|
b.Succs = append(b.Succs, Edge{c, j})
|
|
|
|
|
c.Preds = append(c.Preds, Edge{b, i})
|
2016-09-16 13:50:18 -07:00
|
|
|
b.Func.invalidateCFG()
|
2016-04-28 16:52:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// removePred removes the ith input edge from b.
|
|
|
|
|
// It is the responsibility of the caller to remove
|
2021-10-24 13:46:54 +07:00
|
|
|
// the corresponding successor edge, and adjust any
|
|
|
|
|
// phi values by calling b.removePhiArg(v, i).
|
2016-04-28 16:52:47 -07:00
|
|
|
func (b *Block) removePred(i int) {
|
|
|
|
|
n := len(b.Preds) - 1
|
|
|
|
|
if i != n {
|
|
|
|
|
e := b.Preds[n]
|
|
|
|
|
b.Preds[i] = e
|
|
|
|
|
// Update the other end of the edge we moved.
|
|
|
|
|
e.b.Succs[e.i].i = i
|
|
|
|
|
}
|
|
|
|
|
b.Preds[n] = Edge{}
|
|
|
|
|
b.Preds = b.Preds[:n]
|
2016-09-16 13:50:18 -07:00
|
|
|
b.Func.invalidateCFG()
|
2016-04-28 16:52:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// removeSucc removes the ith output edge from b.
|
|
|
|
|
// It is the responsibility of the caller to remove
|
|
|
|
|
// the corresponding predecessor edge.
|
2023-12-07 11:09:29 -08:00
|
|
|
// Note that this potentially reorders successors of b, so it
|
|
|
|
|
// must be used very carefully.
|
2016-04-28 16:52:47 -07:00
|
|
|
func (b *Block) removeSucc(i int) {
|
|
|
|
|
n := len(b.Succs) - 1
|
|
|
|
|
if i != n {
|
|
|
|
|
e := b.Succs[n]
|
|
|
|
|
b.Succs[i] = e
|
|
|
|
|
// Update the other end of the edge we moved.
|
|
|
|
|
e.b.Preds[e.i].i = i
|
|
|
|
|
}
|
|
|
|
|
b.Succs[n] = Edge{}
|
|
|
|
|
b.Succs = b.Succs[:n]
|
2016-09-16 13:50:18 -07:00
|
|
|
b.Func.invalidateCFG()
|
2016-04-28 16:52:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *Block) swapSuccessors() {
|
|
|
|
|
if len(b.Succs) != 2 {
|
|
|
|
|
b.Fatalf("swapSuccessors with len(Succs)=%d", len(b.Succs))
|
|
|
|
|
}
|
|
|
|
|
e0 := b.Succs[0]
|
|
|
|
|
e1 := b.Succs[1]
|
|
|
|
|
b.Succs[0] = e1
|
|
|
|
|
b.Succs[1] = e0
|
|
|
|
|
e0.b.Preds[e0.i].i = 1
|
|
|
|
|
e1.b.Preds[e1.i].i = 0
|
|
|
|
|
b.Likely *= -1
|
2015-08-28 21:36:29 -05:00
|
|
|
}
|
|
|
|
|
|
2023-12-07 11:09:29 -08:00
|
|
|
// Swaps b.Succs[x] and b.Succs[y].
|
|
|
|
|
func (b *Block) swapSuccessorsByIdx(x, y int) {
|
|
|
|
|
if x == y {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
ex := b.Succs[x]
|
|
|
|
|
ey := b.Succs[y]
|
|
|
|
|
b.Succs[x] = ey
|
|
|
|
|
b.Succs[y] = ex
|
|
|
|
|
ex.b.Preds[ex.i].i = y
|
|
|
|
|
ey.b.Preds[ey.i].i = x
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 13:46:54 +07:00
|
|
|
// removePhiArg removes the ith arg from phi.
|
|
|
|
|
// It must be called after calling b.removePred(i) to
|
|
|
|
|
// adjust the corresponding phi value of the block:
|
|
|
|
|
//
|
|
|
|
|
// b.removePred(i)
|
|
|
|
|
// for _, v := range b.Values {
|
2022-02-03 14:12:08 -05:00
|
|
|
//
|
|
|
|
|
// if v.Op != OpPhi {
|
|
|
|
|
// continue
|
|
|
|
|
// }
|
2023-02-06 07:18:53 +01:00
|
|
|
// b.removePhiArg(v, i)
|
2022-02-03 14:12:08 -05:00
|
|
|
//
|
2021-10-24 13:46:54 +07:00
|
|
|
// }
|
|
|
|
|
func (b *Block) removePhiArg(phi *Value, i int) {
|
|
|
|
|
n := len(b.Preds)
|
|
|
|
|
if numPhiArgs := len(phi.Args); numPhiArgs-1 != n {
|
2023-12-07 11:09:29 -08:00
|
|
|
b.Fatalf("inconsistent state for %v, num predecessors: %d, num phi args: %d", phi, n, numPhiArgs)
|
2021-10-24 13:46:54 +07:00
|
|
|
}
|
|
|
|
|
phi.Args[i].Uses--
|
|
|
|
|
phi.Args[i] = phi.Args[n]
|
|
|
|
|
phi.Args[n] = nil
|
|
|
|
|
phi.Args = phi.Args[:n]
|
2023-02-06 07:18:53 +01:00
|
|
|
phielimValue(phi)
|
2021-10-24 13:46:54 +07:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 14:19:32 -07:00
|
|
|
// uniquePred returns the predecessor of b, if there is exactly one.
|
|
|
|
|
// Returns nil otherwise.
|
|
|
|
|
func (b *Block) uniquePred() *Block {
|
|
|
|
|
if len(b.Preds) != 1 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return b.Preds[0].b
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-29 11:53:18 -04:00
|
|
|
// LackingPos indicates whether b is a block whose position should be inherited
|
|
|
|
|
// from its successors. This is true if all the values within it have unreliable positions
|
|
|
|
|
// and if it is "plain", meaning that there is no control flow that is also very likely
|
|
|
|
|
// to correspond to a well-understood source position.
|
|
|
|
|
func (b *Block) LackingPos() bool {
|
|
|
|
|
// Non-plain predecessors are If or Defer, which both (1) have two successors,
|
|
|
|
|
// which might have different line numbers and (2) correspond to statements
|
|
|
|
|
// in the source code that have positions, so this case ought not occur anyway.
|
|
|
|
|
if b.Kind != BlockPlain {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if b.Pos != src.NoXPos {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for _, v := range b.Values {
|
|
|
|
|
if v.LackingPos() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-04 22:51:15 +01:00
|
|
|
func (b *Block) AuxIntString() string {
|
|
|
|
|
switch b.Kind.AuxIntType() {
|
2020-04-16 11:40:09 +01:00
|
|
|
case "int8":
|
2020-04-04 22:51:15 +01:00
|
|
|
return fmt.Sprintf("%v", int8(b.AuxInt))
|
2020-04-16 11:40:09 +01:00
|
|
|
case "uint8":
|
2020-04-04 22:51:15 +01:00
|
|
|
return fmt.Sprintf("%v", uint8(b.AuxInt))
|
|
|
|
|
case "": // no aux int type
|
|
|
|
|
return ""
|
2023-10-29 06:51:14 +00:00
|
|
|
default: // type specified but not implemented - print as int64
|
|
|
|
|
return fmt.Sprintf("%v", b.AuxInt)
|
2020-04-04 22:51:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
cmd/compile: use depth first topological sort algorithm for layout
The current layout algorithm tries to put consecutive blocks together,
so the priority of the successor block is higher than the priority of
the zero indegree block. This algorithm is beneficial for subsequent
register allocation, but will result in more branch instructions.
The depth-first topological sorting algorithm is a well-known layout
algorithm, which has applications in many languages, and it helps to
reduce branch instructions. This CL applies it to the layout pass.
The test results show that it helps to reduce the code size.
This CL also includes the following changes:
1, Removed the primary predecessor mechanism. The new layout algorithm is
not very friendly to register allocator in some cases, in order to adapt
to the new layout algorithm, a new primary predecessor selection strategy
is introduced.
2, Since the new layout implementation may place non-loop blocks between
loop blocks, some adaptive modifications have also been made to looprotate
pass.
3, The layout also affects the results of codegen, so this CL also adjusted
several codegen tests accordingly.
It is inevitable that this CL will cause the code size or performance of a
few functions to decrease, but the number of cases it improves is much larger
than the number of cases it drops.
Statistical data from compilecmp on linux/amd64 is as follow:
name old time/op new time/op delta
Template 382ms ± 4% 382ms ± 4% ~ (p=0.497 n=49+50)
Unicode 170ms ± 9% 169ms ± 8% ~ (p=0.344 n=48+50)
GoTypes 2.01s ± 4% 2.01s ± 4% ~ (p=0.628 n=50+48)
Compiler 190ms ±10% 189ms ± 9% ~ (p=0.734 n=50+50)
SSA 11.8s ± 2% 11.8s ± 3% ~ (p=0.877 n=50+50)
Flate 241ms ± 9% 241ms ± 8% ~ (p=0.897 n=50+49)
GoParser 366ms ± 3% 361ms ± 4% -1.21% (p=0.004 n=47+50)
Reflect 835ms ± 3% 838ms ± 3% ~ (p=0.275 n=50+49)
Tar 336ms ± 4% 335ms ± 3% ~ (p=0.454 n=48+48)
XML 433ms ± 4% 431ms ± 3% ~ (p=0.071 n=49+48)
LinkCompiler 706ms ± 4% 705ms ± 4% ~ (p=0.608 n=50+49)
ExternalLinkCompiler 1.85s ± 3% 1.83s ± 2% -1.47% (p=0.000 n=49+48)
LinkWithoutDebugCompiler 437ms ± 5% 437ms ± 6% ~ (p=0.953 n=49+50)
[Geo mean] 615ms 613ms -0.37%
name old alloc/op new alloc/op delta
Template 38.7MB ± 1% 38.7MB ± 1% ~ (p=0.834 n=50+50)
Unicode 28.1MB ± 0% 28.1MB ± 0% -0.22% (p=0.000 n=49+50)
GoTypes 168MB ± 1% 168MB ± 1% ~ (p=0.054 n=47+47)
Compiler 23.0MB ± 1% 23.0MB ± 1% ~ (p=0.432 n=50+50)
SSA 1.54GB ± 0% 1.54GB ± 0% +0.21% (p=0.000 n=50+50)
Flate 23.6MB ± 1% 23.6MB ± 1% ~ (p=0.153 n=43+46)
GoParser 35.1MB ± 1% 35.1MB ± 2% ~ (p=0.202 n=50+50)
Reflect 84.7MB ± 1% 84.7MB ± 1% ~ (p=0.333 n=48+49)
Tar 34.5MB ± 1% 34.5MB ± 1% ~ (p=0.406 n=46+49)
XML 44.3MB ± 2% 44.2MB ± 3% ~ (p=0.981 n=50+50)
LinkCompiler 131MB ± 0% 128MB ± 0% -2.74% (p=0.000 n=50+50)
ExternalLinkCompiler 120MB ± 0% 120MB ± 0% +0.01% (p=0.007 n=50+50)
LinkWithoutDebugCompiler 77.3MB ± 0% 77.3MB ± 0% -0.02% (p=0.000 n=50+50)
[Geo mean] 69.3MB 69.1MB -0.22%
file before after Δ %
addr2line 4104220 4043684 -60536 -1.475%
api 5342502 5249678 -92824 -1.737%
asm 4973785 4858257 -115528 -2.323%
buildid 2667844 2625660 -42184 -1.581%
cgo 4686849 4616313 -70536 -1.505%
compile 23667431 23268406 -399025 -1.686%
cover 4959676 4874108 -85568 -1.725%
dist 3515934 3450422 -65512 -1.863%
doc 3995581 3925469 -70112 -1.755%
fix 3379202 3318522 -60680 -1.796%
link 6743249 6629913 -113336 -1.681%
nm 4047529 3991777 -55752 -1.377%
objdump 4456151 4388151 -68000 -1.526%
pack 2435040 2398072 -36968 -1.518%
pprof 13804080 13565808 -238272 -1.726%
test2json 2690043 2645987 -44056 -1.638%
trace 10418492 10232716 -185776 -1.783%
vet 7258259 7121259 -137000 -1.888%
total 113145867 111204202 -1941665 -1.716%
The situation on linux/arm64 is as follow:
name old time/op new time/op delta
Template 280ms ± 1% 282ms ± 1% +0.75% (p=0.000 n=46+48)
Unicode 124ms ± 2% 124ms ± 2% +0.37% (p=0.045 n=50+50)
GoTypes 1.69s ± 1% 1.70s ± 1% +0.56% (p=0.000 n=49+50)
Compiler 122ms ± 1% 123ms ± 1% +0.93% (p=0.000 n=50+50)
SSA 12.6s ± 1% 12.7s ± 0% +0.72% (p=0.000 n=50+50)
Flate 170ms ± 1% 172ms ± 1% +0.97% (p=0.000 n=49+49)
GoParser 262ms ± 1% 263ms ± 1% +0.39% (p=0.000 n=49+48)
Reflect 639ms ± 1% 650ms ± 1% +1.63% (p=0.000 n=49+49)
Tar 243ms ± 1% 245ms ± 1% +0.82% (p=0.000 n=50+50)
XML 324ms ± 1% 327ms ± 1% +0.72% (p=0.000 n=50+49)
LinkCompiler 597ms ± 1% 596ms ± 1% -0.27% (p=0.001 n=48+47)
ExternalLinkCompiler 1.90s ± 1% 1.88s ± 1% -1.00% (p=0.000 n=50+50)
LinkWithoutDebugCompiler 364ms ± 1% 363ms ± 1% ~ (p=0.220 n=49+50)
[Geo mean] 485ms 488ms +0.49%
name old alloc/op new alloc/op delta
Template 38.7MB ± 0% 38.8MB ± 1% ~ (p=0.093 n=43+49)
Unicode 28.4MB ± 0% 28.4MB ± 0% +0.03% (p=0.000 n=49+45)
GoTypes 169MB ± 1% 169MB ± 1% +0.23% (p=0.010 n=50+50)
Compiler 23.2MB ± 1% 23.2MB ± 1% +0.11% (p=0.000 n=40+44)
SSA 1.54GB ± 0% 1.55GB ± 0% +0.45% (p=0.000 n=47+49)
Flate 23.8MB ± 2% 23.8MB ± 1% ~ (p=0.543 n=50+50)
GoParser 35.3MB ± 1% 35.4MB ± 1% ~ (p=0.792 n=50+50)
Reflect 85.2MB ± 1% 85.2MB ± 0% ~ (p=0.055 n=50+47)
Tar 34.5MB ± 1% 34.5MB ± 1% +0.06% (p=0.015 n=50+50)
XML 43.8MB ± 2% 43.9MB ± 2% +0.19% (p=0.000 n=48+48)
LinkCompiler 137MB ± 0% 136MB ± 0% -0.92% (p=0.000 n=50+50)
ExternalLinkCompiler 127MB ± 0% 127MB ± 0% ~ (p=0.516 n=50+50)
LinkWithoutDebugCompiler 84.0MB ± 0% 84.0MB ± 0% ~ (p=0.057 n=50+50)
[Geo mean] 70.4MB 70.4MB +0.01%
file before after Δ %
addr2line 4021557 4002933 -18624 -0.463%
api 5127847 5028503 -99344 -1.937%
asm 5034716 4936836 -97880 -1.944%
buildid 2608118 2594094 -14024 -0.538%
cgo 4488592 4398320 -90272 -2.011%
compile 22501129 22213592 -287537 -1.278%
cover 4742301 4713573 -28728 -0.606%
dist 3388071 3365311 -22760 -0.672%
doc 3802250 3776082 -26168 -0.688%
fix 3306147 3216939 -89208 -2.698%
link 6404483 6363699 -40784 -0.637%
nm 3941026 3921930 -19096 -0.485%
objdump 4383330 4295122 -88208 -2.012%
pack 2404547 2389515 -15032 -0.625%
pprof 12996234 12856818 -139416 -1.073%
test2json 2668500 2586788 -81712 -3.062%
trace 9816276 9609580 -206696 -2.106%
vet 6900682 6787338 -113344 -1.643%
total 108535806 107056973 -1478833 -1.363%
Change-Id: Iaec1cdcaacca8025e9babb0fb8a532fddb70c87d
Reviewed-on: https://go-review.googlesource.com/c/go/+/255239
Reviewed-by: eric fang <eric.fang@arm.com>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: eric fang <eric.fang@arm.com>
2020-07-23 10:24:56 +08:00
|
|
|
// likelyBranch reports whether block b is the likely branch of all of its predecessors.
|
|
|
|
|
func (b *Block) likelyBranch() bool {
|
|
|
|
|
if len(b.Preds) == 0 {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for _, e := range b.Preds {
|
|
|
|
|
p := e.b
|
|
|
|
|
if len(p.Succs) == 1 || len(p.Succs) == 2 && (p.Likely == BranchLikely && p.Succs[0].b == b ||
|
|
|
|
|
p.Likely == BranchUnlikely && p.Succs[1].b == b) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-14 10:01:05 -07:00
|
|
|
func (b *Block) Logf(msg string, args ...interface{}) { b.Func.Logf(msg, args...) }
|
|
|
|
|
func (b *Block) Log() bool { return b.Func.Log() }
|
|
|
|
|
func (b *Block) Fatalf(msg string, args ...interface{}) { b.Func.Fatalf(msg, args...) }
|
2015-08-11 17:28:56 -07:00
|
|
|
|
|
|
|
|
type BranchPrediction int8
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
BranchUnlikely = BranchPrediction(-1)
|
|
|
|
|
BranchUnknown = BranchPrediction(0)
|
|
|
|
|
BranchLikely = BranchPrediction(+1)
|
|
|
|
|
)
|
2024-04-02 11:12:44 -04:00
|
|
|
|
|
|
|
|
type Hotness int8 // Could use negative numbers for specifically non-hot blocks, but don't, yet.
|
|
|
|
|
const (
|
|
|
|
|
// These values are arranged in what seems to be order of increasing alignment importance.
|
|
|
|
|
// Currently only a few are relevant. Implicitly, they are all in a loop.
|
|
|
|
|
HotNotFlowIn Hotness = 1 << iota // This block is only reached by branches
|
|
|
|
|
HotInitial // In the block order, the first one for a given loop. Not necessarily topological header.
|
|
|
|
|
HotPgo // By PGO-based heuristics, this block occurs in a hot loop
|
|
|
|
|
|
|
|
|
|
HotNot = 0
|
|
|
|
|
HotInitialNotFlowIn = HotInitial | HotNotFlowIn // typically first block of a rotated loop, loop is entered with a branch (not to this block). No PGO
|
|
|
|
|
HotPgoInitial = HotPgo | HotInitial // special case; single block loop, initial block is header block has a flow-in entry, but PGO says it is hot
|
|
|
|
|
HotPgoInitialNotFLowIn = HotPgo | HotInitial | HotNotFlowIn // PGO says it is hot, and the loop is rotated so flow enters loop with a branch
|
|
|
|
|
)
|
2025-09-05 19:05:18 -04:00
|
|
|
|
|
|
|
|
type CPUfeatures uint32
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
CPUNone CPUfeatures = 0
|
|
|
|
|
CPUAll CPUfeatures = ^CPUfeatures(0)
|
|
|
|
|
CPUavx CPUfeatures = 1 << iota
|
|
|
|
|
CPUavx2
|
|
|
|
|
CPUavxvnni
|
|
|
|
|
CPUavx512
|
|
|
|
|
CPUbitalg
|
|
|
|
|
CPUgfni
|
|
|
|
|
CPUvbmi
|
|
|
|
|
CPUvbmi2
|
|
|
|
|
CPUvpopcntdq
|
|
|
|
|
CPUavx512vnni
|
|
|
|
|
|
|
|
|
|
CPUneon
|
|
|
|
|
CPUsve2
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (f CPUfeatures) String() string {
|
|
|
|
|
if f == CPUNone {
|
|
|
|
|
return "none"
|
|
|
|
|
}
|
|
|
|
|
if f == CPUAll {
|
|
|
|
|
return "all"
|
|
|
|
|
}
|
|
|
|
|
s := ""
|
|
|
|
|
foo := func(what string, feat CPUfeatures) {
|
|
|
|
|
if feat&f != 0 {
|
|
|
|
|
if s != "" {
|
|
|
|
|
s += "+"
|
|
|
|
|
}
|
|
|
|
|
s += what
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
foo("avx", CPUavx)
|
|
|
|
|
foo("avx2", CPUavx2)
|
|
|
|
|
foo("avx512", CPUavx512)
|
|
|
|
|
foo("avxvnni", CPUavxvnni)
|
|
|
|
|
foo("bitalg", CPUbitalg)
|
|
|
|
|
foo("gfni", CPUgfni)
|
|
|
|
|
foo("vbmi", CPUvbmi)
|
|
|
|
|
foo("vbmi2", CPUvbmi2)
|
|
|
|
|
foo("popcntdq", CPUvpopcntdq)
|
|
|
|
|
foo("avx512vnni", CPUavx512vnni)
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|