b4cfeb1093
When compiled to WebAssembly, the Dart SDK needs access to external host functions to implement regular expressions, stack traces, timers and more. Currently, `dart2wasm` relies on `js_interop` definition to implement these functions in JavaScript. As discussed in https://github.com/dart-lang/sdk/issues/53884, an alternative is to use `wasm:import` annotations to let an arbitrary embedder that doesn't necessarily run in a JavaScript context inject implementations for these host functions. This would allow running `dart2wasm` apps by e.g. - using a runtime like wasmtime and defining host functions in Rust. - defining a wrapper module implementing required functions by delegating to WASI definitions, and then using say `wasm-merge` to run the app in any WASI-compatible runtime. This prepares the `--standalone` flag on `dart2wasm` to do just that. When enabled, the compiler uses a different SDK platform to use imports instead of JS interop. For now, these platforms are almost identical: I've ported the timer logic to use wasm imports as a demo, but the rest is still based on existing patch files. We can revisit in subsequent CLs to incrementally reduce `js_interop` dependencies before removing that library from the `dart2wasm_standalone` target entirely. Change-Id: I3f406afbf2dab65506094de5c3f4067f4db66f3e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/486380 Reviewed-by: Slava Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
311 lines
8.2 KiB
Bash
Executable File
311 lines
8.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
|
# for details. All rights reserved. Use of this source code is governed by a
|
|
# BSD-style license that can be found in the LICENSE file.
|
|
|
|
# Script to compile a benchmark using dart2wasm. Assumes the Dart repo's
|
|
# directory structure.
|
|
|
|
set -e
|
|
|
|
function follow_links() {
|
|
file="$1"
|
|
while [ -h "$file" ]; do
|
|
# On Mac OS, readlink -f doesn't work.
|
|
file="$(readlink "$file")"
|
|
done
|
|
echo "$file"
|
|
}
|
|
|
|
# Unlike $0, $BASH_SOURCE points to the absolute path of this file.
|
|
PROG_NAME="$(follow_links "$BASH_SOURCE")"
|
|
|
|
# Handle the case where dart-sdk/bin has been symlinked to.
|
|
PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
|
|
source "$PROG_DIR/utils.sh"
|
|
|
|
# Locate build directory, containing executables, snapshots and platform dill.
|
|
if [[ `uname` == 'Darwin' ]]; then
|
|
OUT_DIR="$SDK_DIR/xcodebuild"
|
|
else
|
|
OUT_DIR="$SDK_DIR/out"
|
|
fi
|
|
HOST_ARCH="$(get_host_arch | tr '[:lower:]' '[:upper:]')"
|
|
DART_CONFIGURATION=${DART_CONFIGURATION:-Release$HOST_ARCH}
|
|
BIN_DIR="$OUT_DIR/$DART_CONFIGURATION"
|
|
|
|
BINARYEN="$BIN_DIR/wasm-opt"
|
|
DART="$BIN_DIR/dart"
|
|
DART_AOT_RUNTIME="$BIN_DIR/dartaotruntime"
|
|
LIBRARIES_JSON_ARG="--libraries-spec=$SDK_DIR/sdk/lib/libraries.json"
|
|
|
|
RUN_BINARYEN=1
|
|
RUN_SRC=0
|
|
GENERATE_SOURCE_MAP=1
|
|
COMPILE_BENCHMARK_BASE_NAME=""
|
|
PLATFORM_FILENAME="$BIN_DIR/dart2wasm_platform.dill"
|
|
SNAPSHOT_NAME="dart2wasm"
|
|
RUN_BENCHMARK=0
|
|
USE_TEMP_OUTPUT=0
|
|
|
|
# All arguments will be passed along to dart2wasm except specially recognized
|
|
# flags.
|
|
VM_ARGS=()
|
|
DART2WASM_ARGS=("--require-js-string-builtin")
|
|
DART_FILE=""
|
|
OUTPUT_FILE=""
|
|
PHASES=""
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--compile-benchmark=*)
|
|
COMPILE_BENCHMARK_BASE_NAME=$(echo $1 | sed 's/--compile-benchmark=//g')
|
|
shift
|
|
;;
|
|
|
|
--src)
|
|
RUN_SRC=1
|
|
shift
|
|
;;
|
|
|
|
--run)
|
|
RUN_BENCHMARK=1
|
|
RUN_BENCHMARK_SHELL="d8"
|
|
shift
|
|
;;
|
|
|
|
--run=*)
|
|
RUN_BENCHMARK=1
|
|
RUN_BENCHMARK_SHELL="${1#--run=}"
|
|
shift
|
|
;;
|
|
|
|
--temp-output)
|
|
USE_TEMP_OUTPUT=1
|
|
shift
|
|
;;
|
|
|
|
--js-compatibility)
|
|
PLATFORM_FILENAME="$BIN_DIR/dart2wasm_js_compatibility_platform.dill"
|
|
DART2WASM_ARGS+=("--js-compatibility")
|
|
shift
|
|
;;
|
|
|
|
--standalone)
|
|
PLATFORM_FILENAME="$BIN_DIR/dart2wasm_standalone_platform.dill"
|
|
DART2WASM_ARGS+=("--standalone")
|
|
shift
|
|
;;
|
|
|
|
--compiler-asserts)
|
|
SNAPSHOT_NAME="dart2wasm_asserts"
|
|
VM_ARGS+=("--enable-asserts")
|
|
shift
|
|
;;
|
|
|
|
-O0 | --optimization-level=0 | --extra-compiler-option=-O0 )
|
|
DART2WASM_ARGS+=("-O0")
|
|
RUN_BINARYEN=0
|
|
shift
|
|
;;
|
|
|
|
-O* | --optimization-level=*)
|
|
DART2WASM_ARGS+=("$1")
|
|
RUN_BINARYEN=1
|
|
shift
|
|
;;
|
|
|
|
--phases=*)
|
|
PHASES="${1#--phases=}"
|
|
shift
|
|
;;
|
|
|
|
--extra-compiler-option=--platform=*)
|
|
PLATFORM_FILENAME="${1#--extra-compiler-option=--platform=}"
|
|
shift
|
|
;;
|
|
|
|
--extra-compiler-option=--no-js-compatibility)
|
|
PLATFORM_FILENAME="$BIN_DIR/dart2wasm_platform.dill"
|
|
DART2WASM_ARGS+=("--no-js-compatibility")
|
|
shift
|
|
;;
|
|
|
|
--extra-compiler-option=*)
|
|
DART2WASM_ARGS+=("${1#--extra-compiler-option=}")
|
|
shift
|
|
;;
|
|
|
|
--no-source-maps)
|
|
GENERATE_SOURCE_MAP=0
|
|
shift
|
|
;;
|
|
|
|
-o)
|
|
shift
|
|
OUTPUT_FILE="$1"
|
|
shift
|
|
;;
|
|
|
|
--* | -*)
|
|
DART2WASM_ARGS+=("$1")
|
|
shift
|
|
;;
|
|
|
|
*)
|
|
if [ -z "$DART_FILE" ]; then
|
|
DART_FILE="$1"
|
|
shift
|
|
else
|
|
if [ -z "$OUTPUT_FILE" ]; then
|
|
OUTPUT_FILE="$1"
|
|
shift
|
|
else
|
|
echo "Unexpected argument $1"
|
|
exit 1
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$DART_FILE" ]; then
|
|
echo "Expected <file.dart> [file.wasm]"
|
|
exit 1
|
|
fi
|
|
|
|
if [ $USE_TEMP_OUTPUT -eq 1 ] && [ -n "$OUTPUT_FILE" ]; then
|
|
echo "Cannot specify both --temp-output and an explicit output file!"
|
|
exit 1
|
|
fi
|
|
|
|
if [ $USE_TEMP_OUTPUT -eq 0 ] && [ -z "$OUTPUT_FILE" ]; then
|
|
echo "Expected <file.dart> <file.wasm>"
|
|
exit 1
|
|
fi
|
|
|
|
if [ $USE_TEMP_OUTPUT -eq 1 ]; then
|
|
BASENAME=$(basename "$DART_FILE" .dart)
|
|
TEMP_DIR=$(mktemp -d)
|
|
trap "rm -rf $TEMP_DIR" EXIT
|
|
OUTPUT_FILE="$TEMP_DIR/${BASENAME}.wasm"
|
|
echo "Compiling to temporary file: $OUTPUT_FILE"
|
|
fi
|
|
|
|
if [ -z "$PHASES" ]; then
|
|
PHASES="cfe,tfa,codegen"
|
|
fi
|
|
|
|
|
|
PLATFORM_ARG="--platform=$PLATFORM_FILENAME"
|
|
DART2WASM_AOT_SNAPSHOT="$BIN_DIR/$SNAPSHOT_NAME.snapshot"
|
|
DART2WASM_SRC="$SDK_DIR/pkg/dart2wasm/bin/dart2wasm.dart"
|
|
|
|
function measure() {
|
|
set +e
|
|
RESULT=$( { /usr/bin/time --format="\nMemory: %M KB, Time: %e s" "$@"; } 2>&1 )
|
|
EXIT=$?
|
|
if [ $EXIT -ne 0 ]; then
|
|
echo "Running $@ resulted in exit code $EXIT"
|
|
echo "$RESULT"
|
|
exit 1
|
|
fi
|
|
set -e
|
|
MEMORY=$(echo "$RESULT" | tail -n1 | sed 's/Memory: \([0-9.]\+\) KB, Time: \([0-9.]\+\) s.*/\1/g')
|
|
MEMORY=$(($MEMORY * 1024))
|
|
TIME=$(echo "$RESULT" | tail -n1 | sed 's/Memory: \([0-9.]\+\) KB, Time: \([0-9.]\+\) s.*/\2/g')
|
|
}
|
|
|
|
function measure_size() {
|
|
SIZE=$(cat $1 | wc -c)
|
|
GZIP_SIZE=$(cat $1 | gzip -c6 | wc -c)
|
|
}
|
|
|
|
function run_if_binaryen_enabled() {
|
|
if [ $RUN_BINARYEN -eq 1 ] && [[ $OUTPUT_FILE == *.wasm ]]; then
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
COMPILER_SIZE=0
|
|
COMPILER_GZIP_SIZE=0
|
|
|
|
if [ $RUN_SRC -eq 1 ]; then
|
|
BASE_CMD=("$DART" "${VM_ARGS[@]}" "$DART2WASM_SRC" "$LIBRARIES_JSON_ARG")
|
|
else
|
|
BASE_CMD=("$DART_AOT_RUNTIME" "${VM_ARGS[@]}" "$DART2WASM_AOT_SNAPSHOT" "$PLATFORM_ARG")
|
|
fi
|
|
|
|
function run_compiler() {
|
|
local dart2wasm_command=("${BASE_CMD[@]}" "${DART2WASM_ARGS[@]}" "--phases=$PHASES" "$DART_FILE" "$OUTPUT_FILE")
|
|
|
|
if [ -n "$COMPILE_BENCHMARK_BASE_NAME" ]; then
|
|
measure "${dart2wasm_command[@]}"
|
|
COMPILER_TIME=$TIME
|
|
COMPILER_MEMORY=$MEMORY
|
|
|
|
measure_size "${OUTPUT_FILE%.wasm}.mjs"
|
|
MJS_SIZE=$SIZE
|
|
MJS_GZIP_SIZE=$GZIP_SIZE
|
|
|
|
for OUTPUT_FILE in "${OUTPUT_FILE%.wasm}"*.wasm; do
|
|
measure_size "$OUTPUT_FILE"
|
|
(( COMPILER_SIZE+=$SIZE ))
|
|
(( COMPILER_GZIP_SIZE+=$GZIP_SIZE ))
|
|
done
|
|
else
|
|
"${dart2wasm_command[@]}"
|
|
fi
|
|
}
|
|
|
|
BINARYEN_TIME=0
|
|
BINARYEN_MEMORY=0
|
|
BINARYEN_SIZE=0
|
|
BINARYEN_GZIP_SIZE=0
|
|
|
|
function run_binaryen() {
|
|
local opt_command=("${BASE_CMD[@]}" "${DART2WASM_ARGS[@]}" "--phases=opt" "--wasm-opt=$BINARYEN" "$OUTPUT_FILE" "$OUTPUT_FILE")
|
|
|
|
if [ -n "$COMPILE_BENCHMARK_BASE_NAME" ]; then
|
|
# If we're measuring run each binaryen command sequentially.
|
|
measure "${opt_command[@]}"
|
|
BINARYEN_TIME=$(echo "$BINARYEN_TIME + $TIME" | bc)
|
|
BINARYEN_MEMORY=$(($BINARYEN_MEMORY > $MEMORY ? $BINARYEN_MEMORY : $MEMORY ))
|
|
measure_size "$OUTPUT_FILE"
|
|
BINARYEN_SIZE=$(echo "$BINARYEN_SIZE + $SIZE" | bc)
|
|
BINARYEN_GZIP_SIZE=$(echo "$BINARYEN_GZIP_SIZE + $GZIP_SIZE" | bc)
|
|
MAX_MEMORY=$(($COMPILER_MEMORY > $BINARYEN_MEMORY ? $COMPILER_MEMORY : $BINARYEN_MEMORY ))
|
|
TOTAL_TIME=$(echo "$COMPILER_TIME + $BINARYEN_TIME" | bc)
|
|
else
|
|
"${opt_command[@]}"
|
|
fi
|
|
}
|
|
|
|
run_compiler
|
|
run_if_binaryen_enabled run_binaryen
|
|
|
|
if [ -n "$COMPILE_BENCHMARK_BASE_NAME" ]; then
|
|
# CompileTime
|
|
run_if_binaryen_enabled echo "$COMPILE_BENCHMARK_BASE_NAME.Time.Total(CompileTime): $TOTAL_TIME s"
|
|
echo "$COMPILE_BENCHMARK_BASE_NAME.Time.Dart2Wasm(CompileTime): $COMPILER_TIME s"
|
|
run_if_binaryen_enabled echo "$COMPILE_BENCHMARK_BASE_NAME.Time.Wasm2WasmOpt(CompileTime): $BINARYEN_TIME s"
|
|
|
|
# CodeSize
|
|
echo "$COMPILE_BENCHMARK_BASE_NAME.Size.mjs(CodeSize): $MJS_SIZE bytes"
|
|
echo "$COMPILE_BENCHMARK_BASE_NAME.Size.mjs.gz(CodeSize): $MJS_GZIP_SIZE bytes"
|
|
echo "$COMPILE_BENCHMARK_BASE_NAME.Size.wasm(CodeSize): $COMPILER_SIZE bytes"
|
|
echo "$COMPILE_BENCHMARK_BASE_NAME.Size.wasm.gz(CodeSize): $COMPILER_GZIP_SIZE bytes"
|
|
run_if_binaryen_enabled echo "$COMPILE_BENCHMARK_BASE_NAME.Size.wasm.opt(CodeSize): $BINARYEN_SIZE bytes"
|
|
run_if_binaryen_enabled echo "$COMPILE_BENCHMARK_BASE_NAME.Size.wasm.opt.gz(CodeSize): $BINARYEN_GZIP_SIZE bytes"
|
|
|
|
# MemoryUse
|
|
run_if_binaryen_enabled echo "$COMPILE_BENCHMARK_BASE_NAME.MemoryUse.Max(MemoryUse): $MAX_MEMORY bytes"
|
|
echo "$COMPILE_BENCHMARK_BASE_NAME.MemoryUse.Dart2Wasm(MemoryUse): $COMPILER_MEMORY bytes"
|
|
run_if_binaryen_enabled echo "$COMPILE_BENCHMARK_BASE_NAME.MemoryUse.Wasm2WasmOpt(MemoryUse): $BINARYEN_MEMORY bytes"
|
|
fi
|
|
|
|
if [ $RUN_BENCHMARK -eq 1 ]; then
|
|
echo ""
|
|
echo "RUNNING $OUTPUT_FILE"
|
|
"$PROG_DIR/run_benchmark" "--$RUN_BENCHMARK_SHELL" "$OUTPUT_FILE"
|
|
fi
|