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  }