Skip to content

Commit a0c94ea

Browse files
committed
feat: enhance WIT stub generation with parallel processing and CMake support
Signed-off-by: Gordon Smith <GordonJSmith@gmail.com>
1 parent 36e192e commit a0c94ea

7 files changed

Lines changed: 272 additions & 72 deletions

File tree

include/wamr.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,19 +246,22 @@ namespace cmcpp
246246
inline LiftLowerContext create_lift_lower_context(
247247
wasm_module_inst_t module_inst,
248248
wasm_exec_env_t exec_env,
249-
wasm_function_inst_t cabi_realloc,
250249
Encoding encoding = Encoding::Utf8)
251250
{
252251
wasm_memory_inst_t memory = wasm_runtime_lookup_memory(module_inst, "memory");
253252
if (!memory)
254253
{
255254
throw std::runtime_error("Failed to lookup memory instance");
256255
}
257-
258256
uint8_t *mem_start_addr = static_cast<uint8_t *>(wasm_memory_get_base_address(memory));
259257
uint8_t *mem_end_addr = nullptr;
260258
wasm_runtime_get_native_addr_range(module_inst, mem_start_addr, nullptr, &mem_end_addr);
261259

260+
wasm_function_inst_t cabi_realloc = wasm_runtime_lookup_function(module_inst, "cabi_realloc");
261+
if (!cabi_realloc)
262+
{
263+
throw std::runtime_error("Failed to lookup cabi_realloc function");
264+
}
262265
GuestRealloc realloc = create_guest_realloc(exec_env, cabi_realloc);
263266
LiftLowerOptions opts(encoding, std::span<uint8_t>(mem_start_addr, mem_end_addr - mem_start_addr), realloc);
264267

@@ -280,7 +283,7 @@ namespace cmcpp
280283
wasm_function_inst_t cabi_realloc = wasm_runtime_lookup_function(module_inst, "cabi_realloc");
281284

282285
// Use the helper function to create LiftLowerContext
283-
LiftLowerContext liftLowerContext = create_lift_lower_context(module_inst, exec_env, cabi_realloc);
286+
LiftLowerContext liftLowerContext = create_lift_lower_context(module_inst, exec_env);
284287
auto params = lift_flat_values<params_t>(liftLowerContext, MAX_FLAT_PARAMS, lower_params);
285288

286289
if constexpr (ValTrait<result_t>::flat_types.size() > 0)

samples/wamr/generated/sample_wamr.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,4 @@ namespace wasm_utils {
9797
const uint32_t DEFAULT_STACK_SIZE = 8192;
9898
const uint32_t DEFAULT_HEAP_SIZE = 8192;
9999

100-
// Note: create_guest_realloc() and create_lift_lower_context() have been
101-
// moved to <wamr.hpp> in the cmcpp namespace and are available for use directly
102-
103100
} // namespace wasm_utils

samples/wamr/main.cpp

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,14 @@ int main(int argc, char **argv)
104104

105105
std::cout << "Using WASM file: " << wasm_path << std::endl;
106106

107-
char *buffer, error_buf[128];
108-
wasm_module_t module;
109-
wasm_module_inst_t module_inst;
110-
wasm_function_inst_t cabi_realloc;
111-
wasm_exec_env_t exec_env;
112107
uint32_t size, stack_size = wasm_utils::DEFAULT_STACK_SIZE, heap_size = wasm_utils::DEFAULT_HEAP_SIZE;
113108

114109
/* initialize the wasm runtime by default configurations */
115110
wasm_runtime_init();
116111
std::cout << "WAMR runtime initialized successfully" << std::endl;
117112

118113
/* read WASM file into a memory buffer */
119-
buffer = read_wasm_binary_to_buffer(wasm_path, &size);
114+
char *buffer = read_wasm_binary_to_buffer(wasm_path, &size);
120115
if (!buffer)
121116
{
122117
std::cerr << "Failed to read WASM file" << std::endl;
@@ -139,7 +134,8 @@ int main(int argc, char **argv)
139134
std::cout << "\nTotal: " << (registered_count + 1) << " host functions registered (all imports from guest perspective)" << std::endl;
140135

141136
// Parse the WASM file from buffer and create a WASM module
142-
module = wasm_runtime_load((uint8_t *)buffer, size, error_buf, sizeof(error_buf));
137+
char error_buf[128];
138+
wasm_module_t module = wasm_runtime_load((uint8_t *)buffer, size, error_buf, sizeof(error_buf));
143139
if (!module)
144140
{
145141
std::cerr << "Failed to load WASM module: " << error_buf << std::endl;
@@ -150,7 +146,7 @@ int main(int argc, char **argv)
150146
std::cout << "\nSuccessfully loaded WASM module" << std::endl;
151147

152148
// Create an instance of the WASM module (WASM linear memory is ready)
153-
module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, error_buf, sizeof(error_buf));
149+
wasm_module_inst_t module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, error_buf, sizeof(error_buf));
154150
if (!module_inst)
155151
{
156152
std::cerr << "Failed to instantiate WASM module: " << error_buf << std::endl;
@@ -161,21 +157,10 @@ int main(int argc, char **argv)
161157
}
162158
std::cout << "Successfully instantiated WASM module" << std::endl;
163159

164-
cabi_realloc = wasm_runtime_lookup_function(module_inst, "cabi_realloc");
165-
if (!cabi_realloc)
166-
{
167-
std::cerr << "Failed to lookup cabi_realloc function" << std::endl;
168-
wasm_runtime_deinstantiate(module_inst);
169-
wasm_runtime_unload(module);
170-
delete[] buffer;
171-
wasm_runtime_destroy();
172-
return 1;
173-
}
174-
175-
exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
160+
wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
176161

177162
// Use the helper from wamr.hpp to create the LiftLowerContext
178-
LiftLowerContext liftLowerContext = cmcpp::create_lift_lower_context(module_inst, exec_env, cabi_realloc);
163+
LiftLowerContext liftLowerContext = cmcpp::create_lift_lower_context(module_inst, exec_env);
179164

180165
std::cout << "\n=== Testing Guest Functions (Exports) ===" << std::endl;
181166
std::cout << "Note: These are functions the GUEST implements, HOST calls them\n"

test/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,56 @@ cd test && ./generate_test_stubs.sh
8383

8484
# Filter specific files
8585
./generate_test_stubs.py -f "streams"
86+
87+
# Skip CMakeLists.txt generation (if you don't need them)
88+
./generate_test_stubs.py --no-cmake
89+
```
90+
91+
#### Individual Stub Compilation
92+
93+
Each generated stub includes its own `CMakeLists.txt` in a dedicated subdirectory for standalone compilation testing:
94+
95+
```bash
96+
# Generate stubs with CMakeLists.txt (default, parallelized)
97+
cmake --build build --target generate-test-stubs
98+
99+
# Navigate to a specific stub directory
100+
cd build/test/generated_stubs/simple-functions
101+
102+
# Build the individual stub
103+
cmake -S . -B build
104+
cmake --build build
86105
```
87106

107+
**Performance:** Generation is parallelized using all available CPU cores by default. For 199 WIT files:
108+
- Sequential: ~20 seconds
109+
- Parallel (32 cores): ~4 seconds (5x faster)
110+
111+
Control parallelization:
112+
```bash
113+
python test/generate_test_stubs.py -j 8 # Use 8 parallel jobs
114+
python test/generate_test_stubs.py -j 1 # Sequential (for debugging)
115+
python test/generate_test_stubs.py --no-cmake # Skip CMakeLists.txt generation
116+
```
117+
118+
**Structure:** Each stub gets its own directory with all files:
119+
```
120+
generated_stubs/
121+
├── simple-functions/
122+
│ ├── CMakeLists.txt
123+
│ ├── simple-functions.hpp
124+
│ ├── simple-functions_wamr.hpp
125+
│ └── simple-functions_wamr.cpp
126+
├── integers/
127+
│ ├── CMakeLists.txt
128+
│ ├── integers.hpp
129+
│ ├── integers_wamr.hpp
130+
│ └── integers_wamr.cpp
131+
...
132+
```
133+
134+
**Note:** Stubs with `_wamr.cpp` files require WAMR (WebAssembly Micro Runtime) headers. The generated CMakeLists.txt includes helpful comments about dependencies and will automatically find the local cmcpp headers.
135+
88136
#### Code Generation Validation
89137

90138
The framework also validates generated code by attempting to compile it:

test/StubGenerationTests.cmake

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,23 @@ set(TEST_STUBS_TO_COMPILE
103103
)
104104

105105
# Collect source files that should exist after generation
106+
# NOTE: Files are now organized in subdirectories, one per stub
106107
set(STUB_SOURCES "")
107108
set(STUB_HEADERS "")
108109
foreach(stub_name ${TEST_STUBS_TO_COMPILE})
110+
# Each stub gets its own subdirectory
111+
set(stub_dir "${STUB_OUTPUT_DIR}/${stub_name}")
112+
109113
# Add main header
110-
set(stub_header "${STUB_OUTPUT_DIR}/${stub_name}.hpp")
114+
set(stub_header "${stub_dir}/${stub_name}.hpp")
111115
list(APPEND STUB_HEADERS ${stub_header})
112116

113117
# Add WAMR header
114-
set(stub_wamr_hpp "${STUB_OUTPUT_DIR}/${stub_name}_wamr.hpp")
118+
set(stub_wamr_hpp "${stub_dir}/${stub_name}_wamr.hpp")
115119
list(APPEND STUB_HEADERS ${stub_wamr_hpp})
116120

117121
# Add WAMR implementation
118-
set(stub_wamr_cpp "${STUB_OUTPUT_DIR}/${stub_name}_wamr.cpp")
122+
set(stub_wamr_cpp "${stub_dir}/${stub_name}_wamr.cpp")
119123
list(APPEND STUB_SOURCES ${stub_wamr_cpp})
120124
endforeach()
121125

@@ -136,10 +140,16 @@ target_link_libraries(test-stubs-compiled
136140
)
137141

138142
# Add include directories
143+
# Since each stub is in its own subdirectory, we need to add each one
139144
target_include_directories(test-stubs-compiled PRIVATE
140145
${CMAKE_SOURCE_DIR}/include
141-
${STUB_OUTPUT_DIR}
142146
)
147+
# Add each stub directory to the include path
148+
foreach(stub_name ${TEST_STUBS_TO_COMPILE})
149+
target_include_directories(test-stubs-compiled PRIVATE
150+
"${STUB_OUTPUT_DIR}/${stub_name}"
151+
)
152+
endforeach()
143153

144154
# Set C++ standard
145155
target_compile_features(test-stubs-compiled PUBLIC cxx_std_20)

0 commit comments

Comments
 (0)