Skip to content

Commit d451607

Browse files
committed
Added examples for WASM arch
1 parent 95a15f0 commit d451607

File tree

16 files changed

+909
-0
lines changed

16 files changed

+909
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(wasm_example CXX)
3+
4+
find_package(Eigen3 REQUIRED)
5+
add_executable(wasm_example main.cpp)
6+
7+
target_link_libraries(${PROJECT_NAME} PRIVATE Eigen3::Eigen)
8+
9+
install(TARGETS wasm_example DESTINATION "."
10+
RUNTIME DESTINATION bin
11+
ARCHIVE DESTINATION lib
12+
LIBRARY DESTINATION lib
13+
)
14+
15+
# Required for Emscripten + embind
16+
set(CMAKE_EXECUTABLE_SUFFIX ".html")
17+
18+
# set_target_properties(wasm_example PROPERTIES
19+
# LINK_FLAGS "-sEXPORTED_FUNCTIONS=['_malloc','_free'] -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','getValue','setValue'] -sENVIRONMENT=web -sALLOW_MEMORY_GROWTH=1 -sNO_EXIT_RUNTIME=1 --shell-file ${CMAKE_SOURCE_DIR}/shell.html"
20+
# )
21+
22+
set_target_properties(wasm_example PROPERTIES
23+
LINK_FLAGS
24+
"-sEXPORTED_FUNCTIONS=['_malloc','_free'] \
25+
-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','getValue','setValue'] \
26+
-sENVIRONMENT=web \
27+
-sALLOW_MEMORY_GROWTH=1 \
28+
-sNO_EXIT_RUNTIME=1 \
29+
--shell-file ${CMAKE_SOURCE_DIR}/shell.html"
30+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# WASM project with bindings and conan dependency
2+
3+
4+
## Build and run
5+
6+
To compile the project:
7+
8+
```sh
9+
$ conan build . -pr:h ../profiles/wasm32 --build=missing
10+
```
11+
12+
To open a WASM webpage locally, most of the browsers will complain due to security reasons as WASM must be loaded asynchronous
13+
14+
The easiest way of opening the generated webpage (should be in `build/release-wasm/wasm_example.html`) is by running a local server.
15+
This can be done via `emrun` command (needs to be downloaded):
16+
17+
```sh
18+
$ emrun --browser <browser_name> build/release-wasm/wasm_example.html
19+
```
20+
21+
Or using python `http.server` module:
22+
23+
```sh
24+
$ python -m http.server 8080
25+
```
26+
Then, navigating to your build folder and open `wasm_example.html`
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import os
2+
3+
from conan import ConanFile
4+
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
5+
from conan.tools.files import copy
6+
7+
8+
class WasmExampleRecipe(ConanFile):
9+
name = "wasm-example"
10+
version = "1.0"
11+
package_type = "application"
12+
13+
# Optional metadata
14+
license = "<Put the package license here>"
15+
author = "<Put your name here> <And your email here>"
16+
url = "<Package recipe repository url here, for issues about the package>"
17+
description = "<Description of exe package here>"
18+
topics = ("<Put some tag here>", "<here>", "<and here>")
19+
20+
# Binary configuration
21+
settings = "os", "compiler", "build_type", "arch"
22+
23+
# Sources are located in the same place as this recipe, copy them to the recipe
24+
exports_sources = "CMakeLists.txt", "src/*"
25+
26+
def layout(self):
27+
cmake_layout(self)
28+
29+
def requirements(self):
30+
self.requires("eigen/3.4.0")
31+
32+
def generate(self):
33+
deps = CMakeDeps(self)
34+
deps.generate()
35+
tc = CMakeToolchain(self)
36+
tc.generate()
37+
38+
def build(self):
39+
cmake = CMake(self)
40+
cmake.configure()
41+
cmake.build()
42+
43+
def package(self):
44+
cmake = CMake(self)
45+
cmake.install()
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include <cstdint>
2+
#include <emscripten/emscripten.h>
3+
#include <iostream>
4+
#include <Eigen/Core>
5+
#include <string>
6+
7+
#ifdef __cplusplus
8+
#define EXTERN extern "C"
9+
#else
10+
#define EXTERN
11+
#endif
12+
13+
14+
EXTERN EMSCRIPTEN_KEEPALIVE
15+
uint32_t fib(uint32_t n) {
16+
std::cout << "Calculating Fibonacci for n = " << n << std::endl;
17+
if (n <= 1) return n;
18+
uint32_t a = 0, b = 1, c;
19+
for (uint32_t i = 2; i <= n; ++i) {
20+
c = a + b;
21+
a = b;
22+
b = c;
23+
}
24+
return c;
25+
}
26+
27+
EXTERN EMSCRIPTEN_KEEPALIVE
28+
void printMessage(const char* message) {
29+
std::cout << "Message from C: " << message << std::endl;
30+
std::string script = "alert('Message from C++: " + std::string(message) + "')";
31+
std::cout << "Executing script: " << script << std::endl;
32+
emscripten_run_script(script.c_str());
33+
}
34+
35+
EXTERN EMSCRIPTEN_KEEPALIVE
36+
void addOne(int32_t *input, int32_t *output){
37+
*output = *input + 1;
38+
}
39+
40+
EXTERN EMSCRIPTEN_KEEPALIVE
41+
float sumArray(const float* data, int32_t size) {
42+
// print data input
43+
std::cout << "Data input: ";
44+
for (int i = 0; i < size; ++i) {
45+
std::cout << data[i] << " ";
46+
}
47+
std::cout << std::endl;
48+
Eigen::Map<const Eigen::ArrayXf> vec(data, size);
49+
return vec.sum();
50+
}
51+
52+
53+
int main() {
54+
std::cout << "Hello World!" << std::endl;
55+
auto data = new float[5]{1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
56+
std::cout << sumArray(data, 5) << std::endl;
57+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<!doctype html>
2+
<html lang="en-us">
3+
<body>
4+
<h1>Conan C++ WebAssembly Example</h1>
5+
<br />
6+
<div id="status">Downloading...</div>
7+
<div>
8+
<progress value="0" max="100" id="progress" hidden="1"></progress>
9+
</div>
10+
<textarea id="output" rows="8" style="width: 100%"></textarea>
11+
<hr />
12+
13+
<!-- Example of calling JS from C -->
14+
<button onclick="printMessage()">Print Message</button>
15+
<hr />
16+
17+
<!-- Example of a simple invocation to fibonachi -->
18+
<input type="number" id="fibInput" placeholder="e.g., 10" />
19+
<button onclick="fibExample()">Compute Fibonacci</button>
20+
<p id="fibResult"></p>
21+
<hr />
22+
23+
<!-- Example of a function call using two buffers -->
24+
<button onclick="pressBtn()">Click me to increase counter!</button>
25+
<p id="counterResult"></p>
26+
<hr />
27+
28+
<!-- Example of a function call using a Float32Array and Eigen -->
29+
<input
30+
type="text"
31+
id="numbersInput"
32+
placeholder="e.g., 42.2, 2.1, 8"
33+
size="50"
34+
/>
35+
<button onclick="sumExample()">Compute Float32 Sum with Eigen</button>
36+
<p id="sumResult"></p>
37+
38+
<script type="text/javascript">
39+
var statusElement = document.getElementById("status");
40+
var progressElement = document.getElementById("progress");
41+
42+
var Module = {
43+
print: (function () {
44+
var element = document.getElementById("output");
45+
if (element) element.value = ""; // clear browser cache
46+
return (...args) => {
47+
var text = args.join(" ");
48+
console.log(text);
49+
if (element) {
50+
element.value += text + "\n";
51+
element.scrollTop = element.scrollHeight; // focus on bottom
52+
}
53+
};
54+
})(),
55+
setStatus: (text) => {
56+
Module.setStatus.last ??= { time: Date.now(), text: "" };
57+
if (text === Module.setStatus.last.text) return;
58+
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
59+
var now = Date.now();
60+
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
61+
Module.setStatus.last.time = now;
62+
Module.setStatus.last.text = text;
63+
if (m) {
64+
text = m[1];
65+
progressElement.value = parseInt(m[2]) * 100;
66+
progressElement.max = parseInt(m[4]) * 100;
67+
progressElement.hidden = false;
68+
} else {
69+
progressElement.value = null;
70+
progressElement.max = null;
71+
progressElement.hidden = true;
72+
}
73+
statusElement.innerHTML = text;
74+
},
75+
totalDependencies: 0,
76+
monitorRunDependencies: (left) => {
77+
this.totalDependencies = Math.max(this.totalDependencies, left);
78+
Module.setStatus(
79+
left
80+
? "Preparing... (" +
81+
(this.totalDependencies - left) +
82+
"/" +
83+
this.totalDependencies +
84+
")"
85+
: "All downloads complete.",
86+
);
87+
},
88+
};
89+
Module.setStatus("Downloading...");
90+
window.onerror = () => {
91+
Module.setStatus("Exception thrown, see JavaScript console");
92+
Module.setStatus = (text) => {
93+
if (text) console.error("[post-exception status] " + text);
94+
};
95+
};
96+
97+
// Example of auto string handle by WASM on simple string parameter passing
98+
const printMessage = () => {
99+
var printer = Module.ccall(
100+
"printMessage",
101+
null,
102+
["string"],
103+
["Hello from C++ WebAssembly!"],
104+
);
105+
};
106+
107+
// Example of a simple invocation to fibonachi
108+
const fibExample = () => {
109+
var fib = Module.cwrap("fib", "number", ["number"]); // returns a number
110+
var input = parseInt(document.getElementById("fibInput").value);
111+
if (isNaN(input)) {
112+
alert("Please enter a valid integer.");
113+
return;
114+
}
115+
var result = fib(input);
116+
document.getElementById("fibResult").textContent =
117+
"Fibonacci of " + input + " is: " + result;
118+
};
119+
120+
var value = 0; // (static) value to increment by one
121+
const pressBtn = () => {
122+
var addOne = Module.cwrap("addOne", null, ["number", "number"]); // void function
123+
// alloc 4 bytes of memory for the input and 4 for the output (32-bit integers)
124+
var input_ptr = Module._malloc(4);
125+
var output_ptr = Module._malloc(4);
126+
127+
Module.setValue(input_ptr, value, "i32"); // set the value in WASM memory
128+
addOne(input_ptr, output_ptr); // call the WASM function
129+
var result = Module.getValue(output_ptr, "i32"); // extract the result from WASM memory
130+
value = result;
131+
document.getElementById("counterResult").textContent = "Sum: " + result;
132+
133+
// dealloc memory to avoid memory leaks
134+
Module._free(input_ptr);
135+
Module._free(output_ptr);
136+
};
137+
138+
const sumExample = () => {
139+
var sumArray = Module.cwrap("sumArray", "number", ["number", "number"]);
140+
// Get the input string and split by commas
141+
const inputStr = document.getElementById("numbersInput").value;
142+
const numberStrings = inputStr.split(",").map((s) => s.trim());
143+
144+
// Convert to Float32Array
145+
const inputArray = new Float32Array(numberStrings.map(Number));
146+
var len = inputArray.length;
147+
var bytesPerElement = inputArray.BYTES_PER_ELEMENT;
148+
var inputPtr = Module._malloc(len * bytesPerElement);
149+
Module.HEAPF32.set(inputArray, inputPtr / bytesPerElement);
150+
var result = sumArray(inputPtr, len);
151+
Module._free(inputPtr);
152+
document.getElementById("sumResult").textContent = "Sum: " + result;
153+
};
154+
</script>
155+
{{{ SCRIPT }}}
156+
</body>
157+
</html>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[settings]
2+
arch=wasm
3+
build_type=Release
4+
compiler=apple-clang
5+
compiler.cppstd=gnu17
6+
compiler.cstd=gnu11
7+
compiler.libcxx=libc++
8+
compiler.version=17
9+
os=Emscripten
10+
11+
[tool_requires]
12+
emsdk/[*]
13+
14+
[conf]
15+
tools.build:exelinkflags=['-sALLOW_MEMORY_GROWTH=1', '-sMAXIMUM_MEMORY=4GB', '-sINITIAL_MEMORY=64MB' ]
16+
tools.build:sharedlinkflags=[ '-sALLOW_MEMORY_GROWTH=1', '-sMAXIMUM_MEMORY=4GB', '-sINITIAL_MEMORY=64MB' ]
17+
18+
# Uncomment the following line to use emcc and em++ system compilers instead of conan packaged ones
19+
# tools.build:compiler_executables={'c':'emcc', 'cpp':'em++'}
20+
21+
# Verbosity
22+
tools.build:verbosity=verbose
23+
tools.compilation:verbosity=verbose
24+
25+
# Distinguish between wasm32 and wasm64 architecture
26+
tools.cmake.cmake_layout:build_folder_vars=['settings.build_type', 'settings.arch']
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[settings]
2+
arch=wasm64
3+
build_type=Release
4+
compiler=apple-clang
5+
compiler.cppstd=gnu17
6+
compiler.cstd=gnu11
7+
compiler.libcxx=libc++
8+
compiler.version=17
9+
os=Emscripten
10+
11+
[tool_requires]
12+
emsdk/[*]
13+
14+
[conf]
15+
tools.build:cflags=['-sMEMORY64=1']
16+
tools.build:cxxflags=['-sMEMORY64=1']
17+
18+
# In this early stage of wasm64, ALLOW_MEMORY_GROWTH is not having effect. Also it may not be the most efficient solution.
19+
# wasm64 for now needs to declare INITIAL_MEMORY as the maximum memory
20+
tools.build:exelinkflags=[ '-sMEMORY64=1', '-sALLOW_MEMORY_GROWTH=1', '-sMAXIMUM_MEMORY=16GB', '-sINITIAL_MEMORY=16GB' ]
21+
tools.build:sharedlinkflags=[ '-sMEMORY64=1', '-sALLOW_MEMORY_GROWTH=1', '-sMAXIMUM_MEMORY=16GB', '-sINITIAL_MEMORY=16GB' ]
22+
23+
# Uncomment the following line to use emcc and em++ system compilers instead of conan packaged ones
24+
# tools.build:compiler_executables={'c':'emcc', 'cpp':'em++'}
25+
26+
# General config
27+
tools.build:verbosity=verbose
28+
tools.compilation:verbosity=verbose
29+
30+
# Distinguish between wasm32 and wasm64 architecture
31+
tools.cmake.cmake_layout:build_folder_vars=['settings.build_type', 'settings.arch']
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(wasm-alloc CXX)
3+
4+
add_executable(wasm-alloc main.cpp)

0 commit comments

Comments
 (0)