wazevo: passes huge stack integration tests (#1722)

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
Takeshi Yoneda
2023-09-19 16:17:35 +09:00
committed by GitHub
parent 2229baca1f
commit 2686665e2a
5 changed files with 126 additions and 12 deletions

View File

@@ -190,8 +190,10 @@ func (a *abiImpl) CalleeGenFunctionArgsToVRegs(args []ssa.Value) {
switch arg.Type {
case ssa.TypeI32, ssa.TypeI64:
load.asULoad(operandNR(reg), amode, bits)
case ssa.TypeF32, ssa.TypeF64:
case ssa.TypeF32, ssa.TypeF64, ssa.TypeV128:
load.asFpuLoad(operandNR(reg), amode, bits)
default:
panic("BUG")
}
m.insert(load)
a.m.unresolvedAddressModes = append(a.m.unresolvedAddressModes, load)
@@ -290,7 +292,7 @@ func (a *abiImpl) callerGenFunctionReturnVReg(retIndex int, reg regalloc.VReg) {
switch r.Type {
case ssa.TypeI32, ssa.TypeI64:
ldr.asULoad(operandNR(reg), amode, r.Type.Bits())
case ssa.TypeF32, ssa.TypeF64:
case ssa.TypeF32, ssa.TypeF64, ssa.TypeV128:
ldr.asFpuLoad(operandNR(reg), amode, r.Type.Bits())
default:
panic("BUG")
@@ -303,6 +305,9 @@ func (m *machine) resolveAddressModeForOffsetAndInsert(cur *instruction, offset
m.pendingInstructions = m.pendingInstructions[:0]
mode := m.resolveAddressModeForOffset(offset, dstBits, rn)
for _, instr := range m.pendingInstructions {
// This is called after/during alloc, so set the flag so that
// this insertion won't interfere with the register allocation.
instr.addedAfterLowering = true
cur = linkInstr(cur, instr)
}
return cur, mode

View File

@@ -87,12 +87,12 @@ const (
// addressModeKindArgStackSpace is used to resolve the address of the argument stack space
// exiting right above the stack pointer. Since we don't know the exact stack space needed for a function
// at a compilation phase, this is used as a placeholder and further lowered to a real addressing mode like ahove.
// at a compilation phase, this is used as a placeholder and further lowered to a real addressing mode like above.
addressModeKindArgStackSpace
// addressModeKindResultStackSpace is used to resolve the address of the result stack space
// exiting right above the stack pointer. Since we don't know the exact stack space needed for a function
// at a compilation phase, this is used as a placeholder and further lowered to a real addressing mode like ahove.
// at a compilation phase, this is used as a placeholder and further lowered to a real addressing mode like above.
addressModeKindResultStackSpace
)

View File

@@ -77,7 +77,6 @@ func (a *Allocator) assignRegistersPerInstr(f Function, pc programCounter, instr
uses := instr.Uses()
for i, u := range uses {
if u.IsRealReg() {
a.vs = append(a.vs, u)
continue
}
if wazevoapi.RegAllocLoggingEnabled {

View File

@@ -3,6 +3,7 @@ package regalloc
import (
"fmt"
"sort"
"strings"
"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
)
@@ -88,16 +89,23 @@ func (a *Allocator) coloringFor(allocatable []RealReg) {
sort.SliceStable(degreeSortedNodes, func(i, j int) bool {
return currentDegrees[degreeSortedNodes[i]] < currentDegrees[degreeSortedNodes[j]]
})
if wazevoapi.RegAllocLoggingEnabled {
fmt.Println("-------------------------------")
fmt.Printf("coloringStack: ")
for _, c := range coloringStack {
fmt.Printf("v%d ", c.v.ID())
if c.v.IsRealReg() {
fmt.Printf("%s ", a.regInfo.RealRegName(c.v.RealReg()))
} else {
fmt.Printf("v%d ", c.v.ID())
}
}
fmt.Printf("\ndegreeSortedNodes: ")
for _, n := range degreeSortedNodes {
fmt.Printf("v%d ", n.v.ID())
if n.v.IsRealReg() {
fmt.Printf("%s ", a.regInfo.RealRegName(n.v.RealReg()))
} else {
fmt.Printf("v%d ", n.v.ID())
}
}
fmt.Printf("\ncurrentDegrees: ")
for n, degree := range currentDegrees {
@@ -195,13 +203,17 @@ func (a *Allocator) coloringFor(allocatable []RealReg) {
}
if wazevoapi.RegAllocLoggingEnabled {
fmt.Printf("\tneighborColors: %v\n", neighborColors)
var s []string
for _, r := range neighborColors {
s = append(s, a.regInfo.RealRegName(r))
}
fmt.Printf("\tneighborColors: %v\n", strings.Join(s, ","))
}
a.assignColor(n, neighborColorsSet, allocatable)
if wazevoapi.RegAllocLoggingEnabled {
fmt.Printf("\tassigned color: %s\n", n.r)
fmt.Printf("\tassigned color: %s\n", a.regInfo.RealRegName(n.r))
}
// Reset the map for the next iteration.

View File

@@ -37,7 +37,7 @@ type testCase struct {
}
var tests = map[string]testCase{
"huge stack": {f: testHugeStack, wazevoSkip: true},
"huge stack": {f: testHugeStack},
"unreachable": {f: testUnreachable},
"recursive entry": {f: testRecursiveEntry},
"host func memory": {f: testHostFuncMemory},
@@ -61,6 +61,7 @@ var tests = map[string]testCase{
"before listener globals": {f: testBeforeListenerGlobals},
"before listener stack iterator": {f: testBeforeListenerStackIterator},
"before listener stack iterator offsets": {f: testListenerStackIteratorOffset, wazevoSkip: true},
"many params many results": {f: testManyParamsResults, wazevoSkip: true},
}
func TestEngineCompiler(t *testing.T) {
@@ -77,7 +78,7 @@ func TestEngineInterpreter(t *testing.T) {
// testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
const i32, i64 = wasm.ValueTypeI32, wasm.ValueTypeI64
const i32, i64, f32, f64, v128 = wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueTypeV128
var memoryCapacityPages = uint32(2)
@@ -1616,3 +1617,100 @@ func (f *fnListener) Abort(ctx context.Context, mod api.Module, def api.Function
f.abortFn(ctx, mod, def, err)
}
}
func testManyParamsResults(t *testing.T, r wazero.Runtime) {
mainType := wasm.FunctionType{}
swapperType := wasm.FunctionType{}
doublerType := wasm.FunctionType{}
for i := 0; i < 20; i++ {
swapperType.Params = append(swapperType.Params, i32, i64, f32, f64, v128)
swapperType.Results = append(swapperType.Results, v128, f64, f32, i64, i32)
mainType.Params = append(mainType.Params, i32, i64, f32, f64, v128)
mainType.Results = append(mainType.Results, v128, f64, f32, i64, i32)
doublerType.Params = append(doublerType.Results, v128, f64, f32, i64, i32)
doublerType.Results = append(doublerType.Results, v128, f64, f32, i64, i32)
}
var mainBody []byte
for i := 0; i < 100; i++ {
mainBody = append(mainBody, wasm.OpcodeLocalGet)
mainBody = append(mainBody, leb128.EncodeUint32(uint32(i))...)
}
mainBody = append(mainBody, wasm.OpcodeCall, 1) // Call swapper.
mainBody = append(mainBody, wasm.OpcodeCall, 2) // Call doubler.
mainBody = append(mainBody, wasm.OpcodeEnd)
var swapperBody []byte
for i := 0; i < 100; i++ {
swapperBody = append(swapperBody, wasm.OpcodeLocalGet)
swapperBody = append(swapperBody, leb128.EncodeUint32(uint32(99-i))...)
}
swapperBody = append(swapperBody, wasm.OpcodeEnd)
var doublerBody []byte
for i := 0; i < 100; i += 5 {
// Returns v128 as-is.
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i))...)
// Double f64.
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+1))...)
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+1))...)
doublerBody = append(doublerBody, wasm.OpcodeF64Add)
// Double f32.
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+2))...)
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+2))...)
doublerBody = append(doublerBody, wasm.OpcodeF32Add)
// Double i64.
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+3))...)
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+3))...)
doublerBody = append(doublerBody, wasm.OpcodeI64Add)
// Double i32.
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+4))...)
doublerBody = append(doublerBody, wasm.OpcodeLocalGet)
doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+4))...)
doublerBody = append(doublerBody, wasm.OpcodeI32Add)
}
doublerBody = append(doublerBody, wasm.OpcodeEnd)
bin := binaryencoding.EncodeModule(&wasm.Module{
TypeSection: []wasm.FunctionType{mainType, swapperType, doublerType},
ExportSection: []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
FunctionSection: []wasm.Index{0, 1, 2},
CodeSection: []wasm.Code{{Body: mainBody}, {Body: swapperBody}, {Body: doublerBody}},
})
mod, err := r.Instantiate(testCtx, bin)
require.NoError(t, err)
main := mod.ExportedFunction("main")
require.NotNil(t, main)
var param []uint64
for i := 0; i < 100; i += 5 {
param = append(param, uint64(i))
param = append(param, uint64(i+1))
param = append(param, uint64(i+2))
param = append(param, uint64(i+3))
// Vector needs two values.
param = append(param, uint64(i+3))
param = append(param, uint64(i+3))
}
results, err := main.Call(testCtx, param...)
require.NoError(t, err)
exp := []uint64{
98, 98, 196, 194, 192, 190, 93, 93, 186, 184, 182, 180, 88, 88, 176, 174, 172, 170, 83, 83, 166, 164, 162,
160, 78, 78, 156, 154, 152, 150, 73, 73, 146, 144, 142, 140, 68, 68, 136, 134, 132, 130, 63, 63, 126, 124,
122, 120, 58, 58, 116, 114, 112, 110, 53, 53, 106, 104, 102, 100, 48, 48, 96, 94, 92, 90, 43, 43, 86, 84,
82, 80, 38, 38, 76, 74, 72, 70, 33, 33, 66, 64, 62, 60, 28, 28, 56, 54, 52, 50, 23, 23, 46, 44, 42, 40, 18,
18, 36, 34, 32, 30, 13, 13, 26, 24, 22, 20, 8, 8, 16, 14, 12, 10, 3, 3, 6, 4, 2, 0,
}
require.Equal(t, exp, results)
}