layout: "../layouts/BlogPost.astro" title: "An example of compiling C code to Wasm" slug: example-of-compiling-c-code-to-wasm description: "" added: "July 23 2023" tags: [web]
Sometimes you want to use a library that is only available as C or C++ code. Traditionally, this is where you give up. Well, not anymore, because now we have Emscripten and WebAssembly.
WebAssembly is a type of code that can be run in modern web browsers — it is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages such as C/C++, C# and Rust with a compilation target so that they can run on the web.
asm.js
, a strict subset of JavaScript that could be used as a low-level, efficient target language for compilers.asm.js
still works in browsers, it has been superseded by WebAssembly..wat
(for WebAssembly text format). If you really wanted to, you could write it by hand. The binary format that uses the file extension .wasm
is not meant for human consumption, let alone human creation..wat
nor .wasm
are particularly very human-friendly. This is where a compiler like Emscripten comes into play. It lets you compile from higher-level languages like C and C++.Emscripten compiles C and C++ code, or any other language that uses LLVM, into WebAssembly, and run it on the Web, Node.js, or other wasm runtimes. Emscripten provides Web support for popular portable APIs such as OpenGL, allowing complex graphical native applications to be ported, such as the Unity game engine and Google Earth.
First, let's set up the required development environment. Get the Emscripten SDK, using the instructions on their website or using Homebrew.
# https://formulae.brew.sh/formula/emscripten
brew install emscripten
emcc --version
emcc --help
There are a number of options available when compiling with Emscripten, but the main two scenarios are:
Take a copy of the following simple C example, and save it in a file called hello.c
.
#include <stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
To build the JavaScript version of this code, simply specify the C file after emcc
command. The Emscripten Compiler Frontend (emcc
) is used to call the Emscripten compiler from the command line. It is effectively a drop-in replacement for a standard compiler like gcc
or clang
.
emcc hello.c
You should see two files generated by that command: a.out.js
and a.out.wasm
. The second is a WebAssembly file containing the compiled code, and the first is a JavaScript file containing the runtime support to load and execute it. You can run them using node.js node a.out.js
, and it prints “Hello World” to the console as expected.
WebAssembly.compile(...).then(module => {
const instance = new WebAssembly.Instance(module)
const { add, square } = instance.exports
console.log('2 + 4 =', add(2, 4))
console.log('3^2 =', square(3))
console.log('(2 + 5)^2 =', square(add(2 + 5)))
})
Another command can be used is emcc hello.c -o hello.html
. This specifies that we want Emscripten to generate an HTML page to run our code, as well as the Wasm module and the JavaScript "glue" code to compile and instantiate the Wasm so it can be used in the web environment. (Emscripten requires a large variety of JavaScript "glue" code to handle memory allocation, memory leaks, and a host of other problems.) Now all that remains is for you to load the resulting hello.html
in a browser that supports WebAssembly.
<img alt="emcc example" src="https://raw.gitmirror.com/kexiZeroing/blog-images/main/ad4c5db4-c53a-424d-a509-f5228cde0a16%202.png" width="700" />
Sometimes you will want to use a custom HTML template. Search for the file shell_minimal.html in Emscripten repo, and copy it into a subdirectory called html_template
.
emcc -o hello2.html hello.c -O3 --shell-file html_template/shell_minimal.html
-o hello2.html
, meaning that the compiler will still output the JavaScript glue code and .html
.-O3
, which is used to optimize the code. Emcc has optimization levels like any other C compiler, including: -O0
(no optimization), -O1
, -O2
, -Os
, -Oz
, -Og
, and -O3
. -O3
is a good setting for release builds. See more at https://emscripten.org/docs/tools_reference/emcc.html#arguments--shell-file html_template/shell_minimal.html
provides the path to the HTML template you want to use to create the HTML you will run your example through.<img alt="emcc example2" src="https://raw.gitmirror.com/kexiZeroing/blog-images/main/4n1zrr.png" width="700" />