Skip to content

internal/task: use asyncify on WASM #2011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 75 additions & 19 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ commands:
qemu-system-arm \
qemu-user \
gcc-avr \
avr-libc
avr-libc \
cmake \
ninja-build
Comment on lines +29 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you do this installation only when these tools are needed in the build-binaryen step? That speeds up the CI in the common case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this like this for 2 reasons:

  1. We need to install these differently on buster and stretch.
  2. We need these for both LLVM and Binaryen, and avoiding a double install seemed annoying.

I guess I could potentially restore both caches before installing, although it seems to make this build config a lot more complicated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to only make CI 1 second slower. I don't think the extra complexity is worthwhile.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to only make CI 1 second slower. I don't think the extra complexity is worthwhile.

How did you measure this? It seems rather unlikely that installing software is that fast.
(If it is really just 1 second, then installing it unconditionally would be fine).

Copy link
Member Author

@niaow niaow Nov 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at 2 a build on the dev branch and a build from this PR. The "install apt dependencies steps" took 22 seconds on this PR and 23 seconds on the dev branch. Looking through more CI runs on dev now I see a lot more variation, but the difference in download size is 180 MB to 186 MB which gives me a seemingly appropriate estimate of 3%. These packages are relatively small compared to the other packages which we install.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Well maybe it is fine then. We can revisit this later, especially when we move away from Debian stretch.

install-node:
steps:
- run:
Expand All @@ -49,6 +51,13 @@ commands:
command: |
curl https://wasmtime.dev/install.sh -sSf | bash
sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime
install-cmake:
steps:
- run:
name: "Install CMake"
command: |
wget https://github.com/Kitware/CMake/releases/download/v3.21.4/cmake-3.21.4-linux-x86_64.tar.gz
sudo tar --strip-components=1 -C /usr/local -xf cmake-3.21.4-linux-x86_64.tar.gz
install-xtensa-toolchain:
parameters:
variant:
Expand Down Expand Up @@ -76,6 +85,39 @@ commands:
- llvm-project/clang/include
- llvm-project/lld/include
- llvm-project/llvm/include
hack-ninja-jobs:
steps:
- run:
name: "Hack Ninja to use less jobs"
command: |
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
build-binaryen-linux:
steps:
- restore_cache:
keys:
- binaryen-linux-v1
- run:
name: "Build Binaryen"
command: |
make binaryen
- save_cache:
key: binaryen-linux-v1
paths:
- build/wasm-opt
build-binaryen-linux-stretch:
steps:
- restore_cache:
keys:
- binaryen-linux-stretch-v1
- run:
name: "Build Binaryen"
command: |
CC=$PWD/llvm-build/bin/clang make binaryen
- save_cache:
key: binaryen-linux-stretch-v1
paths:
- build/wasm-opt
build-wasi-libc:
steps:
- restore_cache:
Expand All @@ -100,6 +142,8 @@ commands:
- install-node
- install-chrome
- install-wasmtime
- hack-ninja-jobs
- build-binaryen-linux
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
Expand Down Expand Up @@ -141,9 +185,13 @@ commands:
qemu-system-arm \
qemu-user \
gcc-avr \
avr-libc
avr-libc \
ninja-build \
python3
Comment on lines +189 to +190
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

- install-node
- install-wasmtime
- install-cmake
- hack-ninja-jobs
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
Expand All @@ -159,14 +207,9 @@ commands:
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# fetch LLVM source
# fetch LLVM source (may only have headers right now)
rm -rf llvm-project
make llvm-source
# install dependencies
sudo apt-get install cmake ninja-build
# hack ninja to use less jobs
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
# build!
make ASSERT=1 llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
Expand All @@ -175,6 +218,7 @@ commands:
key: llvm-build-11-linux-v4-assert
paths:
llvm-build
- build-binaryen-linux-stretch
- run: make ASSERT=1
- build-wasi-libc
- run:
Expand Down Expand Up @@ -206,9 +250,13 @@ commands:
qemu-system-arm \
qemu-user \
gcc-avr \
avr-libc
avr-libc \
ninja-build \
python3
Comment on lines +254 to +255
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here.

- install-node
- install-wasmtime
- install-cmake
- hack-ninja-jobs
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
Expand All @@ -224,14 +272,9 @@ commands:
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# fetch LLVM source
# fetch LLVM source (may only have headers right now)
rm -rf llvm-project
make llvm-source
# install dependencies
sudo apt-get install cmake ninja-build
# hack ninja to use less jobs
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
# build!
make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
Expand All @@ -240,6 +283,7 @@ commands:
key: llvm-build-11-linux-v4-noassert
paths:
llvm-build
- build-binaryen-linux-stretch
- build-wasi-libc
- run:
name: "Test TinyGo"
Expand Down Expand Up @@ -283,7 +327,7 @@ commands:
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu cmake ninja
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here.

- install-xtensa-toolchain:
variant: "macos"
- restore_cache:
Expand Down Expand Up @@ -311,11 +355,9 @@ commands:
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# fetch LLVM source
# fetch LLVM source (may only have headers right now)
rm -rf llvm-project
make llvm-source
# install dependencies
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
# build!
make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
Expand All @@ -324,6 +366,20 @@ commands:
key: llvm-build-11-macos-v5
paths:
llvm-build
- restore_cache:
keys:
- binaryen-macos-v1
- run:
name: "Build Binaryen"
command: |
if [ ! -f build/wasm-opt ]
then
make binaryen
fi
- save_cache:
key: binaryen-macos-v1
paths:
- build/wasm-opt
- restore_cache:
keys:
- wasi-libc-sysroot-macos-v4
Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ jobs:
run: |
choco install qemu --version=2020.06.12
echo "C:\Program Files\QEMU" >> $GITHUB_PATH
- name: Install Ninja
shell: bash
run: |
choco install ninja
- name: Checkout
uses: actions/checkout@v2
with:
Expand Down Expand Up @@ -50,8 +54,6 @@ jobs:
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# install dependencies
choco install ninja
# build!
make llvm-build
# Remove unnecessary object files (to reduce cache size).
Expand All @@ -65,6 +67,15 @@ jobs:
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Cache Binaryen
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-v1
path: build/binaryen
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: make binaryen
- name: Test TinyGo
shell: bash
run: make test
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
build
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see you've moved the .gitignore line into a separate file in the build directory.
I guess that's fine, just wondering what the reason is?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to keep the build directory, and for some reason was struggling to get it to track as-is (negated patterns seemed to be behaving weirdly). This is just how I normally do that.

docs/_build
src/device/avr/*.go
src/device/avr/*.ld
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@
[submodule "lib/musl"]
path = lib/musl
url = git://git.musl-libc.org/musl
[submodule "lib/binaryen"]
path = lib/binaryen
url = https://github.com/WebAssembly/binaryen.git
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ COPY --from=tinygo-base /tinygo/targets /tinygo/targets

RUN cd /tinygo/ && \
apt-get update && \
apt-get install -y make clang-11 libllvm11 lld-11 && \
make wasi-libc
apt-get install -y make clang-11 libllvm11 lld-11 cmake ninja-build && \
mkdir build && \
make wasi-libc binaryen

# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
FROM tinygo-base AS tinygo-avr
Expand Down
15 changes: 12 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ ifeq ($(OS),Windows_NT)
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
CGO_LDFLAGS_EXTRA += -lversion

BINARYEN_OPTION += -DCMAKE_EXE_LINKER_FLAGS='-static-libgcc -static-libstdc++'

LIBCLANG_NAME = libclang

else ifeq ($(shell uname -s),Darwin)
Expand Down Expand Up @@ -163,12 +165,18 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: llvm-source
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)

# Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
cd $(LLVM_BUILDDIR); ninja $(NINJA_BUILD_TARGETS)
cd $(LLVM_BUILDDIR) && ninja $(NINJA_BUILD_TARGETS)

# Build Binaryen
.PHONY: binaryen
binaryen: build/wasm-opt
build/wasm-opt:
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON $(BINARYEN_OPTION) && ninja
cp lib/binaryen/bin/wasm-opt build/wasm-opt

# Build wasi-libc sysroot
.PHONY: wasi-libc
Expand Down Expand Up @@ -474,7 +482,7 @@ endif
wasmtest:
$(GO) test ./tests/wasm

build/release: tinygo gen-device wasi-libc
build/release: tinygo gen-device wasi-libc binaryen
@mkdir -p build/release/tinygo/bin
@mkdir -p build/release/tinygo/lib/clang/include
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
Expand All @@ -491,6 +499,7 @@ build/release: tinygo gen-device wasi-libc
@mkdir -p build/release/tinygo/pkg/armv7em-unknown-unknown-eabi
@echo copying source files
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
@cp -p build/wasm-opt$(EXE) build/release/tinygo/bin
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
Expand Down
2 changes: 2 additions & 0 deletions build/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
33 changes: 33 additions & 0 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import (
"io/ioutil"
"math/bits"
"os"
"os/exec"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"

"github.com/tinygo-org/tinygo/cgo"
Expand Down Expand Up @@ -658,6 +660,37 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
}
}

// Run wasm-opt if necessary.
if config.Scheduler() == "asyncify" {
var optLevel, shrinkLevel int
switch config.Options.Opt {
case "none", "0":
case "1":
optLevel = 1
case "2":
optLevel = 2
case "s":
optLevel = 2
shrinkLevel = 1
case "z":
optLevel = 2
shrinkLevel = 2
default:
return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
}
cmd := exec.Command(goenv.Get("WASMOPT"), "--asyncify", "-g",
"--optimize-level", strconv.Itoa(optLevel),
"--shrink-level", strconv.Itoa(shrinkLevel),
executable, "--output", executable)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

err := cmd.Run()
if err != nil {
return fmt.Errorf("wasm-opt failed: %w", err)
}
}

// Print code size if requested.
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
packagePathMap := make(map[string]string, len(lprogram.Packages))
Expand Down
2 changes: 1 addition & 1 deletion compileopts/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
// target.
func (c *Config) FuncImplementation() string {
switch c.Scheduler() {
case "tasks":
case "tasks", "asyncify":
// A func value is implemented as a pair of pointers:
// {context, function pointer}
// where the context may be a pointer to a heap-allocated struct
Expand Down
2 changes: 1 addition & 1 deletion compileopts/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

var (
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
validSchedulerOptions = []string{"none", "tasks", "coroutines", "asyncify"}
validSerialOptions = []string{"none", "uart", "usb"}
validPrintSizeOptions = []string{"none", "short", "full"}
validPanicStrategyOptions = []string{"print", "trap"}
Expand Down
2 changes: 1 addition & 1 deletion compileopts/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func TestVerifyOptions(t *testing.T) {

expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, extalloc, conservative`)
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines`)
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines, asyncify`)
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)

Expand Down
5 changes: 5 additions & 0 deletions compileopts/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
if err != nil {
return nil, err
}

if spec.Scheduler == "asyncify" {
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_asyncify_wasm.S")
}

return spec, nil
}

Expand Down
Loading