wa-lang.org/wazero@v1.0.2/internal/engine/compiler/impl_vec_arm64_test.go (about) 1 package compiler 2 3 import ( 4 "encoding/binary" 5 "testing" 6 7 "wa-lang.org/wazero/internal/asm" 8 "wa-lang.org/wazero/internal/asm/arm64" 9 "wa-lang.org/wazero/internal/testing/require" 10 "wa-lang.org/wazero/internal/wasm" 11 "wa-lang.org/wazero/internal/wazeroir" 12 ) 13 14 // TestArm64Compiler_V128Shuffle_ConstTable_MiddleOfFunction ensures that flushing constant table in the middle of 15 // function works well by intentionally setting arm64.AssemblerImpl MaxDisplacementForConstantPool = 0. 16 func TestArm64Compiler_V128Shuffle_ConstTable_MiddleOfFunction(t *testing.T) { 17 env := newCompilerEnvironment() 18 compiler := env.requireNewCompiler(t, newCompiler, 19 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 20 21 err := compiler.compilePreamble() 22 require.NoError(t, err) 23 24 lanes := [16]byte{1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0} 25 v := [16]byte{0: 0xa, 1: 0xb, 10: 0xc} 26 w := [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} 27 exp := [16]byte{ 28 0xb, 0xb, 0xb, 0xb, 29 0xa, 0xa, 0xa, 0xa, 30 0xc, 0xc, 0xc, 0xc, 31 0xa, 0xa, 0xa, 0xa, 32 } 33 34 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 35 Lo: binary.LittleEndian.Uint64(v[:8]), 36 Hi: binary.LittleEndian.Uint64(v[8:]), 37 }) 38 require.NoError(t, err) 39 40 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 41 Lo: binary.LittleEndian.Uint64(w[:8]), 42 Hi: binary.LittleEndian.Uint64(w[8:]), 43 }) 44 require.NoError(t, err) 45 46 err = compiler.compileV128Shuffle(&wazeroir.OperationV128Shuffle{Lanes: lanes}) 47 require.NoError(t, err) 48 49 assembler := compiler.(*arm64Compiler).assembler.(*arm64.AssemblerImpl) 50 assembler.MaxDisplacementForConstantPool = 0 // Ensures that constant table for shuffle will be flushed immediately. 51 52 err = compiler.compileReturnFunction() 53 require.NoError(t, err) 54 55 // Generate and run the code under test. 56 code, _, err := compiler.compile() 57 require.NoError(t, err) 58 59 env.exec(code) 60 61 lo, hi := env.stackTopAsV128() 62 var actual [16]byte 63 binary.LittleEndian.PutUint64(actual[:8], lo) 64 binary.LittleEndian.PutUint64(actual[8:], hi) 65 require.Equal(t, exp, actual) 66 } 67 68 func TestArm64Compiler_V128Shuffle_combinations(t *testing.T) { 69 movValueRegisterToRegister := func(t *testing.T, c *arm64Compiler, src *runtimeValueLocation, dst asm.Register) { 70 c.assembler.CompileTwoVectorRegistersToVectorRegister(arm64.VORR, src.register, src.register, dst, 71 arm64.VectorArrangement16B) 72 c.locationStack.markRegisterUnused(src.register) 73 src.setRegister(dst) 74 // We have to set the lower 64-bits' location as well. 75 c.locationStack.stack[src.stackPointer-1].setRegister(dst) 76 c.locationStack.markRegisterUsed(dst) 77 } 78 79 tests := []struct { 80 name string 81 init func(t *testing.T, c *arm64Compiler) 82 wReg, vReg asm.Register 83 verifyFnc func(t *testing.T, env *compilerEnv) 84 expStackPointerAfterShuffle uint64 85 }{ 86 { 87 name: "w=v1, v=v2", 88 wReg: arm64.RegV1, 89 vReg: arm64.RegV2, 90 init: func(t *testing.T, c *arm64Compiler) {}, 91 verifyFnc: func(t *testing.T, env *compilerEnv) {}, 92 expStackPointerAfterShuffle: 2, 93 }, 94 { 95 name: "w=v2, v=v1", 96 wReg: arm64.RegV2, 97 vReg: arm64.RegV1, 98 init: func(t *testing.T, c *arm64Compiler) {}, 99 verifyFnc: func(t *testing.T, env *compilerEnv) {}, 100 expStackPointerAfterShuffle: 2, 101 }, 102 { 103 name: "w=v29, v=v30", 104 wReg: arm64.RegV29, // will be moved to v30. 105 vReg: arm64.RegV30, // will be moved to v29. 106 init: func(t *testing.T, c *arm64Compiler) {}, 107 verifyFnc: func(t *testing.T, env *compilerEnv) {}, 108 expStackPointerAfterShuffle: 2, 109 }, 110 { 111 name: "w=v12, v=v30", 112 wReg: arm64.RegV12, // will be moved to v30. 113 vReg: arm64.RegV30, // will be moved to v29. 114 init: func(t *testing.T, c *arm64Compiler) { 115 // Set up the previous value on the v3 register. 116 err := c.compileV128Const(&wazeroir.OperationV128Const{ 117 Lo: 1234, 118 Hi: 5678, 119 }) 120 require.NoError(t, err) 121 movValueRegisterToRegister(t, c, c.locationStack.peek(), arm64.RegV29) 122 }, 123 verifyFnc: func(t *testing.T, env *compilerEnv) { 124 // Previous value on the V3 register must be saved onto the stack. 125 lo, hi := env.stack()[callFrameDataSizeInUint64], env.stack()[callFrameDataSizeInUint64+1] 126 require.Equal(t, uint64(1234), lo) 127 require.Equal(t, uint64(5678), hi) 128 }, 129 expStackPointerAfterShuffle: 4, 130 }, 131 { 132 name: "w=v29, v=v12", 133 wReg: arm64.RegV29, // will be moved to v30. 134 vReg: arm64.RegV12, // will be moved to v29. 135 init: func(t *testing.T, c *arm64Compiler) { 136 // Set up the previous value on the v3 register. 137 err := c.compileV128Const(&wazeroir.OperationV128Const{ 138 Lo: 1234, 139 Hi: 5678, 140 }) 141 require.NoError(t, err) 142 movValueRegisterToRegister(t, c, c.locationStack.peek(), arm64.RegV30) 143 }, 144 verifyFnc: func(t *testing.T, env *compilerEnv) { 145 // Previous value on the V3 register must be saved onto the stack. 146 lo, hi := env.stack()[callFrameDataSizeInUint64], env.stack()[callFrameDataSizeInUint64+1] 147 require.Equal(t, uint64(1234), lo) 148 require.Equal(t, uint64(5678), hi) 149 }, 150 expStackPointerAfterShuffle: 4, 151 }, 152 } 153 154 lanes := [16]byte{1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 31} 155 v := [16]byte{0: 0xa, 1: 0xb, 10: 0xc} 156 w := [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1} 157 exp := [16]byte{ 158 0xb, 0xb, 0xb, 0xb, 159 0xa, 0xa, 0xa, 0xa, 160 0xc, 0xc, 0xc, 0xc, 161 0xa, 0xa, 0xa, 1, 162 } 163 164 for _, tc := range tests { 165 tc := tc 166 t.Run(tc.name, func(t *testing.T) { 167 env := newCompilerEnvironment() 168 compiler := env.requireNewCompiler(t, newCompiler, 169 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 170 171 err := compiler.compilePreamble() 172 require.NoError(t, err) 173 174 ac := compiler.(*arm64Compiler) 175 tc.init(t, ac) 176 177 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 178 Lo: binary.LittleEndian.Uint64(v[:8]), 179 Hi: binary.LittleEndian.Uint64(v[8:]), 180 }) 181 require.NoError(t, err) 182 183 vLocation := compiler.runtimeValueLocationStack().peek() 184 movValueRegisterToRegister(t, ac, vLocation, tc.vReg) 185 186 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 187 Lo: binary.LittleEndian.Uint64(w[:8]), 188 Hi: binary.LittleEndian.Uint64(w[8:]), 189 }) 190 require.NoError(t, err) 191 192 wLocation := compiler.runtimeValueLocationStack().peek() 193 movValueRegisterToRegister(t, ac, wLocation, tc.wReg) 194 195 err = compiler.compileV128Shuffle(&wazeroir.OperationV128Shuffle{Lanes: lanes}) 196 require.NoError(t, err) 197 198 requireRuntimeLocationStackPointerEqual(t, tc.expStackPointerAfterShuffle, compiler) 199 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 200 201 err = compiler.compileReturnFunction() 202 require.NoError(t, err) 203 204 // Generate and run the code under test. 205 code, _, err := compiler.compile() 206 require.NoError(t, err) 207 208 env.exec(code) 209 210 lo, hi := env.stackTopAsV128() 211 var actual [16]byte 212 binary.LittleEndian.PutUint64(actual[:8], lo) 213 binary.LittleEndian.PutUint64(actual[8:], hi) 214 require.Equal(t, exp, actual) 215 216 tc.verifyFnc(t, env) 217 }) 218 } 219 }