Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Using WebAssembly in LLVM on Windows

Using WebAssembly in LLVM on Windows

Forewarning: this can be a bit painful and may not work as expected. I've already had issues with even including stdlib through clang.

Installing Dependencies

GIT

Make sure you have git installed and properly configured before continuing. This is trivial on Windows these days (https://git-scm.com/download/win) but is required to pull down Binaryen and Wabt.

Make sure to add the binary to your PATH variable in Windows.

Subversion

You will first need to install a Subversion client for Windows. This will be used to clone the LLVM and Clang repositories that contain experimental WebAssembly support. I recommend Win32SVN. However, you can install a variety of clients as long as they support the usual command line syntax.

Make sure to add the binary to your PATH variable in Windows.

CMake

To configure LLVM, Binaryen, and Wabt to build you will need CMake. Note: do not use the Cygwin version of this tool as it will lack platform support for Visual Studio.

Official Windows version.

Make sure to add the binary to your PATH variable in Windows.

Visual Studio Community

To build LLVM, Binaryen, and Wabt you will need to install Visual Studio with support for building C\C++ applications and be sure to include the optional packages which include standard libraries, etc.

Get the installer here.

LLVM

rem locations, e.g.
mkdir c:\llvm

rem checkout LLVM
cd c:\llvm
svn co http://llvm.org/svn/llvm-project/llvm/trunk source

rem checkout clang
cd c:\llvm\tools
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang

rem build folder
cd c:\llvm
mkdir build
cd build
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_INSTALL_PREFIX=c:\llvm -DLLVM_TARGETS_TO_BUILD= -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly c:\llvm\source

rem compile (or open the LLVM.sln file in Visual Studio and build using the ALL_BUILD target)
cmake --build --target ALL_BUILD c:\llvm\source

Add "c:\llvm\build\Debug\bin" to your PATH to gain access to "clang.exe" and "llc.exe" from the command line.

Binaryen

cd c:\
git clone git@github.com:WebAssembly/binaryen.git
cd binaryen
cmake --build .

Add "c:\binaryen\bin" to your PATH to gain access to the "s2wasm.exe" from the command line.

Wabt

cd c:\
git clone git@github.com:WebAssembly/wabt.git
cd wabt
cmake --build .

Add "c:\wabt\Debug" to your PATH to gain access to the "wast2wasm.exe" from the command line.

Example

Let's create "fib.c" and compile it, first to LLVM bitcode, then to WebAssembly-like file. (Notice -O2 or above near clang to avoid failure during llc run.)

clang -emit-llvm --target=wasm32 -Oz fib.c -c -o fib.bc
llc -asm-verbose=false -o fib.s fib.bc

See output at "fib.s".

Convert to WebAssembly s-expressions:

s2wasm fib.s > fib.wast

See output at "fib.wast".

Convert to WebAssembly:

wast2wasm fib.wast -o fib.wasm

See output at "fib.wasm".

Note: a "compile.bat" script has been included to repeat these steps automatically. See below.

To run this example, see: https://developer.mozilla.org/en-US/docs/WebAssembly/Loading_and_running

References

Built from the original guide made by yurydelendik at (https://gist.github.com/yurydelendik/4eeff8248aeb14ce763e).

@echo off
clang -emit-llvm --target=wasm32 -Oz "%1" -c -o "%~n1.bc" >nul 2>&1
if %ERRORLEVEL% GEQ 1 (
echo ERR: "clang" failed to generate LLVM Bitcode
exit /B 1
)
llc -asm-verbose=false -o "%~n1.s" "%~n1.bc"
if %ERRORLEVEL% GEQ 1 (
echo ERR: "llc" failed to generate assembly for WASM32
exit /B 1
)
s2wasm "%~n1.s" > "%~n1.wast"
if %ERRORLEVEL% GEQ 1 (
echo ERR: "s2wasm" failed to generate WASM32 S-Expressions.
exit /B 1
)
wast2wasm "%~n1.wast" -o "%~n1.wasm"
if %ERRORLEVEL% GEQ 1 (
echo ERR: "wast2wasm" failed to generate WebAssembly.
exit /B 1
)
int fib(int n) {
int i, t, a = 0, b = 1;
for (i = 0; i < n; i++) {
t = a + b; a = b; b = t;
}
return b;
}
.text
.file "fib.bc"
.hidden fib
.globl fib
.type fib,@function
fib:
.param i32
.result i32
.local i32, i32, i32, i32
i32.const $2=, 0
i32.const $3=, 0
i32.const $4=, 1
.LBB0_1:
block
loop
i32.ge_s $push0=, $2, $0
br_if 1, $pop0
i32.const $push1=, 1
i32.add $2=, $2, $pop1
i32.add $1=, $4, $3
copy_local $3=, $4
copy_local $4=, $1
br 0
.LBB0_3:
end_loop
end_block
copy_local $push2=, $4
.endfunc
.Lfunc_end0:
.size fib, .Lfunc_end0-fib
.ident "clang version 5.0.0 (trunk 297770) (llvm/trunk 297768)"
00000000 00 61 73 6d 01 00 00 00 01 06 01 60 01 7f 01 7f |.asm.......`....|
00000010 03 02 01 00 04 04 01 70 00 00 05 03 01 00 01 07 |.......p........|
00000020 10 02 06 6d 65 6d 6f 72 79 02 00 03 66 69 62 00 |...memory...fib.|
00000030 00 0a 39 01 37 01 04 7f 41 00 21 02 41 00 21 03 |..9.7...A.!.A.!.|
00000040 41 01 21 04 02 40 03 40 20 02 20 00 4e 0d 01 20 |A.!..@.@ . .N.. |
00000050 02 41 01 6a 21 02 20 04 20 03 6a 21 01 20 04 21 |.A.j!. . .j!. .!|
00000060 03 20 01 21 04 0c 00 0b 0b 20 04 0b |. .!..... ..|
0000006c
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "fib" (func $fib))
(func $fib (param $0 i32) (result i32)
(local $1 i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(set_local $2
(i32.const 0)
)
(set_local $3
(i32.const 0)
)
(set_local $4
(i32.const 1)
)
(block $label$0
(loop $label$1
(br_if $label$0
(i32.ge_s
(get_local $2)
(get_local $0)
)
)
(set_local $2
(i32.add
(get_local $2)
(i32.const 1)
)
)
(set_local $1
(i32.add
(get_local $4)
(get_local $3)
)
)
(set_local $3
(get_local $4)
)
(set_local $4
(get_local $1)
)
(br $label$1)
)
)
(get_local $4)
)
)
@Markyparky56

This comment has been minimized.

Copy link

commented Sep 29, 2017

wast2wasm has been renamed to wat2wasm as of this wabt commit.

@TheFlyingFiddle

This comment has been minimized.

Copy link

commented Feb 3, 2018

I had to change the llc command to make this work.

The changed command I used was:

llc fib.s --asm-verbose=false -mtriple=wasm32-unknown-unknown-elf -filetype=asm -o fib.wast
@DragonOsman

This comment has been minimized.

Copy link

commented May 7, 2019

I tried this on my Windows 10 machine with LLVM installed just now (tip-of-the-tree, to use as Wasm Backend with Emscripten--which currently isn't working due to an issue with gen_struct_info which is why I thought I'd try this), and I got an error saying that it can't find stdio.h. I used a Developer Command Prompt for VS 2019. I've tried generating a regular .exe file using Clang and seen that it works, so this seems to only be a problem with targeting Wasm using Clang.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.