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