Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/unit/smart-tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
cmake_minimum_required(VERSION 3.12)

add_subdirectory(interpreter)
add_subdirectory(constants)
123 changes: 123 additions & 0 deletions tests/unit/smart-tests/constants/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

cmake_minimum_required(VERSION 3.14)

project(enhanced_constants_test)

# Platform-specific definitions
add_definitions(-DRUN_ON_LINUX)

set(WAMR_BUILD_INTERP 1)
set(WAMR_BUILD_AOT 1)
if (WAMR_BUILD_TARGET STREQUAL "X86_64")
set(WAMR_BUILD_JIT 1)
else ()
set(WAMR_BUILD_JIT 0)
endif ()
set(WAMR_BUILD_FAST_JIT 0)
set(WAMR_BUILD_LIBC_WASI 1)
set(WAMR_BUILD_APP_FRAMEWORK 0)
set(WAMR_BUILD_MEMORY_PROFILING 0)

# Include shared unit test configuration
include(../../unit_common.cmake)

find_package(LLVM REQUIRED CONFIG)
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})

include(${IWASM_DIR}/compilation/iwasm_compl.cmake)

include_directories(${CMAKE_CURRENT_SOURCE_DIR})

set(i32_const_test_sources
enhanced_i32_const_test.cc
${WAMR_RUNTIME_LIB_SOURCE}
${IWASM_COMPL_SOURCE}
${UNCOMMON_SHARED_SOURCE}
)

# Prepare unit test sources for i64.const test
# set(i64_const_test_sources
# enhanced_i64_const_test.cc
# ${WAMR_RUNTIME_LIB_SOURCE}
# ${IWASM_COMPL_SOURCE}
# ${UNCOMMON_SHARED_SOURCE}
# )

# Prepare unit test sources for f32.const test
set(f32_const_test_sources
enhanced_f32_const_test.cc
${WAMR_RUNTIME_LIB_SOURCE}
${IWASM_COMPL_SOURCE}
${UNCOMMON_SHARED_SOURCE}
)

# Prepare unit test sources for f64.const test
# set(f64_const_test_sources
# enhanced_f64_const_test.cc
# ${WAMR_RUNTIME_LIB_SOURCE}
# ${IWASM_COMPL_SOURCE}
# ${UNCOMMON_SHARED_SOURCE}
# )

# Create test executables
add_executable(enhanced_i32_const_test ${i32_const_test_sources})
# add_executable(enhanced_i64_const_test ${i64_const_test_sources})
add_executable(enhanced_f32_const_test ${f32_const_test_sources})
# add_executable(enhanced_f64_const_test ${f64_const_test_sources})


# Link required libraries
target_link_libraries(enhanced_i32_const_test ${LLVM_AVAILABLE_LIBS} gtest_main)
# target_link_libraries(enhanced_i64_const_test ${LLVM_AVAILABLE_LIBS} gtest_main)
target_link_libraries(enhanced_f32_const_test ${LLVM_AVAILABLE_LIBS} gtest_main)
# target_link_libraries(enhanced_f64_const_test ${LLVM_AVAILABLE_LIBS} gtest_main)

# Post-build: Copy WASM test files to build directory for all tests
add_custom_command(TARGET enhanced_i32_const_test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
$<TARGET_FILE_DIR:enhanced_i32_const_test>/wasm-apps
COMMENT "Copying WASM test files to i32_const test build directory"
)

# add_custom_command(TARGET enhanced_i64_const_test POST_BUILD
# COMMAND ${CMAKE_COMMAND} -E copy_directory
# ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
# $<TARGET_FILE_DIR:enhanced_i64_const_test>/wasm-apps
# COMMENT "Copying WASM test files to i64_const test build directory"
# )

add_custom_command(TARGET enhanced_f32_const_test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
$<TARGET_FILE_DIR:enhanced_f32_const_test>/wasm-apps
COMMENT "Copying WASM test files to f32_const test build directory"
)

# add_custom_command(TARGET enhanced_f64_const_test POST_BUILD
# COMMAND ${CMAKE_COMMAND} -E copy_directory
# ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
# $<TARGET_FILE_DIR:enhanced_f64_const_test>/wasm-apps
# COMMENT "Copying WASM test files to f64_const test build directory"
# )

include(GoogleTest)

gtest_discover_tests(enhanced_i32_const_test
PROPERTIES RUN_SERIAL TRUE
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

# gtest_discover_tests(enhanced_i64_const_test
# PROPERTIES RUN_SERIAL TRUE
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

gtest_discover_tests(enhanced_f32_const_test
PROPERTIES RUN_SERIAL TRUE
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

# gtest_discover_tests(enhanced_f64_const_test
# PROPERTIES RUN_SERIAL TRUE
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
265 changes: 265 additions & 0 deletions tests/unit/smart-tests/constants/enhanced_f32_const_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#include <gtest/gtest.h>
#include <cstdint>
#include <cfloat>
#include <cmath>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include "test_helper.h"
#include "wasm_runtime_common.h"
#include "bh_read_file.h"

static std::string CWD;
static std::string WASM_FILE;

static int app_argc;
static char **app_argv;

/**
* Test fixture for f32.const opcode validation
*
* This class provides comprehensive testing infrastructure for the f32.const WebAssembly opcode,
* ensuring proper constant loading functionality across different execution modes (interpreter and AOT).
* Tests validate that f32.const correctly pushes immediate 32-bit IEEE 754 floating-point values onto
* the execution stack without consuming any stack operands. Includes comprehensive validation of
* special IEEE 754 values including NaN, infinity, zero, subnormals, and boundary conditions.
*/
class F32ConstTest : public testing::TestWithParam<RunningMode>
{
protected:
WAMRRuntimeRAII<> runtime;
wasm_module_t module = nullptr;
wasm_module_inst_t module_inst = nullptr;
wasm_exec_env_t exec_env = nullptr;
uint32_t buf_size, stack_size = 8092, heap_size = 8092;
uint8_t *buf = nullptr;
char error_buf[128] = { 0 };
const char *exception = nullptr;

/**
* Set up test environment for f32.const opcode testing
*
* Initializes WAMR runtime with appropriate configuration for testing f32.const operations.
* Configures memory allocation, execution mode, and loads the f32.const test module.
* Ensures proper runtime state before executing individual test cases.
*/
void SetUp() override
{
memset(error_buf, 0, sizeof(error_buf));

buf = (uint8_t *)bh_read_file_to_buffer(WASM_FILE.c_str(), &buf_size);
ASSERT_NE(buf, nullptr) << "Failed to read WASM file: " << WASM_FILE;

module = wasm_runtime_load(buf, buf_size, error_buf, sizeof(error_buf));
ASSERT_NE(module, nullptr) << "Failed to load WASM module: " << error_buf;

module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, error_buf, sizeof(error_buf));
ASSERT_NE(module_inst, nullptr) << "Failed to instantiate WASM module: " << error_buf;

wasm_runtime_set_running_mode(module_inst, GetParam());

exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
ASSERT_NE(exec_env, nullptr) << "Failed to create execution environment";
}

/**
* Clean up test environment after f32.const opcode testing
*
* Performs proper cleanup of WASM module instances, modules, and runtime resources.
* Ensures no memory leaks or resource conflicts between test cases.
* Maintains clean test environment for subsequent test execution.
*/
void TearDown() override
{
if (exec_env != nullptr) {
wasm_runtime_destroy_exec_env(exec_env);
exec_env = nullptr;
}
if (module_inst != nullptr) {
wasm_runtime_deinstantiate(module_inst);
module_inst = nullptr;
}
if (module != nullptr) {
wasm_runtime_unload(module);
module = nullptr;
}
if (buf != nullptr) {
wasm_runtime_free(buf);
buf = nullptr;
}
}

/**
* Execute f32.const test function and return the loaded constant value
*
* @param func_name Name of the WASM function to execute (must return f32)
* @return The f32 constant value loaded by the function
*/
float call_const_func(const char* func_name)
{
wasm_function_inst_t func_inst = wasm_runtime_lookup_function(module_inst, func_name);
EXPECT_NE(func_inst, nullptr) << "Failed to lookup function: " << func_name;

wasm_val_t results[1];
wasm_val_t arguments[1]; // f32.const doesn't need arguments but declaring for consistency

bool success = wasm_runtime_call_wasm_a(exec_env, func_inst, 1, results, 0, arguments);
EXPECT_TRUE(success) << "Failed to call function: " << func_name
<< ", exception: " << wasm_runtime_get_exception(module_inst);

return results[0].of.f32;
}

/**
* Execute multiple f32.const test function and return the loaded constant values
*
* @param func_name Name of the WASM function to execute (must return multiple f32 values)
* @param result_count Number of f32 values to expect
* @param results Array to store the returned f32 constant values
*/
void call_multi_const_func(const char* func_name, uint32_t result_count, float* results)
{
wasm_function_inst_t func_inst = wasm_runtime_lookup_function(module_inst, func_name);
ASSERT_NE(func_inst, nullptr) << "Failed to lookup function: " << func_name;

wasm_val_t wasm_results[16]; // Sufficient for multiple constants
wasm_val_t arguments[1]; // f32.const doesn't need arguments

bool success = wasm_runtime_call_wasm_a(exec_env, func_inst, result_count, wasm_results, 0, arguments);
ASSERT_TRUE(success) << "Failed to call function: " << func_name
<< ", exception: " << wasm_runtime_get_exception(module_inst);

for (uint32_t i = 0; i < result_count; i++) {
results[i] = wasm_results[i].of.f32;
}
}

/**
* Check if two f32 values are bitwise identical (handles NaN correctly)
*
* @param a First f32 value
* @param b Second f32 value
* @return true if bit patterns are identical, false otherwise
*/
bool are_f32_bitwise_equal(float a, float b)
{
uint32_t bits_a, bits_b;
memcpy(&bits_a, &a, sizeof(uint32_t));
memcpy(&bits_b, &b, sizeof(uint32_t));
return bits_a == bits_b;
}

/**
* Get bit pattern of f32 value as uint32_t
*
* @param value f32 value to get bit pattern for
* @return uint32_t representing the bit pattern
*/
uint32_t get_f32_bits(float value)
{
uint32_t bits;
memcpy(&bits, &value, sizeof(uint32_t));
return bits;
}
};




/**
* @test SpecialValues_PreservesIEEE754
* @brief Validates f32.const preserves special IEEE 754 values correctly
* @details Tests NaN, positive/negative infinity, positive/negative zero to ensure
* f32.const correctly handles all special IEEE 754 single-precision values.
* @test_category Edge - Special IEEE 754 value validation
* @coverage_target core/iwasm/interpreter/wasm_interp_classic.c:f32_const_operation
* @input_conditions NaN, +/-infinity, +/-zero IEEE 754 values
* @expected_behavior Exact preservation of special values with correct IEEE 754 semantics
* @validation_method Special value checks and bitwise comparison for signed zeros
*/
TEST_P(F32ConstTest, SpecialValues_PreservesIEEE754)
{
// Test NaN (Not-a-Number)
float nan_result = call_const_func("get_nan");
ASSERT_TRUE(std::isnan(nan_result))
<< "f32.const failed to preserve NaN value";

// Test positive infinity
ASSERT_TRUE(std::isinf(call_const_func("get_pos_inf")) && call_const_func("get_pos_inf") > 0)
<< "f32.const failed to preserve positive infinity";

// Test negative infinity
ASSERT_TRUE(std::isinf(call_const_func("get_neg_inf")) && call_const_func("get_neg_inf") < 0)
<< "f32.const failed to preserve negative infinity";

// Test positive zero (0x00000000)
float pos_zero = call_const_func("get_pos_zero");
ASSERT_EQ(pos_zero, 0.0f) << "f32.const failed to load positive zero";
ASSERT_EQ(get_f32_bits(pos_zero), 0x00000000U)
<< "f32.const positive zero has incorrect bit pattern";

// Test negative zero (0x80000000)
float neg_zero = call_const_func("get_neg_zero");
ASSERT_EQ(neg_zero, -0.0f) << "f32.const failed to load negative zero";
ASSERT_EQ(get_f32_bits(neg_zero), 0x80000000U)
<< "f32.const negative zero has incorrect bit pattern";
}



/**
* @test ConstantsInOperations_FunctionsCorrectly
* @brief Validates f32.const values work correctly in subsequent operations
* @details Tests using f32.const values in f32.add operations to ensure constants
* are properly loaded and available for arithmetic operations.
* @test_category Integration - Constant usage in operations
* @coverage_target core/iwasm/interpreter/wasm_interp_classic.c:f32_const_operation
* @input_conditions f32.const values used as operands in f32.add
* @expected_behavior Correct arithmetic results using loaded constants
* @validation_method Verification of arithmetic operation results
*/
TEST_P(F32ConstTest, ConstantsInOperations_FunctionsCorrectly)
{
// Test f32.const 2.5 + f32.const 3.7 = 6.2
ASSERT_EQ(call_const_func("add_two_constants"), 6.2f)
<< "f32.const values failed in addition operation";

// Test f32.const 10.0 - f32.const 3.5 = 6.5
ASSERT_EQ(call_const_func("subtract_constants"), 6.5f)
<< "f32.const values failed in subtraction operation";

// Test f32.const 2.0 * f32.const 1.5 = 3.0
ASSERT_EQ(call_const_func("multiply_constants"), 3.0f)
<< "f32.const values failed in multiplication operation";
}

// Parameterized test instantiation for both interpreter and AOT modes
INSTANTIATE_TEST_SUITE_P(RunningModeTest, F32ConstTest,
testing::Values(Mode_Interp, Mode_LLVM_JIT),
[](const testing::TestParamInfo<F32ConstTest::ParamType> &info) {
return info.param == Mode_Interp ? "INTERP" : "AOT";
});

int main(int argc, char **argv)
{
char *cwd = getcwd(nullptr, 0);
if (cwd != nullptr) {
CWD = std::string(cwd);
free(cwd);
} else {
CWD = ".";
}

WASM_FILE = CWD + "/wasm-apps/f32_const_test.wasm";

app_argc = argc;
app_argv = argv;

::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Loading
Loading