Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add option to use weval, an experimental JS AOT option using partia…
…l evaluation.

This PR pulls in some still-experimental work to partially evaluate the
SpiderMonkey JS interpreter together with JS scripts, ahead-of-time, to
produce compiled code.

It should not yet be considered production-worthy, pending more testing
and proving out its stability and performance; but, it is at a point
where we believe it should be under a *non-default* option so that users
of the SDK may try it. No guarantees! It may explode your bytecode!

The support works by bundling two builds of the engine Wasm -- with and
without the weval intrinsics -- and invoking either Wizer with the
"normal" build or weval with the "weval" build, as appropriate. The
`--enable-weval` option selects the latter.
  • Loading branch information
cfallin authored and JakeChampion committed Jun 12, 2023
commit 88638f719d700d530a4a6a410ed3f36295ebb3f0
3 changes: 2 additions & 1 deletion js-compute-runtime-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { addSdkMetadataField } from "./src/addSdkMetadataField.js";

const {
enableExperimentalHighResolutionTimeMethods,
enableWeval,
wasmEngine,
input,
component,
Expand All @@ -27,7 +28,7 @@ if (version) {
// it could be that the user is using an older version of js-compute-runtime
// and a newer version does not support the platform they are using.
const {compileApplicationToWasm} = await import('./src/compileApplicationToWasm.js')
await compileApplicationToWasm(input, output, wasmEngine, enableExperimentalHighResolutionTimeMethods);
await compileApplicationToWasm(input, output, wasmEngine, enableExperimentalHighResolutionTimeMethods, enableWeval);
if (component) {
const {compileComponent} = await import('./src/component.js');
await compileComponent(output);
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"test": "npm run test:types && npm run test:cli",
"test:cli": "brittle --bail integration-tests/cli/**.test.js",
"test:types": "tsd",
"build": "make -j8 -C runtime/js-compute-runtime && cp runtime/js-compute-runtime/*.wasm runtime/js-compute-runtime/fastly.wit .",
"build:debug": "DEBUG=true make -j8 -C runtime/js-compute-runtime && cp runtime/js-compute-runtime/*.wasm runtime/js-compute-runtime/fastly.wit .",
"build": "make -j8 -C runtime/js-compute-runtime && make -j8 -C runtime/js-compute-runtime VARIANT=-weval && cp runtime/js-compute-runtime/*.wasm runtime/js-compute-runtime/fastly.wit .",
"build:debug": "DEBUG=true make -j8 -C runtime/js-compute-runtime && make -j8 -C runtime/js-compute-runtime VARIANT=-weval && cp runtime/js-compute-runtime/*.wasm runtime/js-compute-runtime/fastly.wit .",
"check-changelog": "cae-release-notes-format-checker CHANGELOG.md"
},
"devDependencies": {
Expand All @@ -47,6 +47,7 @@
"dependencies": {
"@bytecodealliance/jco": "^0.7.0",
"@bytecodealliance/wizer": "^3.0.1",
"@cfallin/weval": "^0.1.3",
"acorn": "^8.8.2",
"acorn-walk": "^8.2.0",
"esbuild": "^0.17.18",
Expand Down
39 changes: 22 additions & 17 deletions runtime/js-compute-runtime/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ else
WASM_STRIP = wasm-opt --strip-debug -o $1 $1
Copy link
Contributor

Choose a reason for hiding this comment

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

We likely do have to tackle this one head-on here I think - wasm-opt is in PATH in all our current workflows, so would need to be renamed. Binaryen bins are added to PATH in https://github.com/fastly/js-compute-runtime/blob/main/.github/workflows/main.yml#L93.

endif

# The build variant (normal or weval).
VARIANT ?= -normal

# The path to the wasm-tools executable
WASM_TOOLS ?= $(shell which wasm-tools)

Expand All @@ -80,10 +83,10 @@ endif
BUILD := $(FSM_SRC)/build

# The output directory for the current build mode (relase/debug).
OBJ_DIR := $(BUILD)/$(MODE)
OBJ_DIR := $(BUILD)/$(MODE)$(VARIANT)

# The path to the //runtime/spidermonkey/$(MODE) directory.
SM_SRC := $(ROOT_SRC)/spidermonkey/$(MODE)
SM_SRC := $(ROOT_SRC)/spidermonkey/$(MODE)$(VARIANT)

# The objects we link in from spidermonkey
SM_OBJ := $(wildcard $(SM_SRC)/lib/*.o)
Expand Down Expand Up @@ -121,14 +124,16 @@ LD_FLAGS += -L$(BUILD)/openssl/libx32 -lcrypto
# Default targets ##############################################################

.PHONY: all
all: $(FSM_SRC)/js-compute-runtime.wasm
all: $(FSM_SRC)/js-compute-runtime-component.wasm
all: $(FSM_SRC)/js-compute-runtime$(VARIANT).wasm
all: $(FSM_SRC)/js-compute-runtime-component$(VARIANT).wasm

# Remove just the build artifacts for the current runtime build.
.PHONY: clean
clean:
$(call cmd,rm,$(FSM_SRC)/js-compute-runtime.wasm)
$(call cmd,rm,$(FSM_SRC)/js-compute-runtime-component.wasm)
$(call cmd,rm,$(FSM_SRC)/js-compute-runtime-normal.wasm)
$(call cmd,rm,$(FSM_SRC)/js-compute-runtime-component-normal.wasm)
$(call cmd,rm,$(FSM_SRC)/js-compute-runtime-weval.wasm)
$(call cmd,rm,$(FSM_SRC)/js-compute-runtime-component-weval.wasm)
$(call cmd,rmdir,$(BUILD)/release)
$(call cmd,rmdir,$(BUILD)/debug)

Expand Down Expand Up @@ -304,9 +309,9 @@ $(eval $(call compile_cxx,$(FSM_SRC)/fastly-world/fastly_world_adapter.cpp))
# NOTE: we shadow wasm-opt by adding $(FSM_SRC)/scripts to the path, which
# includes a script called wasm-opt that immediately exits successfully. See
# that script for more information about why we do this.
$(OBJ_DIR)/js-compute-runtime.wasm: $(FSM_OBJ) $(SM_OBJ) $(RUST_URL_LIB) $(RUST_ENCODING_LIB)
$(OBJ_DIR)/js-compute-runtime.wasm: $(OBJ_DIR)/impl/main.o
$(OBJ_DIR)/js-compute-runtime.wasm: $(OBJ_DIR)/fastly-world/fastly_world_adapter.o
$(OBJ_DIR)/js-compute-runtime$(VARIANT).wasm: $(FSM_OBJ) $(SM_OBJ) $(RUST_URL_LIB) $(RUST_ENCODING_LIB)
$(OBJ_DIR)/js-compute-runtime$(VARIANT).wasm: $(OBJ_DIR)/impl/main.o
$(OBJ_DIR)/js-compute-runtime$(VARIANT).wasm: $(OBJ_DIR)/fastly-world/fastly_world_adapter.o
$(call cmd_format,WASI_LD,$@) PATH="$(FSM_SRC)/scripts:$$PATH" \
$(WASI_CXX) $(LD_FLAGS) $(OPENSSL_LIBS) -o $@ $^
$(call cmd_format,WASM_STRIP,$@) $(call WASM_STRIP,$@)
Expand All @@ -324,13 +329,13 @@ $(eval $(call compile_c,$(FSM_SRC)/fastly-world/fastly_world.c))
# NOTE: we shadow wasm-opt by adding $(FSM_SRC)/scripts to the path, which
# includes a script called wasm-opt that immediately exits successfully. See
# that script for more information about why we do this.
$(OBJ_DIR)/js-compute-runtime-component.wasm: $(FSM_OBJ) $(SM_OBJ) $(RUST_URL_LIB) $(RUST_ENCODING_LIB)
$(OBJ_DIR)/js-compute-runtime-component.wasm: $(OBJ_DIR)/impl/main_component.o
$(OBJ_DIR)/js-compute-runtime-component$(VARIANT).wasm: $(FSM_OBJ) $(SM_OBJ) $(RUST_URL_LIB) $(RUST_ENCODING_LIB)
$(OBJ_DIR)/js-compute-runtime-component$(VARIANT).wasm: $(OBJ_DIR)/impl/main_component.o
# NOTE: we don't currently link in the component type object because we
# explicitly reference fastly.wit when building the component. If this changes,
# uncommenting this line will link the component type object in.
# $(OBJ_DIR)/js-compute-runtime-component.wasm: $(FSM_SRC)/fastly-world/fastly_world_component_type.o
$(OBJ_DIR)/js-compute-runtime-component.wasm: $(OBJ_DIR)/fastly-world/fastly_world.o
# $(OBJ_DIR)/js-compute-runtime-component$(VARIANT).wasm: $(FSM_SRC)/fastly-world/fastly_world_component_type.o
$(OBJ_DIR)/js-compute-runtime-component$(VARIANT).wasm: $(OBJ_DIR)/fastly-world/fastly_world.o
$(call cmd_format,WASI_LD,$@) PATH="$(FSM_SRC)/scripts:$$PATH" \
$(WASI_CXX) $(LD_FLAGS) $(OPENSSL_LIBS) -o $@ $^
$(call cmd_format,WASM_STRIP,$@) $(call WASM_STRIP,$@)
Expand Down Expand Up @@ -360,12 +365,12 @@ shared/librust_encoding.a: $(RUST_ENCODING_LIB) | shared
# Without marking them phony, the wasm won't be copied in the last invocation of
# make, as it will look up-to-date.

.PHONY: $(FSM_SRC)/js-compute-runtime.wasm
$(FSM_SRC)/js-compute-runtime.wasm: $(OBJ_DIR)/js-compute-runtime.wasm
.PHONY: $(FSM_SRC)/js-compute-runtime$(VARIANT).wasm
$(FSM_SRC)/js-compute-runtime$(VARIANT).wasm: $(OBJ_DIR)/js-compute-runtime$(VARIANT).wasm
$(call cmd,cp,$@)

.PHONY: $(FSM_SRC)/js-compute-runtime-component.wasm
$(FSM_SRC)/js-compute-runtime-component.wasm: $(OBJ_DIR)/js-compute-runtime-component.wasm
.PHONY: $(FSM_SRC)/js-compute-runtime-component$(VARIANT).wasm
$(FSM_SRC)/js-compute-runtime-component$(VARIANT).wasm: $(OBJ_DIR)/js-compute-runtime-component$(VARIANT).wasm
$(call cmd,cp,$@)


Expand Down
47 changes: 33 additions & 14 deletions src/compileApplicationToWasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { mkdir, readFile } from "node:fs/promises";
import { isFile } from "./isFile.js";
import { isFileOrDoesNotExist } from "./isFileOrDoesNotExist.js";
import wizer from "@bytecodealliance/wizer";
import weval from "@cfallin/weval";
Copy link
Contributor

Choose a reason for hiding this comment

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

A standard technique for lazy initialization in JavaScript would be to turn this into a dynamic import() nested into the async code path where Weval is used only.

import { precompile } from "./precompile.js";
import { bundle } from "./bundle.js";
import { containsSyntaxErrors } from "./containsSyntaxErrors.js";

export async function compileApplicationToWasm(input, output, wasmEngine, enableExperimentalHighResolutionTimeMethods = false) {
export async function compileApplicationToWasm(input, output, wasmEngine, enableExperimentalHighResolutionTimeMethods = false, enableWeval = false) {
try {
if (!(await isFile(input))) {
console.error(
Expand Down Expand Up @@ -82,17 +83,8 @@ export async function compileApplicationToWasm(input, output, wasmEngine, enable
let application = precompile(contents.outputFiles[0].text);

try {
let wizerProcess = spawnSync(
wizer,
[
"--inherit-env=true",
"--allow-wasi",
`--dir=.`,
`--wasm-bulk-memory=true`,
"-r _start=wizer.resume",
`-o=${output}`,
wasmEngine,
],
let wizerProcess;
let spawnEnv =
{
stdio: [null, process.stdout, process.stderr],
input: application,
Expand All @@ -101,8 +93,35 @@ export async function compileApplicationToWasm(input, output, wasmEngine, enable
env: {
ENABLE_EXPERIMENTAL_HIGH_RESOLUTION_TIME_METHODS: enableExperimentalHighResolutionTimeMethods ? '1' : '0'
}
}
);
};
if (enableWeval) {
let wevalBinary = await weval(); // Lazily fetch the toolchain.
wizerProcess = spawnSync(
wevalBinary,
[
"-w",
"-o",
output,
"-i",
wasmEngine,
],
spawnEnv
);
} else {
wizerProcess = spawnSync(
wizer,
[
"--inherit-env=true",
"--allow-wasi",
`--dir=.`,
`--wasm-bulk-memory=true`,
"-r _start=wizer.resume",
`-o=${output}`,
wasmEngine,
],
spawnEnv
);
}
if (wizerProcess.status !== 0) {
throw new Error(`Wizer initialization failure`);
}
Expand Down
23 changes: 20 additions & 3 deletions src/parseInputs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ export async function parseInputs(cliInputs) {

let component = false;
let enableExperimentalHighResolutionTimeMethods = false;
let enableWeval = false;
let customEngineSet = false;
let wasmEngine = join(__dirname, "../js-compute-runtime.wasm");
let wasmEngineBase = join(__dirname, "../js-compute-runtime");
let wasmEngine;
let customInputSet = false;
let input = join(process.cwd(), "bin/index.js");
let customOutputSet = false;
Expand All @@ -25,6 +27,10 @@ export async function parseInputs(cliInputs) {
enableExperimentalHighResolutionTimeMethods = true;
break;
}
case "--enable-weval": {
enableWeval = true;
break;
}
case "-V":
case "--version": {
return { version: true };
Expand All @@ -35,7 +41,6 @@ export async function parseInputs(cliInputs) {
}
case "--component": {
component = true;
wasmEngine = join(__dirname, "../js-compute-runtime-component.wasm");
break;
}
case "--engine-wasm": {
Expand Down Expand Up @@ -93,5 +98,17 @@ export async function parseInputs(cliInputs) {
}
}
}
return { wasmEngine, component, input, output, enableExperimentalHighResolutionTimeMethods };

if (!wasmEngine) {
wasmEngine = wasmEngineBase;
if (component) {
wasmEngine += "-component";
}
if (enableWeval) {
wasmEngine += "-weval.wasm";
} else {
wasmEngine += "-normal.wasm";
}
}
return { wasmEngine, component, input, output, enableExperimentalHighResolutionTimeMethods, enableWeval };
}
2 changes: 1 addition & 1 deletion tests/wpt-harness/run-wpt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ if ! npm run build > "$output" 2>&1; then
cat "$output"
exit 1
fi
cp js-compute-runtime.wasm "$working_dir"
cp js-compute-runtime-normal.wasm "$working_dir"/js-compute-runtime.wasm

cd "$working_dir"

Expand Down
Loading