update go-ffmpreg to v0.2.5 (pulls in latest tetratelabs/wazero) (#3203)

This commit is contained in:
kim
2024-08-15 00:08:55 +00:00
committed by GitHub
parent 6fe96a5611
commit 09f24e0446
75 changed files with 1772 additions and 1913 deletions

View File

@ -34,9 +34,6 @@ type BasicBlock interface {
// The returned Value is the definition of the param in this block.
Param(i int) Value
// InsertInstruction inserts an instruction that implements Value into the tail of this block.
InsertInstruction(raw *Instruction)
// Root returns the root instruction of this block.
Root() *Instruction
@ -81,7 +78,7 @@ type (
rootInstr, currentInstr *Instruction
// params are Values that represent parameters to a basicBlock.
// Each parameter can be considered as an output of PHI instruction in traditional SSA.
params []Value
params Values
preds []basicBlockPredecessorInfo
success []*basicBlock
// singlePred is the alias to preds[0] for fast lookup, and only set after Seal is called.
@ -179,23 +176,23 @@ func (bb *basicBlock) ReturnBlock() bool {
// AddParam implements BasicBlock.AddParam.
func (bb *basicBlock) AddParam(b Builder, typ Type) Value {
paramValue := b.allocateValue(typ)
bb.params = append(bb.params, paramValue)
bb.params = bb.params.Append(&b.(*builder).varLengthPool, paramValue)
return paramValue
}
// addParamOn adds a parameter to this block whose value is already allocated.
func (bb *basicBlock) addParamOn(value Value) {
bb.params = append(bb.params, value)
func (bb *basicBlock) addParamOn(b *builder, value Value) {
bb.params = bb.params.Append(&b.varLengthPool, value)
}
// Params implements BasicBlock.Params.
func (bb *basicBlock) Params() int {
return len(bb.params)
return len(bb.params.View())
}
// Param implements BasicBlock.Param.
func (bb *basicBlock) Param(i int) Value {
return bb.params[i]
return bb.params.View()[i]
}
// Valid implements BasicBlock.Valid.
@ -208,8 +205,8 @@ func (bb *basicBlock) Sealed() bool {
return bb.sealed
}
// InsertInstruction implements BasicBlock.InsertInstruction.
func (bb *basicBlock) InsertInstruction(next *Instruction) {
// insertInstruction implements BasicBlock.InsertInstruction.
func (bb *basicBlock) insertInstruction(b *builder, next *Instruction) {
current := bb.currentInstr
if current != nil {
current.next = next
@ -221,12 +218,12 @@ func (bb *basicBlock) InsertInstruction(next *Instruction) {
switch next.opcode {
case OpcodeJump, OpcodeBrz, OpcodeBrnz:
target := next.blk.(*basicBlock)
target.addPred(bb, next)
target := BasicBlockID(next.rValue)
b.basicBlock(target).addPred(bb, next)
case OpcodeBrTable:
for _, _target := range next.targets {
target := _target.(*basicBlock)
target.addPred(bb, next)
for _, _target := range next.rValues.View() {
target := BasicBlockID(_target)
b.basicBlock(target).addPred(bb, next)
}
}
}
@ -268,7 +265,7 @@ func (bb *basicBlock) Tail() *Instruction {
// reset resets the basicBlock to its initial state so that it can be reused for another function.
func resetBasicBlock(bb *basicBlock) {
bb.params = bb.params[:0]
bb.params = ValuesNil
bb.rootInstr, bb.currentInstr = nil, nil
bb.preds = bb.preds[:0]
bb.success = bb.success[:0]
@ -310,8 +307,8 @@ func (bb *basicBlock) addPred(blk BasicBlock, branch *Instruction) {
// formatHeader returns the string representation of the header of the basicBlock.
func (bb *basicBlock) formatHeader(b Builder) string {
ps := make([]string, len(bb.params))
for i, p := range bb.params {
ps := make([]string, len(bb.params.View()))
for i, p := range bb.params.View() {
ps[i] = p.formatWithType(b)
}
@ -339,7 +336,9 @@ func (bb *basicBlock) validate(b *builder) {
if len(bb.preds) > 0 {
for _, pred := range bb.preds {
if pred.branch.opcode != OpcodeBrTable {
if target := pred.branch.blk; target != bb {
blockID := int(pred.branch.rValue)
target := b.basicBlocksPool.View(blockID)
if target != bb {
panic(fmt.Sprintf("BUG: '%s' is not branch to %s, but to %s",
pred.branch.Format(b), bb.Name(), target.Name()))
}
@ -349,14 +348,14 @@ func (bb *basicBlock) validate(b *builder) {
if bb.ReturnBlock() {
exp = len(b.currentSignature.Results)
} else {
exp = len(bb.params)
exp = len(bb.params.View())
}
if len(pred.branch.vs.View()) != exp {
panic(fmt.Sprintf(
"BUG: len(argument at %s) != len(params at %s): %d != %d: %s",
pred.blk.Name(), bb.Name(),
len(pred.branch.vs.View()), len(bb.params), pred.branch.Format(b),
len(pred.branch.vs.View()), len(bb.params.View()), pred.branch.Format(b),
))
}

View File

@ -1,5 +1,3 @@
//go:build go1.21
package ssa
import (

View File

@ -1,24 +0,0 @@
//go:build !go1.21
// TODO: delete after the floor Go version is 1.21
package ssa
import "sort"
func sortBlocks(blocks []*basicBlock) {
sort.SliceStable(blocks, func(i, j int) bool {
iBlk, jBlk := blocks[i], blocks[j]
if jBlk.ReturnBlock() {
return true
}
if iBlk.ReturnBlock() {
return false
}
iRoot, jRoot := iBlk.rootInstr, jBlk.rootInstr
if iRoot == nil || jRoot == nil { // For testing.
return true
}
return iBlk.rootInstr.id < jBlk.rootInstr.id
})
}

View File

@ -94,9 +94,9 @@ type Builder interface {
// Returns nil if there's no unseen BasicBlock.
BlockIteratorNext() BasicBlock
// ValueRefCounts returns the map of ValueID to its reference count.
// The returned slice must not be modified.
ValueRefCounts() []int
// ValuesInfo returns the data per Value used to lower the SSA in backend.
// This is indexed by ValueID.
ValuesInfo() []ValueInfo
// BlockIteratorReversePostOrderBegin is almost the same as BlockIteratorBegin except it returns the BasicBlock in the reverse post-order.
// This is available after RunPasses is run.
@ -129,20 +129,24 @@ type Builder interface {
// InsertZeroValue inserts a zero value constant instruction of the given type.
InsertZeroValue(t Type)
// BasicBlock returns the BasicBlock of the given ID.
BasicBlock(id BasicBlockID) BasicBlock
// InstructionOfValue returns the Instruction that produces the given Value or nil if the Value is not produced by any Instruction.
InstructionOfValue(v Value) *Instruction
}
// NewBuilder returns a new Builder implementation.
func NewBuilder() Builder {
return &builder{
instructionsPool: wazevoapi.NewPool[Instruction](resetInstruction),
basicBlocksPool: wazevoapi.NewPool[basicBlock](resetBasicBlock),
varLengthBasicBlockPool: wazevoapi.NewVarLengthPool[BasicBlock](),
varLengthPool: wazevoapi.NewVarLengthPool[Value](),
valueAnnotations: make(map[ValueID]string),
signatures: make(map[SignatureID]*Signature),
valueIDAliases: make(map[ValueID]Value),
redundantParameterIndexToValue: make(map[int]Value),
returnBlk: &basicBlock{id: basicBlockIDReturnBlock},
instructionsPool: wazevoapi.NewPool[Instruction](resetInstruction),
basicBlocksPool: wazevoapi.NewPool[basicBlock](resetBasicBlock),
varLengthBasicBlockPool: wazevoapi.NewVarLengthPool[BasicBlock](),
varLengthPool: wazevoapi.NewVarLengthPool[Value](),
valueAnnotations: make(map[ValueID]string),
signatures: make(map[SignatureID]*Signature),
returnBlk: &basicBlock{id: basicBlockIDReturnBlock},
}
}
@ -159,19 +163,16 @@ type builder struct {
currentBB *basicBlock
returnBlk *basicBlock
// variables track the types for Variable with the index regarded Variable.
variables []Type
// nextValueID is used by builder.AllocateValue.
nextValueID ValueID
// nextVariable is used by builder.AllocateVariable.
nextVariable Variable
valueIDAliases map[ValueID]Value
// valueAnnotations contains the annotations for each Value, only used for debugging.
valueAnnotations map[ValueID]string
// valueRefCounts is used to lower the SSA in backend, and will be calculated
// by the last SSA-level optimization pass.
valueRefCounts []int
// valuesInfo contains the data per Value used to lower the SSA in backend. This is indexed by ValueID.
valuesInfo []ValueInfo
// dominators stores the immediate dominator of each BasicBlock.
// The index is blockID of the BasicBlock.
@ -184,12 +185,10 @@ type builder struct {
loopNestingForestRoots []BasicBlock
// The followings are used for optimization passes/deterministic compilation.
instStack []*Instruction
valueIDToInstruction []*Instruction
blkStack []*basicBlock
blkStack2 []*basicBlock
ints []int
redundantParameterIndexToValue map[int]Value
instStack []*Instruction
blkStack []*basicBlock
blkStack2 []*basicBlock
redundantParams []redundantParam
// blockIterCur is used to implement blockIteratorBegin and blockIteratorNext.
blockIterCur int
@ -207,6 +206,34 @@ type builder struct {
zeros [typeEnd]Value
}
// ValueInfo contains the data per Value used to lower the SSA in backend.
type ValueInfo struct {
// RefCount is the reference count of the Value.
RefCount uint32
alias Value
}
// redundantParam is a pair of the index of the redundant parameter and the Value.
// This is used to eliminate the redundant parameters in the optimization pass.
type redundantParam struct {
// index is the index of the redundant parameter in the basicBlock.
index int
// uniqueValue is the Value which is passed to the redundant parameter.
uniqueValue Value
}
// BasicBlock implements Builder.BasicBlock.
func (b *builder) BasicBlock(id BasicBlockID) BasicBlock {
return b.basicBlock(id)
}
func (b *builder) basicBlock(id BasicBlockID) *basicBlock {
if id == basicBlockIDReturnBlock {
return b.returnBlk
}
return b.basicBlocksPool.View(int(id))
}
// InsertZeroValue implements Builder.InsertZeroValue.
func (b *builder) InsertZeroValue(t Type) {
if b.zeros[t].Valid() {
@ -256,7 +283,7 @@ func (b *builder) Init(s *Signature) {
sig.used = false
}
b.ints = b.ints[:0]
b.redundantParams = b.redundantParams[:0]
b.blkStack = b.blkStack[:0]
b.blkStack2 = b.blkStack2[:0]
b.dominators = b.dominators[:0]
@ -265,17 +292,11 @@ func (b *builder) Init(s *Signature) {
for v := ValueID(0); v < b.nextValueID; v++ {
delete(b.valueAnnotations, v)
delete(b.valueIDAliases, v)
b.valueRefCounts[v] = 0
b.valueIDToInstruction[v] = nil
b.valuesInfo[v] = ValueInfo{alias: ValueInvalid}
}
b.nextValueID = 0
b.reversePostOrderedBasicBlocks = b.reversePostOrderedBasicBlocks[:0]
b.doneBlockLayout = false
for i := range b.valueRefCounts {
b.valueRefCounts[i] = 0
}
b.currentSourceOffset = sourceOffsetUnknown
}
@ -355,7 +376,7 @@ func (b *builder) Idom(blk BasicBlock) BasicBlock {
// InsertInstruction implements Builder.InsertInstruction.
func (b *builder) InsertInstruction(instr *Instruction) {
b.currentBB.InsertInstruction(instr)
b.currentBB.insertInstruction(b, instr)
if l := b.currentSourceOffset; l.Valid() {
// Emit the source offset info only when the instruction has side effect because
@ -377,7 +398,7 @@ func (b *builder) InsertInstruction(instr *Instruction) {
}
r1 := b.allocateValue(t1)
instr.rValue = r1
instr.rValue = r1.setInstructionID(instr.id)
tsl := len(ts)
if tsl == 0 {
@ -386,20 +407,14 @@ func (b *builder) InsertInstruction(instr *Instruction) {
rValues := b.varLengthPool.Allocate(tsl)
for i := 0; i < tsl; i++ {
rValues = rValues.Append(&b.varLengthPool, b.allocateValue(ts[i]))
rn := b.allocateValue(ts[i])
rValues = rValues.Append(&b.varLengthPool, rn.setInstructionID(instr.id))
}
instr.rValues = rValues
}
// DefineVariable implements Builder.DefineVariable.
func (b *builder) DefineVariable(variable Variable, value Value, block BasicBlock) {
if b.variables[variable].invalid() {
panic("BUG: trying to define variable " + variable.String() + " but is not declared yet")
}
if b.variables[variable] != value.Type() {
panic(fmt.Sprintf("BUG: inconsistent type for variable %d: expected %s but got %s", variable, b.variables[variable], value.Type()))
}
bb := block.(*basicBlock)
bb.lastDefinitions[variable] = value
}
@ -426,20 +441,9 @@ func (b *builder) EntryBlock() BasicBlock {
// DeclareVariable implements Builder.DeclareVariable.
func (b *builder) DeclareVariable(typ Type) Variable {
v := b.allocateVariable()
iv := int(v)
if l := len(b.variables); l <= iv {
b.variables = append(b.variables, make([]Type, 2*(l+1))...)
}
b.variables[v] = typ
return v
}
// allocateVariable allocates a new variable.
func (b *builder) allocateVariable() (ret Variable) {
ret = b.nextVariable
v := b.nextVariable
b.nextVariable++
return
return v.setType(typ)
}
// allocateValue implements Builder.AllocateValue.
@ -475,8 +479,7 @@ func (b *builder) findValueInLinearPath(variable Variable, blk *basicBlock) Valu
// MustFindValue implements Builder.MustFindValue.
func (b *builder) MustFindValue(variable Variable) Value {
typ := b.definedVariableType(variable)
return b.findValue(typ, variable, b.currentBB)
return b.findValue(variable.getType(), variable, b.currentBB)
}
// findValue recursively tries to find the latest definition of a `variable`. The algorithm is described in
@ -504,7 +507,7 @@ func (b *builder) findValue(typ Type, variable Variable, blk *basicBlock) Value
return value
} else if blk.EntryBlock() {
// If this is the entry block, we reach the uninitialized variable which has zero value.
return b.zeros[b.definedVariableType(variable)]
return b.zeros[variable.getType()]
}
if pred := blk.singlePred; pred != nil {
@ -536,14 +539,13 @@ func (b *builder) findValue(typ Type, variable Variable, blk *basicBlock) Value
if uniqueValue != ValueInvalid {
// If all the predecessors have the same definition, we can use that value.
b.DefineVariable(variable, uniqueValue, blk)
b.alias(tmpValue, uniqueValue)
return uniqueValue
} else {
// Otherwise, add the tmpValue to this block as a parameter which may or may not be redundant, but
// later we eliminate trivial params in an optimization pass. This must be done before finding the
// definitions in the predecessors so that we can break the cycle.
blk.addParamOn(tmpValue)
blk.addParamOn(b, tmpValue)
// After the new param is added, we have to manipulate the original branching instructions
// in predecessors so that they would pass the definition of `variable` as the argument to
// the newly added PHI.
@ -566,8 +568,8 @@ func (b *builder) Seal(raw BasicBlock) {
for _, v := range blk.unknownValues {
variable, phiValue := v.variable, v.value
typ := b.definedVariableType(variable)
blk.addParamOn(phiValue)
typ := variable.getType()
blk.addParamOn(b, phiValue)
for i := range blk.preds {
pred := &blk.preds[i]
predValue := b.findValue(typ, variable, pred.blk)
@ -579,15 +581,6 @@ func (b *builder) Seal(raw BasicBlock) {
}
}
// definedVariableType returns the type of the given variable. If the variable is not defined yet, it panics.
func (b *builder) definedVariableType(variable Variable) Type {
typ := b.variables[variable]
if typ.invalid() {
panic(fmt.Sprintf("%s is not defined yet", variable))
}
return typ
}
// Format implements Builder.Format.
func (b *builder) Format() string {
str := strings.Builder{}
@ -689,15 +682,24 @@ func (b *builder) blockIteratorReversePostOrderNext() *basicBlock {
}
}
// ValueRefCounts implements Builder.ValueRefCounts.
func (b *builder) ValueRefCounts() []int {
return b.valueRefCounts
// ValuesInfo implements Builder.ValuesInfo.
func (b *builder) ValuesInfo() []ValueInfo {
return b.valuesInfo
}
// alias records the alias of the given values. The alias(es) will be
// eliminated in the optimization pass via resolveArgumentAlias.
func (b *builder) alias(dst, src Value) {
b.valueIDAliases[dst.ID()] = src
did := int(dst.ID())
if did >= len(b.valuesInfo) {
l := did + 1 - len(b.valuesInfo)
b.valuesInfo = append(b.valuesInfo, make([]ValueInfo, l)...)
view := b.valuesInfo[len(b.valuesInfo)-l:]
for i := range view {
view[i].alias = ValueInvalid
}
}
b.valuesInfo[did].alias = src
}
// resolveArgumentAlias resolves the alias of the arguments of the given instruction.
@ -722,10 +724,13 @@ func (b *builder) resolveArgumentAlias(instr *Instruction) {
// resolveAlias resolves the alias of the given value.
func (b *builder) resolveAlias(v Value) Value {
info := b.valuesInfo
l := ValueID(len(info))
// Some aliases are chained, so we need to resolve them recursively.
for {
if src, ok := b.valueIDAliases[v.ID()]; ok {
v = src
vid := v.ID()
if vid < l && info[vid].alias.Valid() {
v = info[vid].alias
} else {
break
}
@ -773,3 +778,13 @@ func (b *builder) LoopNestingForestRoots() []BasicBlock {
func (b *builder) LowestCommonAncestor(blk1, blk2 BasicBlock) BasicBlock {
return b.sparseTree.findLCA(blk1.ID(), blk2.ID())
}
// InstructionOfValue returns the instruction that produces the given Value, or nil
// if the Value is not produced by any instruction.
func (b *builder) InstructionOfValue(v Value) *Instruction {
instrID := v.instructionID()
if instrID <= 0 {
return nil
}
return b.instructionsPool.View(instrID - 1)
}

View File

@ -25,11 +25,13 @@ type Instruction struct {
v3 Value
vs Values
typ Type
blk BasicBlock
targets []BasicBlock
prev, next *Instruction
rValue Value
// rValue is the (first) return value of this instruction.
// For branching instructions except for OpcodeBrTable, they hold BlockID to jump cast to Value.
rValue Value
// rValues are the rest of the return values of this instruction.
// For OpcodeBrTable, it holds the list of BlockID to jump cast to Value.
rValues Values
gid InstructionGroupID
sourceOffset SourceOffset
@ -105,6 +107,9 @@ type InstructionGroupID uint32
// Returns Value(s) produced by this instruction if any.
// The `first` is the first return value, and `rest` is the rest of the values.
func (i *Instruction) Returns() (first Value, rest []Value) {
if i.IsBranching() {
return ValueInvalid, nil
}
return i.rValue, i.rValues.View()
}
@ -2077,7 +2082,7 @@ func (i *Instruction) InvertBrx() {
}
// BranchData returns the branch data for this instruction necessary for backends.
func (i *Instruction) BranchData() (condVal Value, blockArgs []Value, target BasicBlock) {
func (i *Instruction) BranchData() (condVal Value, blockArgs []Value, target BasicBlockID) {
switch i.opcode {
case OpcodeJump:
condVal = ValueInvalid
@ -2087,17 +2092,17 @@ func (i *Instruction) BranchData() (condVal Value, blockArgs []Value, target Bas
panic("BUG")
}
blockArgs = i.vs.View()
target = i.blk
target = BasicBlockID(i.rValue)
return
}
// BrTableData returns the branch table data for this instruction necessary for backends.
func (i *Instruction) BrTableData() (index Value, targets []BasicBlock) {
func (i *Instruction) BrTableData() (index Value, targets Values) {
if i.opcode != OpcodeBrTable {
panic("BUG: BrTableData only available for OpcodeBrTable")
}
index = i.v
targets = i.targets
targets = i.rValues
return
}
@ -2105,7 +2110,7 @@ func (i *Instruction) BrTableData() (index Value, targets []BasicBlock) {
func (i *Instruction) AsJump(vs Values, target BasicBlock) *Instruction {
i.opcode = OpcodeJump
i.vs = vs
i.blk = target
i.rValue = Value(target.ID())
return i
}
@ -2130,7 +2135,7 @@ func (i *Instruction) AsBrz(v Value, args Values, target BasicBlock) {
i.opcode = OpcodeBrz
i.v = v
i.vs = args
i.blk = target
i.rValue = Value(target.ID())
}
// AsBrnz initializes this instruction as a branch-if-not-zero instruction with OpcodeBrnz.
@ -2138,15 +2143,16 @@ func (i *Instruction) AsBrnz(v Value, args Values, target BasicBlock) *Instructi
i.opcode = OpcodeBrnz
i.v = v
i.vs = args
i.blk = target
i.rValue = Value(target.ID())
return i
}
// AsBrTable initializes this instruction as a branch-table instruction with OpcodeBrTable.
func (i *Instruction) AsBrTable(index Value, targets []BasicBlock) {
// targets is a list of basic block IDs cast to Values.
func (i *Instruction) AsBrTable(index Value, targets Values) {
i.opcode = OpcodeBrTable
i.v = index
i.targets = targets
i.rValues = targets
}
// AsCall initializes this instruction as a call instruction with OpcodeCall.
@ -2531,7 +2537,8 @@ func (i *Instruction) Format(b Builder) string {
if i.IsFallthroughJump() {
vs[0] = " fallthrough"
} else {
vs[0] = " " + i.blk.(*basicBlock).Name()
blockId := BasicBlockID(i.rValue)
vs[0] = " " + b.BasicBlock(blockId).Name()
}
for idx := range view {
vs[idx+1] = view[idx].Format(b)
@ -2542,7 +2549,8 @@ func (i *Instruction) Format(b Builder) string {
view := i.vs.View()
vs := make([]string, len(view)+2)
vs[0] = " " + i.v.Format(b)
vs[1] = i.blk.(*basicBlock).Name()
blockId := BasicBlockID(i.rValue)
vs[1] = b.BasicBlock(blockId).Name()
for idx := range view {
vs[idx+2] = view[idx].Format(b)
}
@ -2551,8 +2559,8 @@ func (i *Instruction) Format(b Builder) string {
// `BrTable index, [label1, label2, ... labelN]`
instSuffix = fmt.Sprintf(" %s", i.v.Format(b))
instSuffix += ", ["
for i, target := range i.targets {
blk := target.(*basicBlock)
for i, target := range i.rValues.View() {
blk := b.BasicBlock(BasicBlockID(target))
if i == 0 {
instSuffix += blk.Name()
} else {
@ -2621,11 +2629,12 @@ func (i *Instruction) Format(b Builder) string {
instr := i.opcode.String() + instSuffix
var rvs []string
if rv := i.rValue; rv.Valid() {
rvs = append(rvs, rv.formatWithType(b))
r1, rs := i.Returns()
if r1.Valid() {
rvs = append(rvs, r1.formatWithType(b))
}
for _, v := range i.rValues.View() {
for _, v := range rs {
rvs = append(rvs, v.formatWithType(b))
}

View File

@ -112,7 +112,7 @@ func passDeadBlockEliminationOpt(b *builder) {
// This requires the reverse post-order traversal to be calculated before calling this function,
// hence passCalculateImmediateDominators must be called before this.
func passRedundantPhiEliminationOpt(b *builder) {
redundantParameterIndexes := b.ints[:0] // reuse the slice from previous iterations.
redundantParams := b.redundantParams[:0] // reuse the slice from previous iterations.
// TODO: this might be costly for large programs, but at least, as far as I did the experiment, it's almost the
// same as the single iteration version in terms of the overall compilation time. That *might be* mostly thanks to the fact
@ -128,10 +128,11 @@ func passRedundantPhiEliminationOpt(b *builder) {
_ = b.blockIteratorReversePostOrderBegin() // skip entry block!
// Below, we intentionally use the named iteration variable name, as this comes with inevitable nested for loops!
for blk := b.blockIteratorReversePostOrderNext(); blk != nil; blk = b.blockIteratorReversePostOrderNext() {
paramNum := len(blk.params)
params := blk.params.View()
paramNum := len(params)
for paramIndex := 0; paramIndex < paramNum; paramIndex++ {
phiValue := blk.params[paramIndex]
phiValue := params[paramIndex]
redundant := true
nonSelfReferencingValue := ValueInvalid
@ -162,55 +163,58 @@ func passRedundantPhiEliminationOpt(b *builder) {
}
if redundant {
b.redundantParameterIndexToValue[paramIndex] = nonSelfReferencingValue
redundantParameterIndexes = append(redundantParameterIndexes, paramIndex)
redundantParams = append(redundantParams, redundantParam{
index: paramIndex, uniqueValue: nonSelfReferencingValue,
})
}
}
if len(b.redundantParameterIndexToValue) == 0 {
if len(redundantParams) == 0 {
continue
}
changed = true
// Remove the redundant PHIs from the argument list of branching instructions.
for predIndex := range blk.preds {
var cur int
redundantParamsCur, predParamCur := 0, 0
predBlk := blk.preds[predIndex]
branchInst := predBlk.branch
view := branchInst.vs.View()
for argIndex, value := range view {
if _, ok := b.redundantParameterIndexToValue[argIndex]; !ok {
view[cur] = value
cur++
if len(redundantParams) == redundantParamsCur ||
redundantParams[redundantParamsCur].index != argIndex {
view[predParamCur] = value
predParamCur++
} else {
redundantParamsCur++
}
}
branchInst.vs.Cut(cur)
branchInst.vs.Cut(predParamCur)
}
// Still need to have the definition of the value of the PHI (previously as the parameter).
for _, redundantParamIndex := range redundantParameterIndexes {
phiValue := blk.params[redundantParamIndex]
onlyValue := b.redundantParameterIndexToValue[redundantParamIndex]
for i := range redundantParams {
redundantValue := &redundantParams[i]
phiValue := params[redundantValue.index]
// Create an alias in this block from the only phi argument to the phi value.
b.alias(phiValue, onlyValue)
b.alias(phiValue, redundantValue.uniqueValue)
}
// Finally, Remove the param from the blk.
var cur int
paramsCur, redundantParamsCur := 0, 0
for paramIndex := 0; paramIndex < paramNum; paramIndex++ {
param := blk.params[paramIndex]
if _, ok := b.redundantParameterIndexToValue[paramIndex]; !ok {
blk.params[cur] = param
cur++
param := params[paramIndex]
if len(redundantParams) == redundantParamsCur || redundantParams[redundantParamsCur].index != paramIndex {
params[paramsCur] = param
paramsCur++
} else {
redundantParamsCur++
}
}
blk.params = blk.params[:cur]
blk.params.Cut(paramsCur)
// Clears the map for the next iteration.
for _, paramIndex := range redundantParameterIndexes {
delete(b.redundantParameterIndexToValue, paramIndex)
}
redundantParameterIndexes = redundantParameterIndexes[:0]
redundantParams = redundantParams[:0]
}
if !changed {
@ -219,7 +223,7 @@ func passRedundantPhiEliminationOpt(b *builder) {
}
// Reuse the slice for the future passes.
b.ints = redundantParameterIndexes
b.redundantParams = redundantParams
}
// passDeadCodeEliminationOpt traverses all the instructions, and calculates the reference count of each Value, and
@ -231,11 +235,13 @@ func passRedundantPhiEliminationOpt(b *builder) {
// TODO: the algorithm here might not be efficient. Get back to this later.
func passDeadCodeEliminationOpt(b *builder) {
nvid := int(b.nextValueID)
if nvid >= len(b.valueRefCounts) {
b.valueRefCounts = append(b.valueRefCounts, make([]int, nvid-len(b.valueRefCounts)+1)...)
}
if nvid >= len(b.valueIDToInstruction) {
b.valueIDToInstruction = append(b.valueIDToInstruction, make([]*Instruction, nvid-len(b.valueIDToInstruction)+1)...)
if nvid >= len(b.valuesInfo) {
l := nvid - len(b.valuesInfo) + 1
b.valuesInfo = append(b.valuesInfo, make([]ValueInfo, l)...)
view := b.valuesInfo[len(b.valuesInfo)-l:]
for i := range view {
view[i].alias = ValueInvalid
}
}
// First, we gather all the instructions with side effects.
@ -255,14 +261,6 @@ func passDeadCodeEliminationOpt(b *builder) {
// The strict side effect should create different instruction groups.
gid++
}
r1, rs := cur.Returns()
if r1.Valid() {
b.valueIDToInstruction[r1.ID()] = cur
}
for _, r := range rs {
b.valueIDToInstruction[r.ID()] = cur
}
}
}
@ -283,28 +281,28 @@ func passDeadCodeEliminationOpt(b *builder) {
v1, v2, v3, vs := live.Args()
if v1.Valid() {
producingInst := b.valueIDToInstruction[v1.ID()]
producingInst := b.InstructionOfValue(v1)
if producingInst != nil {
liveInstructions = append(liveInstructions, producingInst)
}
}
if v2.Valid() {
producingInst := b.valueIDToInstruction[v2.ID()]
producingInst := b.InstructionOfValue(v2)
if producingInst != nil {
liveInstructions = append(liveInstructions, producingInst)
}
}
if v3.Valid() {
producingInst := b.valueIDToInstruction[v3.ID()]
producingInst := b.InstructionOfValue(v3)
if producingInst != nil {
liveInstructions = append(liveInstructions, producingInst)
}
}
for _, v := range vs {
producingInst := b.valueIDToInstruction[v.ID()]
producingInst := b.InstructionOfValue(v)
if producingInst != nil {
liveInstructions = append(liveInstructions, producingInst)
}
@ -352,34 +350,19 @@ func (b *builder) incRefCount(id ValueID, from *Instruction) {
if wazevoapi.SSALoggingEnabled {
fmt.Printf("v%d referenced from %v\n", id, from.Format(b))
}
b.valueRefCounts[id]++
info := &b.valuesInfo[id]
info.RefCount++
}
// passNopInstElimination eliminates the instructions which is essentially a no-op.
func passNopInstElimination(b *builder) {
if int(b.nextValueID) >= len(b.valueIDToInstruction) {
b.valueIDToInstruction = append(b.valueIDToInstruction, make([]*Instruction, int(b.nextValueID)-len(b.valueIDToInstruction)+1)...)
}
for blk := b.blockIteratorBegin(); blk != nil; blk = b.blockIteratorNext() {
for cur := blk.rootInstr; cur != nil; cur = cur.next {
r1, rs := cur.Returns()
if r1.Valid() {
b.valueIDToInstruction[r1.ID()] = cur
}
for _, r := range rs {
b.valueIDToInstruction[r.ID()] = cur
}
}
}
for blk := b.blockIteratorBegin(); blk != nil; blk = b.blockIteratorNext() {
for cur := blk.rootInstr; cur != nil; cur = cur.next {
switch cur.Opcode() {
// TODO: add more logics here.
case OpcodeIshl, OpcodeSshr, OpcodeUshr:
x, amount := cur.Arg2()
definingInst := b.valueIDToInstruction[amount.ID()]
definingInst := b.InstructionOfValue(amount)
if definingInst == nil {
// If there's no defining instruction, that means the amount is coming from the parameter.
continue

View File

@ -33,7 +33,7 @@ func passLayoutBlocks(b *builder) {
}
nonSplitBlocks = append(nonSplitBlocks, blk)
if i != len(b.reversePostOrderedBasicBlocks)-1 {
_ = maybeInvertBranches(blk, b.reversePostOrderedBasicBlocks[i+1])
_ = maybeInvertBranches(b, blk, b.reversePostOrderedBasicBlocks[i+1])
}
}
@ -111,7 +111,7 @@ func passLayoutBlocks(b *builder) {
}
fallthroughBranch := blk.currentInstr
if fallthroughBranch.opcode == OpcodeJump && fallthroughBranch.blk == trampoline {
if fallthroughBranch.opcode == OpcodeJump && BasicBlockID(fallthroughBranch.rValue) == trampoline.id {
// This can be lowered as fallthrough at the end of the block.
b.reversePostOrderedBasicBlocks = append(b.reversePostOrderedBasicBlocks, trampoline)
trampoline.visited = 1 // mark as inserted.
@ -157,7 +157,7 @@ func (b *builder) markFallthroughJumps() {
for i, blk := range b.reversePostOrderedBasicBlocks {
if i < l {
cur := blk.currentInstr
if cur.opcode == OpcodeJump && cur.blk == b.reversePostOrderedBasicBlocks[i+1] {
if cur.opcode == OpcodeJump && BasicBlockID(cur.rValue) == b.reversePostOrderedBasicBlocks[i+1].id {
cur.AsFallthroughJump()
}
}
@ -168,7 +168,7 @@ func (b *builder) markFallthroughJumps() {
// nextInRPO is the next block in the reverse post-order.
//
// Returns true if the branch is inverted for testing purpose.
func maybeInvertBranches(now *basicBlock, nextInRPO *basicBlock) bool {
func maybeInvertBranches(b *builder, now *basicBlock, nextInRPO *basicBlock) bool {
fallthroughBranch := now.currentInstr
if fallthroughBranch.opcode == OpcodeBrTable {
return false
@ -187,7 +187,8 @@ func maybeInvertBranches(now *basicBlock, nextInRPO *basicBlock) bool {
// So this block has two branches (a conditional branch followed by an unconditional branch) at the end.
// We can invert the condition of the branch if it makes the fallthrough more likely.
fallthroughTarget, condTarget := fallthroughBranch.blk.(*basicBlock), condBranch.blk.(*basicBlock)
fallthroughTarget := b.basicBlock(BasicBlockID(fallthroughBranch.rValue))
condTarget := b.basicBlock(BasicBlockID(condBranch.rValue))
if fallthroughTarget.loopHeader {
// First, if the tail's target is loopHeader, we don't need to do anything here,
@ -231,8 +232,8 @@ invert:
}
condBranch.InvertBrx()
condBranch.blk = fallthroughTarget
fallthroughBranch.blk = condTarget
condBranch.rValue = Value(fallthroughTarget.ID())
fallthroughBranch.rValue = Value(condTarget.ID())
if wazevoapi.SSALoggingEnabled {
fmt.Printf("inverting branches at %d->%d and %d->%d\n",
now.ID(), fallthroughTarget.ID(), now.ID(), condTarget.ID())
@ -275,7 +276,7 @@ func (b *builder) splitCriticalEdge(pred, succ *basicBlock, predInfo *basicBlock
// Replace originalBranch with the newBranch.
newBranch := b.AllocateInstruction()
newBranch.opcode = originalBranch.opcode
newBranch.blk = trampoline
newBranch.rValue = Value(trampoline.ID())
switch originalBranch.opcode {
case OpcodeJump:
case OpcodeBrz, OpcodeBrnz:
@ -303,7 +304,7 @@ func (b *builder) splitCriticalEdge(pred, succ *basicBlock, predInfo *basicBlock
trampoline.validate(b)
}
if len(trampoline.params) > 0 {
if len(trampoline.params.View()) > 0 {
panic("trampoline should not have params")
}

View File

@ -15,17 +15,31 @@ import (
//
// Variable is useful to track the SSA Values of a variable in the source program, and
// can be used to find the corresponding latest SSA Value via Builder.FindValue.
//
// Higher 4-bit is used to store Type for this variable.
type Variable uint32
// String implements fmt.Stringer.
func (v Variable) String() string {
return fmt.Sprintf("var%d", v)
return fmt.Sprintf("var%d", v&0x0fffffff)
}
func (v Variable) setType(typ Type) Variable {
if v >= 1<<28 {
panic(fmt.Sprintf("Too large variable: %d", v))
}
return Variable(typ)<<28 | v
}
func (v Variable) getType() Type {
return Type(v >> 28)
}
// Value represents an SSA value with a type information. The relationship with Variable is 1: N (including 0),
// that means there might be multiple Variable(s) for a Value.
//
// Higher 32-bit is used to store Type for this value.
// 32 to 59-bit is used to store the unique identifier of the Instruction that generates this value if any.
// 60 to 63-bit is used to store Type for this value.
type Value uint64
// ValueID is the lower 32bit of Value, which is the pure identifier of Value without type info.
@ -33,7 +47,7 @@ type ValueID uint32
const (
valueIDInvalid ValueID = math.MaxUint32
ValueInvalid Value = Value(valueIDInvalid)
ValueInvalid = Value(valueIDInvalid)
)
// Format creates a debug string for this Value using the data stored in Builder.
@ -54,7 +68,7 @@ func (v Value) formatWithType(b Builder) (ret string) {
if wazevoapi.SSALoggingEnabled { // This is useful to check live value analysis bugs.
if bd := b.(*builder); bd.donePostBlockLayoutPasses {
id := v.ID()
ret += fmt.Sprintf("(ref=%d)", bd.valueRefCounts[id])
ret += fmt.Sprintf("(ref=%d)", bd.valuesInfo[id].RefCount)
}
}
return ret
@ -67,7 +81,7 @@ func (v Value) Valid() bool {
// Type returns the Type of this value.
func (v Value) Type() Type {
return Type(v >> 32)
return Type(v >> 60)
}
// ID returns the valueID of this value.
@ -77,7 +91,20 @@ func (v Value) ID() ValueID {
// setType sets a type to this Value and returns the updated Value.
func (v Value) setType(typ Type) Value {
return v | Value(typ)<<32
return v | Value(typ)<<60
}
// setInstructionID sets an Instruction.id to this Value and returns the updated Value.
func (v Value) setInstructionID(id int) Value {
if id < 0 || uint(id) >= 1<<28 {
panic(fmt.Sprintf("Too large instruction ID: %d", id))
}
return v | Value(id)<<32
}
// instructionID() returns the Instruction.id of this Value.
func (v Value) instructionID() int {
return int(v>>32) & 0x0fffffff
}
// Values is a slice of Value. Use this instead of []Value to reuse the underlying memory.