github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/compiler/compiler_global_test.go (about) 1 package compiler 2 3 import ( 4 "testing" 5 6 "github.com/wasilibs/wazerox/internal/asm" 7 "github.com/wasilibs/wazerox/internal/testing/require" 8 "github.com/wasilibs/wazerox/internal/wasm" 9 "github.com/wasilibs/wazerox/internal/wazeroir" 10 ) 11 12 func TestCompiler_compileGlobalGet(t *testing.T) { 13 const globalValue uint64 = 12345 14 for _, tp := range []wasm.ValueType{ 15 wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeExternref, wasm.ValueTypeFuncref, 16 } { 17 tp := tp 18 t.Run(wasm.ValueTypeName(tp), func(t *testing.T) { 19 env := newCompilerEnvironment() 20 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{ 21 Globals: []wasm.GlobalType{{}, {ValType: tp}}, 22 }) 23 24 // Setup the global. (start with nil as a dummy so that global index can be non-trivial.) 25 globals := []*wasm.GlobalInstance{nil, {Val: globalValue, Type: wasm.GlobalType{ValType: tp}}} 26 env.addGlobals(globals...) 27 28 // Emit the code. 29 err := compiler.compilePreamble() 30 require.NoError(t, err) 31 op := operationPtr(wazeroir.NewOperationGlobalGet(1)) 32 err = compiler.compileGlobalGet(op) 33 require.NoError(t, err) 34 35 // At this point, the top of stack must be the retrieved global on a register. 36 global := compiler.runtimeValueLocationStack().peek() 37 require.True(t, global.onRegister()) 38 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list())) 39 switch tp { 40 case wasm.ValueTypeF32, wasm.ValueTypeF64: 41 require.True(t, isVectorRegister(global.register)) 42 case wasm.ValueTypeI32, wasm.ValueTypeI64: 43 require.True(t, isGeneralPurposeRegister(global.register)) 44 } 45 err = compiler.compileReturnFunction() 46 require.NoError(t, err) 47 48 code := asm.CodeSegment{} 49 defer func() { require.NoError(t, code.Unmap()) }() 50 51 // Generate the code under test. 52 _, err = compiler.compile(code.NextCodeSection()) 53 require.NoError(t, err) 54 55 // Run the code assembled above. 56 env.exec(code.Bytes()) 57 58 // Since we call global.get, the top of the stack must be the global value. 59 require.Equal(t, globalValue, env.stackTopAsUint64()) 60 // Plus as we push the value, the stack pointer must be incremented. 61 require.Equal(t, uint64(1), env.stackPointer()) 62 }) 63 } 64 } 65 66 func TestCompiler_compileGlobalGet_v128(t *testing.T) { 67 const v128Type = wasm.ValueTypeV128 68 env := newCompilerEnvironment() 69 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{ 70 Globals: []wasm.GlobalType{{}, {ValType: v128Type}}, 71 }) 72 73 // Setup the global. (start with nil as a dummy so that global index can be non-trivial.) 74 globals := []*wasm.GlobalInstance{nil, {Val: 12345, ValHi: 6789, Type: wasm.GlobalType{ValType: v128Type}}} 75 env.addGlobals(globals...) 76 77 // Emit the code. 78 err := compiler.compilePreamble() 79 require.NoError(t, err) 80 op := operationPtr(wazeroir.NewOperationGlobalGet(1)) 81 err = compiler.compileGlobalGet(op) 82 require.NoError(t, err) 83 84 // At this point, the top of stack must be the retrieved global on a register. 85 global := compiler.runtimeValueLocationStack().peek() 86 require.True(t, global.onRegister()) 87 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list())) 88 require.True(t, isVectorRegister(global.register)) 89 err = compiler.compileReturnFunction() 90 require.NoError(t, err) 91 92 code := asm.CodeSegment{} 93 defer func() { require.NoError(t, code.Unmap()) }() 94 95 // Generate the code under test. 96 _, err = compiler.compile(code.NextCodeSection()) 97 require.NoError(t, err) 98 99 // Run the code assembled above. 100 env.exec(code.Bytes()) 101 102 require.Equal(t, uint64(2), env.stackPointer()) 103 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 104 105 // Since we call global.get, the top of the stack must be the global value. 106 actual := globals[1] 107 sp := env.ce.stackContext.stackPointer 108 stack := env.stack() 109 require.Equal(t, actual.Val, stack[sp-2]) 110 require.Equal(t, actual.ValHi, stack[sp-1]) 111 } 112 113 func TestCompiler_compileGlobalSet(t *testing.T) { 114 const valueToSet uint64 = 12345 115 for _, tp := range []wasm.ValueType{ 116 wasm.ValueTypeF32, wasm.ValueTypeF64, 117 wasm.ValueTypeI32, wasm.ValueTypeI64, 118 wasm.ValueTypeExternref, wasm.ValueTypeFuncref, 119 } { 120 tp := tp 121 t.Run(wasm.ValueTypeName(tp), func(t *testing.T) { 122 env := newCompilerEnvironment() 123 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{ 124 Globals: []wasm.GlobalType{{}, {ValType: tp}}, 125 }) 126 127 // Setup the global. (start with nil as a dummy so that global index can be non-trivial.) 128 env.addGlobals(nil, &wasm.GlobalInstance{Val: 40, Type: wasm.GlobalType{ValType: tp}}) 129 130 err := compiler.compilePreamble() 131 require.NoError(t, err) 132 133 // Place the set target value. 134 loc := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack() 135 switch tp { 136 case wasm.ValueTypeI32: 137 loc.valueType = runtimeValueTypeI32 138 case wasm.ValueTypeI64, wasm.ValueTypeExternref, wasm.ValueTypeFuncref: 139 loc.valueType = runtimeValueTypeI64 140 case wasm.ValueTypeF32: 141 loc.valueType = runtimeValueTypeF32 142 case wasm.ValueTypeF64: 143 loc.valueType = runtimeValueTypeF64 144 } 145 env.stack()[loc.stackPointer] = valueToSet 146 147 const index = 1 148 op := operationPtr(wazeroir.NewOperationGlobalSet(index)) 149 err = compiler.compileGlobalSet(op) 150 requireRuntimeLocationStackPointerEqual(t, 0, compiler) 151 152 require.NoError(t, err) 153 154 err = compiler.compileReturnFunction() 155 require.NoError(t, err) 156 157 code := asm.CodeSegment{} 158 defer func() { require.NoError(t, code.Unmap()) }() 159 160 // Generate the code under test. 161 _, err = compiler.compile(code.NextCodeSection()) 162 require.NoError(t, err) 163 env.exec(code.Bytes()) 164 165 // The global value should be set to valueToSet. 166 actual := env.globals()[index] 167 require.Equal(t, valueToSet, actual.Val) 168 // Plus we consumed the top of the stack, the stack pointer must be decremented. 169 require.Equal(t, uint64(0), env.stackPointer()) 170 }) 171 } 172 } 173 174 func TestCompiler_compileGlobalSet_v128(t *testing.T) { 175 const v128Type = wasm.ValueTypeV128 176 const valueToSetLo, valueToSetHi uint64 = 0xffffff, 1 177 178 env := newCompilerEnvironment() 179 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{ 180 Globals: []wasm.GlobalType{{}, {ValType: v128Type}}, 181 }) 182 183 // Setup the global. (start with nil as a dummy so that global index can be non-trivial.) 184 env.addGlobals(nil, &wasm.GlobalInstance{Val: 0, ValHi: 0, Type: wasm.GlobalType{ValType: v128Type}}) 185 186 err := compiler.compilePreamble() 187 require.NoError(t, err) 188 189 // Place the set target value. 190 lo := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack() 191 lo.valueType = runtimeValueTypeV128Lo 192 env.stack()[lo.stackPointer] = valueToSetLo 193 hi := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack() 194 hi.valueType = runtimeValueTypeV128Hi 195 env.stack()[hi.stackPointer] = valueToSetHi 196 197 const index = 1 198 op := operationPtr(wazeroir.NewOperationGlobalSet(index)) 199 err = compiler.compileGlobalSet(op) 200 requireRuntimeLocationStackPointerEqual(t, 0, compiler) 201 require.NoError(t, err) 202 203 err = compiler.compileReturnFunction() 204 require.NoError(t, err) 205 206 code := asm.CodeSegment{} 207 defer func() { require.NoError(t, code.Unmap()) }() 208 209 // Generate the code under test. 210 _, err = compiler.compile(code.NextCodeSection()) 211 require.NoError(t, err) 212 env.exec(code.Bytes()) 213 214 require.Equal(t, uint64(0), env.stackPointer()) 215 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 216 217 // The global value should be set to valueToSet. 218 actual := env.globals()[index] 219 require.Equal(t, valueToSetLo, actual.Val) 220 require.Equal(t, valueToSetHi, actual.ValHi) 221 }