|
| 1 | +/* |
| 2 | +* Copyright (C) 2019 Intel Corporation. All rights reserved. |
| 3 | +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 4 | +*/ |
| 5 | + |
| 6 | +#include <gtest/gtest.h> |
| 7 | +#include <cstdint> |
| 8 | +#include <cfloat> |
| 9 | +#include <cmath> |
| 10 | +#include <cstdlib> |
| 11 | +#include <unistd.h> |
| 12 | +#include <cstring> |
| 13 | +#include "test_helper.h" |
| 14 | +#include "wasm_runtime_common.h" |
| 15 | +#include "bh_read_file.h" |
| 16 | + |
| 17 | +static std::string CWD; |
| 18 | +static std::string WASM_FILE; |
| 19 | + |
| 20 | +static int app_argc; |
| 21 | +static char **app_argv; |
| 22 | + |
| 23 | +/** |
| 24 | +* Test fixture for f32.const opcode validation |
| 25 | +* |
| 26 | +* This class provides comprehensive testing infrastructure for the f32.const WebAssembly opcode, |
| 27 | +* ensuring proper constant loading functionality across different execution modes (interpreter and AOT). |
| 28 | +* Tests validate that f32.const correctly pushes immediate 32-bit IEEE 754 floating-point values onto |
| 29 | +* the execution stack without consuming any stack operands. Includes comprehensive validation of |
| 30 | +* special IEEE 754 values including NaN, infinity, zero, subnormals, and boundary conditions. |
| 31 | +*/ |
| 32 | +class F32ConstTest : public testing::TestWithParam<RunningMode> |
| 33 | +{ |
| 34 | +protected: |
| 35 | + WAMRRuntimeRAII<> runtime; |
| 36 | + wasm_module_t module = nullptr; |
| 37 | + wasm_module_inst_t module_inst = nullptr; |
| 38 | + wasm_exec_env_t exec_env = nullptr; |
| 39 | + uint32_t buf_size, stack_size = 8092, heap_size = 8092; |
| 40 | + uint8_t *buf = nullptr; |
| 41 | + char error_buf[128] = { 0 }; |
| 42 | + const char *exception = nullptr; |
| 43 | + |
| 44 | + /** |
| 45 | + * Set up test environment for f32.const opcode testing |
| 46 | + * |
| 47 | + * Initializes WAMR runtime with appropriate configuration for testing f32.const operations. |
| 48 | + * Configures memory allocation, execution mode, and loads the f32.const test module. |
| 49 | + * Ensures proper runtime state before executing individual test cases. |
| 50 | + */ |
| 51 | + void SetUp() override |
| 52 | + { |
| 53 | + memset(error_buf, 0, sizeof(error_buf)); |
| 54 | + |
| 55 | + buf = (uint8_t *)bh_read_file_to_buffer(WASM_FILE.c_str(), &buf_size); |
| 56 | + ASSERT_NE(buf, nullptr) << "Failed to read WASM file: " << WASM_FILE; |
| 57 | + |
| 58 | + module = wasm_runtime_load(buf, buf_size, error_buf, sizeof(error_buf)); |
| 59 | + ASSERT_NE(module, nullptr) << "Failed to load WASM module: " << error_buf; |
| 60 | + |
| 61 | + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, error_buf, sizeof(error_buf)); |
| 62 | + ASSERT_NE(module_inst, nullptr) << "Failed to instantiate WASM module: " << error_buf; |
| 63 | + |
| 64 | + wasm_runtime_set_running_mode(module_inst, GetParam()); |
| 65 | + |
| 66 | + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); |
| 67 | + ASSERT_NE(exec_env, nullptr) << "Failed to create execution environment"; |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * Clean up test environment after f32.const opcode testing |
| 72 | + * |
| 73 | + * Performs proper cleanup of WASM module instances, modules, and runtime resources. |
| 74 | + * Ensures no memory leaks or resource conflicts between test cases. |
| 75 | + * Maintains clean test environment for subsequent test execution. |
| 76 | + */ |
| 77 | + void TearDown() override |
| 78 | + { |
| 79 | + if (exec_env != nullptr) { |
| 80 | + wasm_runtime_destroy_exec_env(exec_env); |
| 81 | + exec_env = nullptr; |
| 82 | + } |
| 83 | + if (module_inst != nullptr) { |
| 84 | + wasm_runtime_deinstantiate(module_inst); |
| 85 | + module_inst = nullptr; |
| 86 | + } |
| 87 | + if (module != nullptr) { |
| 88 | + wasm_runtime_unload(module); |
| 89 | + module = nullptr; |
| 90 | + } |
| 91 | + if (buf != nullptr) { |
| 92 | + wasm_runtime_free(buf); |
| 93 | + buf = nullptr; |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + /** |
| 98 | + * Execute f32.const test function and return the loaded constant value |
| 99 | + * |
| 100 | + * @param func_name Name of the WASM function to execute (must return f32) |
| 101 | + * @return The f32 constant value loaded by the function |
| 102 | + */ |
| 103 | + float call_const_func(const char* func_name) |
| 104 | + { |
| 105 | + wasm_function_inst_t func_inst = wasm_runtime_lookup_function(module_inst, func_name); |
| 106 | + EXPECT_NE(func_inst, nullptr) << "Failed to lookup function: " << func_name; |
| 107 | + |
| 108 | + wasm_val_t results[1]; |
| 109 | + wasm_val_t arguments[1]; // f32.const doesn't need arguments but declaring for consistency |
| 110 | + |
| 111 | + bool success = wasm_runtime_call_wasm_a(exec_env, func_inst, 1, results, 0, arguments); |
| 112 | + EXPECT_TRUE(success) << "Failed to call function: " << func_name |
| 113 | + << ", exception: " << wasm_runtime_get_exception(module_inst); |
| 114 | + |
| 115 | + return results[0].of.f32; |
| 116 | + } |
| 117 | + |
| 118 | + /** |
| 119 | + * Execute multiple f32.const test function and return the loaded constant values |
| 120 | + * |
| 121 | + * @param func_name Name of the WASM function to execute (must return multiple f32 values) |
| 122 | + * @param result_count Number of f32 values to expect |
| 123 | + * @param results Array to store the returned f32 constant values |
| 124 | + */ |
| 125 | + void call_multi_const_func(const char* func_name, uint32_t result_count, float* results) |
| 126 | + { |
| 127 | + wasm_function_inst_t func_inst = wasm_runtime_lookup_function(module_inst, func_name); |
| 128 | + ASSERT_NE(func_inst, nullptr) << "Failed to lookup function: " << func_name; |
| 129 | + |
| 130 | + wasm_val_t wasm_results[16]; // Sufficient for multiple constants |
| 131 | + wasm_val_t arguments[1]; // f32.const doesn't need arguments |
| 132 | + |
| 133 | + bool success = wasm_runtime_call_wasm_a(exec_env, func_inst, result_count, wasm_results, 0, arguments); |
| 134 | + ASSERT_TRUE(success) << "Failed to call function: " << func_name |
| 135 | + << ", exception: " << wasm_runtime_get_exception(module_inst); |
| 136 | + |
| 137 | + for (uint32_t i = 0; i < result_count; i++) { |
| 138 | + results[i] = wasm_results[i].of.f32; |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + /** |
| 143 | + * Check if two f32 values are bitwise identical (handles NaN correctly) |
| 144 | + * |
| 145 | + * @param a First f32 value |
| 146 | + * @param b Second f32 value |
| 147 | + * @return true if bit patterns are identical, false otherwise |
| 148 | + */ |
| 149 | + bool are_f32_bitwise_equal(float a, float b) |
| 150 | + { |
| 151 | + uint32_t bits_a, bits_b; |
| 152 | + memcpy(&bits_a, &a, sizeof(uint32_t)); |
| 153 | + memcpy(&bits_b, &b, sizeof(uint32_t)); |
| 154 | + return bits_a == bits_b; |
| 155 | + } |
| 156 | + |
| 157 | + /** |
| 158 | + * Get bit pattern of f32 value as uint32_t |
| 159 | + * |
| 160 | + * @param value f32 value to get bit pattern for |
| 161 | + * @return uint32_t representing the bit pattern |
| 162 | + */ |
| 163 | + uint32_t get_f32_bits(float value) |
| 164 | + { |
| 165 | + uint32_t bits; |
| 166 | + memcpy(&bits, &value, sizeof(uint32_t)); |
| 167 | + return bits; |
| 168 | + } |
| 169 | +}; |
| 170 | + |
| 171 | + |
| 172 | + |
| 173 | + |
| 174 | +/** |
| 175 | +* @test SpecialValues_PreservesIEEE754 |
| 176 | +* @brief Validates f32.const preserves special IEEE 754 values correctly |
| 177 | +* @details Tests NaN, positive/negative infinity, positive/negative zero to ensure |
| 178 | +* f32.const correctly handles all special IEEE 754 single-precision values. |
| 179 | +* @test_category Edge - Special IEEE 754 value validation |
| 180 | +* @coverage_target core/iwasm/interpreter/wasm_interp_classic.c:f32_const_operation |
| 181 | +* @input_conditions NaN, +/-infinity, +/-zero IEEE 754 values |
| 182 | +* @expected_behavior Exact preservation of special values with correct IEEE 754 semantics |
| 183 | +* @validation_method Special value checks and bitwise comparison for signed zeros |
| 184 | +*/ |
| 185 | +TEST_P(F32ConstTest, SpecialValues_PreservesIEEE754) |
| 186 | +{ |
| 187 | + // Test NaN (Not-a-Number) |
| 188 | + float nan_result = call_const_func("get_nan"); |
| 189 | + ASSERT_TRUE(std::isnan(nan_result)) |
| 190 | + << "f32.const failed to preserve NaN value"; |
| 191 | + |
| 192 | + // Test positive infinity |
| 193 | + ASSERT_TRUE(std::isinf(call_const_func("get_pos_inf")) && call_const_func("get_pos_inf") > 0) |
| 194 | + << "f32.const failed to preserve positive infinity"; |
| 195 | + |
| 196 | + // Test negative infinity |
| 197 | + ASSERT_TRUE(std::isinf(call_const_func("get_neg_inf")) && call_const_func("get_neg_inf") < 0) |
| 198 | + << "f32.const failed to preserve negative infinity"; |
| 199 | + |
| 200 | + // Test positive zero (0x00000000) |
| 201 | + float pos_zero = call_const_func("get_pos_zero"); |
| 202 | + ASSERT_EQ(pos_zero, 0.0f) << "f32.const failed to load positive zero"; |
| 203 | + ASSERT_EQ(get_f32_bits(pos_zero), 0x00000000U) |
| 204 | + << "f32.const positive zero has incorrect bit pattern"; |
| 205 | + |
| 206 | + // Test negative zero (0x80000000) |
| 207 | + float neg_zero = call_const_func("get_neg_zero"); |
| 208 | + ASSERT_EQ(neg_zero, -0.0f) << "f32.const failed to load negative zero"; |
| 209 | + ASSERT_EQ(get_f32_bits(neg_zero), 0x80000000U) |
| 210 | + << "f32.const negative zero has incorrect bit pattern"; |
| 211 | +} |
| 212 | + |
| 213 | + |
| 214 | + |
| 215 | +/** |
| 216 | +* @test ConstantsInOperations_FunctionsCorrectly |
| 217 | +* @brief Validates f32.const values work correctly in subsequent operations |
| 218 | +* @details Tests using f32.const values in f32.add operations to ensure constants |
| 219 | +* are properly loaded and available for arithmetic operations. |
| 220 | +* @test_category Integration - Constant usage in operations |
| 221 | +* @coverage_target core/iwasm/interpreter/wasm_interp_classic.c:f32_const_operation |
| 222 | +* @input_conditions f32.const values used as operands in f32.add |
| 223 | +* @expected_behavior Correct arithmetic results using loaded constants |
| 224 | +* @validation_method Verification of arithmetic operation results |
| 225 | +*/ |
| 226 | +TEST_P(F32ConstTest, ConstantsInOperations_FunctionsCorrectly) |
| 227 | +{ |
| 228 | + // Test f32.const 2.5 + f32.const 3.7 = 6.2 |
| 229 | + ASSERT_EQ(call_const_func("add_two_constants"), 6.2f) |
| 230 | + << "f32.const values failed in addition operation"; |
| 231 | + |
| 232 | + // Test f32.const 10.0 - f32.const 3.5 = 6.5 |
| 233 | + ASSERT_EQ(call_const_func("subtract_constants"), 6.5f) |
| 234 | + << "f32.const values failed in subtraction operation"; |
| 235 | + |
| 236 | + // Test f32.const 2.0 * f32.const 1.5 = 3.0 |
| 237 | + ASSERT_EQ(call_const_func("multiply_constants"), 3.0f) |
| 238 | + << "f32.const values failed in multiplication operation"; |
| 239 | +} |
| 240 | + |
| 241 | +// Parameterized test instantiation for both interpreter and AOT modes |
| 242 | +INSTANTIATE_TEST_SUITE_P(RunningModeTest, F32ConstTest, |
| 243 | + testing::Values(Mode_Interp, Mode_LLVM_JIT), |
| 244 | + [](const testing::TestParamInfo<F32ConstTest::ParamType> &info) { |
| 245 | + return info.param == Mode_Interp ? "INTERP" : "AOT"; |
| 246 | + }); |
| 247 | + |
| 248 | + |
| 249 | +int main(int argc, char **argv) |
| 250 | +{ |
| 251 | + char *cwd = getcwd(nullptr, 0); |
| 252 | + if (cwd != nullptr) { |
| 253 | + CWD = std::string(cwd); |
| 254 | + free(cwd); |
| 255 | + } else { |
| 256 | + CWD = "."; |
| 257 | + } |
| 258 | + |
| 259 | + WASM_FILE = CWD + "/wasm-apps/f32_const_test.wasm"; |
| 260 | + |
| 261 | + app_argc = argc; |
| 262 | + app_argv = argv; |
| 263 | + |
| 264 | + ::testing::InitGoogleTest(&argc, argv); |
| 265 | + return RUN_ALL_TESTS(); |
| 266 | +} |
0 commit comments