Skip to content

Instantly share code, notes, and snippets.

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

NOTE: the content is out-of-date. All development is moved to the https://github.com/yurydelendik/wasmception

Using WebAssembly in LLVM

Compiling

# locations, e.g.
export WORKDIR=~/llvmwasm; mkdir -p $WORKDIR
export INSTALLDIR=$WORKDIR

# checkout LLVM
cd $WORKDIR
svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm

# checkout clang
cd $WORKDIR/llvm/tools
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang

# checkout lld
svn co http://llvm.org/svn/llvm-project/lld/trunk lld

# build folder (~14 min; ~1 hour /wo -j)
mkdir $WORKDIR/llvm-build
cd $WORKDIR/llvm-build
# For Debug build:
# cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=$INSTALLDIR -DLLVM_TARGETS_TO_BUILD= -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly -DCMAKE_BUILD_TYPE=Debug $WORKDIR/llvm
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=$INSTALLDIR -DLLVM_TARGETS_TO_BUILD= -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly $WORKDIR/llvm 
make -j 8

# install llvm
make install

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.)

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

See output at fib.s.

Convert to WebAssebly s-expressions (needs binaryen s2wasm tool):

$BINARYENDIR/bin/s2wasm fib.s > fib.wast

See output at fib.wast.

References

#/bin/bash
llvm_root=~/llvmwasm/llvm-build/bin
binaryen_root=~/llvmwasm/binaryen/bin
cfile=$1
bcfile=${cfile%.*}.bc
sfile=${cfile%.*}.s
wastfile=${cfile%.*}.wast
$llvm_root/clang -emit-llvm --target=wasm32 -Oz "$cfile" -c -o "$bcfile"
if [ $? != 0 ];then
exit 1
fi
$llvm_root/llc -asm-verbose=false -o "$sfile" "$bcfile"
if [ $? != 0 ];then
exit 2
fi
$binaryen_root/s2wasm "$sfile" > "$wastfile"
if [ $? != 0 ];then
exit 3
fi
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"
.globl fib
.type fib,@function
fib:
.param i32
.result i32
block $BB0_3
i32.const 1
set_local @9, pop
i32.const 0
set_local @13, pop
i32.const -1
set_local @12, pop
@0
set_local @6, pop
@9
set_local @14, pop
BB0_1:
loop $BB0_3
add @12, @9
set_local @12, pop
sge @12, @6
set_local @11, pop
brif $BB0_3, @11
add @14, @13
set_local @5, pop
@14
set_local @13, pop
@5
set_local @14, pop
br $BB0_1
BB0_3:
return @14
func_end0:
.size fib, func_end0-fib
(module
(import $print_i32 "stdio" "print" (param i32))
(import $print_f32 "stdio" "print" (param f32))
(import $print_i64 "stdio" "print" (param i64))
(import $print_f64 "stdio" "print" (param f64))
(func $fib
(param i32)
(result i32)
(block $BB0_3
(set_local @9 (i32.const 1))
(set_local @13 (i32.const 0))
(set_local @12 (i32.const -1))
(set_local @6 (@0))
(set_local @14 (@9))
(loop $BB0_1
(set_local @12 (add @12 @9))
(set_local @11 (sge @12 @6))
(brif $BB0_3 @11)
(set_local @5 (add @14 @13))
(set_local @13 (@14))
(set_local @14 (@5))
(br $BB0_1)
)
)
(return @14)
)
(memory 0 0
(segment 0
""
)
)
)
ROOT_DIR=$(shell pwd)
LLVM_WORKDIR=$(ROOT_DIR)/src/llvm
LLVM_BUILDDIR=$(ROOT_DIR)/build/llvm
BINARYEN_WORKDIR=$(ROOT_DIR)/src/binaryen
BINARYEN_BUILDDIR=$(ROOT_DIR)/build/binaryen
INSTALLDIR=$(ROOT_DIR)/bin
build: build-llvm build-binaryen
clean: clean-llvm clean-binaryen
rm -rf $(INSTALLDIR)
install: install-llvm install-binaryen
$(LLVM_WORKDIR)/.svn:
rm -rf $(LLVM_WORKDIR)
mkdir -p $(LLVM_WORKDIR)
svn co http://llvm.org/svn/llvm-project/llvm/trunk $(LLVM_WORKDIR)
svn co http://llvm.org/svn/llvm-project/cfe/trunk $(LLVM_WORKDIR)/tools/clang
svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk $(LLVM_WORKDIR)/projects/compiler-rt
$(LLVM_BUILDDIR)/Makefile: $(LLVM_WORKDIR)/.svn
mkdir -p $(LLVM_BUILDDIR)
cd $(LLVM_BUILDDIR); cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=$(INSTALLDIR) -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly -DLLVM_TARGETS_TO_BUILD= -DCMAKE_BUILD_TYPE=Release $(LLVM_WORKDIR)
build-llvm: $(LLVM_BUILDDIR)/Makefile
$(MAKE) -C $(LLVM_BUILDDIR) -j 8 clang llc llvm-lib
clean-llvm:
rm -rf $(LLVM_WORKDIR)
rm -rf $(LLVM_BUILDDIR)
install-llvm:
mkdir -p $(INSTALLDIR)
$(MAKE) -C $(LLVM_BUILDDIR) install-clang install-llc install-llvm-lib
$(BINARYEN_WORKDIR)/.git:
rm -rf $(BINARYEN_WORKDIR)
mkdir -p $(BINARYEN_WORKDIR)
git clone https://github.com/WebAssembly/binaryen $(BINARYEN_WORKDIR)
$(BINARYEN_BUILDDIR)/Makefile: $(BINARYEN_WORKDIR)/.git
mkdir -p $(BINARYEN_BUILDDIR)
cd $(BINARYEN_BUILDDIR); cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=$(INSTALLDIR) -DCMAKE_BUILD_TYPE=Release $(BINARYEN_WORKDIR)
build-binaryen: $(BINARYEN_BUILDDIR)/Makefile
$(MAKE) -C $(BINARYEN_BUILDDIR) -j 8 wasm-as wasm-dis s2wasm
clean-binaryen:
rm -rf $(BINARYEN_WORKDIR)
rm -rf $(BINARYEN_BUILDDIR)
install-binaryen:
mkdir -p $(INSTALLDIR)
cp $(addprefix $(BINARYEN_BUILDDIR)/bin/, wasm-as wasm-dis s2wasm) $(INSTALLDIR)
.PHONY: build clean install build-llvm clean-llvm install-llvm build-binaryen clean-binaryen install-binaryen
@msimpson

This comment has been minimized.

Copy link

msimpson commented Mar 15, 2017

Thanks for this guide, it was a great help when figuring out a Windows tool chain. I've posted modified Windows instructions here:
https://gist.github.com/msimpson/cd7e9372be9b3cbf7ba89ce1439c9ee1

@mko-io

This comment has been minimized.

Copy link

mko-io commented Mar 17, 2017

thank you for this guide, It's useful for me to try out wasm

@phraemer

This comment has been minimized.

Copy link

phraemer commented Mar 23, 2017

(Notice -O2 or above near clang to avoid failure during llc run.)

What's that about? Anyone got any more information about it?

@puzrin

This comment has been minimized.

Copy link

puzrin commented Apr 11, 2017

@yurydelendik FYI, llvm by default puts memory into export section. While in emsdk memory is placed to import section. To fix that, call s2wasm with --emscripten-glue option. In other case you will not be able pass data blocks from JS into wasm.

@yurydelendik

This comment has been minimized.

Copy link
Owner Author

yurydelendik commented Apr 17, 2017

After http://llvm.org/viewvc/llvm-project?view=revision&revision=300490 , bin/clang --target=wasm32-unknown-unknown-wasm fib.c -c -o fib.wasm produces valid wasm.

@yurydelendik

This comment has been minimized.

Copy link
Owner Author

yurydelendik commented Apr 18, 2017

WebAssembly/binaryen@5fb669a adds --import-memory to the s2wasm

@alexhultman

This comment has been minimized.

Copy link

alexhultman commented May 3, 2017

@yurydelendik --target=wasm32-unknown-unknown-wasm fib.c -c -o fib.wasm works perfectly, but don't forget the -O3 flag. It works and produces far smaller and better code. Great gist btw.

@liangshuochen

This comment has been minimized.

Copy link

liangshuochen commented May 4, 2017

thanks a lot!

@reklatsmasters

This comment has been minimized.

Copy link

reklatsmasters commented Sep 3, 2017

@yurydelendik My clang@4.0 print error No available targets are compatible with this triple. when i use --target=wasm32-unknown-unknown-wasm.

@puzrin

This comment has been minimized.

Copy link

puzrin commented Sep 5, 2017

@yurydelendik more optimizations:

https://github.com/nodeca/multimath/blob/master/support/llvmwasm_install.sh

Major:

  1. Minimize build size (18gb -> 800mb)
  2. Use Gold linker (reduce memory consumption)

Other:

  • pin llvm/binaryen versions (more reproducible if problem happens)
@yurydelendik

This comment has been minimized.

Copy link
Owner Author

yurydelendik commented Nov 16, 2017

Added lld compilation. Compiled by bin/clang --target=wasm32-unknown-unknown-wasm fib.c -c -o fib.wasm file can be linked with bin/lld -flavor wasm fib.wasm -o fib.final.wasm -e fib

@yurydelendik

This comment has been minimized.

Copy link
Owner Author

yurydelendik commented Dec 22, 2017

@fospring

This comment has been minimized.

Copy link

fospring commented Mar 28, 2019

@yurydelendik
When I use command:

s2wasm fib.s > fib.wast

Console output:

[[function element:]]:
==========
.functype	fib (i32) -> (i32)
	.local  	i32, i32, i32, i32
	i
==========
Abort trap: 6

And the fib.wast file is empty.
Can you tell me why?
My plateform is Mac(Darwin Kernel Version 17.7.0,macOS 10.13.6)

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.