ssa: reuses slices for basicBlock.params (#2247)

This replaces the basicBlock.params field with the reusable
VarLength[Value] type. As a result, the compilation starts
using less memory and allocations.

```
goos: darwin
goarch: arm64
pkg: github.com/tetratelabs/wazero
                      │  old.txt   │             new.txt              │
                      │   sec/op   │   sec/op    vs base              │
Compilation/wazero-10   2.004 ± 2%   2.001 ± 0%       ~ (p=0.620 n=7)
Compilation/zig-10      4.164 ± 1%   4.174 ± 3%       ~ (p=0.097 n=7)
geomean                 2.888        2.890       +0.06%

                      │   old.txt    │              new.txt               │
                      │     B/op     │     B/op      vs base              │
Compilation/wazero-10   297.7Mi ± 0%   297.5Mi ± 0%  -0.06% (p=0.001 n=7)
Compilation/zig-10      594.0Mi ± 0%   593.9Mi ± 0%  -0.01% (p=0.001 n=7)
geomean                 420.5Mi        420.3Mi       -0.03%

                      │   old.txt   │              new.txt              │
                      │  allocs/op  │  allocs/op   vs base              │
Compilation/wazero-10   472.5k ± 0%   457.1k ± 0%  -3.25% (p=0.001 n=7)
Compilation/zig-10      277.2k ± 0%   275.7k ± 0%  -0.53% (p=0.001 n=7)
geomean                 361.9k        355.0k       -1.90%
```

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
Takeshi Yoneda
2024-06-13 13:01:58 -07:00
committed by GitHub
parent 37fea1236c
commit 65650d399d
4 changed files with 21 additions and 20 deletions

View File

@@ -81,7 +81,7 @@ type (
rootInstr, currentInstr *Instruction rootInstr, currentInstr *Instruction
// params are Values that represent parameters to a basicBlock. // params are Values that represent parameters to a basicBlock.
// Each parameter can be considered as an output of PHI instruction in traditional SSA. // Each parameter can be considered as an output of PHI instruction in traditional SSA.
params []Value params Values
preds []basicBlockPredecessorInfo preds []basicBlockPredecessorInfo
success []*basicBlock success []*basicBlock
// singlePred is the alias to preds[0] for fast lookup, and only set after Seal is called. // singlePred is the alias to preds[0] for fast lookup, and only set after Seal is called.
@@ -179,23 +179,23 @@ func (bb *basicBlock) ReturnBlock() bool {
// AddParam implements BasicBlock.AddParam. // AddParam implements BasicBlock.AddParam.
func (bb *basicBlock) AddParam(b Builder, typ Type) Value { func (bb *basicBlock) AddParam(b Builder, typ Type) Value {
paramValue := b.allocateValue(typ) paramValue := b.allocateValue(typ)
bb.params = append(bb.params, paramValue) bb.params = bb.params.Append(&b.(*builder).varLengthPool, paramValue)
return paramValue return paramValue
} }
// addParamOn adds a parameter to this block whose value is already allocated. // addParamOn adds a parameter to this block whose value is already allocated.
func (bb *basicBlock) addParamOn(value Value) { func (bb *basicBlock) addParamOn(b *builder, value Value) {
bb.params = append(bb.params, value) bb.params = bb.params.Append(&b.varLengthPool, value)
} }
// Params implements BasicBlock.Params. // Params implements BasicBlock.Params.
func (bb *basicBlock) Params() int { func (bb *basicBlock) Params() int {
return len(bb.params) return len(bb.params.View())
} }
// Param implements BasicBlock.Param. // Param implements BasicBlock.Param.
func (bb *basicBlock) Param(i int) Value { func (bb *basicBlock) Param(i int) Value {
return bb.params[i] return bb.params.View()[i]
} }
// Valid implements BasicBlock.Valid. // Valid implements BasicBlock.Valid.
@@ -268,7 +268,7 @@ func (bb *basicBlock) Tail() *Instruction {
// reset resets the basicBlock to its initial state so that it can be reused for another function. // reset resets the basicBlock to its initial state so that it can be reused for another function.
func resetBasicBlock(bb *basicBlock) { func resetBasicBlock(bb *basicBlock) {
bb.params = bb.params[:0] bb.params = ValuesNil
bb.rootInstr, bb.currentInstr = nil, nil bb.rootInstr, bb.currentInstr = nil, nil
bb.preds = bb.preds[:0] bb.preds = bb.preds[:0]
bb.success = bb.success[:0] bb.success = bb.success[:0]
@@ -310,8 +310,8 @@ func (bb *basicBlock) addPred(blk BasicBlock, branch *Instruction) {
// formatHeader returns the string representation of the header of the basicBlock. // formatHeader returns the string representation of the header of the basicBlock.
func (bb *basicBlock) formatHeader(b Builder) string { func (bb *basicBlock) formatHeader(b Builder) string {
ps := make([]string, len(bb.params)) ps := make([]string, len(bb.params.View()))
for i, p := range bb.params { for i, p := range bb.params.View() {
ps[i] = p.formatWithType(b) ps[i] = p.formatWithType(b)
} }
@@ -349,14 +349,14 @@ func (bb *basicBlock) validate(b *builder) {
if bb.ReturnBlock() { if bb.ReturnBlock() {
exp = len(b.currentSignature.Results) exp = len(b.currentSignature.Results)
} else { } else {
exp = len(bb.params) exp = len(bb.params.View())
} }
if len(pred.branch.vs.View()) != exp { if len(pred.branch.vs.View()) != exp {
panic(fmt.Sprintf( panic(fmt.Sprintf(
"BUG: len(argument at %s) != len(params at %s): %d != %d: %s", "BUG: len(argument at %s) != len(params at %s): %d != %d: %s",
pred.blk.Name(), bb.Name(), 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

@@ -543,7 +543,7 @@ func (b *builder) findValue(typ Type, variable Variable, blk *basicBlock) Value
// Otherwise, add the tmpValue to this block as a parameter which may or may not be redundant, but // 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 // 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. // 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 // 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 // in predecessors so that they would pass the definition of `variable` as the argument to
// the newly added PHI. // the newly added PHI.
@@ -567,7 +567,7 @@ func (b *builder) Seal(raw BasicBlock) {
for _, v := range blk.unknownValues { for _, v := range blk.unknownValues {
variable, phiValue := v.variable, v.value variable, phiValue := v.variable, v.value
typ := b.definedVariableType(variable) typ := b.definedVariableType(variable)
blk.addParamOn(phiValue) blk.addParamOn(b, phiValue)
for i := range blk.preds { for i := range blk.preds {
pred := &blk.preds[i] pred := &blk.preds[i]
predValue := b.findValue(typ, variable, pred.blk) predValue := b.findValue(typ, variable, pred.blk)

View File

@@ -128,10 +128,11 @@ func passRedundantPhiEliminationOpt(b *builder) {
_ = b.blockIteratorReversePostOrderBegin() // skip entry block! _ = b.blockIteratorReversePostOrderBegin() // skip entry block!
// Below, we intentionally use the named iteration variable name, as this comes with inevitable nested for loops! // 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() { 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++ { for paramIndex := 0; paramIndex < paramNum; paramIndex++ {
phiValue := blk.params[paramIndex] phiValue := params[paramIndex]
redundant := true redundant := true
nonSelfReferencingValue := ValueInvalid nonSelfReferencingValue := ValueInvalid
@@ -189,7 +190,7 @@ func passRedundantPhiEliminationOpt(b *builder) {
// Still need to have the definition of the value of the PHI (previously as the parameter). // Still need to have the definition of the value of the PHI (previously as the parameter).
for _, redundantParamIndex := range redundantParameterIndexes { for _, redundantParamIndex := range redundantParameterIndexes {
phiValue := blk.params[redundantParamIndex] phiValue := params[redundantParamIndex]
onlyValue := b.redundantParameterIndexToValue[redundantParamIndex] onlyValue := b.redundantParameterIndexToValue[redundantParamIndex]
// Create an alias in this block from the only phi argument to the phi value. // Create an alias in this block from the only phi argument to the phi value.
b.alias(phiValue, onlyValue) b.alias(phiValue, onlyValue)
@@ -198,13 +199,13 @@ func passRedundantPhiEliminationOpt(b *builder) {
// Finally, Remove the param from the blk. // Finally, Remove the param from the blk.
var cur int var cur int
for paramIndex := 0; paramIndex < paramNum; paramIndex++ { for paramIndex := 0; paramIndex < paramNum; paramIndex++ {
param := blk.params[paramIndex] param := params[paramIndex]
if _, ok := b.redundantParameterIndexToValue[paramIndex]; !ok { if _, ok := b.redundantParameterIndexToValue[paramIndex]; !ok {
blk.params[cur] = param params[cur] = param
cur++ cur++
} }
} }
blk.params = blk.params[:cur] blk.params.Cut(cur)
// Clears the map for the next iteration. // Clears the map for the next iteration.
for _, paramIndex := range redundantParameterIndexes { for _, paramIndex := range redundantParameterIndexes {

View File

@@ -303,7 +303,7 @@ func (b *builder) splitCriticalEdge(pred, succ *basicBlock, predInfo *basicBlock
trampoline.validate(b) trampoline.validate(b)
} }
if len(trampoline.params) > 0 { if len(trampoline.params.View()) > 0 {
panic("trampoline should not have params") panic("trampoline should not have params")
} }