authenticity is all you need

This commit is contained in:
Barton Rhodes 2023-05-18 03:22:13 +00:00
parent 7c36e03dfb
commit 119bddaa32
143 changed files with 17898 additions and 1 deletions

View file

@ -1,7 +1,8 @@
[package]
name = "nexus"
version = "0.0.0"
edition = "2021"
edition = "2033"
[dependencies]
c2pa = "0.21.0"
yrs = "0.16.5"

View file

@ -0,0 +1,33 @@
name: 'With Docker'
description: 'Run a given stage with Docker Image'
inputs:
container-name:
description: 'Docker container name to use'
required: true
runs:
using: 'composite'
steps:
- name: 'Set up Docker'
shell: bash {0}
env:
CONTAINER_NAME: ${{ inputs.container-name }}
run: |
set -euxo pipefail
TAG=runtimeverificationinc/${CONTAINER_NAME}
K_COMMIT=$(cat ./deps/k_release)
docker build . --tag ${TAG} --build-arg K_COMMIT=${K_COMMIT}
docker run \
--name ${CONTAINER_NAME} \
--rm \
--interactive \
--tty \
--detach \
--user root \
--workdir /home/user \
${TAG}
docker cp . ${CONTAINER_NAME}:/home/user
docker exec ${CONTAINER_NAME} chown -R user:user /home/user

View file

@ -0,0 +1,26 @@
name: 'Master Push'
on:
push:
branches:
- master
jobs:
release:
name: 'Publish Release'
runs-on: ubuntu-latest
environment: production
steps:
- name: 'Update dependents'
env:
GITHUB_TOKEN: ${{ secrets.JENKINS_GITHUB_PAT }}
run: |
set -x
version="${GITHUB_SHA}"
curl --fail \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/runtimeverification/devops/dispatches \
-d '{"event_type":"on-demand-test","client_payload":{"repo":"runtimeverification/wasm-semantics","version":"'${version}'"}}'

View file

@ -0,0 +1,123 @@
name: 'Run Tests'
on:
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
pykwasm-code-quality-checks:
name: 'Code Quality Checks'
runs-on: ubuntu-latest
steps:
- name: 'Check out code'
uses: actions/checkout@v3
- name: 'Install Poetry'
uses: Gr1N/setup-poetry@v8
- name: 'Build and run code quality checks'
run: make -C pykwasm check
pykwasm-unit-tests:
needs: pykwasm-code-quality-checks
name: 'Unit Tests'
runs-on: ubuntu-latest
steps:
- name: 'Check out code'
uses: actions/checkout@v3
- name: 'Install Poetry'
uses: Gr1N/setup-poetry@v8
- name: 'Build and run unit tests'
run: make -C pykwasm cov-unit
pykwasm-integration-tests:
needs: pykwasm-code-quality-checks
name: 'Integration Tests'
runs-on: [self-hosted, linux, normal]
steps:
- name: 'Check out code'
uses: actions/checkout@v3
- name: 'Set up Docker'
uses: ./.github/actions/with-docker
with:
container-name: kwasm-ci-pykwasm-${{ github.sha }}
- name: 'Build and run integration tests'
run: docker exec -u user kwasm-ci-pykwasm-${GITHUB_SHA} make -C pykwasm cov-integration
- name: 'Tear down Docker'
if: always()
run: |
docker stop --time=0 kwasm-ci-pykwasm-${GITHUB_SHA}
parse-tests:
needs: [pykwasm-unit-tests, pykwasm-integration-tests]
name: 'Parser Tests'
runs-on: [self-hosted, linux, normal]
timeout-minutes: 18
steps:
- name: 'Check out code'
uses: actions/checkout@v3
with:
submodules: recursive
- name: 'Set up Docker'
uses: ./.github/actions/with-docker
with:
container-name: kwasm-ci-parse-${{ github.sha }}
- name: 'Build LLVM Backend and pykwasm'
run: docker exec -u user kwasm-ci-parse-${GITHUB_SHA} make -j2 build-llvm pykwasm-poetry-install
- name: 'Binary parse'
run: docker exec -u user kwasm-ci-parse-${GITHUB_SHA} make -j6 TEST_CONCRETE_BACKEND=llvm test-binary-parser
- name: 'Conformance parse'
run: docker exec -u user kwasm-ci-parse-${GITHUB_SHA} make -j4 test-conformance-parse
- name: 'Tear down Docker'
if: always()
run: |
docker stop --time=0 kwasm-ci-parse-${GITHUB_SHA}
conformance-tests:
needs: [pykwasm-unit-tests, pykwasm-integration-tests]
name: 'Conformance Tests'
runs-on: [self-hosted, linux, normal]
timeout-minutes: 18
steps:
- name: 'Check out code'
uses: actions/checkout@v3
with:
submodules: recursive
- name: 'Set up Docker'
uses: ./.github/actions/with-docker
with:
container-name: kwasm-ci-conformance-${{ github.sha }}
- name: 'Build LLVM Backend'
run: docker exec -u user kwasm-ci-conformance-${GITHUB_SHA} make -j2 build-llvm
- name: 'Simple tests'
run: docker exec -u user kwasm-ci-conformance-${GITHUB_SHA} make -j6 TEST_CONCRETE_BACKEND=llvm test-simple
- name: 'Conformance run'
run: docker exec -u user kwasm-ci-conformance-${GITHUB_SHA} make -j6 TEST_CONCRETE_BACKEND=llvm test-conformance-supported
- name: 'Tear down Docker'
if: always()
run: |
docker stop --time=0 kwasm-ci-conformance-${GITHUB_SHA}
prove-tests:
needs: [pykwasm-unit-tests, pykwasm-integration-tests]
name: 'Prover Tests'
runs-on: [self-hosted, linux, normal]
timeout-minutes: 18
steps:
- name: 'Check out code'
uses: actions/checkout@v3
with:
submodules: recursive
- name: 'Set up Docker'
uses: ./.github/actions/with-docker
with:
container-name: kwasm-ci-prove-${{ github.sha }}
- name: 'Build Haskell Backend'
run: docker exec -u user kwasm-ci-prove-${GITHUB_SHA} make -j2 build-haskell
- name: 'Prove'
run: docker exec -u user kwasm-ci-prove-${GITHUB_SHA} make -j6 test-prove
- name: 'Tear down Docker'
if: always()
run: |
docker stop --time=0 kwasm-ci-prove-${GITHUB_SHA}

View file

@ -0,0 +1,45 @@
name: 'Update Version'
on:
push:
branches:
- '_update-deps/runtimeverification/k'
- '_update-deps/runtimeverification/pyk'
# Stop in progress workflows on the same branch and same workflow to use latest committed code
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
update-versions:
name: 'Update dependency versions'
runs-on: ubuntu-22.04
steps:
- name: 'Check out code'
uses: actions/checkout@v3
with:
submodules: recursive
token: ${{ secrets.JENKINS_GITHUB_PAT }}
- run: |
git config user.name devops
git config user.email devops@runtimeverification.com
- name: 'Update K submodule'
run: |
K_VERSION="$(cat deps/k_release)"
cd deps/k
git fetch --tags
git checkout "v${K_VERSION}"
cd -
git add deps/k && git commit -m "deps/k: sync submodule v${K_VERSION}" || true
- name: 'Update pyk Release tag'
run: |
curl -sSL https://install.python-poetry.org | python3 - --version 1.3.2
poetry --version
pyk_version="$(cat deps/pyk_release)"
sed -i 's!pyk = { git = "https://github.com/runtimeverification/pyk.git", tag="[v0-9\.]*" }!pyk = { git = "https://github.com/runtimeverification/pyk.git", tag="'${pyk_version}'" }!' pykwasm/pyproject.toml
cd pykwasm
poetry update
cd -
git add pykwasm/ && git commit -m "pykwasm/: sync poetry files ${pyk_version}" || true
- name: 'Push updates'
run: git push

View file

@ -0,0 +1,10 @@
/.build/*
/tests/*/*-out
*.pdf
*.sty
.krun*
.kprove*
.kompile*

View file

@ -0,0 +1,12 @@
[submodule "deps/k"]
path = deps/k
url = https://github.com/kframework/k
ignore = untracked
[submodule "tests/wasm-tests"]
path = tests/wasm-tests
url = https://github.com/webassembly/spec
ignore = untracked
[submodule "deps/py-wasm"]
path = deps/py-wasm
url = https://github.com/ethereum/py-wasm
ignore = untracked

View file

@ -0,0 +1,63 @@
Contributor License Agreement
This Contributor License Agreement (“Agreement”) sets out the agreement between Runtime Verification, Inc. (“Runtime”) and the party signing below (“You”), and conveys certain license rights to Runtime with respect to Your contributions to Runtime open source activities. This Agreement is effective as of the date of your electronic signature.
This Agreement is legally binding on You upon execution, and shall be relied upon by Runtime without need for execution by Runtime, so please read it carefully before agreeing to it.
1. *Definitions.*
- “Contribution” means any work of authorship that is submitted by You to Runtime in which You own or assert ownership of the Copyright.
- “Submit” is the act of uploading, submitting, transmitting, or distributing, computer software code, whether in human-readable or machine-executable form (“Code”), or other content to Runtime, whether in electronic, verbal, or written communication and regardless of the form of media or manner, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by or on behalf of Runtime, for the purpose of discussing, addressing and/or improving any Runtime open source activities, excluding, however, any communication that is conspicuously marked or otherwise designated in writing by You as “Not a Submission.”
- “Submission” means all Code and other materials, including any associated comments and documentation, Submitted by You that are copyrightable, patentable or that are otherwise subject to any intellectual property rights protection.
2. *Your Submission.*
You must agree to the terms of this Agreement before making a Submission to Runtime.
This Agreement covers any and all Submissions that You make to Runtime now or in the future (except as described in Section 4 below).
You acknowledge that Runtime is not obligated to use Your Submission, but may decide to include Your Submission in or with any other materials and in any manner it considers appropriate.
3. *Original Work.*
You warrant that each of Your Submissions is entirely Your original work.
Should You wish to Submit materials that are not Your original work, You may Submit them separately if You: (a) retain all copyright and license information that was in the materials as You received them; (b) in the description accompanying Your Submission, include the statement “this Submission contains materials of a third party:” followed by the name(s) of any such third party(ies) and any licenses or other restrictions of which You are aware; and (c) follow any other instructions or guidelines from Runtime concerning Submissions.
4. *Your Employer.*
References to “employer” in this Agreement include Your employer or anyone else for whom You are acting in making Your Submission, whether as a contractor, vendor, agent or otherwise.
If Your Submission is made in the course of Your work for an employer or Your employer has intellectual property rights in your Submission by contract or applicable law, You must secure permission from Your employer to make the submission before signing this Agreement, and the term “You” in this Agreement refers to You and the employer collectively.
If You change employers in the future and desire to Submit additional Submissions for the new employer, You agree to sign a new Agreement and secure permission from Your new employer before Submitting those Submissions.
5. *Licenses; Moral Rights.*
5.1. *Copyright License.*
You grant Runtime, and those who receive the Submission directly or indirectly from Runtime, a perpetual, worldwide, non-exclusive, royalty-free, transferrable, irrevocable license in the Submission to reproduce, modify and prepare derivative works of, publicly display, publicly perform, and distribute the Submission and such derivative works, and to sublicense any or all of the foregoing rights to third parties under such terms as Runtime determines.
5.2. *Patent License.*
You grant Runtime, and those who receive the Submission directly or indirectly from Runtime, a perpetual, worldwide, non-exclusive, royalty-free, transferrable, irrevocable license under Your patent claims that are necessarily infringed by the Submission, or the combination of the Submission with other materials of Runtime with which it may be combined, to make, have made, use, offer to sell, sell and import or otherwise dispose of the Submission alone or with other materials and under such terms as Runtime determines.
5.3. *Moral Rights.*
If moral rights apply to the Submission, to the maximum extent permitted by law You waive and agree not to assert such moral rights against Runtime or our successors in interest, or any of our licensees or sublicensees, either direct or indirect.
5.4. *Other Rights Reserved.*
Each party reserves all rights not expressly granted in this Agreement.
Subject to the rights granted herein, You retain ownership of the copyright in Your Submission and have the same rights to use or license the Submission which You would have had without entering into the Agreement.
No additional licenses or rights whatsoever (including, without limitation, any implied licenses) are granted hereby, by implication or otherwise.
6. *Representations and Warranties.*
You represent and warrant that: (a) You own all rights necessary to, and are legally entitled to, grant the above licenses; (b) Each of Your Submissions is entirely Your original work (except as You may have disclosed under Section 3); (c) You have the authority to make the Submission, and have secured permission from Your employer to make the Submission in cases where Your Submission is made in the course of Your work for Your employer or Your employer has intellectual property rights in Your Submission by contract or applicable law; (d) Your grant of rights under this Agreement does not violate any grant of rights which You have made to third parties, including Your employer; and (e) If Your employer has any rights with respect to any Submission, You have the necessary authority to bind Your employer to the obligations contained in this Agreement.
7. *Disclaimer.*
You are not expected to provide support for Your Submission, unless You choose to do so.
UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, AND EXCEPT FOR THE WARRANTIES EXPRESSLY STATED IN SECTIONS 3, AND 6, YOUR SUBMISSION IS PROVIDED WITHOUT WARRANTY OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF NONINFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8. *Notice to Runtime.*
You agree to notify Runtime in writing of any facts or circumstances of which You later become aware that would make Your representations and warranties in this Agreement inaccurate in any respect.
9. *Information about Submissions.*
You agree that Submissions to Runtime and information about Submissions may be maintained indefinitely and disclosed publicly, including Your name and other information that You submit with Your Submission.
10. *Governing Law/Jurisdiction.*
This Agreement is governed by the laws of the State of Illinois, and the parties consent to exclusive jurisdiction and venue in the state and federal courts sitting in Champaign County, Illinois.
The parties waive all defenses of lack of personal jurisdiction and forum non-conveniens.
11. *Entire Agreement/Assignment.*
This Agreement is the entire agreement between the parties, and supersedes any and all prior agreements, understandings or communications, written or oral, between the parties relating to the subject matter hereof.
This Agreement may be assigned by Runtime.
By signing, You accept and agree to the terms of this Contributor License Agreement for Your present and all future Submissions to Runtime.

View file

@ -0,0 +1,43 @@
ARG K_COMMIT
FROM runtimeverificationinc/kframework-k:ubuntu-jammy-${K_COMMIT}
RUN apt-get update \
&& apt-get upgrade --yes \
&& apt-get install --yes \
cmake \
curl \
pandoc \
python3 \
python3-pip
RUN git clone 'https://github.com/z3prover/z3' --branch=z3-4.8.15 \
&& cd z3 \
&& python3 scripts/mk_make.py \
&& cd build \
&& make -j8 \
&& make install \
&& cd ../.. \
&& rm -rf z3
RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/usr python3 - \
&& poetry --version
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN groupadd -g $GROUP_ID user && useradd -m -u $USER_ID -s /bin/sh -g user user
USER user:user
WORKDIR /home/user
RUN pip3 install \
cytoolz \
numpy
RUN git clone 'https://github.com/WebAssembly/wabt' --branch 1.0.13 --recurse-submodules wabt \
&& cd wabt \
&& mkdir build \
&& cd build \
&& cmake .. \
&& cmake --build .
ENV PATH=/home/user/wabt/build:/home/user/.local/bin:$PATH

View file

@ -0,0 +1,59 @@
==============================================================================
The WebAssembly Semantics in K Release License
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2009-2019 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:
K Team (http://kframework.org)
with members from:
* University of Illinois at Urbana-Champaign (http://fsl.cs.illinois.edu/)
* Runtime Verification, Inc (https://www.runtimeverification.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the K Team, Runtime Verification, the University of
Illinois at Urbana-Champaign, nor the names of its contributors may be
used to endorse or promote products derived from this Software without
specific prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
==============================================================================
Copyrights and Licenses for Third Party Software Distributed with the WebAssembly
Semantics in K:
==============================================================================
The WebAssembly Semantics in K software contains code written by third parties.
Licenses for this software can be found in the licenses directory
in the file as specified below. These files will describe the copyrights,
license, and restrictions which apply to that code.
The disclaimer of warranty in the University of Illinois Open Source License
applies to all code in the WebAssembly Semantics in K Distribution, and nothing
in any of the other licenses gives permission to use the names of the K Team,
Runtime Verification, or the University of Illinois
to endorse or promote products derived from this Software.

View file

@ -0,0 +1,246 @@
# Settings
# --------
BUILD_DIR := .build
DEPS_DIR := deps
DEFN_DIR := $(BUILD_DIR)/defn
K_SUBMODULE := $(DEPS_DIR)/k
ifneq (,$(wildcard deps/k/k-distribution/target/release/k/bin/*))
K_RELEASE ?= $(abspath $(K_SUBMODULE)/k-distribution/target/release/k)
else
K_RELEASE ?= $(dir $(shell which kompile))..
endif
K_BIN := $(K_RELEASE)/bin
K_LIB := $(K_RELEASE)/lib/kframework
export K_RELEASE
ifneq ($(RELEASE),)
K_BUILD_TYPE := Release
else
K_BUILD_TYPE := Debug
endif
PATH := $(K_BIN):$(PATH)
export PATH
PYK_PATH := $(abspath $(K_SUBMODULE)/pyk/src/)
PYWASM_PATH := ./deps/py-wasm
PYTHONPATH := $(PYK_PATH)
export PYTHONPATH
.PHONY: all clean deps \
build build-llvm build-haskell \
test test-execution test-simple test-prove test-binary-parser \
test-conformance test-conformance-parse test-conformance-supported \
media presentations reports
all: build
clean:
rm -rf $(BUILD_DIR)
# Build Dependencies (K Submodule)
# --------------------------------
K_JAR := $(K_SUBMODULE)/k-distribution/target/release/k/lib/java/kernel-1.0-SNAPSHOT.jar
deps: $(K_JAR) $(TANGLER)
$(K_JAR):
cd $(K_SUBMODULE) && mvn package -DskipTests -Dproject.build.type=$(K_BUILD_TYPE)
# Building Definition
# -------------------
KOMPILE_OPTS :=
LLVM_KOMPILE_OPTS :=
HASKELL_KOMPILE_OPTS :=
tangle_selector := k
SOURCE_FILES := data \
kwasm-lemmas \
numeric \
test \
wasm \
wasm-text \
wrc20
EXTRA_SOURCE_FILES :=
ALL_SOURCE_FILES := $(patsubst %, %.md, $(SOURCE_FILES)) $(EXTRA_SOURCE_FILES)
build: build-llvm build-haskell
ifneq (,$(RELEASE))
KOMPILE_OPTS += -O2
else
KOMPILE_OPTS += --debug
endif
ifeq (,$(RELEASE))
LLVM_KOMPILE_OPTS += -g
endif
KOMPILE_LLVM := kompile --backend llvm --md-selector "$(tangle_selector)" \
$(KOMPILE_OPTS) \
$(addprefix -ccopt ,$(LLVM_KOMPILE_OPTS))
KOMPILE_HASKELL := kompile --backend haskell --md-selector "$(tangle_selector)" \
$(KOMPILE_OPTS) \
$(HASKELL_KOMPILE_OPTS)
### LLVM
llvm_dir := $(DEFN_DIR)/llvm
llvm_files := $(ALL_SOURCE_FILES)
llvm_main_module := WASM-TEST
llvm_syntax_module := $(llvm_main_module)-SYNTAX
llvm_main_file := test
llvm_kompiled := $(llvm_dir)/$(llvm_main_file)-kompiled/interpreter
build-llvm: $(llvm_kompiled)
$(llvm_kompiled): $(llvm_files)
$(KOMPILE_LLVM) $(llvm_main_file).md \
--directory $(llvm_dir) -I $(CURDIR) \
--main-module $(llvm_main_module) \
--syntax-module $(llvm_syntax_module)
### Haskell
haskell_dir := $(DEFN_DIR)/haskell
haskell_files := $(ALL_SOURCE_FILES)
haskell_main_module := WASM-TEXT
haskell_syntax_module := $(haskell_main_module)-SYNTAX
haskell_main_file := test
haskell_kompiled := $(haskell_dir)/$(haskell_main_file)-kompiled/definition.kore
build-haskell: $(haskell_kompiled)
$(haskell_kompiled): $(haskell_files)
$(KOMPILE_HASKELL) $(haskell_main_file).md \
--directory $(haskell_dir) -I $(CURDIR) \
--main-module $(haskell_main_module) \
--syntax-module $(haskell_syntax_module)
# Testing
# -------
TEST := ./kwasm
CHECK := git --no-pager diff --no-index --ignore-all-space -R
TEST_CONCRETE_BACKEND := llvm
TEST_SYMBOLIC_BACKEND := haskell
KPROVE_MODULE := KWASM-LEMMAS
KPROVE_OPTS :=
tests/proofs/functions-spec.k.prove: KPROVE_MODULE = FUNCTIONS-LEMMAS
tests/proofs/functions-spec.k.prove: KPROVE_OPTS += --concrete-rules WASM-DATA.wrap-Positive,WASM-DATA.setRange-Positive,WASM-DATA.getRange-Positive
tests/proofs/memory-spec.k.prove: KPROVE_OPTS += --concrete-rules WASM-DATA.wrap-Positive,WASM-DATA.setRange-Positive,WASM-DATA.getRange-Positive
tests/proofs/wrc20-spec.k.prove: KPROVE_MODULE = WRC20-LEMMAS
tests/proofs/wrc20-spec.k.prove: KPROVE_OPTS += --concrete-rules WASM-DATA.wrap-Positive,WASM-DATA.setRange-Positive,WASM-DATA.getRange-Positive,WASM-DATA.get-Existing,WASM-DATA.set-Extend
test: test-execution test-prove test-binary-parser
# Generic Test Harnesses
tests/%.run: tests/% $(llvm_kompiled)
$(TEST) run --backend $(TEST_CONCRETE_BACKEND) $< > tests/$*.$(TEST_CONCRETE_BACKEND)-out
$(CHECK) tests/$*.$(TEST_CONCRETE_BACKEND)-out tests/success-$(TEST_CONCRETE_BACKEND).out
rm -rf tests/$*.$(TEST_CONCRETE_BACKEND)-out
tests/%.run-term: tests/% $(llvm_kompiled)
$(TEST) run --backend $(TEST_CONCRETE_BACKEND) $< > tests/$*.$(TEST_CONCRETE_BACKEND)-out
grep --after-context=2 "<instrs>" tests/$*.$(TEST_CONCRETE_BACKEND)-out > tests/$*.$(TEST_CONCRETE_BACKEND)-out-term
$(CHECK) tests/$*.$(TEST_CONCRETE_BACKEND)-out-term tests/success-k.out
rm -rf tests/$*.$(TEST_CONCRETE_BACKEND)-out
rm -rf tests/$*.$(TEST_CONCRETE_BACKEND)-out-term
tests/%.parse: tests/% $(llvm_kompiled)
$(TEST) kast --backend $(TEST_CONCRETE_BACKEND) $< kast > $@-out
rm -rf $@-out
tests/%.prove: tests/% $(haskell_kompiled)
$(TEST) prove --backend $(TEST_SYMBOLIC_BACKEND) $< $(KPROVE_MODULE) --format-failures \
$(KPROVE_OPTS)
### Execution Tests
test-execution: test-simple
simple_tests := $(wildcard tests/simple/*.wast)
simple_tests_failing := $(shell cat tests/failing.simple)
simple_tests_passing := $(filter-out $(simple_tests_failing), $(simple_tests))
test-simple: $(simple_tests_passing:=.run)
### Conformance Tests
conformance_tests:=$(wildcard tests/wasm-tests/test/core/*.wast)
unsupported_conformance_tests:=$(patsubst %, tests/wasm-tests/test/core/%, $(shell cat tests/conformance/unsupported-$(TEST_CONCRETE_BACKEND).txt))
unparseable_conformance_tests:=$(patsubst %, tests/wasm-tests/test/core/%, $(shell cat tests/conformance/unparseable.txt))
parseable_conformance_tests:=$(filter-out $(unparseable_conformance_tests), $(conformance_tests))
supported_conformance_tests:=$(filter-out $(unsupported_conformance_tests), $(parseable_conformance_tests))
test-conformance-parse: $(parseable_conformance_tests:=.parse)
test-conformance-supported: $(supported_conformance_tests:=.run-term)
test-conformance: test-conformance-parse test-conformance-supported
### Proof Tests
proof_tests:=$(wildcard tests/proofs/*-spec.k)
test-prove: $(proof_tests:=.prove)
### Binary Parser Test
# TODO pyk is not globally installed. use the poetry-installed pyk
# until binary-parser is migrated to pykwasm
BINARY:=poetry -C pykwasm run python3 binary-parser/test.py
tests/binary/%.wasm: tests/binary/%.wat
wat2wasm $< --output=$@
.PHONY: pykwasm-poetry-install
pykwasm-poetry-install:
$(MAKE) -C pykwasm poetry-install
tests/%.wasm.bparse: tests/%.wasm pykwasm-poetry-install
$(BINARY) $<
binary_parser_tests:=$(wildcard tests/binary/*.wat)
test-binary-parser: $(binary_parser_tests:.wat=.wasm.bparse) test-kwasm-ast
test-kwasm-ast: pykwasm-poetry-install
poetry -C pykwasm run pytest binary-parser/test_kwasm_ast.py
# Analysis
# --------
json_build := $(haskell_dir)/$(haskell_main_file)-kompiled/parsed.json
$(json_build):
$(MAKE) build-haskell -B KOMPILE_OPTS="--emit-json"
graph-imports: $(json_build)
kpyk $(haskell_dir)/$(haskell_main_file)-kompiled graph-imports
# Presentation
# ------------
media: presentations reports
media/%.pdf: TO_FORMAT=beamer
presentations: $(patsubst %.md, %.pdf, $(wildcard media/*-presentation-*.md))
media/201903-report-chalmers.pdf: TO_FORMAT=latex
reports: media/201903-report-chalmers.pdf
media/%.pdf: media/%.md media/citations.md
cat $^ | pandoc --from markdown-latex_macros --to $(TO_FORMAT) --filter pandoc-citeproc --output $@
media-clean:
rm media/*.pdf

View file

@ -0,0 +1,163 @@
KWasm: Semantics of WebAssembly in K
====================================
### Want to Support KWasm Development?
[Contribute to our Gitcoin Grant.](https://gitcoin.co/grants/592/kewasm-and-kwasm)
---
This repository presents a prototype formal semantics of [WebAssembly].
It is currently under construction.
For examples of current capabilities, see the unit tests under the `tests/simple` directory.
Repository Structure
--------------------
### Semantics Layout
The following files constitute the KWasm semantics:
- [data.md](data.md) provides the (functional) data of WebAssembly (basic types, type constructors, and values).
- [numeric.md](numeric.md) provides the functional rules for numeric operators.
- [wasm.md](wasm.md) is the main KWasm semantics, containing the configuration and transition rules of WebAssembly.
These additional files extend the semantics to make the repository more useful:
- [test.md](test.md) is an execution harness for KWasm, providing a simple language for describing tests/programs.
### Example usage: `./kwasm` runner script
After building the definition, you can run the definition using `./kwasm`.
The most up-to-date documentation will always be in `./kwasm help`.
Run the file `tests/simple/arithmetic.wast`:
```sh
./kwasm run tests/simple/arithmetic.wast
```
To run proofs, you can similarly use `./kwasm`, but must specify the module to use for proving.
For example, to prove the specification `tests/proofs/simple-arithmetic-spec.k`:
```sh
./kwasm prove tests/proofs/simple-arithmetic-spec.k KWASM-LEMMAS
```
You can optionally override the default backend using the `--backend BACKEND` flag:
```sh
./kwasm run --backend llvm tests/simple/arithmetic.wast
./kwasm prove --backend haskell tests/proofs/simple-arithmetic-spec.k KWASM-LEMMAS
```
Installing/Building
-------------------
### K Backends
There are two backends of K available, the LLVM backend for concrete execution, and the Haskell backend for symbolic reasoning and proofs.
This repository generates the build-products for the backends in `.build/defn/llvm` and `.build/defn/haskell`.
### Dependencies
The following are needed for building/running KWasm:
- [git](https://git-scm.com/)
- [Pandoc >= 1.17](https://pandoc.org) is used to generate the `*.k` files from the `*.md` files.
- GNU [Bison](https://www.gnu.org/software/bison/), [Flex](https://github.com/westes/flex), and [Autoconf](http://www.gnu.org/software/autoconf/).
- GNU [libmpfr](http://www.mpfr.org/) and [libtool](https://www.gnu.org/software/libtool/).
- Java 8 JDK (eg. [OpenJDK](http://openjdk.java.net/))
- [Haskell Stack](https://docs.haskellstack.org/en/stable/install_and_upgrade/#installupgrade).
Note that the version of the `stack` tool provided by your package manager might not be recent enough.
Please follow installation instructions from the Haskell Stack website linked above.
- [LLVM](https://llvm.org/) For building the LLVM backend.
- [Z3](https://github.com/Z3Prover/z3) version 4.8.15
On Ubuntu >= 15.04 (for example):
```sh
sudo apt-get install --yes \
autoconf bison clang++-8 clang-8 cmake curl flex gcc libboost-test-dev libffi-dev \
libgmp-dev libjemalloc-dev libmpfr-dev libprocps-dev libprotobuf-dev libtool \
libyaml-dev lld-8 llvm llvm-8 llvm-8-tools maven openjdk-8-jdk pandoc pkg-config \
protobuf-compiler python3 python-pygments python-recommonmark python-sphinx time \
zlib1g-dev
```
To upgrade `stack` (if needed):
```sh
stack upgrade
export PATH=$HOME/.local/bin:$PATH
```
After installing the above dependencies, make sure the submodules are setup:
```sh
git submodule update --init --recursive
```
Install repository specific dependencies:
```sh
make deps
```
#### Installing Z3
Note that KWASM requires Z3 version 4.8.15, which you may need to install from a
source build if your package manager supplies a different version. To do so,
follow the instructions
[here](https://github.com/Z3Prover/z3#building-z3-using-make-and-gccclang) after
checking out the correct tag in the Z3 repository:
```sh
git clone https://github.com/Z3Prover/z3.git
cd z3
git checkout z3-4.8.15
python scripts/mk_make.py
cd build
make
sudo make install
```
### Building
And then build the semantics:
```sh
make build
```
To only build a specific backend, you can do `make build-llvm` or `make build-haskell`.
### Media and documents
The `media/` directory contains presentations and reports about about KWasm.
The documents are named with an approximate date of presentation/submission, what type of document it is, and a brief contextual name, e.g., name of conference where it was held.
[GhostScript](https://www.ghostscript.com/) is a dependency for building documents of type `report`.
```sh
sudo apt install ghostscript
```
To build all documents in the media file:
```sh
make media
```
Testing
-------
The target `test` contains all the currently passing tests.
```sh
make test
```
Resources
---------
[WebAssembly]: <https://webassembly.github.io/spec/>

View file

@ -0,0 +1,37 @@
Auxiliary Wasm Commands
=======================
Generally useful commands that are not part of the actual Wasm semantics.
```k
require "wasm.md"
module WASM-AUXIL
imports WASM
syntax Stmt ::= Auxil
// ---------------------
syntax Auxil ::= "#clearConfig"
// -------------------------------
rule <instrs> #clearConfig => . ... </instrs>
<curModIdx> _ => .Int </curModIdx>
<valstack> _ => .ValStack </valstack>
<locals> _ => .Map </locals>
<moduleInstances> _ => .Bag </moduleInstances>
<moduleIds> _ => .Map </moduleIds>
<nextModuleIdx> _ => 0 </nextModuleIdx>
<moduleRegistry> _ => .Map </moduleRegistry>
<mainStore>
<nextFuncAddr> _ => 0 </nextFuncAddr>
<funcs> _ => .Bag </funcs>
<nextTabAddr> _ => 0 </nextTabAddr>
<tabs> _ => .Bag </tabs>
<nextMemAddr> _ => 0 </nextMemAddr>
<mems> _ => .Bag </mems>
<nextGlobAddr> _ => 0 </nextGlobAddr>
<globals> _ => .Bag </globals>
</mainStore>
endmodule
```

View file

@ -0,0 +1 @@
__pycache__/

View file

@ -0,0 +1,26 @@
Binary Parser for Wasm Modules
==============================
This python module converts a binary Wasm module into the Kast format accepted by KWasm.
Usage:
------
The entry point is the `wasm2kast` module.
Ensure you have `pyk` installed and available in your Python path.
Import `wasm2kast` in your Python-script.
Pass the module as bytes to the `wasm2kast` function.
It will return the Kast representation in Wasm for that module.
Example:
```py
import wasm2kast
filename = 'hello.wasm'
with open(filename, 'rb') as f:
kast = wasm2kast.wasm2kast(f)
print(kast)
```

View file

@ -0,0 +1,546 @@
#!/usr/bin/env python3
"""
NOTE: The KLabels in this file must be kept up to date with the ones in the K semantics definition.
There is unfortunately no way to do this automatically.
This library provides a convenient interface to create KWasm programs in Kast format.
It is a mirror of the abstract syntax in the K semantics.
"""
from pyk.kast.inner import KSequence, KApply, KToken
from pyk.prelude.bytes import bytesToken
from pyk.utils import dequote_str
###########
# KLabels #
###########
MODULE = 'aModuleDecl'
MODULE_METADATA = 'moduleMeta'
TYPE = 'aTypeDefn'
FUNC_TYPE = 'aFuncType'
VEC_TYPE = 'aVecType'
VAL_TYPES = 'listValTypes'
VAL_TYPES_NIL = '.List{\"listValTypes\"}_ValTypes'
I32 = 'i32'
I64 = 'i32'
INTS = 'listInt'
INTS_NIL = '.List{\"listInt\"}_Ints'
FUNC = 'aFuncDefn'
FUNC_METADATA = 'funcMeta'
TABLE = 'aTableDefn'
MEMORY = 'aMemoryDefn'
GLOBAL = 'aGlobalDefn'
GLOBAL_TYPE = 'aGlobalType'
ELEM = 'aElemDefn'
DATA = 'aDataDefn'
START = 'aStartDefn'
IMPORT = 'aImportDefn'
FUNC_DESC = 'aFuncDesc'
GLOBAL_DESC = 'aGlobalDesc'
TABLE_DESC = 'aTableDesc'
MEMORY_DESC = 'aMemoryDesc'
EXPORT = 'aExportDefn'
DEFNS = '___WASM-COMMON-SYNTAX_Defns_Defn_Defns'
INSTRS = '___WASM-COMMON-SYNTAX_Instrs_Instr_Instrs'
###################
# Basic Datatypes #
###################
def KInt(value : int):
return KToken(str(value), 'Int')
def KFloat(value : float):
return KToken(str(value), 'Float')
def KString(value : str):
return KToken('"%s"' % value, 'String')
def KBytes(bs : bytes):
# Change from python bytes repr to bytes repr in K.
return bytesToken(dequote_str(str(bs))[2:-1])
###########
# Strings #
###########
def wasm_string(s : str):
return KToken('\"%s\"' % s, 'WasmStringToken')
#########
# Lists #
#########
def KNamedList(klabel, empty_klabel, items):
tail = KApply(empty_klabel, [])
while not items == []:
last = items.pop()
tail = KApply(klabel, [last, tail])
return tail
def defns(items):
return KNamedList(DEFNS, EMPTY_STMTS, items)
def instrs(items):
return KNamedList(INSTRS, EMPTY_STMTS, items)
def val_types(items):
return KNamedList(VAL_TYPES, VAL_TYPES_NIL, items)
def ints(iis : [int]):
kis = [KInt(x) for x in iis]
return KNamedList(INTS, INTS_NIL, kis)
###########
# Empties #
###########
EMPTY_ID = KApply('.Identifier', [])
EMPTY_STMTS = '.List{\"listStmt\"}_EmptyStmts'
EMPTY_MAP = KApply('.Map', [])
EMPTY_OPT_STRING = KApply('.String', [])
EMPTY_DEFNS = KApply(EMPTY_STMTS, [])
EMPTY_FUNC_METADATA = KApply(FUNC_METADATA, [EMPTY_ID, EMPTY_MAP])
#########
# Types #
#########
i32 = KApply('i32', [])
i64 = KApply('i64', [])
f32 = KApply('f32', [])
f64 = KApply('f64', [])
MUT_CONST = KApply('mutConst', [])
MUT_VAR = KApply('mutVar', [])
def vec_type(valtypes):
return KApply(VEC_TYPE, [valtypes])
def func_type(params, results):
return KApply(FUNC_TYPE, [params, results])
def limits(tup):
i = tup[0]
j = tup[1]
if j is None:
return KApply('limitsMin', [KInt(i)])
return KApply('limitsMinMax', [KInt(i), KInt(j)])
def global_type(mut, valtype):
return KApply(GLOBAL_TYPE, [mut, valtype])
##########
# Instrs #
##########
########################
# Control Instructions #
########################
NOP = KApply('aNop', [])
UNREACHABLE = KApply('aUnreachable', [])
def BLOCK(vec_type, instrs, block_info):
return KApply('aBlock', [vec_type, instrs, block_info])
def IF(vec_type, then_instrs, else_instrs, block_info):
return KApply('aIf', [vec_type, then_instrs, else_instrs, block_info])
def LOOP(vec_type, instrs, block_info):
return KApply('aLoop', [vec_type, instrs, block_info])
RETURN = KApply('aReturn', [])
def BR(idx : int):
return KApply('aBr', [KInt(idx)])
def BR_IF(idx : int):
return KApply('aBr_if', [KInt(idx)])
def BR_TABLE(idxs : [int], default):
return KApply('aBr_table', [ints(idxs + (default,))])
def CALL(function_idx : int):
return KApply('aCall', [KInt(function_idx)])
def CALL_INDIRECT(type_idx : int):
return KApply('aCall_indirect', [KInt(type_idx)])
##########################
# Parametric Instruction #
##########################
DROP = KApply('aDrop', [])
SELECT = KApply('aSelect', [])
##############
# Float UnOp #
##############
F32_ABS = KApply('aFUnOp', [f32, KApply('aAbs', [])])
F32_CEIL = KApply('aFUnOp', [f32, KApply('aCeil', [])])
F32_FLOOR = KApply('aFUnOp', [f32, KApply('aFloor', [])])
F32_NEAREST = KApply('aFUnOp', [f32, KApply('aNearest', [])])
F32_NEG = KApply('aFUnOp', [f32, KApply('aNeg', [])])
F32_SQRT = KApply('aFUnOp', [f32, KApply('aSqrt', [])])
F32_TRUNC = KApply('aFUnOp', [f32, KApply('aTrunc', [])])
F64_ABS = KApply('aFUnOp', [f64, KApply('aAbs', [])])
F64_CEIL = KApply('aFUnOp', [f64, KApply('aCeil', [])])
F64_FLOOR = KApply('aFUnOp', [f64, KApply('aFloor', [])])
F64_NEAREST = KApply('aFUnOp', [f64, KApply('aNearest', [])])
F64_NEG = KApply('aFUnOp', [f64, KApply('aNeg', [])])
F64_SQRT = KApply('aFUnOp', [f64, KApply('aSqrt', [])])
F64_TRUNC = KApply('aFUnOp', [f64, KApply('aTrunc', [])])
############
# Int UnOp #
############
I32_CLZ = KApply('aIUnOp', [i32, KApply('aClz', [])])
I32_CTZ = KApply('aIUnOp', [i32, KApply('aCtz', [])])
I32_POPCNT = KApply('aIUnOp', [i32, KApply('aPopcnt', [])])
I64_CLZ = KApply('aIUnOp', [i64, KApply('aClz', [])])
I64_CTZ = KApply('aIUnOp', [i64, KApply('aCtz', [])])
I64_POPCNT = KApply('aIUnOp', [i64, KApply('aPopcnt', [])])
###############
# Float BinOp #
###############
F32_ADD = KApply('aFBinOp', [f32, KApply('floatAdd', [])])
F32_SUB = KApply('aFBinOp', [f32, KApply('floatSub', [])])
F32_MUL = KApply('aFBinOp', [f32, KApply('floatMul', [])])
F32_DIV = KApply('aFBinOp', [f32, KApply('floatDiv', [])])
F32_MIN = KApply('aFBinOp', [f32, KApply('floatMin', [])])
F32_MAX = KApply('aFBinOp', [f32, KApply('floatMax', [])])
F32_COPYSIGN = KApply('aFBinOp', [f32, KApply('floatCopysign', [])])
F64_ADD = KApply('aFBinOp', [f64, KApply('floatAdd', [])])
F64_SUB = KApply('aFBinOp', [f64, KApply('floatSub', [])])
F64_MUL = KApply('aFBinOp', [f64, KApply('floatMul', [])])
F64_DIV = KApply('aFBinOp', [f64, KApply('floatDiv', [])])
F64_MIN = KApply('aFBinOp', [f64, KApply('floatMin', [])])
F64_MAX = KApply('aFBinOp', [f64, KApply('floatMax', [])])
F64_COPYSIGN = KApply('aFBinOp', [f64, KApply('floatCopysign', [])])
#############
# Int BinOp #
#############
I32_ADD = KApply('aIBinOp', [i32, KApply('intAdd', [])])
I32_AND = KApply('aIBinOp', [i32, KApply('intAnd', [])])
I32_DIV_S = KApply('aIBinOp', [i32, KApply('intDiv_s', [])])
I32_DIV_U = KApply('aIBinOp', [i32, KApply('intDiv_u', [])])
I32_MUL = KApply('aIBinOp', [i32, KApply('intMul', [])])
I32_OR = KApply('aIBinOp', [i32, KApply('intOr', [])])
I32_REM_S = KApply('aIBinOp', [i32, KApply('intRem_s', [])])
I32_REM_U = KApply('aIBinOp', [i32, KApply('intRem_u', [])])
I32_ROTL = KApply('aIBinOp', [i32, KApply('intRotl', [])])
I32_ROTR = KApply('aIBinOp', [i32, KApply('intRotr', [])])
I32_SHL = KApply('aIBinOp', [i32, KApply('intShl', [])])
I32_SHR_S = KApply('aIBinOp', [i32, KApply('intShr_s', [])])
I32_SHR_U = KApply('aIBinOp', [i32, KApply('intShr_u', [])])
I32_SUB = KApply('aIBinOp', [i32, KApply('intSub', [])])
I32_XOR = KApply('aIBinOp', [i32, KApply('intXor', [])])
I64_ADD = KApply('aIBinOp', [i64, KApply('intAdd', [])])
I64_AND = KApply('aIBinOp', [i64, KApply('intAnd', [])])
I64_DIV_S = KApply('aIBinOp', [i64, KApply('intDiv_s', [])])
I64_DIV_U = KApply('aIBinOp', [i64, KApply('intDiv_u', [])])
I64_MUL = KApply('aIBinOp', [i64, KApply('intMul', [])])
I64_OR = KApply('aIBinOp', [i64, KApply('intOr', [])])
I64_REM_S = KApply('aIBinOp', [i64, KApply('intRem_s', [])])
I64_REM_U = KApply('aIBinOp', [i64, KApply('intRem_u', [])])
I64_ROTL = KApply('aIBinOp', [i64, KApply('intRotl', [])])
I64_ROTR = KApply('aIBinOp', [i64, KApply('intRotr', [])])
I64_SHL = KApply('aIBinOp', [i64, KApply('intShl', [])])
I64_SHR_S = KApply('aIBinOp', [i64, KApply('intShr_s', [])])
I64_SHR_U = KApply('aIBinOp', [i64, KApply('intShr_u', [])])
I64_SUB = KApply('aIBinOp', [i64, KApply('intSub', [])])
I64_XOR = KApply('aIBinOp', [i64, KApply('intXor', [])])
##########
# TestOp #
##########
I32_EQZ = KApply('aTestOp', [i32, KApply('aEqz', [])])
I64_EQZ = KApply('aTestOp', [i64, KApply('aEqz', [])])
#############
# Int RelOp #
#############
I32_EQ = KApply('aIRelOp', [i32, KApply('intEq', [])])
I32_NE = KApply('aIRelOp', [i32, KApply('intNe', [])])
I32_LT_U = KApply('aIRelOp', [i32, KApply('intLt_u', [])])
I32_GT_U = KApply('aIRelOp', [i32, KApply('intGt_u', [])])
I32_LT_S = KApply('aIRelOp', [i32, KApply('intLt_s', [])])
I32_GT_S = KApply('aIRelOp', [i32, KApply('intGt_s', [])])
I32_LE_U = KApply('aIRelOp', [i32, KApply('intLe_u', [])])
I32_GE_U = KApply('aIRelOp', [i32, KApply('intGe_u', [])])
I32_LE_S = KApply('aIRelOp', [i32, KApply('intLe_s', [])])
I32_GE_S = KApply('aIRelOp', [i32, KApply('intGe_s', [])])
I64_EQ = KApply('aIRelOp', [i64, KApply('intEq', [])])
I64_NE = KApply('aIRelOp', [i64, KApply('intNe', [])])
I64_LT_U = KApply('aIRelOp', [i64, KApply('intLt_u', [])])
I64_GT_U = KApply('aIRelOp', [i64, KApply('intGt_u', [])])
I64_LT_S = KApply('aIRelOp', [i64, KApply('intLt_s', [])])
I64_GT_S = KApply('aIRelOp', [i64, KApply('intGt_s', [])])
I64_LE_U = KApply('aIRelOp', [i64, KApply('intLe_u', [])])
I64_GE_U = KApply('aIRelOp', [i64, KApply('intGe_u', [])])
I64_LE_S = KApply('aIRelOp', [i64, KApply('intLe_s', [])])
I64_GE_S = KApply('aIRelOp', [i64, KApply('intGe_s', [])])
###############
# Float RelOp #
###############
F32_LT = KApply('aFRelOp', [f32, KApply('floatLt', [])])
F32_GT = KApply('aFRelOp', [f32, KApply('floatGt', [])])
F32_LE = KApply('aFRelOp', [f32, KApply('floatLe', [])])
F32_GE = KApply('aFRelOp', [f32, KApply('floatGe', [])])
F32_EQ = KApply('aFRelOp', [f32, KApply('floatEq', [])])
F32_NE = KApply('aFRelOp', [f32, KApply('floatNe', [])])
F64_LT = KApply('aFRelOp', [f64, KApply('floatLt', [])])
F64_GT = KApply('aFRelOp', [f64, KApply('floatGt', [])])
F64_LE = KApply('aFRelOp', [f64, KApply('floatLe', [])])
F64_GE = KApply('aFRelOp', [f64, KApply('floatGe', [])])
F64_EQ = KApply('aFRelOp', [f64, KApply('floatEq', [])])
F64_NE = KApply('aFRelOp', [f64, KApply('floatNe', [])])
##############
# Convert Op #
##############
I64_EXTEND_U_I32 = KApply('aCvtOp', [i64, KApply('aExtend_i32_u', [])])
I64_EXTEND_S_I32 = KApply('aCvtOp', [i64, KApply('aExtend_i32_s', [])])
I32_WRAP_I64 = KApply('aCvtOp', [i32, KApply('aWrap_i64', [])])
F64_PROMOTE_F32 = KApply('aCvtOp', [f64, KApply('aPromote_f32', [])])
F32_DEMOTE_F64 = KApply('aCvtOp', [f32, KApply('aDemote_f64', [])])
F32_CONVERT_U_I32 = KApply('aCvtOp', [f32, KApply('aConvert_i32_u', [])])
F64_CONVERT_U_I32 = KApply('aCvtOp', [f64, KApply('aConvert_i32_u', [])])
F32_CONVERT_U_I64 = KApply('aCvtOp', [f32, KApply('aConvert_i64_u', [])])
F64_CONVERT_U_I64 = KApply('aCvtOp', [f64, KApply('aConvert_i64_u', [])])
F32_CONVERT_S_I32 = KApply('aCvtOp', [f32, KApply('aConvert_i32_s', [])])
F64_CONVERT_S_I32 = KApply('aCvtOp', [f64, KApply('aConvert_i32_s', [])])
F32_CONVERT_S_I64 = KApply('aCvtOp', [f32, KApply('aConvert_i64_s', [])])
F64_CONVERT_S_I64 = KApply('aCvtOp', [f64, KApply('aConvert_i64_s', [])])
I32_TRUNC_U_F32 = KApply('aCvtOp', [i32, KApply('aTrunc_f32_u', [])])
I32_TRUNC_U_F64 = KApply('aCvtOp', [i32, KApply('aTrunc_f64_u', [])])
I64_TRUNC_U_F32 = KApply('aCvtOp', [i64, KApply('aTrunc_f32_u', [])])
I64_TRUNC_U_F64 = KApply('aCvtOp', [i64, KApply('aTrunc_f64_u', [])])
I32_TRUNC_S_F32 = KApply('aCvtOp', [i32, KApply('aTrunc_f32_s', [])])
I32_TRUNC_S_F64 = KApply('aCvtOp', [i32, KApply('aTrunc_f64_s', [])])
I64_TRUNC_S_F32 = KApply('aCvtOp', [i64, KApply('aTrunc_f32_s', [])])
I64_TRUNC_S_F64 = KApply('aCvtOp', [i64, KApply('aTrunc_f64_s', [])])
#########
# Const #
#########
def F32_CONST(f: float):
return KApply('aFConst', [f32, KFloat(f)])
def F64_CONST(f: float):
return KApply('aFConst', [f64, KFloat(f)])
def I32_CONST(i : int):
return (KApply('aIConst', [i32, KInt(i)]))
def I64_CONST(i : int):
return (KApply('aIConst', [i64, KInt(i)]))
#######################
# Memory Instructions #
#######################
def F32_STORE(offset : int):
return KApply('aStore', [f32, KApply('storeOpStore', []), KInt(offset)])
def F64_STORE(offset : int):
return KApply('aStore', [f64, KApply('storeOpStore', []), KInt(offset)])
def I32_STORE(offset : int):
return KApply('aStore', [i32, KApply('storeOpStore', []), KInt(offset)])
def I64_STORE(offset : int):
return KApply('aStore', [i64, KApply('storeOpStore', []), KInt(offset)])
def I32_STORE8(offset : int):
return KApply('aStore', [i32, KApply('storeOpStore8', []), KInt(offset)])
def I64_STORE8(offset : int):
return KApply('aStore', [i64, KApply('storeOpStore8', []), KInt(offset)])
def I32_STORE16(offset : int):
return KApply('aStore', [i32, KApply('storeOpStore16', []), KInt(offset)])
def I64_STORE16(offset : int):
return KApply('aStore', [i64, KApply('storeOpStore16', []), KInt(offset)])
def I64_STORE32(offset : int):
return KApply('aStore', [i64, KApply('storeOpStore32', []), KInt(offset)])
def F32_LOAD(offset : int):
return KApply('aLoad', [f32, KApply('loadOpLoad', []), KInt(offset)])
def F64_LOAD(offset : int):
return KApply('aLoad', [f64, KApply('loadOpLoad', []), KInt(offset)])
def I32_LOAD(offset : int):
return KApply('aLoad', [i32, KApply('loadOpLoad', []), KInt(offset)])
def I64_LOAD(offset : int):
return KApply('aLoad', [i64, KApply('loadOpLoad', []), KInt(offset)])
def I32_LOAD16_S(offset : int):
return KApply('aLoad', [i32, KApply('loadOpLoad16_s', []), KInt(offset)])
def I32_LOAD16_U(offset : int):
return KApply('aLoad', [i32, KApply('loadOpLoad16_u', []), KInt(offset)])
def I64_LOAD16_S(offset : int):
return KApply('aLoad', [i64, KApply('loadOpLoad16_s', []), KInt(offset)])
def I64_LOAD16_U(offset : int):
return KApply('aLoad', [i64, KApply('loadOpLoad16_u', []), KInt(offset)])
def I32_LOAD8_S(offset : int):
return KApply('aLoad', [i32, KApply('loadOpLoad8_s', []), KInt(offset)])
def I32_LOAD8_U(offset : int):
return KApply('aLoad', [i32, KApply('loadOpLoad8_u', []), KInt(offset)])
def I64_LOAD8_S(offset : int):
return KApply('aLoad', [i64, KApply('loadOpLoad8_s', []), KInt(offset)])
def I64_LOAD8_U(offset : int):
return KApply('aLoad', [i64, KApply('loadOpLoad8_u', []), KInt(offset)])
def I64_LOAD32_U(offset : int):
return KApply('aLoad', [i64, KApply('loadOpLoad32_u', []), KInt(offset)])
def I64_LOAD32_S(offset : int):
return KApply('aLoad', [i64, KApply('loadOpLoad32_s', []), KInt(offset)])
MEMORY_GROW = KApply('aGrow', [])
MEMORY_SIZE = KApply('aSize', [])
#######################
# Global Instructions #
#######################
def GET_GLOBAL(idx : int):
return KApply('aGlobal.get', [KInt(idx)])
def SET_GLOBAL(idx : int):
return KApply('aGlobal.set', [KInt(idx)])
######################
# Local Instructions #
######################
def GET_LOCAL(idx : int):
return KApply('aLocal.get', [KInt(idx)])
def SET_LOCAL(idx : int):
return KApply('aLocal.set', [KInt(idx)])
def TEE_LOCAL(idx : int):
return KApply('aLocal.tee', [KInt(idx)])
#######################
# Import Descriptions #
#######################
def func_desc(type : int, id=EMPTY_ID):
return KApply(FUNC_DESC, [id, KInt(type)])
def global_desc(global_type, id=EMPTY_ID):
return KApply(GLOBAL_DESC, [id, global_type])
def table_desc(lim, id=EMPTY_ID):
return KApply(TABLE_DESC, [id, limits(lim)])
def memory_desc(lim, id=EMPTY_ID):
return KApply(MEMORY_DESC, [id, limits(lim)])
################
# Declarations #
################
def type(func_type, metadata=EMPTY_ID):
return KApply(TYPE, [func_type, metadata])
def func(type, locals, body, metadata=EMPTY_FUNC_METADATA):
return KApply(FUNC, [type, locals, body, metadata])
def table(lim, metadata=EMPTY_ID):
return KApply(TABLE, [limits(lim), metadata])
def memory(lim, metadata=EMPTY_ID):
return KApply(MEMORY, [limits(lim), metadata])
def glob(type, init, metadata=EMPTY_ID):
return KApply(GLOBAL, [type, init, metadata])
def elem(table_idx : int, offset, init : [int]):
return KApply(ELEM, [KInt(table_idx), offset, ints(init)])
def data(memory_idx : int, offset, data : bytes):
return KApply(DATA, [KInt(memory_idx), offset, KBytes(data)])
def start(start_idx : int):
return KApply(START, [KInt(start_idx)])
def imp(mod_name, name, import_desc):
return KApply(IMPORT, [mod_name, name, import_desc])
def export(name, index):
return KApply(EXPORT, [name, KInt(index)])
def module_metadata(mid=None, fids=None, filename=None):
# TODO: Implement module id and function ids metadata transformation.
kfilename = EMPTY_OPT_STRING if filename is None else KString(filename)
return KApply(MODULE_METADATA, [EMPTY_ID, EMPTY_MAP, kfilename])
EMPTY_MODULE_METADATA = module_metadata()
def module(types=EMPTY_DEFNS,
funcs=EMPTY_DEFNS,
tables=EMPTY_DEFNS,
mems=EMPTY_DEFNS,
globs=EMPTY_DEFNS,
elem=EMPTY_DEFNS,
data=EMPTY_DEFNS,
start=EMPTY_DEFNS,
imports=EMPTY_DEFNS,
exports=EMPTY_DEFNS,
metadata=EMPTY_MODULE_METADATA):
"""Construct a Kast of a module."""
return KApply(MODULE,
[types,
funcs,
tables,
mems,
globs,
elem,
data,
start,
imports,
exports,
metadata])

View file

@ -0,0 +1,44 @@
#!/usr/bin/env python3
import wasm2kast
import json
import pyk
import sys
import subprocess
import tempfile
WASM_definition_llvm_no_coverage_dir = '.build/defn/llvm'
def config_to_kast_term(config):
return { 'format' : 'KAST', 'version': 2, 'term': config.to_dict() }
def run_module(parsed_module):
input_json = config_to_kast_term(parsed_module)
krun_args = [ '--term', '--debug']
with tempfile.NamedTemporaryFile(mode = 'w') as tempf:
tempf.write(json.dumps(input_json))
tempf.flush()
command = ['krun-legacy', '--directory',
WASM_definition_llvm_no_coverage_dir, '--term', tempf.name,
'--parser', 'cat', '--output', 'json']
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate(input=None)
rc = process.returncode
if rc != 0:
raise Exception(f'Received error while running: {str(stderr)}')
def pykPrettyPrint(module):
WASM_definition_llvm_no_coverage_dir = '.build/defn/llvm'
WASM_definition_main_file = 'test'
WASM_definition_llvm_no_coverage = pyk.readKastTerm(WASM_definition_llvm_no_coverage_dir + '/' + WASM_definition_main_file + '-kompiled/compiled.json')
WASM_symbols_llvm_no_coverage = pyk.buildSymbolTable(WASM_definition_llvm_no_coverage)
print(pyk.prettyPrintKast(module, WASM_symbols_llvm_no_coverage))
sys.setrecursionlimit(1500000000)
if __name__ == "__main__":
module = wasm2kast.main()
run_module(module)

View file

@ -0,0 +1,23 @@
import pytest
from kwasm_ast import KBytes
KBYTES_TEST_DATA = (
(bytes([0x0, 0x41, 0xff]), 'b"\x00\x41\xff"'),
(bytes([]), 'b""'),
(b'WASM', 'b"WASM"'),
(b'foo\xAA\x01barbaz', 'b"foo\xAA\x01barbaz"'),
(b'foo\xAAbar\x01baz', 'b"foo\xAAbar\x01baz"'),
(b'abcdefghijklmnopqrstuvwxyz', 'b"abcdefghijklmnopqrstuvwxyz"'),
(0x11223344556677889900aabbccddeeff.to_bytes(length=16, byteorder='big'),
'b"\x11\x22\x33\x44\x55\x66\x77\x88\x99\x00\xaa\xbb\xcc\xdd\xee\xff"')
)
@pytest.mark.parametrize(('input', 'expected'), KBYTES_TEST_DATA)
def test_kbytes(input, expected) -> None:
# When
t = KBytes(input)
# Then
assert t.token == expected
assert t.sort.name == 'Bytes'

View file

@ -0,0 +1 @@
../deps/py-wasm/wasm

View file

@ -0,0 +1,268 @@
#!/usr/bin/env python3
"""
This library provides a translation from the Wasm binary format to Kast.
"""
import sys
import json
import kwasm_ast as a
from wasm.parsers.module import parse_module, Module
from wasm.datatypes import ValType, TypeIdx, FunctionType, Function, Table, TableType, Memory, MemoryType, Limits, Global, GlobalType, Mutability, ElementSegment, DataSegment, StartFunction, Import, Export
from wasm.opcodes import BinaryOpcode
def main():
if len(list(sys.argv)) == 1:
infile = sys.stdin
else:
infile = open(sys.argv[1], 'rb')
module = wasm2kast(infile)
infile.close()
return module
def wasm2kast(wasm_bytes : bytes, filename=None) -> dict:
"""Returns a dictionary representing the Kast JSON."""
ast = parse_module(wasm_bytes)
return ast2kast(ast, filename=filename)
def ast2kast(wasm_ast : Module, filename=None) -> dict:
"""Returns a dictionary representing the Kast JSON."""
types = a.defns([typ(x) for x in wasm_ast.types])
funcs = a.defns([func(x) for x in wasm_ast.funcs])
tables = a.defns([table(x) for x in wasm_ast.tables])
mems = a.defns([memory(x) for x in wasm_ast.mems])
globs = a.defns([glob(x) for x in wasm_ast.globals])
elems = a.defns([elem(x) for x in wasm_ast.elem])
datas = a.defns([data(x) for x in wasm_ast.data])
starts = a.defns(start(wasm_ast.start))
imports = a.defns([imp(x) for x in wasm_ast.imports])
exports = a.defns([export(x) for x in wasm_ast.exports])
meta = a.module_metadata(filename=filename)
return a.module(types=types, funcs=funcs, tables=tables, mems=mems, globs=globs, elem=elems, data=datas, start=starts, imports=imports, exports=exports, metadata=meta)
#########
# Defns #
#########
def typ(t : FunctionType):
return a.type(func_type(t.params, t.results))
def func(f : Function):
type = a.KInt(f.type_idx)
ls_list = [val_type(x) for x in f.locals]
locals = a.vec_type(a.val_types(ls_list))
body = instrs(f.body)
return a.func(type, locals, body)
def table(t : Table):
ls = limits(t.type.limits)
return a.table(ls)
def memory(m : Memory):
ls = limits(m.type)
return a.memory(ls)
def glob(g : Global):
t = global_type(g.type)
init = instrs(g.init)
return a.glob(t, init)
def elem(e : ElementSegment):
offset = instrs(e.offset)
return a.elem(e.table_idx, offset, e.init)
def data(d : DataSegment):
offset = instrs(d.offset)
return a.data(d.memory_idx, offset, d.init)
def start(s : StartFunction):
if s is None:
return []
return [a.start(s.function_idx)]
def imp(i : Import):
mod_name = a.wasm_string(i.module_name)
name = a.wasm_string(i.as_name)
t = type(i.desc)
if t is TypeIdx:
desc = a.func_desc(i.desc)
elif t is GlobalType:
desc = a.global_desc(global_type(i.desc))
elif t is TableType:
desc = a.table_desc(limits(i.desc.limits))
elif t is MemoryType:
desc = a.memory_desc(limits(i.desc))
return a.imp(mod_name, name, desc)
def export(e : Export):
name = a.wasm_string(e.name)
idx = e.desc
return a.export(name, idx)
##########
# Instrs #
##########
block_id = 0
def instrs(iis):
"""Turn a list of instructions into KAST."""
# We ignore `END`.
# The AST supplied by py-wasm has already parsed these and terminated the blocks.
# We also ignore `ELSE`.
# The AST supplied by py-wasm includes the statements in the else-branch as part of the `IF` instruction.
return a.instrs([instr(i) for i in iis
if not i.opcode == BinaryOpcode.END
and not i.opcode == BinaryOpcode.ELSE])
def instr(i):
B = BinaryOpcode
global block_id
if i.opcode == B.BLOCK:
cur_block_id = block_id
block_id += 1
iis = instrs(i.instructions)
res = vec_type(i.result_type)
return a.BLOCK(res, iis, a.KInt(cur_block_id))
if i.opcode == B.BR:
return a.BR(i.label_idx)
if i.opcode == B.BR_IF:
return a.BR_IF(i.label_idx)
if i.opcode == B.BR_TABLE:
return a.BR_TABLE(i.label_indices, i.default_idx)
if i.opcode == B.CALL:
return a.CALL(i.function_idx)
if i.opcode == B.CALL_INDIRECT:
return a.CALL_INDIRECT(i.type_idx)
if i.opcode == B.ELSE:
raise(ValueError("ELSE opcode: should have been filtered out."))
if i.opcode == B.END:
raise(ValueError("End opcode: should have been filtered out."))
if i.opcode == B.F32_CONST:
return a.F32_CONST(i.value)
if i.opcode == B.F64_CONST:
return a.F64_CONST(i.value)
if i.opcode == B.F32_REINTERPRET_I32:
raise(ValueError('Reinterpret instructions not implemented.'))
if i.opcode == B.F64_REINTERPRET_I64:
raise(ValueError('Reinterpret instructions not implemented.'))
if i.opcode == B.GET_GLOBAL:
return a.GET_GLOBAL(i.global_idx)
if i.opcode == B.GET_LOCAL:
return a.GET_LOCAL(i.local_idx)
if i.opcode == B.I32_CONST:
return a.I32_CONST(i.value)
if i.opcode == B.I64_CONST:
return a.I64_CONST(i.value)
if i.opcode == B.I32_REINTERPRET_F32:
raise(ValueError('Reinterpret instructions not implemented.'))
if i.opcode == B.I64_REINTERPRET_F64:
raise(ValueError('Reinterpret instructions not implemented.'))
if i.opcode == B.IF:
cur_block_id = block_id
block_id += 1
thens = instrs(i.instructions)
els = instrs(i.else_instructions)
res = vec_type(i.result_type)
return a.IF(res, thens, els, a.KInt(cur_block_id))
if i.opcode == B.F32_STORE:
return a.F32_STORE(i.memarg.offset)
if i.opcode == B.F64_STORE:
return a.F64_STORE(i.memarg.offset)
if i.opcode == B.I32_STORE:
return a.I32_STORE(i.memarg.offset)
if i.opcode == B.I64_STORE:
return a.I64_STORE(i.memarg.offset)
if i.opcode == B.I32_STORE16:
return a.I32_STORE16(i.memarg.offset)
if i.opcode == B.I64_STORE16:
return a.I64_STORE16(i.memarg.offset)
if i.opcode == B.I32_STORE8:
return a.I32_STORE8(i.memarg.offset)
if i.opcode == B.I64_STORE8:
return a.I64_STORE8(i.memarg.offset)
if i.opcode == B.I64_STORE32:
return a.I64_STORE32(i.memarg.offset)
if i.opcode == B.F32_LOAD:
return a.F32_LOAD(i.memarg.offset)
if i.opcode == B.F64_LOAD:
return a.F64_LOAD(i.memarg.offset)
if i.opcode == B.I32_LOAD:
return a.I32_LOAD(i.memarg.offset)
if i.opcode == B.I64_LOAD:
return a.I64_LOAD(i.memarg.offset)
if i.opcode == B.I32_LOAD16_S:
return a.I32_LOAD16_S(i.memarg.offset)
if i.opcode == B.I32_LOAD16_U:
return a.I32_LOAD16_U(i.memarg.offset)
if i.opcode == B.I64_LOAD16_S:
return a.I64_LOAD16_S(i.memarg.offset)
if i.opcode == B.I64_LOAD16_U:
return a.I64_LOAD16_U(i.memarg.offset)
if i.opcode == B.I32_LOAD8_S:
return a.I32_LOAD8_S(i.memarg.offset)
if i.opcode == B.I32_LOAD8_U:
return a.I32_LOAD8_U(i.memarg.offset)
if i.opcode == B.I64_LOAD8_S:
return a.I64_LOAD8_S(i.memarg.offset)
if i.opcode == B.I64_LOAD8_U:
return a.I64_LOAD8_U(i.memarg.offset)
if i.opcode == B.I64_LOAD32_S:
return a.I64_LOAD32_S(i.memarg.offset)
if i.opcode == B.I64_LOAD32_U:
return a.I64_LOAD32_U(i.memarg.offset)
if i.opcode == B.LOOP:
cur_block_id = block_id
block_id += 1
iis = instrs(i.instructions)
res = vec_type(i.result_type)
return a.LOOP(res, iis, a.KInt(cur_block_id))
if i.opcode == B.SET_GLOBAL:
return a.SET_GLOBAL(i.global_idx)
if i.opcode == B.SET_LOCAL:
return a.SET_LOCAL(i.local_idx)
if i.opcode == B.TEE_LOCAL:
return a.TEE_LOCAL(i.local_idx)
# Catch all for operations without direct arguments.
op = i.opcode
return eval('a.' + i.opcode.name)
########
# Data #
########
def val_type(t : ValType):
if t == ValType.i32:
return a.i32
if t == ValType.i64:
return a.i64
if t == ValType.f32:
return a.f32
if t == ValType.f64:
return a.f64
def vec_type(ts : [ValType]):
_ts = [val_type(x) for x in ts]
return a.vec_type(a.val_types(_ts))
def func_type(params, results):
pvec = vec_type(params)
rvec = vec_type(results)
return a.func_type(pvec, rvec)
def limits(l : Limits):
return (l.min, l.max)
def global_type(t : GlobalType):
vt = val_type(t.valtype)
if t.mut is Mutability.const:
return a.global_type(a.MUT_CONST, vt)
return a.global_type(a.MUT_VAR, vt)
########
# Main #
########
if __name__ == "__main__":
main()

View file

@ -0,0 +1,39 @@
# This script is not used during runtime.
# It just helps converting some conformances test to a test that we can use by removing unsupported functions and converting hexfloat to normal float.
# However it does not support multi-line assertions, function definitions, etc.
# The test files under directory tests/simple/float were generated by this script.
# example usage: python convert.py f32.wast
import re
import sys
def hex2float(h):
print(h)
if "nan" in h:
return h.replace("nan", "NaN")
elif "inf" in h:
return h.replace("inf", "Infinity")
elif "0x" not in h:
return h
else:
return h.split()[0] + " " + "%e" % (float.fromhex(h.split()[1]))
def main():
filename = sys.argv[1]
infile = "tests/wasm-tests/test/core/%s" % filename
outfile = open("tests/simple/%s-c.%s" % tuple(filename.split(".")), "w")
unsupported = ["nan:", "-nan", "reinterpret",
"assert_return_canonical_nan", "assert_return_arithmetic_nan", "assert_invalid", "assert_malformed"]
for line in (open(infile).readlines()):
if any(x in line for x in unsupported):
outfile.write(";; "+line)
else:
outfile.write(re.sub(r"(?:(?:f32|f64)\.const )([^\)]+)",
lambda m: hex2float(m.group()), line))
outfile.write("\n#clearConfig\n")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,606 @@
WebAssembly Data
================
```k
module WASM-DATA-SYNTAX
imports WASM-DATA-COMMON-SYNTAX
endmodule
```
Common Syntax
-------------
```k
module WASM-DATA-COMMON-SYNTAX
imports INT-SYNTAX
imports FLOAT-SYNTAX
```
### WASM Token Sorts
```k
syntax IdentifierToken [token]
syntax WasmIntToken [token]
syntax #Layout [token]
syntax WasmStringToken [token]
// ------------------------------
```
### Identifiers
And we use `OptionalId` to handle the case where an identifier could be omitted.
```k
syntax Identifier ::= IdentifierToken
syntax OptionalId ::= "" [klabel(.Identifier), symbol]
| Identifier
// --------------------------------
```
### Strings
Wasm binary data can sometimes be specified as a string.
The string considered to represent the sequence of UTF-8 bytes that encode it.
The exception is for characters that are explicitly escaped which can represent bytes in hexadecimal form.
```k
syntax WasmString ::= ".WasmString"
| WasmStringToken
// -------------------------------------
syntax String ::= #parseWasmString ( WasmStringToken ) [function, total, hook(STRING.token2string)]
// ----------------------------------------------------------------------------------------------------------
syntax DataString ::= List{WasmString, ""} [klabel(listWasmString)]
// -------------------------------------------------------------------
```
### Indices
Many constructions in Wasm, such a functions, labels, locals, types, etc., are referred to by their index.
In the abstract syntax of Wasm, indices are 32 bit unsigned integers.
However, we extend the `Index` sort with another subsort, `Identifier`, in the text format.
```k
syntax Index ::= WasmInt
// ------------------------
```
### ElemSegment
Element Segment is a list of indices.
It is used when initializing a WebAssembly table, or used as the parameter of the `br_table` function.
```k
syntax ElemSegment ::= List{Index, ""} [klabel(listIndex)]
// ----------------------------------------------------------
```
### WebAssembly Types
#### Base Types
WebAssembly has four basic types, for 32 and 64 bit integers and floats.
```k
syntax IValType ::= "i32" [klabel(i32), symbol] | "i64" [klabel(i64), symbol]
syntax FValType ::= "f32" [klabel(f32), symbol] | "f64" [klabel(f64), symbol]
syntax ValType ::= IValType | FValType
// ---------------------------------------
```
#### Type Constructors
There are two basic type-constructors: sequencing (`[_]`) and function spaces (`_->_`).
```k
syntax ValTypes ::= List{ValType, ""} [klabel(listValTypes), symbol]
// --------------------------------------------------------------------
```
### Integers
Here we define the rules about what integer formats can be used in Wasm.
For the core language, only regular integers are allowed.
```k
syntax WasmInt ::= Int
// ----------------------
```
### Type Mutability
```k
syntax Mut ::= "const" [klabel(mutConst), symbol]
| "var" [klabel(mutVar), symbol]
// -----------------------------------------------
```
### Basic Values
WebAssembly values are either integers or floating-point numbers, of 32- or 64-bit widths.
```k
syntax Number ::= Int | Float
// -----------------------------
```
### External Values
An `external value` is the runtime representation of an entity that can be `imported` or `exported`.
It is an `address` denoting either a `function instance`, `table instance`, `memory instance`, or `global instances` in the shared store.
```k
syntax AllocatedKind ::= "func" | "table" | "memory" | "global"
syntax Externval ::= AllocatedKind Index
// --------------------------------------------
```
```k
endmodule
```
`WASM-DATA` module
------------------
```k
module WASM-DATA
imports WASM-DATA-COMMON-SYNTAX
imports INT
imports BOOL
imports STRING
imports LIST
imports MAP
imports FLOAT
imports BYTES
imports K-EQUAL
```
### Identifiers
In `KWASM` rules, we use `#freshId ( Int )` to generate a fresh identifier based on the element index in the current module.
```k
syntax Identifier ::= #freshId ( Int )
// --------------------------------------
```
In KWasm we store identifiers in maps from `Identifier` to `Int`, the `Int` being an index.
This rule handles adding an `OptionalId` as a map key, but only when it is a proper identifier.
```k
syntax Map ::= #saveId (Map, OptionalId, Int) [function]
// -------------------------------------------------------
rule #saveId (MAP, ID:OptionalId, _) => MAP requires notBool isIdentifier(ID)
rule #saveId (MAP, ID:Identifier, IDX) => MAP [ID <- IDX]
```
`Int` is the basic form of index, and indices always need to resolve to integers.
In the text format, `Index` is extended with the `Identifier` subsort, and there needs to be a way to resolve it to an `Int`.
In Wasm, the current "context" contains a mapping from identifiers to indices.
The `#ContextLookup` function provides an extensible way to get an `Int` from an index.
Any extension of the `Index` type requires that the function `#ContextLookup` is suitably extended.
For `Int`, however, a the context is irrelevant and the index always just resolves to the integer.
```k
syntax Int ::= #ContextLookup ( Map , Index ) [function]
// --------------------------------------------------------
rule #ContextLookup(_IDS:Map, I:Int) => I
```
### ElemSegment
```k
syntax Ints ::= List{Int, ""} [klabel(listInt), symbol]
// -------------------------------------------------------
syntax Int ::= #lenElemSegment (ElemSegment) [function]
syntax Index ::= #getElemSegment (ElemSegment, Int) [function]
syntax Int ::= #lenInts (Ints) [function]
syntax Int ::= #getInts (Ints, Int) [function]
// --------------------------------------------------------------
rule #lenElemSegment(.ElemSegment) => 0
rule #lenElemSegment(_TFIDX ES) => 1 +Int #lenElemSegment(ES)
rule #getElemSegment(E _ES, 0) => E
rule #getElemSegment(_E ES, I) => #getElemSegment(ES, I -Int 1) requires I >Int 0
rule #lenInts(.Ints) => 0
rule #lenInts(_TFIDX ES) => 1 +Int #lenInts(ES)
rule #getInts(E _ES, 0) => E
rule #getInts(_E ES, I) => #getInts(ES, I -Int 1) requires I >Int 0
syntax Ints ::= elemSegment2Ints ( ElemSegment ) [function]
// -----------------------------------------------------------
rule elemSegment2Ints(.ElemSegment) => .Ints
rule elemSegment2Ints(E:Int ES) => E elemSegment2Ints(ES)
```
### OptionalInt
In some cases, an integer is optional, such as when either giving or omitting the max bound when defining a table or memory.
The sort `OptionalInt` provides this potentially "undefined" `Int`.
```k
syntax OptionalInt ::= Int | ".Int"
// -----------------------------------
```
### Limits
Tables and memories have limits, defined as either a single `Int` or two `Int`s, representing min and max bounds.
```k
syntax Limits ::= #limitsMin(Int) [klabel(limitsMin), symbol]
| #limits(Int, Int) [klabel(limitsMinMax), symbol]
// ------------------------------------------------------------------
```
### Type Constructors
There are two basic type-constructors: sequencing (`[_]`) and function spaces (`_->_`).
```k
syntax VecType ::= "[" ValTypes "]" [klabel(aVecType), symbol]
// ---------------------------------------------------------------
syntax FuncType ::= VecType "->" VecType [klabel(aFuncType), symbol]
// --------------------------------------------------------------------
syntax Int ::= lengthValTypes ( ValTypes ) [function, total]
// -----------------------------------------------------------------
rule lengthValTypes(.ValTypes) => 0
rule lengthValTypes(_V VS) => 1 +Int lengthValTypes(VS)
```
All told, a `Type` can be a value type, vector of types, or function type.
```k
syntax Type ::= ".Type" | ValType | VecType | FuncType
// ------------------------------------------------------
```
We can append two `ValTypes`s with the `_+_` operator.
```k
syntax ValTypes ::= ValTypes "+" ValTypes [function, total]
// ----------------------------------------------------------------
rule .ValTypes + VTYPES' => VTYPES'
rule (VT VTYPES) + VTYPES' => VT (VTYPES + VTYPES')
```
Also we can reverse a `ValTypes` with `#revt`
```k
syntax ValTypes ::= #revt ( ValTypes ) [function, total]
| #revt ( ValTypes , ValTypes ) [function, total, klabel(#revtAux)]
// ------------------------------------------------------------------------------------------
rule #revt(VT) => #revt(VT, .ValTypes)
rule #revt(.ValTypes, VT') => VT'
rule #revt(V VT , VT') => #revt(VT, V VT')
```
### Type Information
The `#width` function returns the bit-width of a given `IValType`.
```k
syntax Int ::= #width ( IValType ) [function, total]
syntax Int ::= #numBytes ( IValType ) [function, total, smtlib(numBytes)]
// ------------------------------------------------------------------------------
rule #width(i32) => 32
rule #width(i64) => 64
rule #numBytes(ITYPE) => #width(ITYPE) /Int 8 [concrete]
```
`2 ^Int 32` and `2 ^Int 64` are used often enough to warrant providing helpers for them.
```k
syntax Int ::= #pow ( IValType ) [function, total, smtlib(pow )] /* 2 ^Int #width(T) */
| #pow1 ( IValType ) [function, total, smtlib(pow1)] /* 2 ^Int (#width(T) -Int 1) */
// ------------------------------------------------------------------------------------------------------
rule #pow1(i32) => 2147483648
rule #pow (i32) => 4294967296
rule #pow1(i64) => 9223372036854775808
rule #pow (i64) => 18446744073709551616
```
### Type Mutability
```k
syntax Mut ::= ".Mut"
// ----------------------
```
### Values
Proper values are numbers annotated with their types.
```k
syntax IVal ::= "<" IValType ">" Int [klabel(<_>_)]
syntax FVal ::= "<" FValType ">" Float [klabel(<_>_)]
syntax Val ::= "<" ValType ">" Number [klabel(<_>_)]
| IVal | FVal
// ---------------------------
```
We also add `undefined` as a value, which makes many partial functions in the semantics total.
```k
syntax Val ::= "undefined"
// --------------------------
```
#### Value Operations
The `#chop` function will ensure that an integer value is wrapped to the correct bit-width.
The `#wrap` function wraps an integer to a given byte width.
The `#get` function extracts the underlying K integer from a WASM `IVal`.
```k
syntax IVal ::= #chop ( IVal ) [function, total]
// -----------------------------------------------------
rule #chop(< ITYPE > N) => < ITYPE > (N modInt #pow(ITYPE))
syntax Int ::= #wrap ( Int , Int ) [function, total]
// ---------------------------------------------------------
rule [wrap-Positive] : #wrap(WIDTH, N) => N &Int ((1 <<Int (WIDTH *Int 8)) -Int 1) requires 0 <Int WIDTH
rule #wrap(WIDTH, _N) => 0 requires notBool 0 <Int WIDTH
syntax Int ::= #get( IVal ) [function, total]
// --------------------------------------------------
rule #get(< _ > N) => N
```
In `K` all `Float` numbers are of 64-bits width by default, so we need to downcast a `f32` float to 32-bit manually.
The `#round` function casts a `f64` float to a `f32` float.
`f64` floats has 1 bit for the sign, 53 bits for the value and 11 bits for exponent.
`f32` floats has 1 bit for the sign, 23 bits for the value and 8 bits for exponent.
```k
syntax FVal ::= #round ( FValType , Number ) [function]
// -------------------------------------------------------
rule #round(f64 , N:Float) => < f64 > roundFloat(N, 53, 11) [concrete]
rule #round(f32 , N:Float) => < f32 > roundFloat(N, 24, 8) [concrete]
rule #round(f64 , N:Int ) => < f64 > Int2Float(N, 53, 11) [concrete]
rule #round(f32 , N:Int ) => < f32 > Int2Float(N, 24, 8) [concrete]
```
#### Signed Interpretation
Functions `#signed` and `#unsigned` allow for easier operation on twos-complement numbers.
These functions assume that the argument integer is in the valid range of signed and unsigned values of the respective type, so they will not correctly map arbitrary integers into the corret range.
Some operations extend integers from 1, 2, or 4 bytes, so a special function with a bit width argument helps with the conversion.
```k
syntax Int ::= #signed ( IValType , Int ) [function]
| #unsigned ( IValType , Int ) [function]
| #signedWidth ( Int , Int ) [function]
// ---------------------------------------------------------
rule #signed(ITYPE, N) => N requires 0 <=Int N andBool N <Int #pow1(ITYPE)
rule #signed(ITYPE, N) => N -Int #pow(ITYPE) requires #pow1(ITYPE) <=Int N andBool N <Int #pow (ITYPE)
rule #unsigned( ITYPE, N) => N +Int #pow(ITYPE) requires N <Int 0
rule #unsigned(_ITYPE, N) => N requires 0 <=Int N
rule #signedWidth(1, N) => N requires 0 <=Int N andBool N <Int 128
rule #signedWidth(1, N) => N -Int 256 requires 128 <=Int N andBool N <Int 256
rule #signedWidth(2, N) => N requires 0 <=Int N andBool N <Int 32768
rule #signedWidth(2, N) => N -Int 65536 requires 32768 <=Int N andBool N <Int 65536
rule #signedWidth(4, N) => #signed(i32, N)
```
#### Boolean Interpretation
Function `#bool` converts a `Bool` into an `Int`.
```k
syntax Int ::= #bool ( Bool ) [function, total]
// ----------------------------------------------------
rule #bool( B:Bool ) => 1 requires B
rule #bool( B:Bool ) => 0 requires notBool B
```
### Data Structures
WebAssembly is a stack-machine, so here we provide the stack to operate over.
Operator `_++_` implements an append operator for sort `ValStack`.
```k
syntax ValStack ::= ".ValStack"
| Val ":" ValStack
| ValStack "++" ValStack [function, total]
// -----------------------------------------------------------------
rule .ValStack ++ VALSTACK' => VALSTACK'
rule (SI : VALSTACK) ++ VALSTACK' => SI : (VALSTACK ++ VALSTACK')
```
`#zero` will create a specified stack of zero values in a given type.
`#take` will take the prefix of a given stack.
`#drop` will drop the prefix of a given stack.
`#revs` will reverse a given stack.
**NOTE**: `#take` and `#drop` are _total_, so in case they could not take/drop enough values before running out, they just return the empty `.ValStack`.
Each call site _must_ ensure that this is desired behavior before using these functions.
```k
syntax ValStack ::= #zero ( ValTypes ) [function, total]
| #take ( Int , ValStack ) [function, total]
| #drop ( Int , ValStack ) [function, total]
| #revs ( ValStack ) [function, total]
| #revs ( ValStack , ValStack ) [function, total, klabel(#revsAux)]
// ------------------------------------------------------------------------------------------
rule #zero(.ValTypes) => .ValStack
rule #zero(ITYPE:IValType VTYPES) => < ITYPE > 0 : #zero(VTYPES)
rule #zero(FTYPE:FValType VTYPES) => < FTYPE > 0.0 : #zero(VTYPES)
rule #take(N, _) => .ValStack requires notBool N >Int 0
rule #take(N, .ValStack) => .ValStack requires N >Int 0
rule #take(N, V : VS) => V : #take(N -Int 1, VS) requires N >Int 0
rule #drop(N, VS) => VS requires notBool N >Int 0
rule #drop(N, .ValStack) => .ValStack requires N >Int 0
rule #drop(N, _ : VS) => #drop(N -Int 1, VS) requires N >Int 0
rule #revs(VS) => #revs(VS, .ValStack)
rule #revs(.ValStack, VS') => VS'
rule #revs(V : VS , VS') => #revs(VS, V : VS')
```
### Strings
Wasm uses a different character escape rule with K, so we need to define the `unescape` function ourselves.
```k
syntax String ::= unescape(String) [function]
| unescape(String, Int, String) [function, klabel(unescapeAux)]
// -------------------------------------------------------------------------------
rule unescape(S ) => unescape(S, 1, "")
rule unescape(S, IDX, SB) => SB requires IDX ==Int lengthString(S) -Int 1
```
Unescaped characters just directly gets added to the output.
The escaped character starts with a "\" and followed by 2 hexdigits will be converted to a unescaped character before stored.
```k
rule unescape(S, IDX, SB) => unescape(S, IDX +Int 1, SB +String substrString(S, IDX, IDX +Int 1))
requires IDX <Int lengthString(S) -Int 1
andBool substrString(S, IDX, IDX +Int 1) =/=K "\\"
rule unescape(S, IDX, SB) => unescape(S, IDX +Int 3, SB +String chrChar(String2Base(substrString(S, IDX +Int 1, IDX +Int 3), 16)))
requires IDX <Int lengthString(S) -Int 3
andBool substrString(S, IDX, IDX +Int 1) ==K "\\"
andBool (findChar("0123456789abcdefABCDEF", substrString(S, IDX +Int 1, IDX +Int 2), 0) =/=Int -1 )
```
The characters "\t", "\n", "\r", """, "'", and "\" are interpreted as regular escaped ascii symbols.
```k
rule unescape(S, IDX, SB) => unescape(S, IDX +Int 2, SB +String chrChar(String2Base("09", 16)))
requires IDX <Int lengthString(S) -Int 2
andBool substrString(S, IDX, IDX +Int 1) ==K "\\"
andBool substrString(S, IDX +Int 1, IDX +Int 2) ==K "t"
rule unescape(S, IDX, SB) => unescape(S, IDX +Int 2, SB +String chrChar(String2Base("0A", 16)))
requires IDX <Int lengthString(S) -Int 2
andBool substrString(S, IDX, IDX +Int 1) ==K "\\"
andBool substrString(S, IDX +Int 1, IDX +Int 2) ==K "n"
rule unescape(S, IDX, SB) => unescape(S, IDX +Int 2, SB +String chrChar(String2Base("0D", 16)))
requires IDX <Int lengthString(S) -Int 2
andBool substrString(S, IDX, IDX +Int 1) ==K "\\"
andBool substrString(S, IDX +Int 1, IDX +Int 2) ==K "r"
rule unescape(S, IDX, SB) => unescape(S, IDX +Int 2, SB +String chrChar(String2Base("22", 16)))
requires IDX <Int lengthString(S) -Int 2
andBool substrString(S, IDX, IDX +Int 1) ==K "\\"
andBool substrString(S, IDX +Int 1, IDX +Int 2) ==K "\""
rule unescape(S, IDX, SB) => unescape(S, IDX +Int 2, SB +String chrChar(String2Base("27", 16)))
requires IDX <Int lengthString(S) -Int 2
andBool substrString(S, IDX, IDX +Int 1) ==K "\\"
andBool substrString(S, IDX +Int 1, IDX +Int 2) ==K "'"
rule unescape(S, IDX, SB) => unescape(S, IDX +Int 2, SB +String chrChar(String2Base("5C", 16)))
requires IDX <Int lengthString(S) -Int 2
andBool substrString(S, IDX, IDX +Int 1) ==K "\\"
andBool substrString(S, IDX +Int 1, IDX +Int 2) ==K "\\"
```
Longer byte sequences can be encoded as escaped "Unicode", with `\u{<hexdigit>+}`.
The implementation is not correct for now because the UTF-8 encoding is not implemented.
```k
syntax Int ::= #idxCloseBracket ( String, Int ) [function]
// ----------------------------------------------------------
rule #idxCloseBracket ( S, I ) => I requires substrString(S, I, I +Int 1) ==String "}"
rule #idxCloseBracket ( S, I ) => #idxCloseBracket ( S, I +Int 1 ) requires substrString(S, I, I +Int 1) =/=String "}"
syntax Bytes ::= #encodeUTF8 ( Int ) [function]
// -----------------------------------------------
rule #encodeUTF8 (I) => Int2Bytes(I, BE, Unsigned) requires I <=Int 127
rule #encodeUTF8 (I) => Int2Bytes(((((I &Int 1984) >>Int 6) +Int 192) <<Int 8) +Int ((I &Int 63) +Int 128), BE, Unsigned)
requires I >=Int 128 andBool I <=Int 2047
rule #encodeUTF8 (I) => Int2Bytes(((((I &Int 61440) >>Int 12) +Int 224) <<Int 16) +Int ((((I &Int 4032) >>Int 6) +Int 128) <<Int 8) +Int ((I &Int 63) +Int 128), BE, Unsigned)
requires I >=Int 2048 andBool I <=Int 65535
rule #encodeUTF8 (I) => Int2Bytes(((((I &Int 1835008) >>Int 18) +Int 240) <<Int 24) +Int ((((I &Int 258048) >>Int 12) +Int 128) <<Int 16) +Int ((((I &Int 4032) >>Int6) +Int 128) <<Int 8) +Int ((I &Int 63) +Int 128), BE, Unsigned)
requires I >=Int 65536 andBool I <=Int 1114111
rule unescape(S, IDX, SB) => unescape(S, #idxCloseBracket(S, IDX) +Int 1, SB +String Bytes2String(#encodeUTF8(String2Base(substrString(S, IDX +Int 3, #idxCloseBracket(S, IDX +Int 3)), 16))))
requires substrString(S, IDX, IDX +Int 1) ==K "\\"
andBool substrString(S, IDX +Int 1, IDX +Int 2) ==K "u"
```
`DataString`, as is defined in the wasm semantics, is a list of `WasmString`s.
`#concatDS` concatenates them together into a single string.
The strings to connect needs to be unescaped before concatenated, because the `unescape` function removes the quote sign `"` before and after each substring.
```k
syntax String ::= #concatDS ( DataString ) [function]
| #concatDS ( DataString, String ) [function, klabel(#concatDSAux)]
// -----------------------------------------------------------------------------------
rule #concatDS ( DS ) => #concatDS ( DS, "" )
rule #concatDS ( .DataString , S ) => S
rule #concatDS ( WS:WasmStringToken DS , S ) => #concatDS ( DS , S +String unescape(#parseWasmString(WS)) )
```
`#DS2Bytes` converts a `DataString` to a K builtin `Bytes`.
```k
syntax Bytes ::= #DS2Bytes (DataString) [function]
// --------------------------------------------------
rule #DS2Bytes(DS) => String2Bytes(#concatDS(DS))
```
### Linear Memory
Wasm memories are byte arrays, sized in pages of 65536 bytes, initialized to be all zero bytes.
- `#setBytesRange(BM, START, BS)` assigns a contiguous chunk of `BS` to `BM` starting at position `N`.
- `#setRange(BM, START, VAL, WIDTH)` writes the integer `I` to memory as bytes (little-endian), starting at index `N`.
```k
syntax Bytes ::= #setBytesRange ( Bytes , Int , Bytes ) [function, total]
// ------------------------------------------------------------------------------
rule #setBytesRange(BM, START, BS) => replaceAtBytes(padRightBytes(BM, START +Int lengthBytes(BS), 0), START, BS)
requires 0 <=Int START
rule #setBytesRange(_, START, _ ) => .Bytes
requires notBool (0 <=Int START)
syntax Bytes ::= #setRange ( Bytes , Int , Int , Int ) [function, total, smtlib(setRange)]
// -----------------------------------------------------------------------------------------------
rule #setRange(BM, ADDR, VAL, WIDTH) => BM
requires notBool (0 <Int WIDTH andBool 0 <=Int VAL andBool 0 <=Int ADDR)
rule [setRange-Positive] : #setRange(BM, ADDR, VAL, WIDTH) => #setBytesRange(BM, ADDR, Int2Bytes(WIDTH, VAL, LE))
requires 0 <Int WIDTH andBool 0 <=Int VAL andBool 0 <=Int ADDR
```
- `#getBytesRange(BM, START, WIDTH)` reads off `WIDTH` elements from `BM` beginning at position `START` (padding with zeros as needed).
- `#getRange(BM, START, WIDTH)` reads off `WIDTH` elements from `BM` beginning at position `START`, and converts it into an unsigned integer. The function interprets the range of bytes as little-endian.
```k
syntax Bytes ::= #getBytesRange ( Bytes , Int , Int ) [function, total]
// ----------------------------------------------------------------------------
rule #getBytesRange(_, START, WIDTH) => .Bytes
requires notBool (0 <=Int START andBool 0 <=Int WIDTH)
rule #getBytesRange(BM, START, WIDTH) => substrBytes(padRightBytes(BM, START +Int WIDTH, 0), START, START +Int WIDTH)
requires (0 <=Int START andBool 0 <=Int WIDTH)
andBool START <Int lengthBytes(BM)
rule #getBytesRange(BM, START, WIDTH) => padRightBytes(.Bytes, WIDTH, 0)
requires (0 <=Int START andBool 0 <=Int WIDTH)
andBool notBool (START <Int lengthBytes(BM))
syntax Int ::= #getRange(Bytes, Int, Int) [function, total, smtlib(getRange)]
// ----------------------------------------------------------------------------------
rule #getRange( _, ADDR, WIDTH) => 0
requires notBool (0 <Int WIDTH andBool 0 <=Int ADDR)
rule [getRange-Positive] : #getRange(BM, ADDR, WIDTH) => Bytes2Int(#getBytesRange(BM, ADDR, WIDTH), LE, Unsigned)
requires 0 <Int WIDTH andBool 0 <=Int ADDR
```
```k
endmodule
```

View file

@ -0,0 +1 @@
5.6.7

View file

@ -0,0 +1 @@
v0.1.232

View file

@ -0,0 +1,181 @@
#!/usr/bin/env bash
set -euo pipefail
shopt -s extglob
notif() { echo "== $@" >&2 ; }
fatal() { echo "[FATAL] $@" ; exit 1 ; }
kwasm_dir="${KWASM_DIR:-$(dirname $0)}"
build_dir="$kwasm_dir/.build"
defn_dir="${KWASM_DEFN_DIR:-$build_dir/defn}"
lib_dir="$build_dir/local/lib"
k_release_dir="${K_RELEASE:-$kwasm_dir/deps/k/k-distribution/target/release/k}"
if [[ ! -f "${k_release_dir}/bin/kompile" ]]; then
if which kompile &> /dev/null; then
k_release_dir="$(dirname $(which kompile))/.."
else
fatal "Cannot find K Installation!"
fi
fi
export PATH="$k_release_dir/lib/native/linux:$k_release_dir/lib/native/linux64:$k_release_dir/bin/:$PATH"
export LD_LIBRARY_PATH="$k_release_dir/lib/native/linux64:$lib_dir:${LD_LIBRARY_PATH:-}"
test_logs="$build_dir/logs"
mkdir -p "$test_logs"
test_log="$test_logs/tests.log"
export K_OPTS="${K_OPTS:--Xmx16G -Xss512m}"
# Utilities
# ---------
preprocess() {
local this_script_dir tmp_dir tmp_input
this_script_dir="$(dirname $0)"
tmp_dir="$(mktemp -d)"
tmp_input="$tmp_dir/$(basename $run_file))"
touch "$tmp_input"
python3 "$this_script_dir/preprocessor.py" "$run_file" > "$tmp_input"
run_file="$tmp_input"
}
# Runners
# -------
run_krun() {
local additional_run_args
additional_run_args=()
! $bug_report || additional_run_args+=(--haskell-backend-command "kore-exec --bug-report $bug_report_name")
preprocess
krun --directory "$backend_dir" "$run_file" "${additional_run_args[@]}" "$@"
}
run_krun_legacy() {
local additional_run_args
additional_run_args=()
! $bug_report || additional_run_args+=(--haskell-backend-command "kore-exec --bug-report $bug_report_name")
preprocess
krun-legacy --directory "$backend_dir" "$run_file" "${additional_run_args[@]}" "$@"
}
run_kast() {
local output_mode
preprocess
output_mode="${1:-kast}" ; shift
kast --directory "$backend_dir" "$run_file" --output "$output_mode" "$@"
}
run_prove() {
local additional_proof_args
def_module="$1" ; shift
additional_proof_args=()
! $repl || additional_proof_args+=(--debugger)
! $bug_report || additional_proof_args+=(--haskell-backend-command "kore-exec --bug-report $bug_report_name")
kprove-legacy --directory "$backend_dir" -I "$kwasm_dir" "$run_file" --def-module "$def_module" "${additional_proof_args[@]}" "$@"
}
# Main
# ----
usage() {
echo "
usage: $0 run [--backend (llvm|haskell)] [--bug-report] <pgm> <K args>*
$0 run-legacy [--backend (llvm|haskell)] [--bug-report] <pgm> <K args>*
$0 kast [--backend (llvm|haskell)] <pgm> <output format> <K args>*
$0 prove [--backend (haskell)] [--repl|--bug-report] <spec> <def_module> <K args>*
$0 [help|--help|version|--version]
$0 run : Run a single WebAssembly program
$0 run-legacy : Run a single WebAssembly program using krun-legacy
$0 kast : Parse a single WebAssembly program and output it in supported format
$0 prove : Run a WebAssembly K proof
$0 help : Display this help message.
$0 version : Display the KWasm, K, Kore, and Z3 versions in use.
Note: <pgm> is a path to a file containing a WebAssembly program.
<spec> is a K specification to be proved.
<K args> are any arguments you want to pass to K when executing/proving.
<output format> is the format for Kast to output the term in.
<def_module> is the module to take as axioms for verification.
"
}
usage_fatal() {
usage
fatal "$@"
}
[[ ! -z ${1:-} ]] || usage_fatal "Must supply a command to run."
if [[ "$1" == '--help' ]] || [[ "$1" == 'help' ]]; then
usage
exit 0
fi
if [[ "$1" == 'version' ]] || [[ "$1" == '--version' ]]; then
notif "KWasm Version"
git rev-parse --short HEAD
notif "K Version"
kompile --version
notif "Kore Version"
kore-exec --version
notif "Z3 Version"
z3 --version
exit 0
fi
run_command="$1"; shift
[[ ! -z ${1:-} ]] || usage_fatal "Must supply a file to work on."
backend="llvm"
repl=false
bug_report=false
[[ ! "$run_command" == 'prove' ]] || backend='haskell'
args=()
while [[ $# -gt 0 ]]; do
arg="$1"
case $arg in
--backend) backend="$2" ; shift 2 ;;
--repl) repl=true ; shift ;;
--bug-report) bug_report=true ; shift ;;
*) args+=("$1") ; shift ;;
esac
done
set -- "${args[@]}"
! $repl || [[ "$backend" == "haskell" ]] || fatal "--repl option only available for Haskell backend."
! $bug_report || [[ "$backend" == "haskell" ]] || fatal "--bug-report option only available for Haskell backend."
backend_dir="$defn_dir/$backend"
# get the run file
[[ ! -z ${1:-} ]] || usage_fatal "Must supply a file to run on."
run_file="$1" ; shift
if [[ "$run_file" == '-' ]]; then
tmp_input="$(mktemp)"
trap "rm -rf $tmp_input" INT TERM EXIT
cat - > "$tmp_input"
run_file="$tmp_input"
fi
[[ -f "$run_file" ]] || fatal "File does not exist: $run_file"
bug_report_name="kwasm-bug-$(basename "$run_file")"
case "$run_command-$backend" in
run-@(llvm|haskell) ) run_krun "$@" ;;
run-legacy-@(llvm|haskell) ) run_krun_legacy "$@" ;;
kast-@(llvm|haskell) ) run_kast "$@" ;;
prove-@(haskell) ) run_prove "$@" ;;
*) usage_fatal "Unknown command on '$backend' backend: $run_command" ;;
esac

View file

@ -0,0 +1,344 @@
KWASM Lemmas
============
These lemmas aid in verifying WebAssembly programs behavior.
They are part of the *trusted* base, and so should be scrutinized carefully.
```k
module KWASM-LEMMAS [symbolic]
imports WASM-TEXT
imports BYTES-KORE
imports INT-SYMBOLIC
```
Basic logic
-----------
```k
rule #bool(P) ==Int 0 => notBool P [simplification]
```
Basic arithmetic
----------------
// TODO: Upstream the lemmas in this section into K.
### Modular Arithmetic
Z3 is slow and unreliable in reasoning about modular arithmetic.
Therefore we want to make structural simplifications wherever possible.
`X modInt N` is undefined for `N ==Int 0`, so we must take care to check that `N =/=Int 0`.
At the same time, we don't want to impose unnecessary side-conditions, so the non-zero condition can be implied by the other conditions.
For example, `X modInt N ==Int Y` implies `N =/=Int 0`, because only non-zero `N` could have the right-hand side resolve to an `Int`.
For simplicity, we impose that `N >Int 0`.
Not however that K defines `X modInt N ==Int X modInt (-N)`.
#### Rules for Expressions With Only Modulus
These are given in pure modulus form, and in form with `#wrap`, which is modulus with a power of 2 for positive `N`.
```k
rule #wrap(N, X) => X
requires 0 <=Int N
andBool 0 <=Int X
andBool X <Int (1 <<Int (N *Int 8))
[simplification]
rule _X modInt 1 => 0
[simplification]
```
`modInt` selects the least non-negative representative of a congruence class modulo `N`.
```k
rule (X modInt M) modInt N => X modInt M
requires M >Int 0
andBool M <=Int N
[simplification]
rule #wrap(N, #wrap(M, X)) => #wrap(M, X)
requires M <=Int N
[simplification]
```
Proof:
```
Since 0 <= x mod m < m <= n, (x mod m) mod n = x mod m
```
```k
rule (X modInt M) modInt N => X modInt N
requires M >Int 0
andBool N >Int 0
andBool M modInt N ==Int 0
[simplification]
rule #wrap(N, #wrap(M, X)) => #wrap(N, X)
requires notBool (M <=Int N)
[simplification]
```
Proof:
```
Assume m = n * k for some k > 0.
x = m * q + r, for a unique q and r s.t. 0 <= r < m
(x mod m) mod n
= r mod n
= (n * ( k * q) + r) mod n
= m * q + r mod n
= x mod n
```
#### Modulus Over Addition
```k
rule (_X *Int M +Int Y) modInt N => Y modInt N
requires M >Int 0
andBool N >Int 0
andBool M modInt N ==Int 0
[simplification]
rule (Y +Int _X *Int M) modInt N => Y modInt N
requires M >Int 0
andBool N >Int 0
andBool M modInt N ==Int 0
[simplification]
rule #wrap(N, (_X <<Int M) +Int Y) => #wrap(N, Y)
requires 0 <=Int M
andBool (N *Int 8) <=Int M
[simplification]
rule #wrap(N, Y +Int (_X <<Int M)) => #wrap(N, Y)
requires 0 <=Int M
andBool (N *Int 8) <=Int M
[simplification]
```
Proof:
```
Assume m = k * n for some k (side condition).
x * m + y mod n = x * (k * n) + y mod n = y mod n
```
```k
rule ((X modInt M) +Int Y) modInt N => (X +Int Y) modInt N
requires M >Int 0
andBool N >Int 0
andBool M modInt N ==Int 0
[simplification]
rule (X +Int (Y modInt M)) modInt N => (X +Int Y) modInt N
requires M >Int 0
andBool N >Int 0
andBool M modInt N ==Int 0
[simplification]
rule #wrap(N, #wrap(M, X) +Int Y) => #wrap(N, X +Int Y)
requires N <=Int M
[simplification]
rule #wrap(N, X +Int #wrap(M, Y)) => #wrap(N, X +Int Y)
requires N <=Int M
[simplification]
```
Proof:
```
Assume m = l * n
x = k * m + r, r < m
x mod m + y = r + y
(x + y) mod n
= k * m + r + y mod n
= k * l * n + r + y mod n
= r + y mod n
= (x mod m + y) mod n
```
#### Bit Shifting
We want K to understand what a bit-shift is.
```k
rule (_X <<Int N) modInt M => 0
requires (2 ^Int N) modInt M ==Int 0
[simplification]
rule #wrap(M, _X <<Int N) => 0
requires (M *Int 8) <=Int N
[simplification]
rule (X >>Int N) => 0 requires X <Int 2 ^Int N [simplification]
rule (_X <<Int N) modInt M => 0 requires M <Int 2 ^Int N [simplification]
rule (X >>Int N) >>Int M => X >>Int (N +Int M) [simplification]
rule (X <<Int N) <<Int M => X <<Int (N +Int M) [simplification]
// The Haskell backend accepts negative shifts (the LLVM backend does not).
// So removing the side conditions and keeping one of each rule here could give faster symbolic execution.
rule (X <<Int N) >>Int M => X <<Int (N -Int M) requires N >=Int M [simplification]
rule (X <<Int N) >>Int M => X >>Int (M -Int N) requires notBool (N >=Int M) [simplification]
```
```k
rule ((X <<Int M) +Int Y) >>Int N => (X <<Int (M -Int N)) +Int (Y >>Int N) requires M >=Int N [simplification]
rule (Y +Int (X <<Int M)) >>Int N => (X <<Int (M -Int N)) +Int (Y >>Int N) requires M >=Int N [simplification]
```
Proof:
```
Let x' = x << m
=> The least m bits of x' are 0.
=> The least m bits of x' + y are the same as the least m bits of y, and there can be no carry in adding the least m bits.
=> The least (m-n) bits of (x' + y) >> n are the same as the least (m-n) bits of (y >> n), or y[n..], and there can be no carry in adding the least (m-n) bits.
=> ((x << m) + y) >> n is the same as adding x to the m'th and higher bits of y, and then concatenating the lesat (m-n) bytes of y[n..]
=> ((x << m) + y) >> n = y[n..(m-1)] : (x + y[m..])
=> ((x << m) + y) >> n
= ((x + y[m..]) << (m-n)) + y[n..(m-1)]
= (x << (m-n)) + (y[m..] << (m-n)) + y[n..(m-1)]
= (x << (m-n)) + (y[n..(m-1)] : y[m..])
= (x << (m-n)) + (y >> n)
```
The following rules decrease the modulus by rearranging it around a shift.
```k
rule (X modInt POW) >>Int N => (X >>Int N) modInt (POW /Int (2 ^Int N))
requires N >=Int 0
andBool POW >Int 0
andBool POW modInt (2 ^Int N) ==Int 0
[simplification]
rule (X <<Int N) modInt POW => (X modInt (POW /Int (2 ^Int N))) <<Int N
requires N >=Int 0
andBool POW >Int 0
andBool POW modInt (2 ^Int N) ==Int 0
[simplification]
```
Proof sketch: Taking modulo `2^n * k` can only affect the the `n`-th and higher bits.
A shift right by `n` bits can only erase bits up to the `n-1`-th bit.
Therefore, we may as well shift first and then take the modulus, only we need to make sure the modulus acts on the shifted bits, by taking modulo `k` instead.
The argument for the left shift is similar.
```k
rule (X +Int (_Y <<Int N)) modInt POW => X modInt POW
requires N >=Int 0
andBool POW >Int 0
andBool (2 ^Int N) modInt POW ==Int 0
[simplification]
rule ((_Y <<Int N) +Int X) modInt POW => X modInt POW
requires N >=Int 0
andBool POW >Int 0
andBool (2 ^Int N) modInt POW ==Int 0
[simplification]
```
Proof: These follow from the fact that shifting left by `n` bits is simply multiplying by `2^n`, and from previously proven rules of modular arithmetic.
### Basic Operations
When reasoning about `#chop`, it's often the case that the precondition to the proof contains the information needed to indicate no overflow.
In this case, it's simpler (and safe) to simply discard the `#chop`, instead of evaluating it.
```k
rule X modInt #pow(ITYPE) => #unsigned(ITYPE, X)
requires #inSignedRange (ITYPE, X)
[simplification]
syntax Bool ::= #inUnsignedRange (IValType, Int) [function]
syntax Bool ::= #inSignedRange (IValType, Int) [function]
// -----------------------------------------------------------
rule #inUnsignedRange (ITYPE, I) => 0 <=Int I andBool I <Int #pow (ITYPE)
rule #inSignedRange (ITYPE, I) => #minSigned(ITYPE) <=Int I andBool I <Int #pow1(ITYPE)
syntax Int ::= #minSigned ( IValType ) [function]
// --------------------------------------------------
rule #minSigned(ITYPE) => 0 -Int #pow1(ITYPE)
```
Lookups
-------
```k
rule (_MAP:Map [KEY <- VAL])[KEY] => VAL [simplification]
```
Memory
------
Bounds on `#getRange`:
```k
rule 0 <=Int #getRange(_, _, _) => true [simplification]
rule 0 <=Int VAL <<Int SHIFT => true requires 0 <=Int VAL andBool 0 <=Int SHIFT [simplification]
rule 0 <=Int VAL1 +Int VAL2 => true requires 0 <=Int VAL1 andBool 0 <=Int VAL2 [simplification]
rule #getRange(_, _, WIDTH) <Int MAX => true requires 2 ^Int (8 *Int WIDTH) <=Int MAX [simplification]
rule #getRange(_, _, WIDTH) <<Int SHIFT <Int MAX => true requires 2 ^Int ((8 *Int WIDTH) +Int SHIFT) <=Int MAX [simplification]
rule VAL1 +Int VAL2 <Int MAX => true requires VAL1 <Int MAX andBool VAL2 <Int MAX andBool #distinctBits(VAL1, VAL2) [simplification]
syntax Bool ::= #distinctBits ( Int , Int ) [function]
// ------------------------------------------------------
rule #distinctBits(#getRange(_, _, WIDTH), #getRange(_, _, _) <<Int SHIFT) => true requires WIDTH *Int 8 <=Int SHIFT
[simplification]
```
`#wrap` over `#getRange`:
```k
rule #wrap(MAX_WIDTH, #getRange(BM, ADDR, WIDTH)) => #getRange(BM, ADDR, minInt(MAX_WIDTH, WIDTH))
[simplification]
```
Arithmetic over `#getRange`:
```k
rule #getRange(BM, ADDR, WIDTH) >>Int SHIFT => #getRange(BM, ADDR +Int 1, WIDTH -Int 1) >>Int (SHIFT -Int 8)
requires 0 <=Int ADDR
andBool 0 <Int WIDTH
andBool 8 <=Int SHIFT
[simplification]
rule #getRange(BM, ADDR, WIDTH) modInt MAX => #getRange(BM, ADDR, WIDTH -Int 1) modInt MAX
requires 0 <Int MAX andBool 0 <Int WIDTH
andBool 2 ^Int (8 *Int (WIDTH -Int 1)) modInt MAX ==Int 0
[simplification]
```
`#getRange` over `#setRange`:
```k
rule #getRange(#setRange(BM, ADDR, _VAL, _WIDTH), ADDR', WIDTH') => #getRange(BM, ADDR', WIDTH') requires ADDR' +Int WIDTH' <=Int ADDR [simplification]
rule #getRange(#setRange(BM, ADDR, _VAL, WIDTH), ADDR', WIDTH') => #getRange(BM, ADDR', WIDTH') requires ADDR +Int WIDTH <=Int ADDR' [simplification]
rule #getRange(#setRange(_BM, ADDR, VAL, WIDTH), ADDR', WIDTH') => VAL
requires 0 <=Int ADDR
andBool 0 <Int WIDTH
andBool 0 <=Int VAL andBool VAL <Int 2 ^Int (8 *Int WIDTH)
andBool ADDR' ==Int ADDR
andBool WIDTH' ==Int WIDTH
[simplification]
```
`#setRange` over `#getRange`
```k
rule #setRange(BM, ADDR, #getRange(BM, ADDR, WIDTH), WIDTH) => BM [simplification]
rule #setRange(BM, ADDR, (#getRange(_, _, WIDTH1) #as VAL1) +Int (VAL2 <<Int SHIFT), WIDTH)
=> #setRange(#setRange(BM, ADDR, VAL1, minInt(WIDTH1, WIDTH)), ADDR +Int WIDTH1, VAL2, WIDTH -Int WIDTH1)
requires 0 <=Int ADDR
andBool 0 <Int WIDTH
andBool WIDTH1 *Int 8 ==Int SHIFT
[simplification]
```
```k
endmodule
```

View file

@ -0,0 +1,586 @@
---
title: 'KWASM: Overview and Path to KeWASM'
author: Everett Hildenbrandt
date: '\today'
theme: metropolis
header-includes:
- \newcommand{\K}{\ensuremath{\mathbb{K}}}
- \newcommand{\instr}{instr}
- \newcommand{\LOOP}{\texttt{loop}}
- \newcommand{\LABEL}{\texttt{label}}
- \newcommand{\END}{\texttt{end}}
- \newcommand{\stepto}{\hookrightarrow}
---
Overview
--------
1. Introduction to \K{}
2. KWASM Design
3. Using KWASM (Psuedo-Demo)
4. Future Directions
Introduction to \K{}
====================
The Vision: Language Independence
---------------------------------
Separate development of PL software into two tasks:
. . .
### The Programming Language
PL expert builds rigorous and formal spec of the language in a high-level human-readable semantic framework.
. . .
### The Tooling
Build each tool *once*, and apply it to every language, eg:
- Parser
- Interpreter
- Debugger
- Compiler
- Model Checker
- Program Verifier
The Vision: Language Independence
---------------------------------
![K Tooling Overview](media/img/k-overview.png)
Current Semantics
-----------------
Many languages have full or partial \K{} semantics, this lists some notable ones (and their primary usage).
- [C](https://github.com/kframework/c-semantics): detecting undefined behavior
- [Java](https://github.com/kframework/java-semantics): detecting racy code
- [EVM](https://github.com/kframework/evm-semantics): verifying smart contracts
- [LLVM](https://github.com/kframework/llvm-semantics): compiler validation (to x86)
- [JavaScript](https://github.com/kframework/javascript-semantics): finding disagreements between JS engines
- [P4](https://github.com/kframework/p4-semantics): SDN data-layer verification
- many others ...
\K{} Specifications: Syntax
---------------------------
Concrete syntax built using EBNF style:
```k
syntax Exp ::= Int | Id | "(" Exp ")" [bracket]
| Exp "*" Exp
> Exp "+" Exp // looser binding
syntax Stmt ::= Id ":=" Exp
| Stmt ";" Stmt
| "return" Exp
```
. . .
This would allow correctly parsing programs like:
```imp
a := 3 * 2;
b := 2 * a + 5;
return b
```
\K{} Specifications: Configuration
----------------------------------
Tell \K{} about the structure of your execution state.
For example, a simple imperative language might have:
```k
configuration <k> $PGM:Program </k>
<env> .Map </env>
<store> .Map </store>
```
. . .
> - `<k>` will contain the initial parsed program
> - `<env>` contains bindings of variable names to store locations
> - `<store>` conaints bindings of store locations to integers
\K{} Specifications: Transition Rules
-------------------------------------
Using the above grammar and configuration:
. . .
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
. . .
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
Example Execution
-----------------
### Program
```imp
a := 3 * 2;
b := 2 * a + 5;
return b
```
### Initial Configuration
```k
<k> a := 3 * 2 ; b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 0 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> a := 6 ~> b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 0 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
### Next Configuration
```k
<k> a ~> b := 2 * [] + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
### Next Configuration
```k
<k> 6 ~> b := 2 * [] + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
### Next Configuration
```k
<k> b := 2 * 6 + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> b := 17 ~> return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 17 </store>
```
KWASM Design
============
WASM Specification
------------------
Available at <https://github.com/WebAssembly/spec>.
- Fairly unambiguous[^betterThanEVM].
- Well written with procedural description of execution accompanied by small-step semantic rules.
. . .
Example rule:
1. Let $L$ be the label whose arity is 0 and whose continuation is the start of the loop.
2. `Enter` the block $\instr^\ast$ with label $L$.
. . .
\vspace{-2em}
$$
\LOOP~[t^?]~\instr^\ast~\END
\quad \stepto \quad
\LABEL_0\{\LOOP~[t^?]~\instr^\ast~\END\}~\instr^\ast~\END
$$
[^betterThanEVM]: At least, better than the [YellowPaper](https://github.com/ethereum/yellowpaper).
Translation to \K{}
-------------------
### WASM Spec
\vspace{-1em}
$$
\LOOP~[t^?]~\instr^\ast~\END
\quad \stepto \quad
\LABEL_0\{\LOOP~[t^?]~\instr^\ast~\END\}~\instr^\ast~\END
$$
. . .
### In \K{}
```k
syntax Instr ::= "loop" Type Instrs "end"
// -----------------------------------------
rule <k> loop TYPE IS end
=> IS
~> label [ .ValTypes ] {
loop TYPE IS end
} STACK
...
</k>
<stack> STACK </stack>
```
Design Difference: 1 or 2 Stacks?
---------------------------------
. . .
### WASM Specification
One stack mixing values and instructions.
- Confusing control-flow semantics (with `label`s).
- Use meta-level context operator to describe semantics of `br`.
- Correct label is buried in the value stack (which we must also access the top of).
- Section 4.4.5 of the WASM spec.
. . .
### KWASM
Uses two stacks, values in `<stack>` cell and instructions in `<k>` cell.
- Can access both cells simultaneously, without backtracking/remembering one stack.
- Cleaner semantics, no meta-level context operator needed.
Design Choice: Incremental Semantics
------------------------------------
- KWASM semantics are given incrementally.
- Makes it possible to execute program fragments.
- Allows users to quickly experiment with WASM using KWASM.
. . .
For example, KWASM will happily execute the following fragment (without an enclosing `module`):
```wast
(i32.const 4)
(i32.const 5)
(i32.add)
```
Early Stage Benefits of KWASM
-----------------------------
> - Discovered bugs in spec (one filed on Github, some to come).
> - Suggests clearer presentation of control-flow semantics.
> - Interpreter allowing direct experimentation with program fragments.
Using KWASM (Psuedo-Demo)
=========================
Getting/Building
----------------
Clone the repository:
```sh
git clone 'https://github.com/kframework/wasm-semantics'
cd wasm-semantics
```
Build the dependencies, then the KWASM semantics:
```sh
make deps
make build
```
`kwasm` Script
--------------
The file `./kwasm` is the main runner for KWASM.
### Running `./kwasm help`
```sh
usage: ./kwasm <cmd> <file> <K args>*
# Running
# -------
./kwasm run <pgm> Run a single WASM program
./kwasm debug <pgm> Run a single WASM program in the debugger
...
```
Running a Program
-----------------
### WASM Program `pgm1.wast`
```wasm
(i32.const 4)
(i32.const 5)
(i32.add)
```
### Result of `./kwasm run pgm1.wast`
```k
<generatedTop>
<k>
.
</k>
<stack>
< i32 > 9 : .Stack
</stack>
</generatedTop>
```
Debugging a Program
-------------------
### Run `./kwasm debug pgm1.wast`
```k
== debugging: pgm1.wast
KDebug> s
1 Step(s) Taken.
KDebug> p
<generatedTop>
<k>
i32 . const 4 ~> i32 . const 5 i32 . add .Instrs
</k>
<stack>
.Stack
</stack>
</generatedTop>
```
Debugging a Program (cont.)
---------------------------
### Take a `s`tep then `p`eek at state
```k
KDebug> s
1 Step(s) Taken.
KDebug> p
<generatedTop>
<k>
i32 . const 5 i32 . add .Instrs
</k>
<stack>
< i32 > 4 : .Stack
</stack>
</generatedTop>
```
Debugging a Program (cont.)
---------------------------
### Take a `s`tep then `p`eek at state
```k
KDebug> s
1 Step(s) Taken.
KDebug> p
<generatedTop>
<k>
i32 . const 5 ~> i32 . add .Instrs
</k>
<stack>
< i32 > 4 : .Stack
</stack>
</generatedTop>
```
Debugging a Program (cont.)
---------------------------
### Take 10 `s`teps then `p`eek at state
```k
KDebug> s 10
Attempted 10 step(s). Took 4 steps(s).
Final State Reached
KDebug> p
<generatedTop>
<k>
.
</k>
<stack>
< i32 > 9 : .Stack
</stack>
</generatedTop>
```
Future Directions
=================
Finish KWASM
------------
The semantics are fairly early-stage.
### In progress (1 week out)
- Frame/locals semantics, `call*` and `return` opcodes.
### To be done (1-2 months out)
- Some bitwise operators.
- Everything floating point.
- Tables.
- Memories.
- Modules.
Fork KeWASM
-----------
- eWASM adds the gas metering contract to WASM, but otherwise largely leaves the semantics alone.
- Could we give a direct semantics to gas metering?
- Possibly, perhaps then we could verify that the gas metering contract and the direct gas metering agree.
Verify eWASM Programs
---------------------
- KEVM currently has many verified smart contracts at <https://github.com/runtimeverification/verified-smart-contracts>.
- We similarly would like to build a repository of verified code using KeWASM.
Conclusion
==========
Benefits of \K{} Approach
-------------------------
- Many tools that a PL designer/user would want already exist.
- Powerful (and generic) verification engine "for free".
- Executable specification clears up issues in the existing WASM specification[^similarKEVM].
[^similarKEVM]: Similarly for KEVM, we found several issues/ambiguities in the [YellowPaper](https://github.com/ethereum/yellowpaper).
Questions?
----------
Thanks for listening!
References
----------
\tiny

View file

@ -0,0 +1,773 @@
---
title: 'Semantics of WebAssembly in the K framework'
subtitle: 'What is KWasm?'
author:
- Rikard Hjort \tiny supervised by Magnus Myreen \normalsize
- Everett Hildenbrandt
abstract:
WebAssembly is a low-ish-level language designed to run efficiently on all modern platforms. The Ethereum blockchain currently runs on its own virtual machine (the EVM) but is expected to move to use WebAssembly in the future. The K framework definition of the EVM has become the de facto standard for verifying properties of smart contracts, most notably the Dai coin contracts, currently handling over 100 million USD of value. Since we want to verify Ethereum contracts compiled to WebAssembly -- as well as other WebAssembly programs -- we need to have a K formalization of WebAssembly. That is what I've been working on.
I will be giving a hands-on talk on WebAssembly, how to define languages in K, and how we have been translating the official WebAssembly specification into K."
date: March 25, 2019
institute:
- Chalmers University of Technology
- Runtime Verification, Inc.
theme: metropolis
fontsize: 8pt
header-includes:
- \newcommand{\instr}{instr}
- \newcommand{\STORE}{\textit{S}}
- \newcommand{\FRAME}{\textit{F}}
- \newcommand{\CONST}{\texttt{const~}}
- \newcommand{\DATA}{\texttt{data}}
- \newcommand{\FUNCS}{\texttt{funcs}}
- \newcommand{\GLOBALS}{\texttt{globals}}
- \newcommand{\GROW}{\texttt{grow}}
- \newcommand{\ITHREETWO}{\texttt{i32}}
- \newcommand{\LOCALS}{\texttt{locals}}
- \newcommand{\MEMADDRS}{\texttt{memaddrs}}
- \newcommand{\MEMORY}{\texttt{memory}}
- \newcommand{\MEMS}{\texttt{mems}}
- \newcommand{\MODULE}{\texttt{module}}
- \newcommand{\SIZE}{\texttt{size}}
- \newcommand{\TABLES}{\texttt{tables}}
- \newcommand{\with}{\textit{~with~}}
- \newcommand{\stepto}{~\hookrightarrow~}
- \newcommand{\wif}[1]{\text{if}~#1}
- \newcommand{\diminish}[1]{\begin{footnotesize}#1\end{footnotesize}}
---
Video
-----
This talk was recoreded, and is available in full at <https://www.youtube.com/watch?v=V6tOYuneMqo>
Overview
--------
1. Background
2. Introduction to K
3. Introduction to WebAssembly (Wasm)
4. Demo: implement a Wasm subset
5. (Proving things)
Background
==========
Smart contracts and formal methods
------------
![](media/img/ethereum.png){ width=65%}
- Blockchain technology, **smart contracts** in particular, caught my interest.
- Public, immutable code handling lots of money? Great area of application for formal methods!
Existing projects
-------
![](media/img/maker.png){ width=20% align=center style="margin-bottom:40px"}
- Contacted friends at MakerDAO.
- They have verified the core contracts of their "stablecoin", Dai.
. . .
![](media/img/dapphub.png){ width=40% align=center style="margin-bottom:40px"}
- The verification was largely done by a related organization, DappHub ...
. . .
![](media/img/k.png){ height=15% hspace=30px } &nbsp;&nbsp;&nbsp;
![](media/img/RV-logo-blue.eps){ height=15% hspace=30px } &nbsp;&nbsp;&nbsp;
<!-- ![](media/img/rv.png){ height=15%} -->
- ... using the K framework.
<!--
Verifying Ethereum contracts
---------
1. Contracts are compiled to Ethereum virtual machine (EVM) bytecode.
2. Some property or invariant is specified as a rewrite rule.
3. K tries to construct a proof (using the SMT solver Z3) that every possible execution path eventually rewrites to the correct thing
4. The tool KLab (by DappHub) offers an interactive view of execution paths, great for seeing where and why the prover failed.
-->
The KEVM
--------
![](media/img/kevm-paper.png)
- Verification made possible by KEVM [@hildenbrandt-saxena-zhu-rosu-k-evm], modelling the EVM.
- The EVM is a stack machine with $\approx$ 120 opcodes.
- Everett had begun work on a K specification of another low-level languge: Wasm.
ewasm
----
**Rationale**
How would ewasm be better than EVM?
. . .
- Speed
- Size
- Security
- Write contracts in C/C++, go, or rust
- Static analysis
- Optional metering
- Portability: ewasm contracts will be compatibile with any standard Wasm environment, including IoT and mobile devices
(Brief) Introduction to K
==========================
The Vision: Language Independence
---------------------------------
![K Tooling Overview](media/img/k-overview.png)
K Tooling/Languages
-------------------
### Tools
- Parser
- Interpreter
- Debugger
- Reachability Logic Prover [@stefanescu-park-yuwen-li-rosu-reachability-prover]
. . .
### Languages
- Java 1.4 - 2015 [@bogdanas-rosu-k-java]
- C11 - 2015 [@hathhorn-ellison-rosu-k-c]
- KJS - 2015 [@park-stefanescu-rosu-k-js]
- KEVM - 2018 [@hildenbrandt-saxena-zhu-rosu-k-evm]
- P4K - 2018 [@kheradmand-rosu-k-p4]
- KIELE - 2018 [@kasampalis-guth-moore-rosu-johnson-k-iele]
- KLLVM <https://github.com/kframework/llvm-semantics>
- KX86-64 <https://github.com/kframework/X86-64-semantics>
Parts of a K specification
--------------------------
A language spec in K consists of 3 things
* Syntax
* Configuration ("state")
* Operational semantics as **rewrite rules**
K Specifications: Syntax
------------------------
Concrete syntax built using EBNF style:
```k
syntax Exp ::= Int | Id | "(" Exp ")" [bracket]
| Exp "*" Exp
> Exp "+" Exp // looser binding
syntax Stmt ::= Id ":=" Exp
| Stmt ";" Stmt
| "return" Exp
```
. . .
This would allow correctly parsing programs like:
```imp
a := 3 * 2;
b := 2 * a + 5;
return b
```
K Specifications: Configuration
-------------------------------
Tell K about the structure of your execution state.
For example, a simple imperative language might have:
```k
configuration <k> $PGM:Program </k>
<env> .Map </env>
<store> .Map </store>
```
. . .
> - `<k>` will contain the initial parsed program
> - `<env>` contains bindings of variable names to store locations
> - `<store>` contains bindings of store locations to integers
K Specifications: Transition Rules
----------------------------------
Using the above grammar and configuration:
. . .
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
. . .
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
Example Execution
-----------------
### Program
```imp
a := 3 * 2;
b := 2 * a + 5;
return b
```
### Initial Configuration
```k
<k> a := 3 * 2 ; b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 0 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> a := 6 ~> b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 0 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
### Next Configuration
```k
<k> a ~> b := 2 * [] + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
### Next Configuration
```k
<k> 6 ~> b := 2 * [] + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
### Next Configuration
```k
<k> b := 2 * 6 + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> b := 17 ~> return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 17 </store>
```
Example Execution (cont.)
-------------------------
### Final configuration
```k
<k> return 17 </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 17 </store>
```
(Brief) Introduction to WebAssembly
========
Wasm: your new favorite compile target
----
- It's not web, and it's not assembly, just a great low-ish level language.
- Fast on hardware, but platform agnostic.
- Allows stream compiling.
- Efficient byte format, readable text format.
. . .
- Safety features
- Blocks, loops and breaks, but not arbitrary jumps.
- Allows static validation.
- No implicit casts.
. . .
- Organized in modules *(example coming up)*.
- Can declare functions, allocate and modify their own linear memory, global variables etc.
- Cay export some of its contents.
- Can have `start` functions, which are run when the module is loaded.
. . .
- Wasm is stack-based, but the syntax allows S-expression "folding" *(example coming up)*.
Code fold/unfold
----------------
```scheme
(memory.size) ;; Nullary -- push memory size (i32).
(i64.extend_i32_u) ;; Unary -- i32 ==> i64.
```
. . .
```scheme
(local.get $tmp) ;; Nullary -- push local variable $tmp (i32).
(i64.load8_u) ;; Unary -- load 1 byte from argument address, push.
```
. . .
```scheme
(i64.add) ;; Binary
```
. . .
\vfill{}
becomes
. . .
\vfill{}
```scheme
(i64.add
(i64.extend_i32_u (memory.size))
(i64.load8_u (local.get $tmp)))
```
\vfill{}
. . .
Mix freely! Also OK:
```scheme
(i64.extend_i32_u (memory.size))
(i64.load8_u (local.get $tmp)))
(i64.add)
```
Code example (folded)
---------------------
```scheme
(module
(memory 1)
(func ;; Function descriptors.
(export "myGrowAndStoreFunction")
(param $by i32) (param $put i64) ;; Identifiers: $by and $put.
(result i64)
(local $tmp i32)
;; Body of the function.
(local.set $tmp
(i32.mul (memory.grow (local.get $by)) (i32.const 65536)))
(i64.store (local.get $tmp) (local.get $put))
(i64.add
(i64.extend_i32_u (memory.size))
(i64.load8_u (local.get $tmp)))
) ;; End func.
) ;; End module.
```
Running the example
-------------------
```
$ ./wasm -i myProgram.wast -
wasm 1.0 reference interpreter
> (invoke "myGrowAndStoreFunction" (i32.const 2) (i64.const 0))
3 : i64
```
. . .
```
> (invoke "myGrowAndStoreFunction" (i32.const 2) (i64.const 0))
5 : i64
```
. . .
```
> (invoke "myGrowAndStoreFunction" (i32.const 1) (i64.const -1))
261 : i64
```
Wasm Specification
------------------
Available at <https://github.com/WebAssembly/spec>.
- Fairly unambiguous[^betterThanEVM].
- Well written with procedural description of execution accompanied by small-step semantic rules.
[^betterThanEVM]: Better than the [YellowPaper](https://github.com/ethereum/yellowpaper).
Wasm Specification
------------------
Semantic rule:
$$
\STORE; \FRAME; \MEMORY.\SIZE \stepto \STORE; \FRAME; (\ITHREETWO.\CONST sz)
$$
$$
(\wif{|\STORE.\MEMS[\FRAME.\MODULE.\MEMADDRS[0]].\DATA| = sz * 64 Ki)}
$$
. . .
*store* ($\STORE$) and a *current frame* ($\FRAME$):
\begin{alignat*}{5}
%&store &::=~&\{ & \quad &\FUNCS ~&\quad &funcinst^* & \\
&store &::=~&\{ & \quad &\dots ~&\quad & & \\
%& & & & \quad &\TABLES ~&\quad &tableinst^* & \\
& & & & \quad &\MEMS ~&\quad &meminst^* & \\
& & & & \quad &\dots ~&\quad & &\} \\
%& & & & \quad &\GLOBALS ~&\quad &globalinst^* \quad &\} \\
&frame &::=~&\{ & \quad &\LOCALS ~&\quad &val^* & \\
& & & & \quad &\MODULE ~&\quad &moduleinst \quad &\} \\
&moduleinst~&::=~&\{~& \quad &\dots ~&\quad & & \\
& & & & \quad &\MEMADDRS ~&\quad &memaddr^* & \\
& & & & \quad &\dots ~&\quad & &\}
\end{alignat*}
Wasm Specification (cont.)
--------------------------
**Example rule:** `(memory.size)`
. . .
Execution description:
1. Let $\FRAME$ be the current frame.
2. \diminish{Assert: due to validation, $\FRAME.\MODULE.\MEMADDRS[0]$ exists.}
3. Let $a$ be the memory address $\FRAME.\MODULE.\MEMADDRS[0]$.[^memIdxZero]
4. \diminish{Assert: due to validation, $\STORE.\MEMS[a]$ exists.}
5. Let $mem$ be the memory instance $\STORE.\MEMS[a]$.
6. Let $sz$ be the length of $mem.\DATA$ divided by the page size.
7. Push the value $\ITHREETWO.\CONST sz$ to the stack.
[^memIdxZero]: Every module in Wasm has a single memory for now, so we always implicitly work on `memaddrs[0]`.
Demo: implement a Wasm subset
=============================
`(memory.size)`
---------------
$$
\STORE ; \FRAME ; \MEMORY.\SIZE \stepto \STORE ; \FRAME ; (\ITHREETWO.\CONST sz)
$$
$$
\wif {
|\STORE.\MEMS[\FRAME.\MODULE.\MEMADDRS[0]].\DATA| = sz \cdot 64 Ki
}
$$
\vfill{}
\begin{alignat*}{5}
%&store &::=~&\{ & \quad &\FUNCS ~&\quad &funcinst^* & \\
&store &::=~&\{ & \quad &\dots ~&\quad & & \\
%& & & & \quad &\TABLES ~&\quad &tableinst^* & \\
& & & & \quad &\MEMS ~&\quad &meminst^* & \\
& & & & \quad &\dots ~&\quad & &\} \\
%& & & & \quad &\GLOBALS ~&\quad &globalinst^* \quad &\} \\
&frame &::=~&\{ & \quad &\LOCALS ~&\quad &val^* & \\
& & & & \quad &\MODULE ~&\quad &moduleinst \quad &\} \\
&moduleinst~&::=~&\{~& \quad &\dots ~&\quad & & \\
& & & & \quad &\MEMADDRS ~&\quad &memaddr^* & \\
& & & & \quad &\dots ~&\quad & &\}
\end{alignat*}
`(memory.grow)`
---------------
\begin{align*}
\STORE; \FRAME ; &(\ITHREETWO.\CONST n)~(\MEMORY.\GROW) \stepto \STORE ' ; \FRAME ; (\ITHREETWO.\CONST sz ) \\
(&\wif{\FRAME.\MODULE.\MEMADDRS[0] = a \\
&\land sz = |\STORE.\MEMS[a].\DATA|/64 Ki \\
&\land \STORE ' = \STORE \with \MEMS[a] = growmem(\STORE.\MEMS[a], n))} \\
\\
\STORE; \FRAME ; &(\ITHREETWO.\CONST n)~(\MEMORY.\GROW) \stepto \STORE ; \FRAME ; (\ITHREETWO.\CONST {-1} ) \\
\end{align*}
Future Directions
=================
Finish KWasm
------------
The semantics are fairly early-stage.
### In progress
- Memories.
### To be done
- Everything floating point.
- Tables.
- Modules.
KeWasm
------
- eWasm adds gas metering to Wasm, but otherwise leaves the semantics alone.
\vfill{}
. . .
- KEVM currently has many verified smart contracts at <https://github.com/runtimeverification/verified-smart-contracts>.
- We similarly would like to build a repository of verified code using KeWasm.
Conclusion/Questions?
=====================
References
----------
- Thanks for listening!
\tiny
<!--
SCRATCH
=======
* Explain semantics of imp, that Id's always refer to unique identifiers.
* Replace `loop` example with own example (smth you wrote)
KWasm Design
============
Design Choice: Incremental Semantics
------------------------------------
- KWasm semantics are given incrementally.
- Makes it possible to execute program fragments.
- Allows users to quickly experiment with Wasm using KWasm.
\vfill{}
. . .
For example, KWasm will happily execute the following fragment (without an enclosing `module`):
```wast
(i32.const 4)
(i32.const 5)
(i32.add)
```
Using KWasm (Psuedo-Demo)
=========================
Getting/Building
----------------
Clone the repository:
```sh
git clone 'https://github.com/kframework/wasm-semantics'
cd wasm-semantics
```
Build the dependencies, then the KWasm semantics:
```sh
make deps
make build
```
`kwasm` Script
--------------
The file `./kwasm` is the main runner for KWasm.
### Running `./kwasm help`
```sh
usage: ./kwasm (run|test) [--backend (ocaml|java|haskell)] <pgm> <K args>*
./kwasm prove [--backend (java|haskell)] <spec> <K args>*
./kwasm klab-(run|prove) <spec> <K args>*
./kwasm run : Run a single WebAssembly program
./kwasm test : Run a single WebAssembly program like it's a test
./kwasm prove : Run a WebAssembly K proof
Note: <pgm> is a path to a file containing a WebAssembly program.
<spec> is a K specification to be proved.
<K args> are any arguments you want to pass to K when executing/proving.
```
Running a Program
-----------------
### Wasm Program `pgm1.wast`
```wasm
(i32.const 4)
(i32.const 5)
(i32.add)
```
### Result of `./kwasm run pgm1.wast`
```k
<generatedTop>
<k>
.
</k>
<stack>
< i32 > 9 : .Stack
</stack>
</generatedTop>
```
Conclusion/Questions?
=====================
-->

View file

@ -0,0 +1,375 @@
---
title: 'Intro to K, KEVM, and KWasm'
author:
- Everett Hildenbrandt
- Rikard Hjort
date: '\today'
institute:
- Runtime Verification, Inc.
- Chalmers University of Technology
theme: metropolis
fontsize: 8pt
header-includes:
- \newcommand{\instr}{instr}
- \newcommand{\LOOP}{\texttt{loop}}
- \newcommand{\LABEL}{\texttt{label}}
- \newcommand{\END}{\texttt{end}}
- \newcommand{\stepto}{\hookrightarrow}
---
Overview
--------
1. Introduction to K
2. KEVM
2. KWasm
2. Reachability Logic Prover
4. Future Directions
(Brief) Introduction to K
=========================
K Vision
--------
![K Overview](media/img/k-overview.png)
K Tooling/Languages
-------------------
### Tools
- Parser
- Interpreter
- Debugger
- Reachability Logic Prover [@stefanescu-park-yuwen-li-rosu-reachability-prover]
. . .
### Languages
- Java 1.4 - 2015 [@bogdanas-rosu-k-java]
- C11 - 2015 [@hathhorn-ellison-rosu-k-c]
- KJS - 2015 [@park-stefanescu-rosu-k-js]
- KEVM - 2018 [@hildenbrandt-saxena-zhu-rosu-k-evm]
- P4K - 2018 [@kheradmand-rosu-k-p4]
- KIELE - 2018 [@kasampalis-guth-moore-rosu-johnson-k-iele]
- KLLVM <https://github.com/kframework/llvm-semantics>
- KX86-64 <https://github.com/kframework/X86-64-semantics>
K Specification: The Components
-------------------------------
- **Syntax** of your language (term algebra of programs).
- **Configuration** of your language (term algebra of program states).
- **Rules** describing small-step operational semantics of your language.
K Specifications: Syntax
------------------------
Concrete syntax built using EBNF style:
```k
syntax Exp ::= Int | Id | "(" Exp ")" [bracket]
| Exp "*" Exp
> Exp "+" Exp // looser binding
syntax Stmt ::= Id ":=" Exp
| Stmt ";" Stmt
| "return" Exp
```
. . .
This would allow correctly parsing programs like:
```imp
a := 3 * 2;
b := 2 * a + 5;
return b
```
K Specifications: Configuration
-------------------------------
Tell K about the structure of your execution state.
For example, a simple imperative language might have:
```k
configuration <k> $PGM:Program </k>
<env> .Map </env>
<store> .Map </store>
```
. . .
> - `<k>` will contain the initial parsed program
> - `<env>` contains bindings of variable names to store locations
> - `<store>` conaints bindings of store locations to integers
K Specifications: Transition Rules
----------------------------------
Using the above grammar and configuration:
. . .
### Variable lookup
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
. . .
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
Example Execution
-----------------
### Program
```imp
a := 3 * 2;
b := 2 * a + 5;
return b
```
### Initial Configuration
```k
<k> a := 3 * 2 ; b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 0 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> a := 6 ~> b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 0 1 |-> 0 </store>
```
Example Execution (cont.)
-------------------------
### Variable assignment
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
### Next Configuration
```k
<k> b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
KEVM
====
What is KEVM?
-------------
- Implementation of EVM in K.
- Passes all of the VMTests and the BlockchainTests/GeneralStateTests.
- Derived interpreter nearly as performant as cpp-ethereum.
- The Jello Paper is derived from KEVM <https://jellopaper.org>.
. . .
- Used for commercial verification by Runtime Verification, Inc.
- Used by DappHub to verify the MKR SCD and MCD core contracts.
- Check out repo of verified smart contracts at <https://github.com/runtimeverification/verified-smart-contracts>.
KWasm Design
============
Wasm Specification
------------------
Available at <https://github.com/WebAssembly/spec>.
- Fairly unambiguous[^betterThanEVM].
- Well written with procedural description of execution accompanied by small-step semantic rules.
\vfill{}
. . .
Example rule:
1. Let $L$ be the label whose arity is 0 and whose continuation is the start of the loop.
2. `Enter` the block $\instr^\ast$ with label $L$.
\vfill{}
. . .
$$
\LOOP~[t^?]~\instr^\ast~\END
\quad \stepto \quad
\LABEL_0\{\LOOP~[t^?]~\instr^\ast~\END\}~\instr^\ast~\END
$$
[^betterThanEVM]: Better than the [YellowPaper](https://github.com/ethereum/yellowpaper).
Translation to K
----------------
### Wasm Spec
\vspace{-1em}
$$
\LOOP~[t^?]~\instr^\ast~\END
\quad \stepto \quad
\LABEL_0\{\LOOP~[t^?]~\instr^\ast~\END\}~\instr^\ast~\END
$$
. . .
### In K
```k
syntax Instr ::= "loop" Type Instrs "end"
// -----------------------------------------
rule <k> loop TYPE IS end
=> IS
~> label [ .ValTypes ] {
loop TYPE IS end
} STACK
...
</k>
<stack> STACK </stack>
```
Design Difference: 1 or 2 Stacks?
---------------------------------
. . .
### Wasm Specification
One stack mixing values and instructions.
- Confusing control-flow semantics (with `label`s).
- Use meta-level context operator to describe semantics of `br`.
- Section 4.4.5 of the Wasm spec.
\vfill{}
. . .
### KWasm
Uses two stacks, values in `<stack>` cell and instructions in `<k>` cell.
- Can access both cells simultaneously, without backtracking/remembering one stack.
- Cleaner semantics, no meta-level context operator needed.
Design Choice: Incremental Semantics
------------------------------------
- KWasm semantics are given incrementally.
- Makes it possible to execute program fragments.
- Allows users to quickly experiment with Wasm using KWasm.
\vfill{}
. . .
For example, KWasm will happily execute the following fragment (without an enclosing `module`):
```wast
(i32.const 4)
(i32.const 5)
(i32.add)
```
Future Steps for KWasm
----------------------
### To be done
- Everything floating point.
- Tables.
- Modules.
### KeWasm
- eWasm adds gas metering to Wasm, but otherwise leaves the semantics alone.
Reachability Logic Prover
=========================
Inference System
----------------
![Reachability Logic Inference System](media/img/reachability-logic-inference-system.png)
- Sound and relatively complete.
- Interesting rules are Circularity/Transitivity, allows coinductive reasoning.
Tool
----
- K Reachability Logic prover accepts proof claims in same format as operational semantics axioms[@stefanescu-park-yuwen-li-rosu-reachability-prover].
- On success will print `#True`, on failure will print symbolic counterexample end-states.
- Added instrumentation allows KLab to provide more useful interface to K Prover <https://github.com/dapphub/klab>.
- Proof search is fully automated, only write the theorem (specification), no manual control over how to discharge it.
Example KWasm Proof
-------------------
Non-overflowing addition operation:
```k
rule <k> ( ITYPE:IValType . const X:Int )
( ITYPE . const Y:Int )
( ITYPE . add )
=> .
...
</k>
<stack> S:Stack => < ITYPE > (X +Int Y) : S </stack>
requires 0 <=Int X andBool 0 <=Int Y
andBool (X +Int Y) <Int #pow(ITYPE)
```
- Program which adds two symbolic numbers `X` and `Y`.
- Don't care about bitwidth (`ITYPE` can be either `i32` or `i64`).
- Add pre-condition that overflow doesn't happen: `(X +Int Y) <Int #pow(ITYPE)`.
Conclusion/Questions?
=====================
References
----------
- Thanks for listening!
\tiny

View file

@ -0,0 +1,862 @@
---
title: 'Semantics of WebAssembly in the \K framework'
author:
- Rikard Hjort[^supervised] \quad \href{mailto:hjort@hjorthjort.xyz}{hjort@hjorthjort.xyz}
abstract:
WebAssembly is a low-ish-level language designed to run efficiently on all modern platforms. The Ethereum blockchain currently runs on its own virtual machine (the EVM) but is expected to move to use WebAssembly in the future. The \K framework definition of the EVM has become the *de facto* standard for verifying properties of smart contracts, most notably the Dai coin contracts, currently handling over 100 million USD of value. Since we want to verify Ethereum contracts compiled to WebAssembly -- as well as other WebAssembly programs -- we need to have a \K formalization of WebAssembly. That is what I've been working on.
I will introduce WebAssembly, how to define languages in \K, and how we have been translating the official WebAssembly specification into \K.\newline
\indent This report is based on a talk, available in full at \newline <https://www.youtube.com/watch?v=V6tOYuneMqo> \newline
date: \today
institute:
- Chalmers University of Technology
- Runtime Verification, Inc.
theme: metropolis
fontsize: 8pt
header-includes:
- \usepackage{amssymb}
- \newcommand{\K}{$\mathbb{K}$~}
- \newcommand{\instr}{instr}
- \newcommand{\STORE}{\textit{S}}
- \newcommand{\FRAME}{\textit{F}}
- \newcommand{\CONST}{\texttt{const~}}
- \newcommand{\DATA}{\texttt{data}}
- \newcommand{\FUNCS}{\texttt{funcs}}
- \newcommand{\GLOBALS}{\texttt{globals}}
- \newcommand{\GROW}{\texttt{grow}}
- \newcommand{\ITHREETWO}{\texttt{i32}}
- \newcommand{\LOCALS}{\texttt{locals}}
- \newcommand{\MAX}{\texttt{max}}
- \newcommand{\MEMADDRS}{\texttt{memaddrs}}
- \newcommand{\MEMORY}{\texttt{memory}}
- \newcommand{\MEMS}{\texttt{mems}}
- \newcommand{\MEMINST}{\textit{meminst}}
- \newcommand{\MODULE}{\texttt{module}}
- \newcommand{\SIZE}{\texttt{size}}
- \newcommand{\TABLES}{\texttt{tables}}
- \newcommand{\with}{\text{~with~}}
- \newcommand{\stepto}{~\hookrightarrow~}
- \newcommand{\wif}[1]{\text{if}~#1}
- \newcommand{\diminish}[1]{\begin{footnotesize}#1\end{footnotesize}}
---
[^supervised]: supervised by Magnus Myreen. Parts of the report, especially the
description of \K, is due to Everett Hildenbrandt. \normalsize
\newpage
Background
==========
The motivation for this project came from wanting work on formal verification on
smart contracts. The combination of immutable, public code and processing high
value assets calls for high assurance software engineering. Looking at what has
been done in that space led me to the company MakerDAO[^maker], which famously
have verified the core contracts or their distrubuted application for the Dai
cryptocurrency[^dai-verified]. Much of this verification effort has been carried
out by, or in collaboration with, the organization DappHub[^dapphub]. When I
reached out to them in turn I was told that the *de facto* standard toolset for
verifying smart contracts is the \K framework[^kframework].
[^maker]: <https://makerdao.com/en/>
[^dai-verified]: <https://medium.com/makerdao/the-code-is-ready-2aee2aa62e73>
[^dapphub]: <https://dapphub.com/>
[^kframework]: <http://www.kframework.org/index.php/Main_Page>
The current state-of-the-art method of verifying Ethereum smart contracts goes
something as follows:
1. Contracts are compiled to Ethereum virtual machine (EVM) bytecode.
2. Some property or invariant is specified as a rewrite rule.
3. \K tries to construct a proof (using the SMT solver Z3) that every possible
execution path eventually rewrites to the correct thing
4. The tool KLab (by DappHub) offers an interactive view of execution paths,
great for seeing where and why the prover failed.
The reason \K can be used for verifying smart contracts is that there is a full
formalization of the Ethereum Virtual Machine (EVM) in \K, called KEVM
[@hildenbrandt-saxena-zhu-rosu-k-evm]. The EVM is a stack machine of
approximately 120 opcodes, many of which are very similar to eachother. The EVM
is currently the only virtual machine available to run Ethereum contracts.
However, an ongoing project in the Ethereum community is trying to migrate to
"ewasm"[^ewasm]: an Ethereum virtual machine built on top of WebAssembly.
[^ewasm]: <https://github.com/ewasm/design>
There are several reasons listed in the design documents:
- Speed
- Size
- Security
- Write contracts in C/C++, go, or rust
- Static analysis
- Optional metering
- Portability: ewasm contracts will be compatibile with any standard Wasm
environment, including IoT and mobile devices
Seeing how Ethereum might migrate to ewasm in the future, there is reason to
start looking into formalizing ewasm in \K. Since ewasm mostly extends
WebAssembly (and only alters WebAssembly slightly to allow for gas metering),
the first step to a formalization of ewasm in \K is a formalization of
WebAssembly. As it turns out, the main author of []KEVM, Everett Hildenbrandt,
has been working on a prototype semantics for WebAssembly. Development was put
on hold due to other projects, so I reached out to Everett and asked if I could
pick up the baton, which he happily let me do. Since January 2019, I have been
adding more of the WebAssembly semantics to the project, with help and guidance
from Everett.
This report will serve as a brief introduction to the \K framework, WebAssembly
and KWasm, our formalization of WebAssembly in \K. A reader familiar with \K
and/or WebAssembly may want to skip ahead.
------
\newpage
# Brief introduction to \K #
The \K framework lets one define programming languages by giving their syntax
and semantics. The vision of \K is to provide a language-independent framework
for obtaining not only parsers, interpreters and compilers, but also more
non-standard tools like documentation, program verifiers, and model
checkers. Once a language is definied, all these tools should follow.
Perhaps the most interesting tool that comes out of a \K specification is the
Reachability Logic Prover [@stefanescu-park-yuwen-li-rosu-reachability-prover].
![\K Tooling Overview](media/img/k-overview.png)
Several large and popular programming languages have been specified in \K, among
them Java 1.4 [@bogdanas-rosu-k-java], C11 [@hathhorn-ellison-rosu-k-c], and
JavaScript [@park-stefanescu-rosu-k-js]. There are ongoing projects to specfy
LLVM[^llvm] and X86 assemby[^x86].
[^llvm]: <https://github.com/kframework/llvm-semantics>
[^x86]: <https://github.com/kframework/X86-64-semantics>
A language specification in \K consists of 3 things:
* the concrete syntax,
* a configuration, which is the exectuion state,
* operational semantics as rewrite rules on the configuration.
## \K Specifications: Syntax ##
The concrete syntax is built using EBNF style. As a running example, we use a
small, imperative language.
```k
syntax Exp ::= Int | Id | "(" Exp ")" [bracket]
| Exp "*" Exp
> Exp "+" Exp // looser binding
syntax Stmt ::= Id ":=" Exp
| Stmt ";" Stmt
| "return" Exp
```
This would allow correctly parsing programs like:
```imp
a := 3 * 2;
b := 2 * a + 5;
return b
```
## \K Specifications: Configuration ##
The configuration gives the structure of the execution state.
For example, a simple imperative language might have the following
configuration.
```k
configuration <k> $PGM:Program </k>
<env> .Map </env>
<store> .Map </store>
```
- `<k>` will contain the initial parsed program
- `<env>` contains bindings of variable names to store locations
- `<store>` contains bindings of store locations to integers
Note that the parsed program gets put into the `<k>` cell. Thus, the program is
part of the execution state, and can be reduced or extended as part of rewrites.
## \K Specifications: Transition Rules ##
The actual operational semantics of a \K program is written as rewrite rules.
For example, we might define the following semantic rules for our imperative
language.
A transition rule takes the form
```k
rule <cellA> X => Y </cellA>
<cellB> Z => W </cellB>
```
where the arrow represents a rewrite: the value to the left is rewritten to the
value on the right. The above rule says that "if `<cellA>` contains `X` and
`<cellB>` contains `Z`, change the contents of `<cellA>` to `Y` and the contents
of `<cellB>` to `W`." A rule is an atomic transition, so both cells are
rewritten simultaneously. The rewrite arrows are given inside each cell to make
the rules easier to read and write.
Another feature of the rules is that they only need to mention the parts of the
configuration they match on. If the configuration contains a cell `<cellUnused>`
whose contents we do not want to rewrite, and that we do not want to extract any
information from, it does not have to be mentioned in the rule. Actually,
mentioning unsused cells in a rule is bad practice and makes the configuration
less modular: if we at some point were to remove `<cellUnused>` from the
configuration we would have to also edit rules in which it is not used in any
meaningful way.
Perhaps equally convenient, but less intuitive, is that you can leave out
surrounding cells when writing rules. The rewrite
```k
rule <cellNesting>
<cellInside> X => Y </cellInside>
</cellNesting>
```
can be equivalently written as
```k
rule <cellInside> X => Y </cellInside>
```
This freedom is convenient, since configuration cell can contain many
nestings, but it also increases modularity, as it allows us to nest the cell
`<cellInside>` with more or less context, without having to edit rules for which
the context is irrelevant.
### Syntactic sugar for matching parts of a cell ###
Idiomatically, the `<k>` cell contains an associative
sequencing operator, `~>`, so the `<k>` cell may look something like
```k
<k> STMT1 ~> STMT2 ~> STMT3 ~> MORE_STATEMENTS </k>
```
A rewrite in the `<k>` cell which only cares about the first of these statements
and replaces it by `NEW` (which could be anything) may then look something like
the following, inside a `rule` declaration:
```k
<k> (STMT1 ~> THE_OTHER_STATEMENTS)
=> (NEW ~> THE_OTHER_STATEMENTS)
</k>
```
Equivalently, we can apply the rewrite in place:
```k
<k> (STMT1 => NEW) ~> THE_OTHER_STATEMENTS </k>
```
To avoid the trouble of explicitly mentioning the entire contents of the cell,
there is syntacit sugar that lets us write a rule like this is with ellipses,
which mean "and something else after `~>`".
```k
<k> STMT1 => NEW ... </k>
```
Finally, when the configuration contains a Map, we may look up a key-value pair
with the key `X` using the following syntax:
```k
<cell> MAP_LEFT X |-> SX MAP_RIGHT </env>
```
meaning there is a bunch of map entries to the left, and a bunch of map entries
to the right. `Map`s in \K are defined as either the empty map, `.Map`, the map
of a single entry `X |-> SX`, or the concatenation of two `Maps`, i.e. `syntax
Map ::= Map Map`, the concatenation operator just being a single space in this
case.
Since the map concatenation is commutative and associative, we can match on any
part of the map, wheras when the operator is just associative, like `~>`, it is
only possible to match on the leftmost operand[^assoc-comm].
[^assoc-comm]: The annotations `assoc` and `comm` marks opeartions associative
and commutative.
We can write map lookup in a nicer, sugared form:
```k
<cell> ... X |-> SX ... </env>
```
Rewrites on a `Map` take the form
`Map` rewrites take the following forms.
Adding entries:
```k
<cell> MAP_LEFT (.Map => X |-> SX) MAP_RIGHT </env>
// or
<cell> ... (.Map => X |-> SX) ... </env>
```
Changing values:
```k
<cell> MAP_LEFT X |-> (SX => SX') MAP_RIGHT </env>
// or
<cell> ... X |-> (SX => SX') ... </env>
```
There are other, more idiomatic syntaxes for doing lookups, additions, updates
and deletions, but this syntax is the closest to the underlying definition of
`Map`. The interested reader may see the
[`domains.k`](https://github.com/kframework/k/blob/master/k-distribution/include/builtin/domains.k)
that follows with the K framework.
### Variable lookup ###
When an identifier is the first element in the `<k>` cell, rewrite it to its
corresponding value, by first looking up what store location it is bound to.
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
### Variable assignment ###
Similarly, variable assigment is done by looking up the store location a
variable points to and replacing what is there by the right-hand side of the
assignment.
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
Note that, for this rule to apply, the right-hand side must be an integer
literal, i.e., a fully evaluated expression.
### Example Execution ###
To show how to run the \K semantics, we take the program from before and supply
an initial configuration.
```imp
a := 3 * 2;
b := 2 * a + 5;
return b
```
The configuration already has the variables and store locations initialized. In
a more realistic example of an imperative language, we would need to write
syntax and rules for how to declare and allocate variables, but for this
example, it suffices to assume that the configuration looks as follows.
```k
<k> a := 3 * 2 ; b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 0 1 |-> 0 </store>
```
First, \K (using some auxilliary rules of a language like this) is going to
break out the first statement in the initial list of statements, then break out
the right-hand side of the first assignment, and put it back fully evaluated
(let's not bother with how that is done for now: it's just something some other
rules do). Then, \K sees the following configuration ...
```k
<k> a := 6 ~> b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 0 1 |-> 0 </store>
```
and \K realizes that the following rule matches that configuration ...
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
and applies the rule, giving the next configuration:
```k
<k> b := 2 * a + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
Again, some other rules apply and \K works its magic, until it gets to this
configuration:
```k
<k> a ~> b := 2 * [] + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
What happened was that \K encountered a variable as it tried to evaluate the
right-hand side of the assignment to `b`. So, before it could proceed with the
arithmetic, it needed to get the value of `a`. So \K "heated" `a` and put it
first at the top of the stack, leaving a "hole" in the expression where the
result should be plugged back in.
Now, with a solitary identifier at the head of the `<k>` cell, the following
rule applies ...
```k
rule <k> X:Id => V ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> V ... </store>
```
... leading to this configuration ...
```k
<k> 6 ~> b := 2 * [] + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
... and after plugging back in the result `6` ...
```k
<k> b := 2 * 6 + 5 ; return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
... and again, evaluating the right-hand side to an integer:
```k
<k> b := 17 ~> return b </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 0 </store>
```
After those steps, our assigment rule matches again ...
```k
rule <k> X := I:Int => . ... </k>
<env> ... X |-> SX ... </env>
<store> ... SX |-> (V => I) ... </store>
```
... and we und up with this final configuration
```k
<k> return 17 </k>
<env> a |-> 0 b |-> 1 </env>
<store> 0 |-> 6 1 |-> 17 </store>
```
We take the final configuration to be the result of executing the above program.
If we wanted, we could of course define a further rule that deals with return
statements.
In principle, \K executes programs by taking an initial configuration and
producing a resulting configuration, i.e., the configuration we are left with
when no more rules can be applied. In practice, we often start with an empty
configuration, containing only the very mininal initial values, and end when the
`<k>` cell is completely empty.
-------
\newpage Brief introduction to WebAssembly
========
WebAssembly[@rossberg-web-up-to-speed], commonly known as "Wasm", is a low-ish
level language that is neither web-specific, nor really an assembly language in
the normal sense. Some of the boasted features are
- Fast on modern hardware architectures, but platform agnostic.
- Allows stream compiling.
- Efficient byte format, readable text format.
- Safety features
- Blocks, loops and breaks, but not arbitrary jumps.
- Static validation.
- No implicit casts.
Code fold/unfold
----------------
Wasm comes in both a bytecode format and a text format. I will use the text
format for all my examples.
While Wasm is a stack-based, low-level language, with many of the usual
operations one would expect from a stack based assembly language, Wasm code in
the wild often takes a "folded" structure for increased readbility. The folding
uses S-expression syntax to group operations with their operands.
In a regular stack-based fashion, one might write the following piece of code,
which gets the current memory size (in 64 Kb pages), loads a byte from memory,
and adds the size to the loaded byte.
```scheme
(memory.size) ;; Nullary -- push memory size (i32).
(i64.extend_i32_u) ;; Unary -- i32 ==> i64.
(local.get $tmp) ;; Nullary -- push local variable $tmp (i32).
(i64.load8_u) ;; Unary -- load 1 byte from argument address, push.
(i64.add) ;; Binary
```
Wasm allows us to write the exact same piece of code in the following way.
```scheme
(i64.add
(i64.extend_i32_u (memory.size))
(i64.load8_u (local.get $tmp)))
```
Note how each $n$-ary operation is followed by $n$ (folded) instructions.
We can also mix freely between folded and unfolded syntax. The following is yet
another equivalent way to write the code above:
```scheme
(i64.extend_i32_u (memory.size))
(i64.load8_u (local.get $tmp)))
(i64.add)
```
We are not allowed, however, to fold paritally, i.e., giving the arguemnts to
`add` in this way:
```scheme
(i64.load8_u (local.get $tmp)))
(i64.add (i64.extend_i32_u (memory.size))) ;; Not valid!
```
We either must give all $n$ arguments in the folded way, or none.
Modules
---
All Wasm code is organized in modules, much like some languages organize all
code into classes. A module may declare functions, allocate and modify their own
linear memory, maintain module-global variables, etc. All these assets are
internal to the module by default, but the module may explicitly export them
with a name, which allows other modules to import, and treat as their own, the
assests of other modules.
The following is a simple module showcasing some of the functionality of Wasm by
declaring a memory and two functions that modify memory.
```scheme
(module
(memory 1)
;; The function $init will get run when the module is loaded.
(start $init)
(func $init
;; Put 4 bytes of 1's at the start of the memory.
(i32.store (i32.const 0) (i32.const -1))
)
(func ;; Function descriptors.
(export "myGrowAndStoreFunction")
(param $by i32) (param $put i64)
(result i64)
(local $tmp i32)
;; Body of the function.
;; Grow memory, discard result (old memory size, or -1)
(drop (memory.grow (local.get $by)))
;; Set this variable to the first address
;; in the last page of memory.
(local.set $tmp
(i32.mul
(i32.sub (memory.size) (i32.const 1))
(i32.const 65536)))
;; Store a value in little-endian form at the location $tmp.
(i64.store (local.get $tmp) (local.get $put))
;; Add the
(i64.add
(i64.extend_i32_u (local.get $tmp))
(i64.load8_u (local.get $tmp)))
) ;; End function.
) ;; End module.
```
We can load this module into a reference interpreter[^ref-interpreter] and
invoke the exported function a few times:
[^ref-interpreter]: <https://github.com/WebAssembly/spec/tree/master/interpreter>
```
$ ./wasm -i ../../my_programs/modules.wast -
wasm 1.0 reference interpreter
> (invoke "myGrowAndStoreFunction" (i32.const 2) (i64.const 0))
131072 : i64
> (invoke "myGrowAndStoreFunction" (i32.const 2) (i64.const 0))
262144 : i64
> (invoke "myGrowAndStoreFunction" (i32.const 0) (i64.const -1))
262399 : i64
```
Wasm Specification
------------------
The offical specification is available at <https://github.com/WebAssembly/spec>.
It is well written and contains both a procedural, prose desription of how to
execute every operation, as well as a rewrite-based small-step semantic rules.
For example, the `(memory.size)` operation has the following execution specification:
1. Let $\FRAME$ be the current frame.
2. \diminish{Assert: due to validation, $\FRAME.\MODULE.\MEMADDRS[0]$
exists.}[^validation]
3. Let $a$ be the memory address $\FRAME.\MODULE.\MEMADDRS[0]$.[^memIdxZero]
4. \diminish{Assert: due to validation, $\STORE.\MEMS[a]$ exists.}
5. Let $mem$ be the memory instance $\STORE.\MEMS[a]$.
6. Let $sz$ be the length of $mem.\DATA$ divided by the page size.
7. Push the value $\ITHREETWO.\CONST sz$ to the stack.
[^validation]: Validation happens before execution. In KWasm, we always assume
that code we execute has been validated.
[^memIdxZero]: Every module in Wasm has a single memory for now, so we always
implicitly work on `memaddrs[0]`.
The semantic rewrite rule is as follows.
$$
\STORE; \FRAME; \MEMORY.\SIZE \stepto \STORE; \FRAME; (\ITHREETWO.\CONST sz)
$$
$$
(\wif{|\STORE.\MEMS[\FRAME.\MODULE.\MEMADDRS[0]].\DATA| = sz \cdot 64 Ki)}
$$
Notice the struct-like access to the members of $\STORE$ and $\FRAME$.
The execution state contains a store of all declared modules, their functions,
memories, etc. It also contains a current exectuion frame with all local
variables and the module that the current call happens in. A memory is a byte
vector and an optional, unsigned 32-bit integer defining the maximum size of the
memory. Here's the relevant part of the execution state:
\begin{alignat*}{5}
%&store &::=~&\{ & \quad &\FUNCS ~&\quad &funcinst^* & \\
&store &::=~&\{ & \quad &\dots ~&\quad & & \\
%& & & & \quad &\TABLES ~&\quad &tableinst^* & \\
& & & & \quad &\MEMS ~&\quad &meminst^* & \\
& & & & \quad &\dots ~&\quad & &\} \\
%& & & & \quad &\GLOBALS ~&\quad &globalinst^* \quad &\} \\
&meminst &::=~&\{ & \quad &\DATA ~&\quad &vec(byte) & \\
& & & & \quad &\MAX ~&\quad &u32^? &\} \\
&frame &::=~&\{ & \quad &\LOCALS ~&\quad &val^* & \\
& & & & \quad &\MODULE ~&\quad &moduleinst \quad &\} \\
&moduleinst~&::=~&\{~& \quad &\dots ~&\quad & & \\
& & & & \quad &\MEMADDRS ~&\quad &memaddr^* & \\
& & & & \quad &\dots ~&\quad & &\}
\end{alignat*}
Asterisks indicate an array of 0 or more elements, and a question mark declares
a field as optional.
------
\newpage
KWasm
=====
KWasm is being developed at <https://github.com/kframework/wasm-semantics>.
The high-level goals of KWasm are to give a complete execution semantics of
Wasm, and to serve as a basis for KeWasm.
A complete semantics of Wasm
----------------------------
The goal is to correctly run all valid Wasm programs, i.e., providing semantics
for a superset of Wasm. KWasm may parse programs that are not strictly
syntactically correct Wasm programs, and allows executing standalone script
programs outside of a module.
This approach allows KWasm to run programs outside of modules. For example KWasm
can run the program `(i32.add (i32.const 1) (i32.const 2))` on its own,
producing a value on top of the stack. We consider this to be good from a
pedagogical perspective, and allows us to build the semantics from the bottom
up, starting with simple instructions and moving to modules.
We tend to assume, as a design principle, that any real Wasm program that is
passed to KWasm will first have been validated. From that premise, we can skip
validation steps to keep our semantics simpler.
A basis for KeWasm
------------------
KeWasm should (ideally) only extend KWasm, not modify it. The focus on KeWasm
means that in practice, we may alter the first goal by ignoring floating point
opertions. Implementing correct semantics for floating point arithmetic
according to the Wasm specification is a large undertaking and is not part of
the eWasm semantics, which uses only integer arithmetic, using fixed-point
interpretation to represent fractional numbers.
Future direction
----------------
The semantics are fairly early-stage. Handling memory declaration,
instantiation, load and store are in progress. Everything floating point remains
to be done (but may be pushed until some verification effort requires it). So
does implementing *tables*, a mechanism for having function pointers. Finally,
KWasm does not currently support modules -- as mentioned, we are building the
semantics bottom-up, and modules are the highest-up construct.
KEVM currently has many verified smart contracts at
<https://github.com/runtimeverification/verified-smart-contracts>.
We similarly would like to build a repository of verified code using KeWasm.
Example of a translation from the WebAssembly specification into \K
------------------------------------------------------------------
As a case study, let's look at the `(memory.grow)` operation. It gives an
example of modifying a configuration in `K`, how we diverge from the literal
letter of the Wasm specification for performance reasons, how we use
abstractions and functions to help us develop the semantics, and how we deal
with the few cases of non-determinism in WebAssembly.
### The specification ###
Recall the *store*, *meminst*, *frame* and *modulinst* parts of the runtime
structure:
\begin{alignat*}{5}
%&store &::=~&\{ & \quad &\FUNCS ~&\quad &funcinst^* & \\
&store &::=~&\{ & \quad &\dots ~&\quad & & \\
%& & & & \quad &\TABLES ~&\quad &tableinst^* & \\
& & & & \quad &\MEMS ~&\quad &meminst^* & \\
& & & & \quad &\dots ~&\quad & &\} \\
%& & & & \quad &\GLOBALS ~&\quad &globalinst^* \quad &\} \\
&meminst &::=~&\{ & \quad &\DATA ~&\quad &vec(byte) & \\
& & & & \quad &\MAX ~&\quad &u32^? &\} \\
&frame &::=~&\{ & \quad &\LOCALS ~&\quad &val^* & \\
& & & & \quad &\MODULE ~&\quad &moduleinst \quad &\} \\
&moduleinst~&::=~&\{~& \quad &\dots ~&\quad & & \\
& & & & \quad &\MEMADDRS ~&\quad &memaddr^* & \\
& & & & \quad &\dots ~&\quad & &\}
\end{alignat*}
The following is the semantic rules for `(memory.grow)`. Note that there are two
rules which are not mutually exclusive. This is because growing memory may fail
for reasons unknown to Wasm, i.e., the embdedder has no resources to allocate.
In this situation, the operation returns `-1`. Note also that the other rule is
specified in terms of an auxilliary function, $growmem$. This functon may fail,
in which case that rule isn't appied, and failure happens instead.
\begin{align*}
\STORE; \FRAME ; &(\ITHREETWO.\CONST n)~(\MEMORY.\GROW) \stepto \STORE ' ; \FRAME ; (\ITHREETWO.\CONST sz ) \\
(&\wif{\FRAME.\MODULE.\MEMADDRS[0] = a \\
&\land sz = |\STORE.\MEMS[a].\DATA|/64 Ki \\
&\land \STORE ' = \STORE \with \MEMS[a] = growmem(\STORE.\MEMS[a], n))} \\
\\
\STORE; \FRAME ; &(\ITHREETWO.\CONST n)~(\MEMORY.\GROW) \stepto \STORE ; \FRAME ; (\ITHREETWO.\CONST {-1} ) \\
\end{align*}
\begin{align*}
growmem(meminst, n)\quad =\quad &\MEMINST \with \DATA = \MEMINST.\DATA~(\texttt{0x00})^{n \cdot 64 Ki} \\
&(\wif{len = n + |\MEMINST.\DATA|/64 Ki \\
&\land len \leq 2^{16} \\
&\land (\MEMINST.\MAX = \epsilon \lor len \leq \MEMINST.\MAX)})
\end{align*}
The $\with$ keyword indicates modifying a single field of a structure, in this
case the $\DATA$ field of a $\MEMINST$.
The `(memory.grow)` operation should thus either succeed with the old size of the
memory pushed to the stack, or fail by pushing a `-1` to the stack. Since the
32-bit unsigned interpretation of `-1` is larger than the max allowed memory
size, `2^{16}`, there is no ambiguity here.
In \K
----
This is the full definition of the `(memory.grow)` operation:
```k
syntax Instr ::= "(" "memory.grow" ")"
| "(" "memory.grow" Instr ")"
| "grow" Int
// --------------------------------------------
rule <k>
( memory.grow I:Instr ) =>
I ~> ( memory.grow ) ...
</k>
rule <k> ( memory.grow ) => grow N ... </k>
<stack> < i32 > N : STACK => STACK </stack>
rule <k> grow N
=> < i32 > #if #growthAllowed(SIZE +Int N, MAX)
#then SIZE
#else -1
#fi ... </k>
<memAddrs> 0 |-> ADDR </memAddrs>
<memInst>
<memAddr> ADDR </memAddr>
<mmax> MAX </mmax>
<msize> SIZE => #if #growthAllowed(SIZE +Int N, MAX)
#then SIZE +Int N
#else SIZE
#fi </msize>
...
</memInst>
rule <k> grow N => < i32 > -1 </k>
<deterministicMemoryGrowth>
false
</deterministicMemoryGrowth>
syntax Bool ::= #growthAllowed(Int, MemBound) [function]
// --------------------------------------------------------
rule #growthAllowed(SIZE, .MemBound)
=> SIZE <=Int #maxMemorySize()
rule #growthAllowed(SIZE, I:Int)
=> #growthAllowed(SIZE, .MemBound) andBool SIZE <=Int I
```
The first rule simply unfolds the folded case, and the second rule pops an
operand (which must be of type `i32`, due to validation), and passes it to an
instructon in abstract syntax, `grow`. This instruction is not in Wasm, but
since all syntax defined in \K becomes concrete, it would be possbile to use
this syntax when writing a program for KWasm.
The third and fourth rule implement the semantic rules from the Wasm
specification somewhat faithfully. There is a check made with the help of a
function, `#growthAllowed`, that checks that we don't violate the size
conditions.
A deviation from the official specification is that we store the size of memory
explicitly. The spec, read literally, asks an implementation to allocate full
pages of zereo-bytes when growing (or allocating) memory. For space efficiency,
we store only the size, and represent data as a map from indices to non-zero
bytes.
Finally, we add a flag to the configuration to tell KWasm when to allow
non-deterministic fails. Since we may want to conduct proofs on programs under
the assumption that memory growth does not fail spuriously, we allow a toggle
for this in the configuration, so that proofs can be conducted under different
assumptions. By setting the toggle `<deterministicMemoryGrowth>` to `true`, we
can conduct proofs under the assumption that the foruth rule is never applied.
In the future, we may make this more advanced, by maintaining instead a counter
of available memory pages, decrementing it when memory is allocated or grown,
and stating only allowing the fourth rule when we would run out of memory.
Hopefully this serves as an illustration of how we generally follow the Wasm
specification, but make a few design choices to suit our needs, when we can do
so without changing the semantics of an actual Wasm program.
----
\newpage
References
==========

View file

@ -0,0 +1,394 @@
---
title: 'KWasm'
subtitle: 'Semantics of WebAssembly in the K framework'
author:
- Rikard Hjort
- Everett Hildenbrandt
- Qianyang Peng
date: June 8, 2019
institute:
- Runtime Verification, Inc.
theme: metropolis
fontsize: 8pt
header-includes:
- \usepackage{color}
- \usepackage{fancyvrb}
- \fvset{commandchars=\\\{\}}
- \newcommand{\instr}{instr}
- \newcommand{\STORE}{\textit{S}}
- \newcommand{\FRAME}{\textit{F}}
- \newcommand{\CONST}{\texttt{const~}}
- \newcommand{\LOCALGET}{\texttt{local.get~}}
- \newcommand{\LOCALSET}{\texttt{local.set~}}
- \newcommand{\DATA}{\texttt{data}}
- \newcommand{\FUNCS}{\texttt{funcs}}
- \newcommand{\GLOBALS}{\texttt{globals}}
- \newcommand{\GROW}{\texttt{grow}}
- \newcommand{\ITHREETWO}{\texttt{i32}}
- \newcommand{\LOCALS}{\texttt{locals}}
- \newcommand{\MEMADDRS}{\texttt{memaddrs}}
- \newcommand{\MEMORY}{\texttt{memory}}
- \newcommand{\MEMS}{\texttt{mems}}
- \newcommand{\MODULE}{\texttt{module}}
- \newcommand{\SIZE}{\texttt{size}}
- \newcommand{\TABLES}{\texttt{tables}}
- \newcommand{\with}{\textit{~with~}}
- \newcommand{\stepto}{~\hookrightarrow~}
- \newcommand{\wif}[1]{\text{if}~#1}
- \newcommand{\diminish}[1]{\begin{footnotesize}#1\end{footnotesize}}
---
Overview
--------
> 1. KWasm: Intro and roadmap
> 2. Introduction to K & KEVM
> 3. Deepdive: What the semantics look like
> 4. Demo: Proving things
. . .
\vspace{1em}
Please ask questions as we go.
KWasm: Intro and roadmap
========================
KWasm
-----
> * KWasm is the project name for specifying Wasm in K.
> * K is a framework for creating **runnable specifications** of programming languages.
> * K uses rewrite based semantics, just like those Wasm is defined with [@rossberg-web-up-to-speed].
> * The goal is to use the runnable spec to **formally verify** aspects of blockchain runtimes and smart contracts.
> * There is already a specification of the EVM, called KEVM [@hildenbrandt-saxena-zhu-rosu-k-evm], which we use for formal verification. \newline ![](media/img/kevm-paper.png)
Status
------
![](media/img/github-top-screenshot.png)
* Bulk of the semantics are done.
* Tables and indirect calls in progress.
A few big todos:
- Defining and instantiating several modules.
- Parsing more textual versions of commands (syntactic sugar).
- Add floating point numbers (not top priority).
Design
------
* A very faithful translation of Wasm spec (K and Wasm both use rewrite semantics). Some differences:
- Two stacks: one for operands, one for instructions and control flow.
- We are *more permissive*; allow running instructions directly:
\newline `(i32.add (i32.const 1337) (i32.const 42))` is a full KWasm program.
* Execution focused, we assume validation is done beforehand.
Goals
-----
- "Make KEVM for Ethereum 2.0".
- Create Ewasm semantics, KEwasm, by importing and embedding KWasm.
- We would like to build a repository of verified code using KEwasm.
There is such a repository for KEVM:
[![](media/img/github-verified-contracts-screenshot.png)](https://github.com/runtimeverification/verified-smart-contracts)
Introduction to K
=================
The Vision: Language Independence
---------------------------------
![K Tooling Overview](media/img/k-overview.png)
K Tooling/Languages
-------------------
### Tools
- Parser
- Interpreter
- Debugger
- Reachability Logic Prover [@stefanescu-park-yuwen-li-rosu-reachability-prover]
- ...
. . .
### Languages
- Java 1.4 - 2015 [@bogdanas-rosu-k-java]
- C11 - 2015 [@hathhorn-ellison-rosu-k-c]
- KJS - 2015 [@park-stefanescu-rosu-k-js]
- KEVM - 2018 [@hildenbrandt-saxena-zhu-rosu-k-evm]
- KLLVM <https://github.com/kframework/llvm-semantics>
- KX86-64 <https://github.com/kframework/X86-64-semantics>
- In progress (external groups):
- Solidity <https://github.com/kframework/solidity-semantics>
- Rust
Parts of a K specification
--------------------------
A language spec in K consists of 3 things
* Syntax
* Configuration ("state")
* Operational semantics as **rewrite rules**
K Specifications: Syntax
------------------------
Concrete syntax built using EBNF style:
```k
syntax IValType ::= "i32" | "i64"
syntax Instr ::= "(" IType "." "const" Int ")"
syntax Instr ::= "(" "local.get" Int ")" | "(" "local.set" Int ")"
syntax Instr ::= "(" IValType "." IBinOp ")" // Concrete syntax
| IValType "." IBinOp Int Int // Abstract syntax
syntax IBinOp ::= "div_u"
syntax Instrs ::= List{Instr, ""} // Builtin helper for cons lists.
```
Note: we tend to mix abstract and concrete syntax.
. . .
This would allow parsing a program like this:
```scheme
(local.get 1)
(local.get 0)
(i32.div_u)
(local.set 0)
```
K Specifications: Configuration
-------------------------------
Tell K about the structure of your execution state.
```k
configuration <k> $PGM:Instrs </k>
<valstack> .ValStack </valstack>
<locals> .Map </locals>
```
. . .
> - `<k>` will contain the initial parsed program.
> - `<valstack>` operand stack of `Val` items.
> - `<locals>` a mapping `Int -> Val`
. . .
```k
syntax Val ::= "<" IValType ">" Int
```
K Specifications: Transition Rules
----------------------------------
Using the above grammar and configuration:
. . .
### Push to ValStack
\begin{Verbatim}[]
rule <k> ( ITYPE . const I ) \textcolor{blue}{=>} #chop(< ITYPE > I) \textcolor{blue}{...} </k>
\end{Verbatim}
. . .
> - `=>` is the rewrite arrow.
> - Words in all caps are variables.
> - We match on and rewrite the front of the cell contents, and `...` matches the rest of the cell.
> - We don't need to mention the cells we don't use or modify.
\vfill{}
. . .
\begin{Verbatim}[]
rule <k> \textcolor{blue}{V:Val => \textbf{.}} ... </k>
<valstack> \textcolor{blue}{VALSTACK => V : VALSTACK} </valstack>
\end{Verbatim}
. . .
> - `.` is like $\epsilon$, so rewriting to `.` is erasing.
> - We can rewrite several cells at once.
> - In `<valstack>`, we match on the entire cell.
K Specifications: Transition Rules
----------------------------------
### Helper functions:
\begin{Verbatim}[]
syntax Val ::= #chop ( Val ) \textcolor{blue}{[function]}
// ----------------------------------------
rule #chop(< ITYPE > N) => < ITYPE > (N modInt #pow(ITYPE))
syntax Int ::= #pow ( IValType ) \textcolor{blue}{[function]}
// -------------------------------------------
rule #pow (i32) => 4294967296
\end{Verbatim}
. . .
\vspace{1em}
> - The `[function]` annotation means the rule applies regardless of context.
K Specifications: Transition Rules
----------------------------------
### Binary operators
\begin{Verbatim}[]
rule <k> ( ITYPE . BOP:IBinOp ) => ITYPE . BOP C1 C2 ... </k>
<valstack>
< ITYPE > C2 : < ITYPE > C1 : VALSTACK => VALSTACK
</valstack>
rule <k> ITYPE . div_u I1 I2 => < ITYPE > (I1 /Int I2) ... </k>
\textcolor{blue}{requires I2 =/=Int 0}
rule <k> ITYPE . div_u I1 I2 => undefined ... </k>
\textcolor{blue}{requires I2 ==Int 0}
\end{Verbatim}
\vspace{1em}
- `requires` specifies side conditions.
- We often use K operators specialized by type, e.g. `==Int`
K Specifications: Transition Rules
----------------------------------
### Get local variable
$$
\FRAME ; (\LOCALGET x) \stepto \FRAME ; val
\qquad \textcolor{blue}{(\wif \FRAME.\LOCALS[x] = val)}
$$
. . .
\begin{Verbatim}[]
rule <k> ( local.get INDEX ) => . ... </k>
<valstack> VALSTACK => VALUE : VALSTACK </valstack>
<locals> ... \textcolor{blue}{INDEX |-> VALUE} ... </locals>
\end{Verbatim}
. . .
- `<locals>` is a `Map` (builtin data structure), which is an associative-commutative pair of values. We can put `...` on both sides indictating we are matching *somewhere* in the `Map`.
K Specifications: Transition Rules
----------------------------------
### Set local variable
$$
\FRAME ; (\LOCALSET x) \stepto \FRAME' ; \epsilon
\qquad (\wif \FRAME' = \FRAME \textcolor{blue}{\with \LOCALS[x] = val})
$$
. . .
\begin{Verbatim}[]
rule <k> ( local.set INDEX ) => . ... </k>
<valstack> VALUE : VALSTACK => VALSTACK </valstack>
<locals> ... INDEX |-> \textcolor{blue}{(_ => VALUE)} ... </locals>
\end{Verbatim}
. . .
- `_` is a wildcard (matches any value).
- We can use parentheses to isolate the part of a term we are rewriting, like updating the value part of a map entry.
## Example execution
### We can use KLab to explore execution of our example program.
```scheme
(local.get 1)
(local.get 0)
(i32.div_u)
(local.set 0)
```
with intial configuration
```k
<locals>
0 |-> <i32> 4
1 |-> <i32> 24
</locals>
```
. . .
\vfill
\center\huge DEMO!
Repo tour
=========
Repo layout
---------
* Literate programming style in markdown with K code blocks.
* `wasm.md`: The main part of the semantics
* `data.md`: Some helper data structures.
* `test.md`: Some useful assertion functions.
* `kwasm-lemmas.md`: A trusted base for proving.
Proving
=======
Verifying Wasm programs
----------------------------
> 1. From the KWasm semantics, K generates a parser and a deductive program verifier.
> 2. A verification claim is written like a rewrite rule. `rule A => B` should be read as "`A` will eventually always evaluate to `B`".
> 3. The automatic prover tries to construct a proof (with the help of Z3 to check constraint satisfiability) that every possible execution path starting in `A` eventually rewrites to `B`.
> 4. KLab offers an interactive view of execution, both the successful and the failed paths.
. . .
\vfill
\center\huge DEMO!
RV specializes in formal verification
-------------------------------------
- If you're interested in verification of Wasm programs, talk to us!
- [rikard.hjort@runtimeverification.com](rikard.hjort@runtimeverification.com)
- https://riot.im/app/#/room/#k:matrix.org
Conclusion/Questions?
=====================
Thanks!
-------
- Thanks for listening!
References
----------
\tiny

View file

@ -0,0 +1,687 @@
---
title: 'KWasm'
subtitle: 'Semantics of WebAssembly in the \K framework'
author:
- Rikard Hjort (presenting)
- Everett Hildenbrandt
- Qianyang Peng
- Stephen Skeirik
date: June 8, 2019
institute:
- Runtime Verification, Inc.
theme: metropolis
fontsize: 8pt
header-includes:
- \usepackage{color}
- \usepackage{fancyvrb}
- \usepackage{listings}
- \fvset{commandchars=\\\{\}}
- \newcommand{\instr}{instr}
- \newcommand{\STORE}{\textit{S}}
- \newcommand{\FRAME}{\textit{F}}
- \newcommand{\CONST}{\texttt{const~}}
- \newcommand{\LOCALGET}{\texttt{local.get~}}
- \newcommand{\LOCALSET}{\texttt{local.set~}}
- \newcommand{\DATA}{\texttt{data}}
- \newcommand{\FUNCS}{\texttt{funcs}}
- \newcommand{\GLOBALS}{\texttt{globals}}
- \newcommand{\GROW}{\texttt{grow}}
- \newcommand{\ITHREETWO}{\texttt{i32}}
- \newcommand{\LOCALS}{\texttt{locals}}
- \newcommand{\MEMADDRS}{\texttt{memaddrs}}
- \newcommand{\MEMORY}{\texttt{memory}}
- \newcommand{\MEMS}{\texttt{mems}}
- \newcommand{\MODULE}{\texttt{module}}
- \newcommand{\SIZE}{\texttt{size}}
- \newcommand{\TABLES}{\texttt{tables}}
- \newcommand{\with}{\textit{~with~}}
- \newcommand{\stepto}{~\hookrightarrow~}
- \newcommand{\wif}[1]{\text{if}~#1}
- \newcommand{\diminish}[1]{\begin{footnotesize}#1\end{footnotesize}}
- \newcommand{\K}{$\mathbb{K}$~}
- \newcommand{\lK}{$\mathbb{K}$}
---
Overview
--------
> 1. KWasm: Intro and roadmap
> 2. \K recap
> 3. Proving things
KWasm: Intro and roadmap
========================
KWasm
-----
> * The goal is to use the runnable spec to **formally verify** aspects of blockchain runtimes and smart contracts.
> * The current targets are **Ewasm** (Ethereum smart contract VM) an **Pwasm** (Polkadot client bytecode).
Status
------
![](media/img/github-top-screenshot.png)
* 98 % complete for text format (missing a few floating point instructions).
* We have a full Wasm semantics, and prototype Ewasm and Pwasm **embeddings**.
* No bytecode support (yet), so bytecode is piped through `wasm2wat` tool.
* We have verified a few simple properties, but not yet any "real" programs (in progress with WRC20).
Design
------
* A very faithful translation of Wasm spec (\K and Wasm both use rewrite semantics). Some differences:
- Two stacks: one for operands, one for instructions and control flow.
- We are *more permissive*; allow running instructions directly:
\newline `(i32.add (i32.const 1337) (i32.const 42))` is a full KWasm program.
* Execution focused, we assume validation is done beforehand. No type checking or similar.
* We sometimes need to be more explicit to make rules computational.
Working with \K
=============
The Vision: Language Independence
---------------------------------
\center
\includegraphics[height=0.9\paperheight]{media/img/k-overview}
\K Tooling/Languages
-------------------
### Tools
- Parser
- Interpreter
- Debugger
- Reachability Logic Prover [@stefanescu-park-yuwen-li-rosu-reachability-prover]
- ...
. . .
### Languages
- Java 1.4 - 2015 [@bogdanas-rosu-k-java]
- C11 - 2015 [@hathhorn-ellison-rosu-k-c]
- KJS - 2015 [@park-stefanescu-rosu-k-js]
- KEVM - 2018 [@hildenbrandt-saxena-zhu-rosu-k-evm]
- KLLVM <https://github.com/kframework/llvm-semantics>
- KX86-64 <https://github.com/kframework/X86-64-semantics>
- In progress (external groups):
- Solidity <https://github.com/kframework/solidity-semantics>
- Rust
Parts of a \K specification
--------------------------
A language spec in \K consists of 3 things
* Syntax
* Configuration ("state")
* Operational semantics as **rewrite rules**
<!---
\K Specifications: Syntax
------------------------
Concrete syntax built using EBNF style:
```k
syntax IValType ::= "i32" | "i64"
syntax Instr ::= "(" IType "." "const" Int ")"
syntax Instr ::= "(" "local.get" Int ")" | "(" "local.set" Int ")"
syntax Instr ::= "(" IValType "." IBinOp ")" // Concrete syntax
| IValType "." IBinOp Int Int // Abstract syntax
syntax IBinOp ::= "div_u"
syntax Instrs ::= List{Instr, ""} // Builtin helper for cons lists.
```
. . .
This would allow parsing a program like this:
```scheme
(local.get 1)
(local.get 0)
(i32.div_u)
(local.set 0)
```
\K Specifications: Configuration
-------------------------------
Tell \K about the structure of your execution state.
```k
configuration <k> $PGM:Instrs </k>
<valstack> .ValStack </valstack>
<locals> .Map </locals>
```
. . .
> - `<k>` will contain the initial parsed program.
> - `<valstack>` operand stack of `Val` items.
> - `<locals>` a mapping `Int -> Val`
<!---
. . .
```k
syntax Val ::= "<" IValType ">" Int
```
-->
\K Specifications: Transition Rules
----------------------------------
We have configuration with a few cells.
Among them a `<k>` cell and a `<valstack>` cell.
. . .
### Push to ValStack
\begin{Verbatim}[]
rule <k> ( ITYPE . const I ) \textcolor{blue}{=>} #chop(< ITYPE > I) \textcolor{blue}{...} </k>
\end{Verbatim}
. . .
> - `=>` is the rewrite arrow.
> - Words in all caps are variables.
> - We match on and rewrite the front of the cell contents, and `...` matches the rest of the cell.
> - We don't need to mention the cells we don't use or modify.
\vfill{}
. . .
\begin{Verbatim}[]
rule <k> \textcolor{blue}{V:Val => \textbf{.}} ... </k>
<valstack> \textcolor{blue}{VALSTACK => V : VALSTACK} </valstack>
\end{Verbatim}
. . .
> - `.` is like $\epsilon$, so rewriting to `.` is erasing.
> - We can rewrite several cells at once.
> - In `<valstack>`, we match on the entire cell.
\K Specifications: Transition Rules
----------------------------------
### Helper functions:
\begin{Verbatim}[]
syntax Val ::= #chop ( Val ) \textcolor{blue}{[function]}
// ----------------------------------------
rule #chop(< ITYPE > N) => < ITYPE > (N modInt #pow(ITYPE))
syntax Int ::= #pow ( IValType ) \textcolor{blue}{[function]}
// -------------------------------------------
rule #pow (i32) => 4294967296
\end{Verbatim}
. . .
\vspace{1em}
> - The `[function]` annotation means the rule applies regardless of context.
> - This is also how we write lemmas. A lemma is basically a function rule.
Proving
=======
Verifying Wasm programs
----------------------------
> 1. From the KWasm semantics, \K generates a parser and a deductive program verifier.
> 2. A verification claim is written like a rewrite rule. `rule A => B` should be read as "`A` will eventually always evaluate to `B`".
> 3. The automatic prover tries to construct a proof (with the help of Z3 to check constraint satisfiability) that every possible execution path starting in `A` eventually rewrites to `B`.
> 4. KLab offers an interactive view of execution, both the successful and the failed paths.
The program (pseudo-code)
-----------
```
(func $i64.reverse_bytes(i64 input)
i64 local[1] = 0
i64 local[2] = 0
while True:
if local[1] >= 8:
break
bits = 56 - (local[1] * 8)
local[2] = local[2] + (((input << bits) >> 56) << bits)
local[1] = local[1] + 1
return local[2]
)
```
Specification
-------------
```
rule <k> #wrc20ReverseBytes
~> (i64.load (i32.const ADDR))
(i64.store (i32.const ADDR) (call $i64.reverse_bytes))
=> . ...
</k>
// ...
<mdata> BM => BM' </mdata>
requires #inUnsignedRange(i32, ADDR)
// ...
ensures #get(BM, ADDR +Int 0) ==Int #get(BM', ADDR +Int 7 )
andBool #get(BM, ADDR +Int 1) ==Int #get(BM', ADDR +Int 6 )
andBool #get(BM, ADDR +Int 2) ==Int #get(BM', ADDR +Int 5 )
andBool #get(BM, ADDR +Int 3) ==Int #get(BM', ADDR +Int 4 )
andBool #get(BM, ADDR +Int 4) ==Int #get(BM', ADDR +Int 3 )
andBool #get(BM, ADDR +Int 5) ==Int #get(BM', ADDR +Int 2 )
andBool #get(BM, ADDR +Int 6) ==Int #get(BM', ADDR +Int 1 )
andBool #get(BM, ADDR +Int 7) ==Int #get(BM', ADDR +Int 0 )
```
<!--- TODO continue from here -->
First proof attempt
-------------------
\tiny
```
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 0 ) modInt (2 ^Int 64) >>Int 56 <<Int 0 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 8 ) modInt (2 ^Int 64) >>Int 56 <<Int 8 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 16 ) modInt (2 ^Int 64) >>Int 56 <<Int 16 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 24 ) modInt (2 ^Int 64) >>Int 56 <<Int 24 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 32 ) modInt (2 ^Int 64) >>Int 56 <<Int 32 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 40 ) modInt (2 ^Int 64) >>Int 56 <<Int 40 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 48 ) modInt (2 ^Int 64) >>Int 56 <<Int 48 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 56 ) modInt (2 ^Int 64) >>Int 56 <<Int 56 ) modInt (2 ^Int 64) +Int 0 )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64)
```
\normalsize
> * Each line in the top part represents a byte.
> * The expression is essentially a sum, where the implicit invariant that they can only have `1` bits in separate locations (no addition carry).
First proof attempt
-------------------
Least significant byte in original number, modified the first loop iteration:
```
(((((( #getRange(BM, ADDR, 8) <<Int 56) modInt (2 ^Int 64)) >>Int 56) <<Int 56)
modInt (2 ^Int 64)) +Int 0) modInt (2 ^Int 64)
```
First proof attempt
-------------------
\begin{minipage}[l]{0.40\textwidth}
\begin{tikzpicture}[scale=0.56]
\node (a) {modInt}
child {
node (b) {+Int}
child {
node (d) {modInt}
child {
node (e) {<<Int}
child {
node (g) {>>Int}
child {
node (k) {modInt}
child {
node (m) {<<Int}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (o) {\quad 56}
}
}
child {
node (l) {$2^{64}$}
}
}
child {
node (j) {56}
}
}
child {
node (h) {56}
}
}
child {
node (f) {$2^{64}$}
}
}
child {node (i) {0}}}
child {node (c) {$2^{64}$}};
\end{tikzpicture}
\pause
\end{minipage}
\begin{minipage}[r]{0.58\textwidth}
\vspace*{60pt}
\quad\texttt{rule X +Int 0 => X}
\pause
\vspace*{16pt}
\texttt{rule (X modInt M) modInt N => X modInt M}
\texttt{~~requires M >Int 0}
\texttt{~~~andBool M <=Int N}
\pause
\vspace*{16pt}
\texttt{rule (X <<Int N) modInt POW}
\texttt{~ => (X modInt (POW /Int (2 \^{}Int N)))}
\texttt{~~~~ <<Int N}
\texttt{~~ requires N >=Int 0}
\texttt{~~~ andBool POW >Int 0}
\texttt{~~~ andBool POW modInt (2 \^{}Int N) ==Int 0}
\end{minipage}
New state
---------
\begin{minipage}[l]{0.40\textwidth}
\begin{tikzpicture}[scale=0.56]
\node (a) {modInt}
child {
node (b) {+Int}
child {
node (d) {modInt}
child {
node (e) {<<Int}
child {
node (g) {>>Int}
child {
node (k) {modInt}
child {
node (m) {<<Int}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (o) {\quad 56}
}
}
child {
node (l) {$2^{64}$}
}
}
child {
node (j) {56}
}
}
child {
node (h) {56}
}
}
child {
node (f) {$2^{64}$}
}
}
child {node (i) {0}}}
child {node (c) {$2^{64}$}};
\end{tikzpicture}
\end{minipage}
\hfill
\begin{minipage}[c]{0pt}
\vfill
\begin{tikzpicture}
\node (a) { \Huge $\Rightarrow$ };
\end{tikzpicture}
\vfill
\end{minipage}
\hfill
\begin{minipage}[r]{0.4\textwidth}
\begin{tikzpicture}[scale=0.6]
\node (a) {<<Int}
child {
node (e) {modInt}
child {
node (g) {>>Int}
child {
node (m) {<<Int}
child {
node (k) {modInt}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (l) {\quad $2^{8}$}
}
}
child {
node (o) {56}
}
}
child {
node (j) {56}
}
}
child {
node (h) {$2^{8}$}
}
}
child {node (c) {56}};
\end{tikzpicture}
\end{minipage}
Second proof attempt
---------
\uncover<2->{\begin{minipage}[r]{0.52\textwidth}
\vspace*{60pt}
\texttt{rule (X <<Int N) >>Int M}
\texttt{~ => X <<Int (N -Int M)}
\texttt{~~ requires N >=Int M}
\vspace*{16pt}
\uncover<3->{
\texttt{rule \#getRange(BM, ADDR, WIDTH) modInt 256}
\texttt{~ => \#get(BM, ADDR)}
\texttt{~ requires WIDTH =/=Int 0}
\texttt{~~ andBool \#isByteMap(BM)}
\texttt{~ ensures 0 <=Int \#get(BM, ADDR)}
\texttt{~~ andBool \#get(BM, ADDR) <Int 256}
}
\end{minipage}
}
\hfill
\only<1->{\begin{minipage}[r]{0.39\textwidth}
\begin{tikzpicture}[scale=0.6]
\node (a) {<<Int}
child {
node (e) {modInt}
child {
node (g) {>>Int}
child {
node (m) {<<Int}
child {
node (k) {modInt}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (l) {\quad$2^{8}$}
}
}
child {
node (o) {56}
}
}
child {
node (j) {56}
}
}
child {
node (h) {$2^{8}$}
}
}
child {node (c) {56}};
\end{tikzpicture}
\end{minipage}
}
New state
---------
\begin{minipage}[r]{0.4\textwidth}
\begin{tikzpicture}
\node (a) {<<Int}
child {
node (n) {\#get}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
}
child {node (c) {56}};
\end{tikzpicture}
\end{minipage}
\hfill
\begin{minipage}[c]{30pt}
\vfill
\begin{tikzpicture}
\node (a) { \Huge $\Leftarrow$ };
\end{tikzpicture}
\vfill
\end{minipage}
\hfill
\begin{minipage}[r]{0.4\textwidth}
\begin{tikzpicture}[scale=0.6]
\node (a) {<<Int}
child {
node (e) {modInt}
child {
node (g) {>>Int}
child {
node (m) {<<Int}
child {
node (k) {modInt}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (l) {\quad $2^{8}$}
}
}
child {
node (o) {56}
}
}
child {
node (j) {56}
}
}
child {
node (h) {$2^{8}$}
}
}
child {node (c) {56}};
\end{tikzpicture}
\end{minipage}
Conclusion/Questions?
=====================
Thanks!
-------
- Thanks for listening!
- [rikard.hjort@runtimeverification.com](rikard.hjort@runtimeverification.com)
- https://riot.im/app/#/room/#k:matrix.org
References
----------
\tiny

View file

@ -0,0 +1,748 @@
---
title: 'Formally Verifying WebAssembly with KWasm'
subtitle: 'Towards an Automated Prover for Wasm Smart Contracts'
author:
- Rikard Hjort
- \tiny Supervisor$\colon$Thomas Sewell
- \tiny Examiner$\colon$Wolfgang Ahrendt
- \tiny Opponent$\colon$Jakob Larsson
date: January 30, 2020
institute:
- M.Sc. Thesis at Chalmers University of Technology, Gothenburg, Sweden\newline Department of Computer Science and Engineering
- In collaboration with Runtime Verification, Inc., Urbana, IL, United States
abstract: A smart contract is immutable, public bytecode which handles valuable assets. This makes it a prime target for formal methods. WebAssembly (Wasm) is emerging as bytecode format for smart contracts. KWasm is a mechanization of Wasm in the K framework which can be used for formal verification. The current project aims to verify a single smart contract in Wasm by completing unfinished parts of KWasm, making an Ethereum flavored embedding for the Wasm semantics, and incremententally using KWasm for more complex verification, with a focus on heap reasoning. In the process many arithmetic axioms are added to the reasoning capabilities of the K prover.
theme: metropolis
fontsize: 9pt
header-includes:
- \titlegraphic{\begin{flushright}\includegraphics[width=60pt]{media/img/thesis-logo}\end{flushright}}
- \usepackage{color}
- \usepackage{fancyvrb}
- \usepackage{listings}
- \fvset{commandchars=\\\{\}}
- \newcommand{\instr}{instr}
- \newcommand{\STORE}{\textit{S}}
- \newcommand{\FRAME}{\textit{F}}
- \newcommand{\CONST}{\texttt{const~}}
- \newcommand{\LOCALGET}{\texttt{local.get~}}
- \newcommand{\LOCALSET}{\texttt{local.set~}}
- \newcommand{\DATA}{\texttt{data}}
- \newcommand{\FUNCS}{\texttt{funcs}}
- \newcommand{\GLOBALS}{\texttt{globals}}
- \newcommand{\GROW}{\texttt{grow}}
- \newcommand{\ITHREETWO}{\texttt{i32}}
- \newcommand{\LOCALS}{\texttt{locals}}
- \newcommand{\MEMADDRS}{\texttt{memaddrs}}
- \newcommand{\MEMORY}{\texttt{memory}}
- \newcommand{\MEMS}{\texttt{mems}}
- \newcommand{\MODULE}{\texttt{module}}
- \newcommand{\SIZE}{\texttt{size}}
- \newcommand{\TABLES}{\texttt{tables}}
- \newcommand{\with}{\textit{~with~}}
- \newcommand{\stepto}{~\hookrightarrow~}
- \newcommand{\diminish}[1]{\begin{footnotesize}#1\end{footnotesize}}
- \newcommand{\K}{$\mathbb{K}$~}
- \newcommand{\lK}{$\mathbb{K}$}
---
Goals and Motivation
====================
Smart contracts and formal methods
------------
![](media/img/ethereum.png){ width=50%}
- Smart contracts are code that handles money on blockchains.
- Public, immutable, Turing-complete code handling lots of money? Formal verification is worth the effort!
- Ethereum is the most significant platform, with $700+ million locked in financial smart contracts.[^1]
- Currently execute contracts on their own virtual machine, the *EVM*.
WebAssmebly and Ewasm
----
Planned transition to Ewasm as part of Ethereum 2.0 roadmap.
. . .
- WebAssembly (Wasm) is a stack based low-ish level language.
- Separated into modules with their own functions, global variables, linear memory.
. . .
- Fast
- Small byte code
- Safe memory model
- Compile target of C/C++, Go, Rust, etc.
- Portable
. . .
Wasm is designed to be *embedded*: environment interactions specified by each embedding.
. . .
Ewasm is coming, we want to be able to verify the contracts.
. . .
\flushright\footnotesize (... and Wasm has more applications than just smart contracts.)
WRC20
-----
> * WRC20 is a token contract written in Wasm.
> * The capstone of this project would have been to fully verify it.
. . .
* It has two public functions:
- `balanceOf :: address -> i256`
- `transfer :: (address, i256 value) -> i1`
\$i64.reverse\_bytes
--------------------
WRC20 has one helper function:
`reverse_bytes :: i64 -> i64`
. . .
Ethereum sends parameter big-endian encoded, but Wasm uses little-endian.
Parameters and return values need to be converted accordingly.
. . .
In pseudo-code:
```
func $i64.reverse_bytes(i64 input) (result i64) {
i64 i = 0
i64 res = 0
while i < 8 {
bits = 56 - (i * 8)
res = res + (((input << bits) >> 56) << bits)
i++
}
return res
}
```
. . .
A pure Wasm function!
Overview
--------
1. The tool: KWasm
2. Finishing KWasm
* Completing KWasm
* Ewasm embedding
3. Proofs and axioms
* Example: Verifying the helper function
* Axiom engineering
4. Discussion
KWasm as a tool
===============
Why \lK?
--------
* Give \K an operational semantics, and it produces a powerful and automatic reachability logic prover. [@stefanescu-park-yuwen-li-rosu-reachability-prover]
. . .
* Mature
- Complex languages:
- KEVM - 2018 [@hildenbrandt-saxena-zhu-rosu-k-evm]
- Java 1.4 - 2015 [@bogdanas-rosu-k-java]
- C11 - 2015 [@hathhorn-ellison-rosu-k-c]
- KJS - 2015 [@park-stefanescu-rosu-k-js]
- KLLVM <https://github.com/kframework/llvm-semantics>
- KX86-64 <https://github.com/kframework/X86-64-semantics>
. . .
* Uses non-deterministic rewriting, same formalism as Wasm.
. . .
* The forerunner to KWasm, KEVM, has seen significant adoption, and we want to build on the success.
. . .
* KWasm was a mature prototype when we started---no need to start from scratch.
Steps Towards Finishing KWasm
===========================
Completing KWasm
----------------
![](media/img/github-top-screenshot.png)
* Before the project, Wasm lacked support for more than one module.
* Ad-hoc tests.
. . .
We
* added module support.
* wrote a test harness to test KWasm against the official conformance tests.
. . .
Each are a topic of their own, skipping over them in the interest of time.
Ewasm embedding
---------------
\center
![](media/img/ewasm-contract.png){ width=90%}
\flushleft
> * An Ewasm contract exposes a `"main"` function that is invoked when the contract is called.
> * Ethereum state is accessed through imported host functions, 256-bit results are returned in the Wasm memory.
> * The Wasm state is cleared between each invocation, persistent data lives in the blockchain client.
> * Design: Ethereum client (EEI) and Wasm semantics composed into one semantics with a thin boundary between them.
Proofs and Axioms
=====================
Verifying Wasm programs
----------------------------
> 1. From the KWasm semantics, \K generates a parser and a deductive program verifier.
> 2. A verification claim is written like a rewrite rule. `rule A => B` should be read as "`A` will eventually always evaluate to `B`".
> 3. A proof is (hopefully) constructed for the entire rewrite by composing rules in the semantics. Some equalities and side conditions are proved by Z3, the SMT solver.
> 3. The automatic prover tries to construct a proof (with the help of Z3 to check constraint satisfiability) that every possible execution path starting in `A` eventually rewrites to `B`.
> 4. We can help the prover by adding lemmas (which must be proven), or axioms (which are taken for true). These must be expressed as context-free rewrite rules, i.e. they can rewrite expressions but not step the machine.
Verification Example: `reverse_bytes` Program Again
-----------------
```
func $i64.reverse_bytes(i64 input) (result i64) {
i64 i = 0
i64 res = 0
while i < 8 {
bits = 56 - (i * 8)
res = res + (((input << bits) >> 56) << bits)
i++
}
return res
}
```
Specification
-------------
```
rule <k> #wrc20ReverseBytes
~> (i64.load (i32.const ADDR))
(i64.store (i32.const ADDR) (call $i64.reverse_bytes))
=> . ...
</k>
// ...
<mdata> BM => ?BM' </mdata>
requires #inUnsignedRange(i32, ADDR)
// ...
ensures #get(BM, ADDR +Int 0) ==Int #get(?BM', ADDR +Int 7 )
andBool #get(BM, ADDR +Int 1) ==Int #get(?BM', ADDR +Int 6 )
andBool #get(BM, ADDR +Int 2) ==Int #get(?BM', ADDR +Int 5 )
andBool #get(BM, ADDR +Int 3) ==Int #get(?BM', ADDR +Int 4 )
// ...
```
. . .
> * To test reversing bytes, we load from memory (linear byte array), call the function, store the result memory, and compare memory locations.
> * Expressed as a side condition (`ensures`).
> * Uses a simplifying sidecondition (`requires`).
First proof attempt
-------------------
The prover runs the full program, but ends up storing the following symbolic expression back to the memory location.
\tiny
```
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 0 ) modInt (2 ^Int 64) >>Int 56 <<Int 0 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 8 ) modInt (2 ^Int 64) >>Int 56 <<Int 8 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 16 ) modInt (2 ^Int 64) >>Int 56 <<Int 16 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 24 ) modInt (2 ^Int 64) >>Int 56 <<Int 24 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 32 ) modInt (2 ^Int 64) >>Int 56 <<Int 32 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 40 ) modInt (2 ^Int 64) >>Int 56 <<Int 40 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 48 ) modInt (2 ^Int 64) >>Int 56 <<Int 48 ) modInt (2 ^Int 64) +Int
( ( ( #getRange ( BM , ADDR , 8 ) <<Int 56 ) modInt (2 ^Int 64) >>Int 56 <<Int 56 ) modInt (2 ^Int 64) +Int 0 )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64) )
modInt (2 ^Int 64)
```
\normalsize
> * Each line in the top part represents a byte.
> * The expression is essentially a sum, where the implicit invariant that they can only have `1` bits in separate locations (no addition carry).
First proof attempt
-------------------
> * Our approach will be to extend \lK's ability to reason about these kinds of arithmetic expressions, and about the `#getRange` function
> * Axioms will be added as context-free rewrite rules.
> * Each is simple and has been hand-verified, but not yet machine-verified.
<!--
> * We have chosen to add reasoning capabilities to \K over telling Z3 about the structure of memory.
-->
. . .
Least significant byte in original number, modified the first loop iteration:
```
(((((( #getRange(BM, ADDR, 8)
<<Int 56) modInt (2 ^Int 64))
>>Int 56)
<<Int 56) modInt (2 ^Int 64))
+Int 0) modInt (2 ^Int 64)
```
First proof attempt
-------------------
\small
\begin{minipage}[l]{0.40\textwidth}
\begin{tikzpicture}[scale=0.56]
\node (a) {modInt}
child {
node (b) {+Int}
child {
node (d) {modInt}
child {
node (e) {<<Int}
child {
node (g) {>>Int}
child {
node (k) {modInt}
child {
node (m) {<<Int}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (o) {\quad 56}
}
}
child {
node (l) {$2^{64}$}
}
}
child {
node (j) {56}
}
}
child {
node (h) {56}
}
}
child {
node (f) {$2^{64}$}
}
}
child {node (i) {0}}}
child {node (c) {$2^{64}$}};
\end{tikzpicture}
\pause
\end{minipage}
\begin{minipage}[r]{0.58\textwidth}
\vspace*{60pt}
\quad\texttt{rule X +Int 0 => X}
\pause
\vspace*{16pt}
\texttt{rule (X modInt M) modInt N => X modInt M}
\texttt{~~requires M >Int 0}
\texttt{~~~andBool M <=Int N}
\pause
\vspace*{16pt}
\texttt{rule (X <<Int N) modInt POW}
\texttt{~ => (X modInt (POW /Int (2 \^{}Int N)))}
\texttt{~~~~ <<Int N}
\texttt{~~ requires N >=Int 0}
\texttt{~~~ andBool POW >Int 0}
\texttt{~~~ andBool POW modInt (2 \^{}Int N) ==Int 0}
\end{minipage}
New state
---------
\small
\begin{minipage}[l]{0.40\textwidth}
\begin{tikzpicture}[scale=0.56]
\node (a) {modInt}
child {
node (b) {+Int}
child {
node (d) {modInt}
child {
node (e) {<<Int}
child {
node (g) {>>Int}
child {
node (k) {modInt}
child {
node (m) {<<Int}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (o) {\quad 56}
}
}
child {
node (l) {$2^{64}$}
}
}
child {
node (j) {56}
}
}
child {
node (h) {56}
}
}
child {
node (f) {$2^{64}$}
}
}
child {node (i) {0}}}
child {node (c) {$2^{64}$}};
\end{tikzpicture}
\end{minipage}
\hfill
\begin{minipage}[c]{0pt}
\vfill
\begin{tikzpicture}
\node (a) { \Huge $\Rightarrow$ };
\end{tikzpicture}
\vfill
\end{minipage}
\hfill
\begin{minipage}[r]{0.4\textwidth}
\begin{tikzpicture}[scale=0.6]
\node (a) {<<Int}
child {
node (e) {modInt}
child {
node (g) {>>Int}
child {
node (m) {<<Int}
child {
node (k) {modInt}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (l) {\quad $2^{8}$}
}
}
child {
node (o) {56}
}
}
child {
node (j) {56}
}
}
child {
node (h) {$2^{8}$}
}
}
child {node (c) {56}};
\end{tikzpicture}
\end{minipage}
Second proof attempt
---------
\small
\uncover<2->{\begin{minipage}[r]{0.52\textwidth}
\vspace*{60pt}
\texttt{rule (X <<Int N) >>Int M}
\texttt{~ => X <<Int (N -Int M)}
\texttt{~~ requires N >=Int M}
\vspace*{10pt}
\uncover<3->{
\texttt{rule X <<Int 0 => X}
}
\vspace*{10pt}
\uncover<4->{
\texttt{rule \#getRange(BM, ADDR, WIDTH) modInt 256}
\texttt{~ => \#get(BM, ADDR)}
\texttt{~ requires WIDTH =/=Int 0}
\texttt{~~ andBool \#isByteMap(BM)}
\texttt{~ ensures 0 <=Int \#get(BM, ADDR)}
\texttt{~~ andBool \#get(BM, ADDR) <Int 256}
}
\end{minipage}
}
\hfill
\only<1->{\begin{minipage}[r]{0.391\textwidth}
\begin{tikzpicture}[scale=0.6]
\node (a) {<<Int}
child {
node (e) {modInt}
child {
node (g) {>>Int}
child {
node (m) {<<Int}
child {
node (k) {modInt}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (l) {\quad$2^{8}$}
}
}
child {
node (o) {56}
}
}
child {
node (j) {56}
}
}
child {
node (h) {$2^{8}$}
}
}
child {node (c) {56}};
\end{tikzpicture}
\end{minipage}
}
New state
---------
\small
\begin{minipage}[r]{0.4\textwidth}
\begin{tikzpicture}
\node (a) {<<Int}
child {
node (n) {\#get}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
}
child {node (c) {56}};
\end{tikzpicture}
\end{minipage}
\hfill
\begin{minipage}[c]{30pt}
\vfill
\begin{tikzpicture}
\node (a) { \Huge $\Leftarrow$ };
\end{tikzpicture}
\vfill
\end{minipage}
\hfill
\begin{minipage}[r]{0.4\textwidth}
\begin{tikzpicture}[scale=0.6]
\node (a) {<<Int}
child {
node (e) {modInt}
child {
node (g) {>>Int}
child {
node (m) {<<Int}
child {
node (k) {modInt}
child {
node (n) {\#getRange}
child {
node (p) {BM}
}
child {
node (q) {ADDR}
}
child {
node (r) {8}
}
}
child {
node (l) {\quad $2^{8}$}
}
}
child {
node (o) {56}
}
}
child {
node (j) {56}
}
}
child {
node (h) {$2^{8}$}
}
}
child {node (c) {56}};
\end{tikzpicture}
\end{minipage}
Result
-----------------
We finished the proof of the `reverse_bytes` function.
. . .
We ended up adding 32 axioms to KWasm.
* 25 axioms that can be upstreamed into \lK's reasoning capabilities.
* 7 relate to the `#get` and `#set` operations of KWasm, and can be used in any KWasm verification with memory access.
Axiom Engineering
-------
> * \K supports adding new axioms (as rewrite rules), or new statements to Z3, to help the prover.
> * Whenever we can, we choose to add lemmas to KWasm and upstream them into the \K tool when they are general enough.
> * Axioms in \K apply unconditionally, so there is a risk of infinite rewrites. (No commutativity or associativity axioms!)
> * We found a lexicographic product which the axioms always decrease.
. . .
$$
(b, e, n)
$$
- $b$: Number and height in parse tree of certain operations, (for now `mod`, `<<`, and `>>`.)
- $e$: Expression size.
- $n$: Sum of absolute values of integers.
Discussion & Conclusion
=======================
Discussion & Conclusion
-----------------------
> * We are missing the capstone: verifying a smart contract ...
> * ... but pieces are in place: completed KWasm, embedding, memory lemmas, an example of pure Wasm verification.
> * \K needs more arithmetic reasoning, and KWasm is spearheading the process.
Future Work
-----------
> * Verifying the full WRC20 would tie it all together. Started work, but still exploratory.
> * Committee with other \K projects on what axioms we want in general.
> * Proving lemmas sound, possible even using the \K prover itself.
> * More control over the prover, proof assistant style.
> * A DSL for the prover---expressing properties over blockchain state is verbose and error prone without it.
. . .
Open source project[^2], currently funded, development is ongoing.
Thanks!
-------
- Thanks for listening!
- [rikard.hjort@runtimeverification.com](rikard.hjort@runtimeverification.com)
- https://riot.im/app/#/room/#k:matrix.org
\vfill
### References
\tiny
Cover image by Bogdan Stanciu.
[^1]: <https://defipulse.com/>, as of 2020-01-25
[^2]: <https://github.com/kframework/wasm-semantics>

View file

@ -0,0 +1,14 @@
The files in this directory give a demo of a simple Wasm program, and of trying to prove a property of division.
Standing in this directory, do
```sh
./pre-run.sh
```
This will run the `example-execution.wast` file, and perform proofs on the `div*-spec.k` files.
Two of the proofs will fail, so an error message and non-zero exit codes is to be expected.
You can then explore exection and proofs with `./kwasm klab-view [FILENAME]`.
For example, `./kwasm klab-view div3-spec.k`
See the documentation for [KLab](https://github.com/dapphub/klab) for details of how to use KLab

View file

@ -0,0 +1,31 @@
requires "kwasm-lemmas.k"
module DIV1-SPEC
imports WASM
imports KWASM-LEMMAS
// Try to prove an incorrect property of unsigned division:
// If X' = X / Y, then X' < X.
rule <k> (local.set 0
(i32.div_u (local.get 0) (local.get 1)))
// Rewriting to "." means we expect the program to complete
// without trapping.
=> .
</k>
<locals>
0 |-> < i32 > (X => X')
1 |-> < i32 > Y
</locals>
// We must specify that X and Y fulfill the invariant for locals to
// be in the unsigned range. Otherwise, X and Y can be *any* integers.
requires #inUnsignedRange(i32, X)
andBool #inUnsignedRange(i32, Y)
ensures X' <Int X
// Errors cases that will cause the proof to fail:
// * X = 0
// * Y = 0
// * Y = 1
endmodule

View file

@ -0,0 +1,34 @@
requires "kwasm-lemmas.k"
module DIV2-SPEC
imports WASM
imports KWASM-LEMMAS
// Try to prove an incorrect property of unsigned division:
// If X' = X / Y when Y =/= 0
// and X' = 0 otherwise,
// and X > 0,
// then X' < X
rule <k> (if (local.get 1)
(then (local.set 0
(i32.div_u (local.get 0) (local.get 1))))
(else
(local.set 0 (i32.const 0))))
=> .
</k>
<locals>
0 |-> < i32 > (X => X')
1 |-> < i32 > Y
</locals>
requires #inUnsignedRange(i32, X)
andBool #inUnsignedRange(i32, Y)
andBool X =/=Int 0
ensures X' <Int X
// Errors case that will cause the proof to fail:
// * Y = 1
endmodule

View file

@ -0,0 +1,30 @@
requires "kwasm-lemmas.k"
module DIV3-SPEC
imports WASM
imports KWASM-LEMMAS
// Prove a correct property of unsigned division:
// If X' = X / Y when Y =/= 0
// and X' = 0 otherwise,
// and X > 0,
// and Y > 1,
// then X' < X
rule <k> (if (local.get 1)
(then (local.set 0
(i32.div_u (local.get 0) (local.get 1))))
(else
(local.set 0 (i32.const 0))))
=> .
</k>
<locals>
0 |-> < i32 > (X => X')
1 |-> < i32 > Y
</locals>
requires #inUnsignedRange(i32, X)
andBool #inUnsignedRange(i32, Y)
andBool Y =/=Int 1
andBool X =/=Int 0
ensures X' <Int X
endmodule

View file

@ -0,0 +1,34 @@
requires "kwasm-lemmas.k"
module DIV4-SPEC
imports WASM
imports KWASM-LEMMAS
// Prove a correct property of unsigned division:
// If X' = X / Y when Y =/= 0
// and X' = 0 otherwise,
// and X > 0,
// and Y > 1,
// then X' < X
// Has en extra neat demonstration, that the `br_if` will not cause a branch,
// because we know the value of Y at that point.
rule <k> (if (local.get 1)
(then (local.set 0
(i32.div_u (local.get 0) (local.get 1))))
(else
(br_if 0 (local.get 1)) // Will not branch, due to our constraints.
(local.set 0 (i32.const 0))))
=> .
</k>
<locals>
0 |-> < i32 > (X => X')
1 |-> < i32 > Y
</locals>
requires #inUnsignedRange(i32, X)
andBool #inUnsignedRange(i32, Y)
andBool Y =/=Int 1
andBool X =/=Int 0
ensures X' <Int X
endmodule

View file

@ -0,0 +1,6 @@
init_locals < i32 > 4 : < i32 > 24 : .ValStack
(local.get 1)
(local.get 0)
(i32.div_u)
(local.set 0)

View file

@ -0,0 +1,15 @@
#!/bin/bash
cd ../..
rm -rf .build/klab
./build
./kwasm klab-run media/berlin-demo/example-execution.wast
for i in `seq 4`; do
FILE=div$i-spec.k
echo ""
echo ""
echo "$FILE"
./kwasm klab-prove media/berlin-demo/$FILE -m KWASM-LEMMAS
done
cd -

View file

@ -0,0 +1,234 @@
---
csl: media/ieee.csl
references:
- id: bogdanas-rosu-k-java
type: paper-conference
author:
- family: Bogdănaş
given: Denis
- family: Roşu
given: Grigore
issued:
- year: 2015
title: '<span class="nocase">K-Java: A Complete Semantics of Java</span>'
container-title: Proceedings of the 42nd symposium on principles of programming
languages (popl15)
publisher: ACM
page: 445-456
DOI: http://dx.doi.org/10.1145/2676726.2676982
- id: hathhorn-ellison-rosu-k-c
type: paper-conference
author:
- family: Hathhorn
given: Chris
- family: Ellison
given: Chucky
- family: Roşu
given: Grigore
issued:
- year: 2015
title: Defining the undefinedness of c
container-title: Proceedings of the 36th acm sigplan conference on programming language
design and implementation (pldi15)
publisher: ACM
page: 336-345
DOI: http://dx.doi.org/10.1145/2813885.2737979
- id: park-stefanescu-rosu-k-js
type: paper-conference
author:
- family: Park
given: Daejun
- family: Ştefănescu
given: Andrei
- family: Roşu
given: Grigore
issued:
- year: 2015
title: 'KJS: A complete formal semantics of JavaScript'
title-short: KJS
container-title: Proceedings of the 36th acm sigplan conference on programming language
design and implementation (pldi15)
publisher: ACM
page: 346-356
DOI: http://dx.doi.org/10.1145/2737924.2737991
- id: hildenbrandt-saxena-zhu-rosu-k-evm
type: paper-conference
author:
- family: Hildenbrandt
given: Everett
- family: Saxena
given: Manasvi
- family: Zhu
given: Xiaoran
- family: Rodrigues
given: Nishant
- family: Daian
given: Philip
- family: Guth
given: Dwight
- family: Moore
given: Brandon
- family: Zhang
given: Yi
- family: Park
given: Daejun
- family: Ştefănescu
given: Andrei
- family: Roşu
given: Grigore
issued:
- year: 2018
title: 'KEVM: A complete semantics of the ethereum virtual machine'
title-short: KEVM
container-title: 2018 ieee 31st computer security foundations symposium
publisher: IEEE
page: 204-217
- id: kheradmand-rosu-k-p4
type: report
author:
- family: Kheradmand
given: Ali
- family: Roşu
given: Grigore
issued:
- year: 2018
title: 'P4K: A formal semantics of p4 and applications'
title-short: P4K
publisher: University of Illinois at Urbana-Champaign
number: https://arxiv.org/abs/1804.01468
- id: kasampalis-guth-moore-rosu-johnson-k-iele
type: report
author:
- family: Kasampalis
given: Theodoros
- family: Guth
given: Dwight
- family: Moore
given: Brandon
- family: Serbanuta
given: Traian
- family: Serbanuta
given: Virgil
- family: Filaretti
given: Daniele
- family: Rosu
given: Grigore
- family: Johnson
given: Ralph
issued:
- year: 2018
title: 'IELE: An intermediate-level blockchain language designed and implemented
using formal semantics'
title-short: IELE
publisher: University of Illinois
number: http://hdl.handle.net/2142/100320
- id: stefanescu-park-yuwen-li-rosu-reachability-prover
type: paper-conference
author:
- family: Ştefănescu
given: Andrei
- family: Park
given: Daejun
- family: Yuwen
given: Shijiao
- family: Li
given: Yilong
- family: Roşu
given: Grigore
issued:
- year: 2016
month: 11
title: Semantics-based program verifiers for all languages
container-title: Proceedings of the 31th conference on object-oriented programming,
systems, languages, and applications (oopsla16)
publisher: ACM
page: 74-91
DOI: http://dx.doi.org/10.1145/2983990.2984027
- id: alpuente-cuenca-ortega-escobar-meseguer
type: article-journal
author:
- family: Alpuente
given: María
- family: Cuenca-Ortega
given: Angel
- family: Escobar
given: Santiago
- family: Meseguer
given: José
issued:
- year: 2016
title: Partial evaluation of order-sorted equational programs modulo axioms
container-title: CoRR
volume: abs/1608.03424
URL: http://arxiv.org/abs/1608.03424
- id: meseguer-rusu-generalized-rewrite-coherence-completion
type: paper-conference
author:
- family: Meseguer
given: José
editor:
- family: Rusu
given: Vlad
issued:
- year: '2018'
title: Generalized rewrite theories and coherence completion
container-title: Rewriting logic and its applications - 12th international workshop,
wrla 2018, held as a satellite event of etaps, 2018, proceedings
collection-title: Lecture notes in computer science (including subseries lecture
notes in artificial intelligence and lecture notes in bioinformatics)
publisher: Springer-Verlag
page: '164-183'
keyword: Coherence, Rewriting, Symbolic execution, Variants
DOI: 10.1007/978-3-319-99840-4_10
ISBN: '9783319998398'
- id: rossberg-web-up-to-speed
type: article-journal
author:
- family: Rossberg
given: Andreas
- family: Titzer
given: Ben L.
- family: Haas
given: Andreas
- family: Schuff
given: Derek L.
- family: Gohman
given: Dan
- family: Wagner
given: Luke
- family: Zakai
given: Alon
- family: Bastien
given: J. F.
- family: Holman
given: Michael
issued:
- year: 2018
title: Bringing the web up to speed with webassembly.
container-title: Communications of the ACM
page: 107 - 115
volume: '61'
issue: '12'
abstract: This article discusses the implementation of the low-level computer programming
language for software known as WebAssembly. The authors comment on the use of
WebAssembly in advanced web applications, including three-dimensional visualization,
audio and video software, and gaming. The benefits of WebAssembly include its
ability to function independent of the computer hardware and operating system
on which it runs.
keyword: PROGRAMMING languages, ASSEMBLY languages (Electronic computers), COMPUTER
operating systems, COMPUTER software, THREE-dimensional imaging, WEB-based user
interfaces
ISSN: '00010782'
...

View file

@ -0,0 +1,354 @@
<?xml version="1.0" encoding="utf-8"?>
<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0" demote-non-dropping-particle="sort-only">
<info>
<title>IEEE</title>
<id>http://www.zotero.org/styles/ieee</id>
<link href="http://www.zotero.org/styles/ieee" rel="self"/>
<link href="https://www.ieee.org/content/dam/ieee-org/ieee/web/org/conferences/style_references_manual.pdf" rel="documentation"/>
<author>
<name>Michael Berkowitz</name>
<email>mberkowi@gmu.edu</email>
</author>
<contributor>
<name>Julian Onions</name>
<email>julian.onions@gmail.com</email>
</contributor>
<contributor>
<name>Rintze Zelle</name>
<uri>http://twitter.com/rintzezelle</uri>
</contributor>
<contributor>
<name>Stephen Frank</name>
<uri>http://www.zotero.org/sfrank</uri>
</contributor>
<contributor>
<name>Sebastian Karcher</name>
</contributor>
<contributor>
<name>Giuseppe Silano</name>
<email>g.silano89@gmail.com</email>
<uri>http://giuseppesilano.net</uri>
</contributor>
<category citation-format="numeric"/>
<category field="engineering"/>
<category field="generic-base"/>
<updated>2019-01-02T15:01:14+00:00</updated>
<rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
</info>
<locale xml:lang="en">
<terms>
<term name="chapter" form="short">ch.</term>
<term name="presented at">presented at the</term>
<term name="available at">available</term>
</terms>
</locale>
<!-- Macros -->
<macro name="status">
<choose>
<if variable="page issue volume" match="none">
<text variable="status" text-case="capitalize-first" suffix="" font-weight="bold"/>
</if>
</choose>
</macro>
<macro name="edition">
<choose>
<if type="bill book chapter graphic legal_case legislation motion_picture paper-conference report song" match="any">
<choose>
<if is-numeric="edition">
<group delimiter=" ">
<number variable="edition" form="ordinal"/>
<text term="edition" form="short"/>
</group>
</if>
<else>
<text variable="edition" text-case="capitalize-first" suffix="."/>
</else>
</choose>
</if>
</choose>
</macro>
<macro name="issued">
<choose>
<if type="article-journal report" match="any">
<date variable="issued">
<date-part name="month" form="short" suffix=" "/>
<date-part name="year" form="long"/>
</date>
</if>
<else-if type="bill book chapter graphic legal_case legislation motion_picture paper-conference song thesis" match="any">
<date variable="issued">
<date-part name="year" form="long"/>
</date>
</else-if>
<else>
<date variable="issued">
<date-part name="day" form="numeric-leading-zeros" suffix="-"/>
<date-part name="month" form="short" suffix="-" strip-periods="true"/>
<date-part name="year" form="long"/>
</date>
</else>
</choose>
</macro>
<macro name="author">
<names variable="author">
<name and="text" et-al-min="7" et-al-use-first="1" initialize-with=". "/>
<label form="short" prefix=", " text-case="capitalize-first"/>
<et-al font-style="italic"/>
<substitute>
<names variable="editor"/>
<names variable="translator"/>
</substitute>
</names>
</macro>
<macro name="editor">
<names variable="editor">
<name initialize-with=". " delimiter=", " and="text"/>
<label form="short" prefix=", " text-case="capitalize-first"/>
</names>
</macro>
<macro name="locators">
<group delimiter=", ">
<text macro="edition"/>
<group delimiter=" ">
<text term="volume" form="short"/>
<number variable="volume" form="numeric"/>
</group>
<group delimiter=" ">
<number variable="number-of-volumes" form="numeric"/>
<text term="volume" form="short" plural="true"/>
</group>
<group delimiter=" ">
<text term="issue" form="short"/>
<number variable="issue" form="numeric"/>
</group>
</group>
</macro>
<macro name="title">
<choose>
<if type="bill book graphic legal_case legislation motion_picture song" match="any">
<text variable="title" font-style="italic"/>
</if>
<else>
<text variable="title" quotes="true"/>
</else>
</choose>
</macro>
<macro name="publisher">
<choose>
<if type="bill book chapter graphic legal_case legislation motion_picture paper-conference song" match="any">
<group delimiter=": ">
<text variable="publisher-place"/>
<text variable="publisher"/>
</group>
</if>
<else>
<group delimiter=", ">
<text variable="publisher"/>
<text variable="publisher-place"/>
</group>
</else>
</choose>
</macro>
<macro name="event">
<choose>
<if type="paper-conference speech" match="any">
<choose>
<!-- Published Conference Paper -->
<if variable="container-title">
<group delimiter=", ">
<group delimiter=" ">
<text term="in"/>
<text variable="container-title" font-style="italic"/>
</group>
<text variable="event-place"/>
</group>
</if>
<!-- Unpublished Conference Paper -->
<else>
<group delimiter=", ">
<group delimiter=" ">
<text term="presented at"/>
<text variable="event"/>
</group>
<text variable="event-place"/>
</group>
</else>
</choose>
</if>
</choose>
</macro>
<macro name="access">
<choose>
<if type="webpage">
<choose>
<if variable="URL">
<group delimiter=". ">
<text term="online" prefix="[" suffix="]" text-case="capitalize-first"/>
<group delimiter=": ">
<text term="available at" text-case="capitalize-first"/>
<text variable="URL"/>
</group>
<group prefix="[" suffix="]" delimiter=": ">
<text term="accessed" text-case="capitalize-first"/>
<date variable="accessed">
<date-part name="day" form="numeric-leading-zeros" suffix="-"/>
<date-part name="month" form="short" suffix="-" strip-periods="true"/>
<date-part name="year" form="long"/>
</date>
</group>
</group>
</if>
</choose>
</if>
</choose>
</macro>
<macro name="page">
<group>
<label variable="page" form="short" suffix=" "/>
<text variable="page"/>
</group>
</macro>
<macro name="citation-locator">
<group delimiter=" ">
<choose>
<if locator="page">
<label variable="locator" form="short"/>
</if>
<else>
<label variable="locator" form="short" text-case="capitalize-first"/>
</else>
</choose>
<text variable="locator"/>
</group>
</macro>
<!-- Citation -->
<citation collapse="citation-number">
<sort>
<key variable="citation-number"/>
</sort>
<layout delimiter=", ">
<group prefix="[" suffix="]" delimiter=", ">
<text variable="citation-number"/>
<text macro="citation-locator"/>
</group>
</layout>
</citation>
<!-- Bibliography -->
<bibliography entry-spacing="0" second-field-align="flush">
<layout suffix=".">
<!-- Citation Number -->
<text variable="citation-number" prefix="[" suffix="]"/>
<!-- Author(s) -->
<text macro="author" suffix=", "/>
<!-- Rest of Citation -->
<choose>
<!-- Specific Formats -->
<if type="article-journal">
<group delimiter=", ">
<text macro="title"/>
<text variable="container-title" font-style="italic" form="short"/>
<text macro="locators"/>
<text macro="page"/>
<text macro="issued"/>
<text macro="status"/>
</group>
</if>
<else-if type="paper-conference speech" match="any">
<group delimiter=", ">
<text macro="title"/>
<text macro="event"/>
<text macro="issued"/>
<text macro="locators"/>
<text macro="page"/>
<text macro="status"/>
</group>
</else-if>
<else-if type="report">
<group delimiter=", ">
<text macro="title"/>
<text macro="publisher"/>
<group delimiter=" ">
<text variable="genre"/>
<text variable="number"/>
</group>
<text macro="issued"/>
</group>
</else-if>
<else-if type="thesis">
<group delimiter=", ">
<text macro="title"/>
<text variable="genre"/>
<text macro="publisher"/>
<text macro="issued"/>
</group>
</else-if>
<else-if type="webpage post-weblog" match="any">
<group delimiter=", " suffix=". ">
<text macro="title"/>
<text variable="container-title" font-style="italic"/>
<text macro="issued"/>
</group>
<text macro="access"/>
</else-if>
<else-if type="patent">
<group delimiter=", ">
<text macro="title"/>
<text variable="number"/>
<text macro="issued"/>
</group>
</else-if>
<!-- Generic/Fallback Formats -->
<else-if type="bill book graphic legal_case legislation motion_picture report song" match="any">
<group delimiter=", " suffix=". ">
<text macro="title"/>
<text macro="locators"/>
</group>
<group delimiter=", ">
<text macro="publisher"/>
<text macro="issued"/>
<text macro="page"/>
</group>
</else-if>
<else-if type="article-magazine article-newspaper broadcast interview manuscript map patent personal_communication song speech thesis webpage" match="any">
<group delimiter=", ">
<text macro="title"/>
<text variable="container-title" font-style="italic"/>
<text macro="locators"/>
<text macro="publisher"/>
<text macro="page"/>
<text macro="issued"/>
</group>
</else-if>
<else-if type="chapter paper-conference" match="any">
<group delimiter=", " suffix=", ">
<text macro="title"/>
<group delimiter=" ">
<text term="in"/>
<text variable="container-title" font-style="italic"/>
</group>
<text macro="locators"/>
</group>
<text macro="editor" suffix=" "/>
<group delimiter=", ">
<text macro="publisher"/>
<text macro="issued"/>
<text macro="page"/>
</group>
</else-if>
<else>
<group delimiter=", " suffix=". ">
<text macro="title"/>
<text variable="container-title" font-style="italic"/>
<text macro="locators"/>
</group>
<group delimiter=", ">
<text macro="publisher"/>
<text macro="page"/>
<text macro="issued"/>
</group>
</else>
</choose>
</layout>
</bibliography>
</style>

View file

@ -0,0 +1,214 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 325.82197 515.61768"
version="1.1"
id="svg3965"
sodipodi:docname="cover_image.svg"
width="325.82196"
height="515.61768"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<metadata
id="metadata3969">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>KWasm cover Rikard</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1434"
inkscape:window-height="1737"
id="namedview3967"
showgrid="false"
inkscape:zoom="1.1367437"
inkscape:cx="260.6264"
inkscape:cy="259.00241"
inkscape:window-x="1440"
inkscape:window-y="28"
inkscape:window-maximized="0"
inkscape:current-layer="svg3965" />
<defs
id="defs3863">
<style
id="style3861">.a{fill:#231f20;}.b,.e{fill:#fff;}.c,.d,.f{fill:none;}.c,.e{stroke:#231f20;stroke-width:4px;}.c,.d,.e{stroke-miterlimit:10;}.d{stroke:#fff;stroke-width:3px;}</style>
</defs>
<title
id="title3865">KWasm cover Rikard</title>
<path
class="f"
d="m 7.9066164,72.427692 c -4.69,12.62 -7.43999995,31.319998 5.8999996,48.779998 q 1.9,2.47 3.86,4.9 V 53.917692 a 86.63,86.63 0 0 0 -9.7599996,18.51 z"
id="path3905"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 102.95662,4.0076919 c -2.16,0 -4.240004,0.11 -6.200004,0.29 a 93,93 0 0 0 -31.24,9.1400001 h 67.590004 c -9.72,-7.3200001 -20.71,-9.4300001 -30.15,-9.4300001 z"
id="path3907"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 145.46662,20.207692 a 239.82,239.82 0 0 1 21.35,33.07 v -35.84 h -23.6 c 0.78,0.89 1.54,1.77 2.25,2.77 z"
id="path3909"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 60.766616,167.10769 a 172,172 0 0 0 20,12.29 19,19 0 0 1 -5.57,-12.29 z"
id="path3911"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 21.666616,137.00769 v 26.1 h 27.21 a 225.8,225.8 0 0 1 -27.21,-26.1 z"
id="path3913"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 119.30662,54.877692 h -8.84 a 9.8,9.8 0 0 1 1.57,5.43 70.94,70.94 0 0 1 7.27,-5.43 z"
id="path3915"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 178.19662,163.10769 c 5.75,-12.68 7.64,-28.76 5.34,-47.23 a 167.32,167.32 0 0 0 -12.7,-45.069998 v 92.299998 z"
id="path3917"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 94.206616,181.00769 a 15.1,15.1 0 0 0 15.000004,-13.9 H 79.166616 a 15.11,15.11 0 0 0 15.04,13.9 z"
id="path3919"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="M 166.83662,163.10769 V 62.107692 a 232.36,232.36 0 0 0 -24.52,-39.45 49.91,49.91 0 0 0 -4.59,-5.2 H 57.926616 a 128.11,128.11 0 0 0 -14.32,9.54 123.34,123.34 0 0 0 -21.94,21.47 v 82.439998 a 221.17,221.17 0 0 0 33.68,32.2 z m -61.17,-33.48 c -1.37,0 -2.06,-0.76 -2.06,-2.29 a 1.91,1.91 0 0 1 1.57,-1.93 c 0.08,0 1,-0.17 2.78,-0.49 q 0.6,0 0.6,-0.24 c 0,-0.48 -1.08,-2 -3.26,-4.59 l -21.830004,-26.209998 -3,3 v 21.999998 c 0,2.82 0.49,4.63 1.45,5.44 q 1.21,0.84 5.32,0.84 c 2.25,0 3.38,0.73 3.38,2.18 0,1.45 -0.69,2.29 -2,2.29 h -35.37 c -1.37,0 -2,-0.76 -2,-2.29 0,-1.53 1.08,-2.13 3.33,-2.13 q 4.59,0 5.68,-1.08 1.09,-1.08 1.08,-5.44 V 61.207692 q 0,-4.59 -1.81,-5.68 c -0.8,-0.4 -2.9,-0.6 -6.28,-0.6 -1.37,0 -2,-0.77 -2,-2.3 0,-1.53 0.68,-2.17 2,-2.17 h 35.27 c 1.36,0 2,0.72 2,2.17 0,1.45 -1.13,2.3 -3.38,2.3 q -4.59,0 -5.68,1.08 c -0.72,0.73 -1.09,2.54 -1.09,5.44 v 28.26 l 24.760004,-23.67 a 7.77,7.77 0 0 0 2.42,-5.68 5.69,5.69 0 0 0 -1.39,-3.8 4.46,4.46 0 0 0 -3.56,-1.63 c -1.21,0 -1.82,-0.77 -1.82,-2.3 0,-1.53 0.61,-2.17 1.82,-2.17 h 28.26 c 1.36,0 2.05,0.72 2.05,2.17 a 2.16,2.16 0 0 1 -1.93,2.18 c -1.05,0.16 -2.1,0.36 -3.14,0.6 a 29.71,29.71 0 0 0 -10.15,5.92 q -2.06,1.68 -8.81,8.09 l -12.510004,12.03 29.950004,35.859998 c 1.37,1.62 2.69,3.22 4,4.83 1.93,2 3.58,3 4.95,3 1.37,0 2.06,0.73 2.06,2.18 0,1.45 -0.69,2.29 -2.06,2.29 z"
id="path3921"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 92.736616,83.977692 -6.52,6.28 24.520004,29.229998 a 8.38,8.38 0 0 1 2.29,5.19 v 0.53 h 14 l -4.1,-4.95 z"
id="path3923"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 77.036616,54.877692 h -12.31 a 15,15 0 0 1 1.08,6.28 v 57.479998 a 17.47,17.47 0 0 1 -1,6.52 h 12.19 a 15,15 0 0 1 -1.08,-6.28 V 61.397692 a 15.71,15.71 0 0 1 1.12,-6.52 z"
id="path3925"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 41.166616,23.797692 c 3,-2.27 6,-4.39 9.06,-6.36 h -28.56 v 24.77 a 129.27,129.27 0 0 1 19.5,-18.41 z"
id="path3927"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 166.83662,312.26769 v -127.6 c -0.47,0.39 -1,0.78 -1.43,1.15 -9.82,7.53 -22,11.31 -35.71,11.31 -11.7,0 -24.53,-2.74 -38.000004,-8.24 a 168.53,168.53 0 0 1 -37.7,-21.78 h -32.33 v 145.16 z m -27.37,-69.7 16.65,56.81 h -14.4 l -3.37,-11.75 h -14.63 l -2.6,11.75 h -14.08 l 13.75,-56.81 z m -75.660004,0 5.32,28.35 6.4,-28.35 h 12.49 l 5.8,28.84 6.1,-28.84 h 13.850004 l -13.55,56.81 H 87.216616 l -5.78,-28.63 -6.2,28.63 h -13.27 l -12.2,-56.81 z"
id="path3929"
inkscape:connector-curvature="0"
style="fill:none" />
<polygon
class="f"
points="454.93,642.32 451.51,657.69 459.86,657.69 455.93,642.32 "
id="polygon3931"
style="fill:none"
transform="translate(-325.03338,-382.73231)" />
<path
class="f"
d="M 170.83662,316.26769 H 17.666616 v -149.16 h -6.92 v 157 H 177.74662 v -151.79 a 53.12,53.12 0 0 1 -6.91,8.68 z"
id="path3933"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 113.27662,167.10769 a 19.13,19.13 0 0 1 -19.070004,17.9 c -0.55,0 -1.1,0 -1.64,-0.08 l 0.59,0.25 c 28.000004,11.4 52.810004,10.5 69.810004,-2.54 a 46.92,46.92 0 0 0 3.87,-3.31 v -12.22 z"
id="path3935"
inkscape:connector-curvature="0"
style="fill:none" />
<path
class="f"
d="m 176.19662,167.10769 h -5.36 v 7.92 a 52.14,52.14 0 0 0 5.36,-7.92 z"
id="path3937"
inkscape:connector-curvature="0"
style="fill:none" />
<path
d="m 187.46662,115.38769 c -2.19,-17.589998 -7.92,-35.999998 -16.67,-54.219998 v -47.73 h -31.5 c -11,-10.2000001 -25.69,-14.7300001 -42.910004,-13.12000015 -12.82,1.20000005 -26.42,5.76000005 -39.51,13.12000015 h -39.21 v 33.69 a 93.57,93.57 0 0 0 -13.5099996,23.9 c -7.23,19.46 -4.99999995,37.639998 6.4699996,52.589998 q 3.44,4.49 7,8.75 v 30.74 H 6.7466164 v 165 H 181.74662 v -163.14 c 6.13,-13.38 8.16,-30.26 5.72,-49.58 z m -108.300004,51.72 h 30.080004 a 15.09,15.09 0 0 1 -30.080004,0 z m 1.55,12.29 a 172,172 0 0 1 -20,-12.29 h 14.38 a 19,19 0 0 0 5.62,12.29 z m 13.49,5.61 a 19.13,19.13 0 0 0 19.070004,-17.9 h 53.56 v 12.22 a 46.92,46.92 0 0 1 -3.87,3.31 c -17,13 -41.79,13.94 -69.810004,2.54 l -0.59,-0.25 c 0.54,0.05 1.09,0.08 1.64,0.08 z m 76.630004,-17.9 h 5.36 a 52.14,52.14 0 0 1 -5.36,7.92 z m -4,-149.669998 v 35.84 a 239.82,239.82 0 0 0 -21.37,-33.07 c -0.73,-1 -1.49,-1.88 -2.27,-2.77 z M 96.756616,4.2976919 c 2,-0.18 4.000004,-0.29 6.200004,-0.29 9.44,0 20.43,2.11 30.15,9.4300001 H 65.516616 a 93,93 0 0 1 31.24,-9.1400001 z m -75.09,13.1400001 h 28.56 c -3.06,2 -6.09,4.09 -9.06,6.36 a 129.27,129.27 0 0 0 -19.5,18.41 z m 0,31 a 123.34,123.34 0 0 1 21.94,-21.46 128.11,128.11 0 0 1 14.32,-9.54 h 79.800004 a 49.91,49.91 0 0 1 4.59,5.2 232.36,232.36 0 0 1 24.52,39.45 V 163.08769 H 55.346616 a 221.17,221.17 0 0 1 -33.68,-32.18 z m 0,88.539998 a 225.8,225.8 0 0 0 27.21,26.1 h -27.21 z m 32.33,30.1 a 168.53,168.53 0 0 0 37.65,21.78 c 13.520004,5.5 26.350004,8.24 38.000004,8.24 13.73,0 25.89,-3.78 35.71,-11.31 0.48,-0.37 1,-0.76 1.43,-1.15 v 127.6 H 21.666616 v -145.13 z m -40.19,-45.87 C 0.43661645,103.76769 3.2166164,85.067692 7.9066164,72.447692 a 86.63,86.63 0 0 1 9.7599996,-18.51 v 72.169998 q -1.95,-2.45 -3.86,-4.9 z m 163.940004,202.87 H 10.746616 v -157 h 6.92 v 149.19 H 170.83662 v -135.27 a 53.12,53.12 0 0 0 6.91,-8.68 z m 0.45,-161 h -7.36 V 70.807692 a 167.32,167.32 0 0 1 12.7,45.069998 c 2.3,18.47 0.41,34.55 -5.34,47.23 z"
id="path3939"
inkscape:connector-curvature="0" />
<path
class="b"
d="m 117.89662,295.37769 2.6,-11.75 h 20.87 l 3.37,11.75 h 6 l -14.27,-48.81 h -12.53 l -11.82,48.81 z m 8.79,-39.8 h 7.32 l 6,23.38 h -18.54 z"
id="path3941"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<polygon
class="b"
points="408.21,642.17 415.47,678.11 422.07,678.11 433.71,629.3 428.17,629.3 420.48,665.68 417.08,665.68 409.75,629.3 403.74,629.3 395.63,665.24 392.25,665.24 385.5,629.3 379.75,629.3 390.23,678.11 396.99,678.11 404.77,642.17 "
id="polygon3943"
style="fill:#ffffff"
transform="translate(-325.03338,-382.73231)" />
<path
d="m 75.186616,299.37769 6.2,-28.63 5.78,28.63 h 13.000004 l 13.55,-56.81 H 99.896616 l -6.1,28.84 -5.8,-28.84 h -12.53 l -6.4,28.35 -5.32,-28.35 h -14 l 12.2,56.81 z m -14.72,-52.81 6.75,35.94 h 3.38 l 8.11,-35.94 h 6 l 7.33,36.38 h 3.43 l 7.690004,-36.38 h 5.55 l -11.650004,48.81 h -6.59 l -7.27,-35.94 h -3.44 l -7.78,35.94 h -6.76 l -10.5,-48.81 z"
id="path3945"
inkscape:connector-curvature="0" />
<path
d="m 121.11662,299.37769 2.6,-11.75 h 14.63 l 3.37,11.75 h 14.38 l -16.63,-56.81 h -18.68 l -13.75,56.81 z m 15.35,-52.81 14.31,48.81 h -6 l -3.37,-11.75 h -20.94 l -2.6,11.75 h -5.78 l 11.82,-48.81 z"
id="path3947"
inkscape:connector-curvature="0" />
<path
d="m 134.00662,255.57769 h -7.32 l -5.22,23.38 h 18.49 z m -3.11,4 3.93,15.38 h -8.36 l 3.42,-15.38 z"
id="path3949"
inkscape:connector-curvature="0" />
<path
d="m 137.30662,127.33769 c 0,-1.45 -0.69,-2.18 -2.06,-2.18 -1.37,0 -3,-1 -4.95,-3 -1.29,-1.61 -2.61,-3.21 -4,-4.83 l -29.930004,-35.879998 12.560004,-12.08 q 6.75,-6.41 8.81,-8.09 a 29.71,29.71 0 0 1 10.15,-5.92 c 1,-0.24 2.09,-0.44 3.14,-0.6 a 2.16,2.16 0 0 0 1.93,-2.18 c 0,-1.45 -0.69,-2.17 -2.05,-2.17 h -28.26 c -1.21,0 -1.82,0.72 -1.82,2.17 0,1.45 0.61,2.3 1.82,2.3 a 4.46,4.46 0 0 1 3.56,1.63 5.69,5.69 0 0 1 1.39,3.8 7.77,7.77 0 0 1 -2.42,5.68 l -24.710004,23.67 v -28.26 c 0,-2.9 0.37,-4.71 1.09,-5.44 0.72,-0.73 2.62,-1.08 5.68,-1.08 2.25,0 3.38,-0.77 3.38,-2.3 0,-1.53 -0.69,-2.17 -2,-2.17 h -35.37 c -1.37,0 -2,0.72 -2,2.17 0,1.45 0.68,2.3 2,2.3 3.38,0 5.48,0.2 6.28,0.6 q 1.81,1.1 1.81,5.68 v 57.479998 q 0,4.35 -1.08,5.44 -1.08,1.09 -5.68,1.13 c -2.25,0 -3.38,0.73 -3.38,2.18 0,1.45 0.68,2.29 2,2.29 h 35.27 c 1.36,0 2,-0.76 2,-2.29 0,-1.53 -1,-2.18 -3.28,-2.18 q -4.11,0 -5.32,-0.84 c -1,-0.81 -1.45,-2.62 -1.45,-5.44 V 96.927692 l 3,-3 21.860004,26.209998 c 2.18,2.58 3.26,4.11 3.26,4.59 q 0,0.24 -0.6,0.24 c -1.77,0.32 -2.7,0.49 -2.78,0.49 a 1.91,1.91 0 0 0 -1.57,1.93 c 0,1.53 0.69,2.29 2.06,2.29 h 29.58 c 1.39,-0.05 2.08,-0.81 2.08,-2.34 z m -18,-72.459998 a 70.94,70.94 0 0 0 -7.24,5.43 9.8,9.8 0 0 0 -1.57,-5.43 z M 77.036616,125.20769 h -12.19 a 17.47,17.47 0 0 0 1,-6.52 V 61.207692 a 15,15 0 0 0 -1.08,-6.28 h 12.31 a 15.71,15.71 0 0 0 -1.08,6.52 v 57.479998 a 15,15 0 0 0 1.04,6.28 z m 36.000004,0 v -0.48 a 8.38,8.38 0 0 0 -2.29,-5.19 l -24.520004,-29.229998 6.52,-6.28 30.190004,36.179998 4.1,4.95 z"
id="path3951"
inkscape:connector-curvature="0" />
<path
class="a"
d="m 233.12662,385.38769 h 45.41 a 23.68,23.68 0 1 0 0,-4 h -45.41 v -112.5 h 45.41 a 23.68,23.68 0 1 0 0,-4 h -45.41 v -101.78 h -0.08 v -0.39 h -51.36 v 4 h 47.44 v 98.17 0 4 0 112.5 0 4 0 104.56 0 4 h 49.44 a 23.67,23.67 0 1 0 0,-4 h -45.44 z m 69,-21.68 a 19.68,19.68 0 1 1 -19.66,19.68 19.7,19.7 0 0 1 19.66,-19.68 z m 0,-116.5 a 19.68,19.68 0 1 1 -19.66,19.67 19.69,19.69 0 0 1 19.66,-19.67 z m 0,225.07 a 19.68,19.68 0 1 1 -19.66,19.67 19.69,19.69 0 0 1 19.66,-19.67 z"
id="path3957"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<polygon
class="a"
points="625.93,656.99 617.55,647.84 614.6,650.54 626.85,663.91 639.34,639.89 635.79,638.04 "
id="polygon3959"
style="fill:#231f20"
transform="translate(-325.03338,-382.73231)" />
<polygon
class="a"
points="625.93,773.5 617.55,764.34 614.6,767.04 626.85,780.41 639.34,756.39 635.79,754.55 "
id="polygon3961"
style="fill:#231f20"
transform="translate(-325.03338,-382.73231)" />
<polygon
class="a"
points="625.93,882.06 617.55,872.91 614.6,875.61 626.85,888.98 639.34,864.96 635.79,863.11 "
id="polygon3963"
style="fill:#231f20"
transform="translate(-325.03338,-382.73231)" />
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

View file

@ -0,0 +1,3 @@
.krun-*
*-kompiled

View file

@ -0,0 +1,13 @@
Building and running example
```
kompile --syntax-module WASM wasm.k
krun example-memory.wast
```
Proving
```
kompile --syntax-module WASM --backend java wasm.k
kprove memory-spec.k
```

View file

@ -0,0 +1,6 @@
(i64.const 10)
(memory)
(memory.size)
(memory.grow (i32.const 5))
(memory.size)

View file

@ -0,0 +1,35 @@
module MEMORY-SPEC
imports WASM
rule <k> ( memory.size ) => (i32.const SZ) ... </k>
<memAddrs> 0 |-> A </memAddrs>
<memInst>
<memAddr> A </memAddr>
<memSize> SZ </memSize>
</memInst>
rule <k> (memory.grow (i32.const X)) => (i32.const SZ) ...</k>
<memAddrs> 0 |-> A </memAddrs>
<memInst>
<memAddr> A </memAddr>
<memSize> SZ => (SZ +Int X) </memSize>
</memInst>
// Necessary conditions. Remove them to see what happens!
<spuriousMemoryFail> false </spuriousMemoryFail>
requires X >=Int 0
andBool X <=Int (2 ^Int 16)
andBool SZ >=Int 0
rule <k> (memory.grow (i32.const X)) => (i32.const -1) ...</k>
<memAddrs> 0 |-> A </memAddrs>
<memInst>
<memAddr> A </memAddr>
<memSize> SZ </memSize>
</memInst>
// Necessary conditions. Remove them to see what happens!
<spuriousMemoryFail> true </spuriousMemoryFail>
requires X >=Int (2 ^Int 16)
andBool X <Int (2 ^Int 32)
andBool SZ >=Int 0
endmodule

View file

@ -0,0 +1,144 @@
module WASM
imports DOMAINS
// Solutions to the TODOs are in a comment at the bottom of the file.
configuration
<k color="green"> $PGM:Instrs </k>
<stack color="red"> .Stack </stack>
// TODO: Extend the configuration to have a current frame and and a store.
// Useful definitions.
syntax Instrs ::= List{Instr, ""}
// ---------------------------------
rule <k> .Instrs => . </k>
rule <k> I IS:Instrs => I ~> IS ... </k>
syntax Stack ::= ".Stack" | StackItem ":" Stack
syntax StackItem ::= "<" IType ">" Int
// --------------------------------------
syntax IType ::= "i32" | "i64"
// ------------------------------
// Functions.
syntax Int ::= #chop(IType, Int) [function]
// -------------------------------------------
rule #chop(i32, I) => I modInt (2 ^Int 32)
rule #chop(i64, I) => I modInt (2 ^Int 64)
// Basic instructions.
syntax Instr ::= "(" IType "." "const" Int ")"
// ----------------------------------------------
rule <k> ( ITYPE . const I ) => . ... </k>
<stack> S => < ITYPE > #chop(ITYPE, I) : S </stack>
// Memory instructions.
// Memory instantiation.
syntax Instr ::= "(" "memory" ")"
// ---------------------------------
// TODO: Add a rule that:
// allocates memory with size 0 to the store, and stores saves the address
// to that memory in the current frame.
syntax Instr ::= "(" "memory.size" ")"
// --------------------------------------
// TODO: Add a rule that:
// Looks up the memory of the current frame in the store, and pushes its
// size to the stack.
syntax Instr ::= "(" "memory.grow" ")" | "(" "memory.grow" Instr ")"
// --------------------------------------------------------------------
// TODO: Add a rule that:
// Unfolds the folded syntax rule.
// TODO: Add a rule that:
// Grows the current memory by a specified value (look at the top of the
// stack). This rule should only apply if the resulting memory size is less
// than 2^16 pages.
// TODO: Add a rule that:
// Only applies if we have a toggle set in the configuration, called
// <spuriousMemoryFail>. If so, it just pushes (i32.const -1).
endmodule
/*
configuration
<k color="green"> $PGM:Instrs </k>
<stack color="red"> .Stack </stack>
<frame>
<module>
<memAddrs color="Orchid"> .Map </memAddrs>
</module>
</frame>
<store>
<nextMemAddr> 0 </nextMemAddr>
<mems color="orange">
<memInst multiplicity="*" type="Map">
<memAddr> 0 </memAddr>
<memSize> 0 </memSize>
</memInst>
</mems>
</store>
<spuriousMemoryFail> false </spuriousMemoryFail>
rule <k> (( memory ) ~> ELSE) => ELSE </k>
<memAddrs> .Map => 0 |-> NEXT </memAddrs>
<nextMemAddr> NEXT => NEXT +Int 1 </nextMemAddr>
<mems>
(.Bag =>
<memInst>
<memAddr> NEXT </memAddr>
...
</memInst>)
...
</mems>
syntax Instr ::= "(" "memory.size" ")"
// --------------------------------------
rule <k> ( memory.size ) => ( i32.const SZ ) ... </k>
<memAddrs> 0 |-> A </memAddrs>
<memInst>
<memAddr> A </memAddr>
<memSize> SZ </memSize>
...
</memInst>
rule <k> ( memory.size ) => ( i32.const -1 ) ... </k>
syntax Instr ::= "(" "memory.grow" ")" | "(" "memory.grow" Instr ")"
// --------------------------------------------------------------------
rule <k> (memory.grow I:Instr) => I ~> (memory.grow) ... </k>
rule <k> (memory.grow) => (i32.const SZ) ... </k>
<stack> < i32 > V : S => S </stack>
<memAddrs> 0 |-> A </memAddrs>
<memInst>
<memAddr> A </memAddr>
<memSize> SZ => SZ +Int V </memSize>
</memInst>
requires SZ +Int V <Int 2 ^Int 16
rule <k> (memory.grow) => (i32.const -1) ... </k>
<stack> < i32 > _ : S => S </stack>
<spuriousMemoryFail> true </spuriousMemoryFail>
*/

View file

@ -0,0 +1,455 @@
Numeric Instructions
--------------------
In this file we implement the numeric rules specified in section `4.3 Numerics` of the offical WebAssembly specification.
In the notations of some operators, `sx` is the signedness of the operator and could be either `s` (signed) or `u` (unsigned), which indicates whether the operands should be interpreted as signed integer or unsigned integer.
```k
require "data.md"
module WASM-NUMERIC-SYNTAX
syntax IUnOp ::= "clz" [klabel(aClz), symbol]
| "ctz" [klabel(aCtz), symbol]
| "popcnt" [klabel(aPopcnt), symbol]
// ---------------------------------------------------
syntax FUnOp ::= "abs" [klabel(aAbs) , symbol]
| "neg" [klabel(aNeg) , symbol]
| "sqrt" [klabel(aSqrt) , symbol]
| "floor" [klabel(aFloor) , symbol]
| "ceil" [klabel(aCeil) , symbol]
| "trunc" [klabel(aTrunc) , symbol]
| "nearest" [klabel(aNearest), symbol]
// -----------------------------------------------------
syntax IBinOp ::= "add" [klabel(intAdd), symbol]
| "sub" [klabel(intSub), symbol]
| "mul" [klabel(intMul), symbol]
| "div_u" [klabel(intDiv_u), symbol]
| "rem_u" [klabel(intRem_u), symbol]
| "div_s" [klabel(intDiv_s), symbol]
| "rem_s" [klabel(intRem_s), symbol]
| "and" [klabel(intAnd), symbol]
| "or" [klabel(intOr), symbol]
| "xor" [klabel(intXor), symbol]
| "shl" [klabel(intShl), symbol]
| "shr_u" [klabel(intShr_u), symbol]
| "shr_s" [klabel(intShr_s), symbol]
| "rotl" [klabel(intRotl), symbol]
| "rotr" [klabel(intRotr), symbol]
// --------------------------------------------------
syntax FBinOp ::= "add" [klabel(floatAdd), symbol]
| "sub" [klabel(floatSub), symbol]
| "mul" [klabel(floatMul), symbol]
| "div" [klabel(floatDiv), symbol]
| "min" [klabel(floatMin), symbol]
| "max" [klabel(floatMax), symbol]
| "copysign" [klabel(floatCopysign), symbol]
// ------------------------------------------------------------
syntax TestOp ::= "eqz" [klabel(aEqz), symbol]
// ----------------------------------------------
syntax IRelOp ::= "eq" [klabel(intEq), symbol]
| "ne" [klabel(intNe), symbol]
| "lt_u" [klabel(intLt_u), symbol]
| "gt_u" [klabel(intGt_u), symbol]
| "lt_s" [klabel(intLt_s), symbol]
| "gt_s" [klabel(intGt_s), symbol]
| "le_u" [klabel(intLe_u), symbol]
| "ge_u" [klabel(intGe_u), symbol]
| "le_s" [klabel(intLe_s), symbol]
| "ge_s" [klabel(intGe_s), symbol]
// --------------------------------------------------
syntax FRelOp ::= "lt" [klabel(floatLt), symbol]
| "gt" [klabel(floatGt), symbol]
| "le" [klabel(floatLe), symbol]
| "ge" [klabel(floatGe), symbol]
| "eq" [klabel(floatEq), symbol]
| "ne" [klabel(floatNe), symbol]
// ------------------------------------------------
syntax CvtOp ::= Cvti32Op | Cvti64Op | Cvtf32Op | Cvtf64Op
// ----------------------------------------------------------
syntax Cvti32Op ::= "extend_i32_u" [klabel(aExtend_i32_u), symbol]
| "extend_i32_s" [klabel(aExtend_i32_s), symbol]
| "convert_i32_s" [klabel(aConvert_i32_s), symbol]
| "convert_i32_u" [klabel(aConvert_i32_u), symbol]
// --------------------------------------------------------------------
syntax Cvti64Op ::= "wrap_i64" [klabel(aWrap_i64), symbol]
| "convert_i64_s" [klabel(aConvert_i64_s), symbol]
| "convert_i64_u" [klabel(aConvert_i64_u), symbol]
// --------------------------------------------------------------------
syntax Cvtf32Op ::= "promote_f32" [klabel(aPromote_f32), symbol]
| "trunc_f32_s" [klabel(aTrunc_f32_s), symbol]
| "trunc_f32_u" [klabel(aTrunc_f32_u), symbol]
// ----------------------------------------------------------------
syntax Cvtf64Op ::= "demote_f64" [klabel(aDemote_f64), symbol]
| "trunc_f64_s" [klabel(aTrunc_f64_s), symbol]
| "trunc_f64_u" [klabel(aTrunc_f64_u), symbol]
// ----------------------------------------------------------------
endmodule
module WASM-NUMERIC
imports WASM-NUMERIC-SYNTAX
imports WASM-DATA
```
### Unary Operators
`*UnOp` takes one oprand and returns a `Val`.
```k
syntax Val ::= IValType "." IUnOp Int [klabel(intUnOp) , function]
| FValType "." FUnOp Float [klabel(floatUnOp), function]
// ---------------------------------------------------------------------
```
#### Unary Operators for Integers
There three unary operators for integers: `clz`, `ctz` and `popcnt`.
- `clz` counts the number of leading zero-bits, with 0 having all leading zero-bits.
- `ctz` counts the number of trailing zero-bits, with 0 having all trailing zero-bits.
- `popcnt` counts the number of non-zero bits.
Note: The actual `ctz` operator considers the integer 0 to have *all* zero-bits, whereas the `#ctz` helper function considers it to have *no* zero-bits, in order for it to be width-agnostic.
```k
rule ITYPE . clz I1 => < ITYPE > #width(ITYPE) -Int #minWidth(I1)
rule ITYPE . ctz I1 => < ITYPE > #if I1 ==Int 0 #then #width(ITYPE) #else #ctz(I1) #fi
rule ITYPE . popcnt I1 => < ITYPE > #popcnt(I1)
syntax Int ::= #minWidth ( Int ) [function]
| #ctz ( Int ) [function]
| #popcnt ( Int ) [function]
// -------------------------------------------
rule #minWidth(0) => 0
rule #minWidth(N) => 1 +Int #minWidth(N >>Int 1) requires N =/=Int 0
rule #ctz(0) => 0
rule #ctz(N) => #if N modInt 2 ==Int 1 #then 0 #else 1 +Int #ctz(N >>Int 1) #fi requires N =/=Int 0
rule #popcnt(0) => 0
rule #popcnt(N) => #bool(N modInt 2 ==Int 1) +Int #popcnt(N >>Int 1) requires N =/=Int 0
```
Before we implement the rule for float point numbers, we first need to define a helper function.
- `#isInfinityOrNaN` judges whether a `float` is infinity or NaN.
```k
syntax Bool ::= #isInfinityOrNaN ( Float ) [function]
// -----------------------------------------------------
rule #isInfinityOrNaN ( F ) => (isNaN(F) orBool isInfinite(F))
```
#### Unary Operators for Floats
There are 7 unary operators for floats: `abs`, `neg`, `sqrt`, `floor`, `ceil`, `trunc` and `nearest`.
- `abs` returns the absolute value of the given float point number.
- `neg` returns the additive inverse value of the given float point number.
- `sqrt` returns the square root of the given float point number.
- `floor` returns the greatest integer less than or equal to the given float point number.
- `ceil` returns the least integer greater than or equal to the given float point number.
- `trunc` returns the integral value by discarding the fractional part of the given float.
- `nearest` returns the integral value that is nearest to the given float number; if two values are equally near, returns the even one.
```k
rule FTYPE . abs F => < FTYPE > absFloat (F)
rule FTYPE . neg F => < FTYPE > --Float F
rule FTYPE . sqrt F => < FTYPE > sqrtFloat (F)
rule FTYPE . floor F => < FTYPE > floorFloat (F)
rule FTYPE . ceil F => < FTYPE > ceilFloat (F)
rule FTYPE . trunc F => < FTYPE > truncFloat (F)
rule FTYPE . nearest F => < FTYPE > F requires #isInfinityOrNaN (F)
rule FTYPE . nearest F => #round(FTYPE, Float2Int(F)) requires (notBool #isInfinityOrNaN (F)) andBool notBool (Float2Int(F) ==Int 0 andBool signFloat(F))
rule FTYPE . nearest F => < FTYPE > -0.0 requires (notBool #isInfinityOrNaN (F)) andBool Float2Int(F) ==Int 0 andBool signFloat(F)
```
### Binary Operators
`*BinOp` takes two oprands and returns a `Val`.
A `*BinOp` operator always produces a result of the same type as its operands.
```k
syntax Val ::= IValType "." IBinOp Int Int [klabel(intBinOp) , function]
| FValType "." FBinOp Float Float [klabel(floatBinOp), function]
// -----------------------------------------------------------------------------
```
#### Binary Operators for Integers
There are 12 binary operators for integers: `add`, `sub`, `mul`, `div_sx`, `rem_sx`, `and`, `or`, `xor`, `shl`, `shr_sx`, `rotl`, `rotr`.
- `add` returns the result of adding up the 2 given integers modulo 2^N.
- `sub` returns the result of substracting the second oprand from the first oprand modulo 2^N.
- `mul` returns the result of multiplying the 2 given integers modulo 2^N.
`add`, `sub`, and `mul` are given semantics by lifting the correct K operators through the `#chop` function.
```k
rule ITYPE:IValType . add I1 I2 => #chop(< ITYPE > I1 +Int I2)
rule ITYPE:IValType . sub I1 I2 => #chop(< ITYPE > I1 -Int I2)
rule ITYPE:IValType . mul I1 I2 => #chop(< ITYPE > I1 *Int I2)
```
- `div_sx` returns the result of dividing the first operand by the second oprand, truncated toward zero.
- `rem_sx` returns the remainder of dividing the first operand by the second oprand.
`div_sx` and `rem_sx` have extra side-conditions about when they are defined or not.
```k
rule ITYPE . div_u I1 I2 => < ITYPE > I1 /Int I2 requires I2 =/=Int 0
rule _ITYPE . div_u _I1 I2 => undefined requires I2 ==Int 0
rule ITYPE . rem_u I1 I2 => < ITYPE > I1 %Int I2 requires I2 =/=Int 0
rule _ITYPE . rem_u _I1 I2 => undefined requires I2 ==Int 0
rule ITYPE . div_s I1 I2 => < ITYPE > #unsigned(ITYPE, #signed(ITYPE, I1) /Int #signed(ITYPE, I2))
requires I2 =/=Int 0
andBool #signed(ITYPE, I1) /Int #signed(ITYPE, I2) =/=Int #pow1(ITYPE)
rule _ITYPE . div_s _I1 I2 => undefined
requires I2 ==Int 0
rule ITYPE . div_s I1 I2 => undefined
requires I2 =/=Int 0
andBool #signed(ITYPE, I1) /Int #signed(ITYPE, I2) ==Int #pow1(ITYPE)
rule ITYPE . rem_s I1 I2 => < ITYPE > #unsigned(ITYPE, #signed(ITYPE, I1) %Int #signed(ITYPE, I2))
requires I2 =/=Int 0
rule _ITYPE . rem_s _I1 I2 => undefined
requires I2 ==Int 0
```
- `and` returns the bitwise conjunction of the 2 given floats.
- `or` returns the bitwise disjunction of the 2 given floats.
- `xor` returns the bitwise exclusive disjunction of the 2 given floats.
Of the bitwise operators, `and` will not overflow, but `or` and `xor` could.
These simply are the lifted K operators.
```k
rule ITYPE . and I1 I2 => < ITYPE > I1 &Int I2
rule ITYPE . or I1 I2 => #chop(< ITYPE > I1 |Int I2)
rule ITYPE . xor I1 I2 => #chop(< ITYPE > I1 xorInt I2)
```
- `shl` returns the result of shifting the first operand left by k bits modulo 2^N, in which k is the second operand modulo N.
- `shr_u` returns the result of shifting the first operand right by k bits, and extended with 0 bits.
- `shr_s` returns the result of shifting the first operand right by k bits, and extended with the most significant bit of the original value
Similarly, K bitwise shift operators are lifted for `shl` and `shr_u`.
Careful attention is made for the signed version `shr_s`.
```k
rule ITYPE . shl I1 I2 => #chop(< ITYPE > I1 <<Int (I2 %Int #width(ITYPE)))
rule ITYPE . shr_u I1 I2 => < ITYPE > I1 >>Int (I2 %Int #width(ITYPE))
rule ITYPE . shr_s I1 I2 => < ITYPE > #unsigned(ITYPE, #signed(ITYPE, I1) >>Int (I2 %Int #width(ITYPE)))
```
- `rotl` returns the result of rotating the first operand left by k bits, in which k is the second operand modulo N.
- `rotr` returns the result of rotating the first operand right by k bits, in which k is the second operand modulo N.
The rotation operators `rotl` and `rotr` do not have appropriate K builtins, and so are built with a series of shifts.
```k
rule ITYPE . rotl I1 I2 => #chop(< ITYPE > (I1 <<Int (I2 %Int #width(ITYPE))) +Int (I1 >>Int (#width(ITYPE) -Int (I2 %Int #width(ITYPE)))))
rule ITYPE . rotr I1 I2 => #chop(< ITYPE > (I1 >>Int (I2 %Int #width(ITYPE))) +Int (I1 <<Int (#width(ITYPE) -Int (I2 %Int #width(ITYPE)))))
```
#### Binary Operators for Floats
There are 7 binary operators for integers: `add`, `sub`, `mul`, `div`, `min`, `max`, `copysign`.
- `add` returns the result of adding the 2 given floats and rounded to the nearest representable value.
- `sub` returns the result of substracting the second oprand from the first oprand and rounded to the nearest representable value.
- `mul` returns the result of multiplying the 2 given floats and rounded to the nearest representable value.
- `div` returns the result of dividing the first oprand by the second oprand and rounded to the nearest representable value.
- `min` returns the smaller value of the 2 given floats.
- `max` returns the bigger value of the 2 given floats.
- `copysign` returns the first oprand if the 2 given floats have the same sign, otherwise returns the first oprand with negated sign.
Note: For operators that defined under both sorts `IXXOp` and `FXXOp`, we need to give it a `klabel` and define it as a `symbol` to prevent parsing issue.
```k
rule FTYPE:FValType . add F1 F2 => < FTYPE > F1 +Float F2
rule FTYPE:FValType . sub F1 F2 => < FTYPE > F1 -Float F2
rule FTYPE:FValType . mul F1 F2 => < FTYPE > F1 *Float F2
rule FTYPE . div F1 F2 => < FTYPE > F1 /Float F2
rule FTYPE . min F1 F2 => < FTYPE > minFloat (F1, F2)
rule FTYPE . max F1 F2 => < FTYPE > maxFloat (F1, F2)
rule FTYPE . copysign F1 F2 => < FTYPE > F1 requires signFloat (F1) ==Bool signFloat (F2)
rule FTYPE . copysign F1 F2 => < FTYPE > --Float F1 requires signFloat (F1) =/=Bool signFloat (F2)
```
### Test Operators
Test operations consume one operand and produce a bool, which is an `i32` value.
There is no test operation for float numbers.
```k
syntax Val ::= IValType "." TestOp Int [klabel(intTestOp), function]
// --------------------------------------------------------------------
```
#### Test Operators for Integers
- `eqz` checks wether its operand is 0.
```k
rule _ . eqz I => < i32 > #bool(I ==Int 0)
```
### Relationship Operators
Relationship Operators consume two operands and produce a bool, which is an `i32` value.
```k
syntax Val ::= IValType "." IRelOp Int Int [klabel(intRelOp) , function]
| FValType "." FRelOp Float Float [klabel(floatRelOp), function]
// -----------------------------------------------------------------------------
```
### Relationship Operators for Integers
There are 6 relationship operators for integers: `eq`, `ne`, `lt_sx`, `gt_sx`, `le_sx` and `ge_sx`.
- `eq` returns 1 if the 2 given integers are equal, 0 otherwise.
- `eq` returns 1 if the 2 given integers are not equal, 0 otherwise.
```k
rule _:IValType . eq I1 I2 => < i32 > #bool(I1 ==Int I2)
rule _:IValType . ne I1 I2 => < i32 > #bool(I1 =/=Int I2)
```
- `lt_sx` returns 1 if the first oprand is less than the second opeand, 0 otherwise.
- `gt_sx` returns 1 if the first oprand is greater than the second opeand, 0 otherwise.
```k
rule _ . lt_u I1 I2 => < i32 > #bool(I1 <Int I2)
rule _ . gt_u I1 I2 => < i32 > #bool(I1 >Int I2)
rule ITYPE . lt_s I1 I2 => < i32 > #bool(#signed(ITYPE, I1) <Int #signed(ITYPE, I2))
rule ITYPE . gt_s I1 I2 => < i32 > #bool(#signed(ITYPE, I1) >Int #signed(ITYPE, I2))
```
- `le_sx` returns 1 if the first oprand is less than or equal to the second opeand, 0 otherwise.
- `ge_sx` returns 1 if the first oprand is greater than or equal to the second opeand, 0 otherwise.
```k
rule _ . le_u I1 I2 => < i32 > #bool(I1 <=Int I2)
rule _ . ge_u I1 I2 => < i32 > #bool(I1 >=Int I2)
rule ITYPE . le_s I1 I2 => < i32 > #bool(#signed(ITYPE, I1) <=Int #signed(ITYPE, I2))
rule ITYPE . ge_s I1 I2 => < i32 > #bool(#signed(ITYPE, I1) >=Int #signed(ITYPE, I2))
```
### Relationship Operators for Floats
There are 6 relationship operators for floats: `eq`, `ne`, `lt`, `gt`, `le` and `ge`.
- `eq` returns 1 if the 2 given floats are equal, 0 otherwise.
- `ne` returns 1 if the 2 given floats are not equal, 0 otherwise.
- `lt` returns 1 if the first oprand is less than the second opeand, 0 otherwise.
- `gt` returns 1 if the first oprand is greater than the second opeand, 0 otherwise.
- `le` returns 1 if the first oprand is less than or equal to the second opeand, 0 otherwise.
- `ge` returns 1 if the first oprand is greater than or equal to the second opeand, 0 otherwise.
```k
rule _ . lt F1 F2 => < i32 > #bool(F1 <Float F2)
rule _ . gt F1 F2 => < i32 > #bool(F1 >Float F2)
rule _ . le F1 F2 => < i32 > #bool(F1 <=Float F2)
rule _ . ge F1 F2 => < i32 > #bool(F1 >=Float F2)
rule _:FValType . eq F1 F2 => < i32 > #bool(F1 ==Float F2)
rule _:FValType . ne F1 F2 => < i32 > #bool(F1 =/=Float F2)
```
### Conversion Operators
Conversion operators always take a single argument as input and cast it to another type.
The operators are further broken down into subsorts for their input type, for simpler type-checking.
```k
syntax Val ::= ValType "." CvtOp Number [klabel(numberCvtOp), function]
// -----------------------------------------------------------------------
```
There are 7 conversion operators: `wrap`, `extend`, `trunc`, `convert`, `demote` ,`promote` and `reinterpret`.
- `wrap` takes an `i64` value, cuts of the 32 most significant bits and returns an `i32` value.
```k
rule i32 . wrap_i64 I => #chop(< i32 > I)
```
- `extend` takes an `i32` type value, converts its type into the `i64` and returns the result.
```k
rule i64 . extend_i32_u I:Int => < i64 > I
rule i64 . extend_i32_s I:Int => < i64 > #unsigned(i64, #signed(i32, I))
```
- `convert` takes an `int` type value and convert it to the nearest `float` type value.
```k
rule FTYPE . convert_i32_s I:Int => #round( FTYPE , #signed(i32, I) )
rule FTYPE . convert_i32_u I:Int => #round( FTYPE , I )
rule FTYPE . convert_i64_s I:Int => #round( FTYPE , #signed(i64, I) )
rule FTYPE . convert_i64_u I:Int => #round( FTYPE , I )
```
- `demote` turns an `f64` type value to the nearest `f32` type value.
- `promote` turns an `f32` type value to the nearest `f64` type value:
```k
rule f64 . promote_f32 F => #round( f64 , F )
rule f32 . demote_f64 F => #round( f32 , F )
```
- `trunc` first truncates a float value, then convert the result to the nearest ineger value.
```k
rule ITYPE . trunc_f32_s F => undefined
requires #isInfinityOrNaN (F) orBool (Float2Int(truncFloat(F)) >=Int #pow1(ITYPE)) orBool (0 -Int Float2Int(truncFloat(F)) >Int #pow1 (ITYPE))
rule ITYPE . trunc_f32_u F => undefined
requires #isInfinityOrNaN (F) orBool (Float2Int(truncFloat(F)) >=Int #pow (ITYPE)) orBool (Float2Int(truncFloat(F)) <Int 0)
rule ITYPE . trunc_f32_s F => <ITYPE> #unsigned(ITYPE, Float2Int(truncFloat(F)))
requires notBool (#isInfinityOrNaN (F) orBool (Float2Int(truncFloat(F)) >=Int #pow1(ITYPE)) orBool (0 -Int Float2Int(truncFloat(F)) >Int #pow1 (ITYPE)))
rule ITYPE . trunc_f32_u F => <ITYPE> Float2Int(truncFloat(F))
requires notBool (#isInfinityOrNaN (F) orBool (Float2Int(truncFloat(F)) >=Int #pow (ITYPE)) orBool (Float2Int(truncFloat(F)) <Int 0))
rule ITYPE . trunc_f64_s F => undefined
requires #isInfinityOrNaN (F) orBool (Float2Int(truncFloat(F)) >=Int #pow1(ITYPE)) orBool (0 -Int Float2Int(truncFloat(F)) >Int #pow1 (ITYPE))
rule ITYPE . trunc_f64_u F => undefined
requires #isInfinityOrNaN (F) orBool (Float2Int(truncFloat(F)) >=Int #pow (ITYPE)) orBool (Float2Int(truncFloat(F)) <Int 0)
rule ITYPE . trunc_f64_s F => <ITYPE> #unsigned(ITYPE, Float2Int(truncFloat(F)))
requires notBool (#isInfinityOrNaN (F) orBool (Float2Int(truncFloat(F)) >=Int #pow1(ITYPE)) orBool (0 -Int Float2Int(truncFloat(F)) >Int #pow1 (ITYPE)))
rule ITYPE . trunc_f64_u F => <ITYPE> Float2Int(truncFloat(F))
requires notBool (#isInfinityOrNaN (F) orBool (Float2Int(truncFloat(F)) >=Int #pow (ITYPE)) orBool (Float2Int(truncFloat(F)) <Int 0))
```
**TODO**: Unimplemented: `inn.reinterpret_fnn`, `fnn.reinterpret_inn`.
```k
endmodule
```

View file

@ -0,0 +1,37 @@
# Preprocessor that converts Wasm concrete syntax into a form parseable by K.
# example usage: python convert.py f32.wast
import re
import sys
def hex2float(h):
h = re.sub("_", "", h)
if "nan" in h:
# TODO: Keep bit pattern of float, don't turn all of them into simple NaNs.
return re.sub("-?nan(:.*$)?", "NaN", h)
elif "inf" in h:
return h.replace("inf", "Infinity")
elif "0x" in h:
try:
return h.split()[0] + " " + "%e" % (float.fromhex(h.split()[1]))
except OverflowError:
return h
else:
return h
def main():
if len(list(sys.argv)) == 1:
infile = sys.stdin
else:
infile = open(sys.argv[1])
for line in (infile.readlines()):
sys.stdout.write(re.sub(r"(?:(?:f32|f64)\.const )([^\)]+)",
lambda m: hex2float(m.group()),
line))
infile.close()
if __name__ == "__main__":
main()

View file

@ -0,0 +1,18 @@
{
"template": "https://github.com/runtimeverification/python-project-template.git",
"commit": "e51305c984afe6bfb51d212c1b8834bfe95fa8da",
"checkout": null,
"context": {
"cookiecutter": {
"project_name": "pykwasm",
"project_slug": "pykwasm",
"package_name": "pykwasm",
"version": "0.1.0",
"description": "",
"author_name": "Runtime Verification, Inc.",
"author_email": "contact@runtimeverification.com",
"_template": "https://github.com/runtimeverification/python-project-template.git"
}
},
"directory": null
}

View file

@ -0,0 +1,6 @@
[flake8]
max-line-length = 120
extend-select = B9
extend-ignore = B950,E,W1,W2,W3,W4,W5
per-file-ignores =
*/__init__.py: F401

View file

@ -0,0 +1,3 @@
/dist/
__pycache__/
.coverage

View file

@ -0,0 +1,84 @@
POETRY := poetry
POETRY_RUN := $(POETRY) run
default: check test-unit
all: check cov
.PHONY: clean
clean:
rm -rf dist .coverage cov-* .mypy_cache .pytest_cache
find -type d -name __pycache__ -prune -exec rm -rf {} \;
.PHONY: build
build:
$(POETRY) build
.PHONY: poetry-install
poetry-install:
$(POETRY) install
# Tests
TEST_ARGS :=
test: test-all
test-all: poetry-install
$(POETRY_RUN) pytest src/tests --maxfail=1 --verbose --durations=0 --numprocesses=4 --dist=worksteal $(TEST_ARGS)
test-unit: poetry-install
$(POETRY_RUN) pytest src/tests/unit --maxfail=1 --verbose $(TEST_ARGS)
test-integration: poetry-install
$(POETRY_RUN) pytest src/tests/integration --maxfail=1 --verbose --durations=0 --numprocesses=4 --dist=worksteal $(TEST_ARGS)
# Coverage
COV_ARGS :=
cov: cov-all
cov-%: TEST_ARGS += --cov=pykwasm --no-cov-on-fail --cov-branch --cov-report=term
cov-all: TEST_ARGS += --cov-report=html:cov-all-html $(COV_ARGS)
cov-all: test-all
cov-unit: TEST_ARGS += --cov-report=html:cov-unit-html $(COV_ARGS)
cov-unit: test-unit
cov-integration: TEST_ARGS += --cov-report=html:cov-integration-html $(COV_ARGS)
cov-integration: test-integration
# Checks and formatting
format: autoflake isort black
check: check-flake8 check-mypy check-autoflake check-isort check-black
check-flake8: poetry-install
$(POETRY_RUN) flake8 src
check-mypy: poetry-install
$(POETRY_RUN) mypy src
autoflake: poetry-install
$(POETRY_RUN) autoflake --quiet --in-place src
check-autoflake: poetry-install
$(POETRY_RUN) autoflake --quiet --check src
isort: poetry-install
$(POETRY_RUN) isort src
check-isort: poetry-install
$(POETRY_RUN) isort --check src
black: poetry-install
$(POETRY_RUN) black src
check-black: poetry-install
$(POETRY_RUN) black --check src

View file

@ -0,0 +1,23 @@
# pykwasm
## Installation
Prerequsites: `python 3.8.*`, `pip >= 20.0.2`, `poetry >= 1.3.2`.
```bash
make build
pip install dist/*.whl
```
## For Developers
Use `make` to run common tasks (see the [Makefile](Makefile) for a complete list of available targets).
* `make build`: Build wheel
* `make check`: Check code style
* `make format`: Format code
* `make test-unit`: Run unit tests
For interactive use, spawn a shell with `poetry shell` (after `poetry install`), then run an interpreter.

View file

@ -0,0 +1,41 @@
{
description = "pykwasm - ";
inputs = {
nixpkgs.url = "nixpkgs/nixos-22.05";
flake-utils.url = "github:numtide/flake-utils";
poetry2nix.url = "github:nix-community/poetry2nix";
};
outputs = { self, nixpkgs, flake-utils, poetry2nix }:
let
allOverlays = [
poetry2nix.overlay
(final: prev: {
pykwasm = prev.poetry2nix.mkPoetryApplication {
python = prev.python39;
projectDir = ./.;
groups = [];
# We remove `dev` from `checkGroups`, so that poetry2nix does not try to resolve dev dependencies.
checkGroups = [];
};
})
];
in flake-utils.lib.eachSystem [
"x86_64-linux"
"x86_64-darwin"
"aarch64-linux"
"aarch64-darwin"
] (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = allOverlays;
};
in {
packages = rec {
inherit (pkgs) pykwasm;
default = pykwasm;
};
}) // {
overlay = nixpkgs.lib.composeManyExtensions allOverlays;
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "pykwasm"
version = "0.1.0"
description = ""
authors = [
"Runtime Verification, Inc. <contact@runtimeverification.com>",
]
[tool.poetry.dependencies]
python = "^3.10"
cytoolz = "^0.12.1"
numpy = "^1.24.2"
pyk = { git = "https://github.com/runtimeverification/pyk.git", tag="v0.1.232" }
[tool.poetry.group.dev.dependencies]
autoflake = "*"
black = "*"
flake8 = "*"
flake8-bugbear = "*"
flake8-comprehensions = "*"
flake8-quotes = "*"
isort = "*"
mypy = "*"
pep8-naming = "*"
pytest = "*"
pytest-cov = "*"
pytest-mock = "*"
pytest-xdist = "*"
[tool.isort]
profile = "black"
line_length = 120
[tool.autoflake]
recursive = true
expand-star-imports = true
remove-all-unused-imports = true
ignore-init-module-imports = true
remove-duplicate-keys = true
remove-unused-variables = true
[tool.black]
line-length = 120
skip-string-normalization = true
[tool.mypy]
disallow_untyped_defs = true

View file

@ -0,0 +1,2 @@
def hello(name: str) -> str:
return f'Hello, {name}!'

View file

@ -0,0 +1,5 @@
from pykwasm.hello import hello
def test_hello() -> None:
assert hello('World') == 'Hello, World!'

View file

@ -0,0 +1,5 @@
from pykwasm.hello import hello
def test_hello() -> None:
assert hello('World') == 'Hello, World!'

View file

@ -0,0 +1,530 @@
KWasm Testing
=============
For testing, we augment the semantics with some helpers.
```k
require "wasm-text.md"
require "auxil.md"
```
Module `WASM-TEST-SYNTAX` is just used for program parsing and `WASM-TEST` consists of the definitions both for parsing and execution.
```k
module WASM-TEST-SYNTAX
imports WASM-TEST
imports WASM-TEXT-SYNTAX
imports WASM-AUXIL
imports WASM-REFERENCE-INTERPRETER-SYNTAX
endmodule
module WASM-REFERENCE-INTERPRETER-SYNTAX
imports WASM-COMMON-SYNTAX
syntax Auxil ::= Action
syntax Action ::= "(" "invoke" OptionalId WasmString Instrs ")"
| "invoke" Int WasmString
| "(" "get" OptionalId WasmString ")"
| "get" Int WasmString
// ----------------------------------------------------
syntax Auxil ::= "(" "register" WasmString ")"
| "(" "register" WasmString Index ")"
// ----------------------------------------------------
syntax DefnStrings ::= List{WasmString, ""}
syntax ModuleDecl ::= "(" "module" OptionalId "binary" DataString ")" [macro]
| "(" "module" OptionalId "quote" DefnStrings ")" [macro]
// ------------------------------------------------------------------------------
```
Assertions for KWasm tests
--------------------------
We'll make `Assertion` a subsort of `Auxil`, since it is a form of top-level embedder instrucion.
```k
syntax Auxil ::= Assertion
// --------------------------
```
### Conformance Assertions
Here we inplement the conformance assertions specified in [spec interpreter] including:
```
( assert_return <action> <expr>* ) ;; assert action has expected results. Sometimes <expr>* is just empty.
( assert_return_canonical_nan <action> ) ;; assert action results in NaN in a canonical form
( assert_return_arithmetic_nan <action> ) ;; assert action results in NaN with 1 in MSB of fraction field
( assert_trap <action> <failure> ) ;; assert action traps with given failure string
( assert_exhaustion <action> <failure> ) ;; assert action exhausts system resources
( assert_malformed <module> <failure> ) ;; assert module cannot be decoded with given failure string
( assert_invalid <module> <failure> ) ;; assert module is invalid with given failure string
( assert_unlinkable <module> <failure> ) ;; assert module fails to link
( assert_trap <module> <failure> ) ;; assert module traps on instantiation
```
```k
syntax Assertion ::= "(" "assert_return" Action Instr ")"
| "(" "assert_return" Action ")"
| "(" "assert_return_canonical_nan" Action ")"
| "(" "assert_return_arithmetic_nan" Action ")"
| "(" "assert_trap" Action WasmString ")"
| "(" "assert_exhaustion" Action WasmString ")"
| "(" "assert_malformed" ModuleDecl WasmString ")"
| "(" "assert_invalid" ModuleDecl WasmString ")"
| "(" "assert_unlinkable" ModuleDecl WasmString ")"
| "(" "assert_trap" ModuleDecl WasmString ")"
// ---------------------------------------------------------------------------------
```
```k
endmodule
module WASM-TEST
imports WASM-REFERENCE-INTERPRETER-SYNTAX
imports WASM-AUXIL
imports WASM-TEXT
```
Configuration
-------------
```k
configuration
<wasm-test>
<k> $PGM:Stmts </k>
<wasm/>
</wasm-test>
```
Passing Control
---------------
The test embedder passes control to the execution cell in Wasm.
```k
rule <k> PGM => . </k>
<instrs> .K => sequenceStmts(text2abstract(PGM)) </instrs>
```
Bare Allocations
----------------
We allow allocations to appear outside of modules, for example interleaved with assertions in tests.
This is purely a KWasm feature, which is useful for testing.
```k
rule <instrs> A:Alloc => #emptyModule() ~> A ... </instrs>
<curModIdx> .Int </curModIdx>
[owise]
```
Instruction sugar
-----------------
We allow writing instructions at the top level in the test embedder.
```k
rule <instrs> FI:FoldedInstr => sequenceInstrs(unfoldInstrs(FI .Instrs)) ... </instrs>
```
Auxiliary
---------
We add `token` as a value in order to implement some test assertions.
```k
syntax Val ::= "token"
// ----------------------
```
Reference Interpreter Commands
------------------------------
TODO: Move this to a separate `EMBEDDER` module?
The official test suite contains some special auxillary instructions outside of the standard Wasm semantics.
The reference interpreter is a particular embedder with auxillary instructions, specified in [spec interpreter](https://github.com/WebAssembly/spec/blob/master/interpreter/README.md).
### Actions
We allow 2 kinds of actions:
- We allow to `invoke` a function by its exported name.
- We allow to `get` a global export.
```k
rule <instrs> ( invoke OID:OptionalId ENAME:WasmString IS:Instrs ) => sequenceInstrs(IS) ~> ( invoke OID ENAME .Instrs ) ... </instrs>
requires IS =/=K .Instrs
rule <instrs> ( invoke ENAME:WasmString .Instrs ) => invoke CUR ENAME ... </instrs>
<curModIdx> CUR </curModIdx>
rule <instrs> ( invoke ID:Identifier ENAME:WasmString .Instrs ) => invoke MODIDX ENAME ... </instrs>
<moduleIds> ... ID |-> MODIDX ... </moduleIds>
rule <instrs> invoke MODIDX:Int ENAME:WasmString => ( invoke FADDR ):Instr ... </instrs>
<moduleInst>
<modIdx> MODIDX </modIdx>
<exports> ... ENAME |-> IDX ... </exports>
<funcAddrs> ... IDX |-> FADDR ... </funcAddrs>
...
</moduleInst>
rule <instrs> ( get NAME:WasmString ) => get CUR NAME ... </instrs>
<curModIdx> CUR </curModIdx>
rule <instrs> ( get MOD:Identifier NAME:WasmString ) => get MODIDX NAME ... </instrs>
<moduleIds> ... MOD |-> MODIDX ... </moduleIds>
rule <instrs> get MODIDX:Int NAME:WasmString => VAL ... </instrs>
<moduleInst>
<modIdx> MODIDX </modIdx>
<exports> ... NAME |-> TFIDX ... </exports>
<globIds> IDS </globIds>
<globalAddrs> ... #ContextLookup(IDS, TFIDX) |-> ADDR ... </globalAddrs>
...
</moduleInst>
<globalInst>
<gAddr> ADDR </gAddr>
<gValue> VAL </gValue>
...
</globalInst>
```
### Registering Modules
We will reference modules by name in imports.
`register` is the instruction that allows us to associate a name with a module.
```k
rule <instrs> ( register S ) => ( register S (NEXT -Int 1) )... </instrs> // Register last instantiated module.
<nextModuleIdx> NEXT </nextModuleIdx>
requires NEXT >Int 0
rule <instrs> ( register S ID:Identifier ) => ( register S IDX ) ... </instrs>
<moduleIds> ... ID |-> IDX ... </moduleIds>
rule <instrs> ( register S:WasmString IDX:Int ) => . ... </instrs>
<moduleRegistry> ... .Map => S |-> IDX ... </moduleRegistry>
```
### Addtional Module Syntax
The conformance test cases contain the syntax of declaring modules in the format of `(module binary <string>*)` and `(module quote <string>*)`.
They are not defined in the official specification.
In order to parse the conformance test cases, we handle these declarations here and just reduce them to the empty module.
```k
rule ( module OID binary _DS ) => ( module OID .Defns )
rule ( module OID quote _DS ) => ( module OID .Defns )
```
The conformance tests contain imports of the `"spectest"` module.
For now, we just introduce some special handling that ignores any tests that make use of `"spectest"`.
The handling consists of trapping whenever a `"spectest"` function is called, and removing the trap whenever a new module or assertion comes up.
TODO: Actually implement the `"spectest"` module, or call out to the supplied on in the spec repo.
```k
syntax Instr ::= "spectest_trap"
// --------------------------------
rule <instrs> spectest_trap ~> (_L:Label => .) ... </instrs>
rule <instrs> spectest_trap ~> (_F:Frame => .) ... </instrs>
rule <instrs> spectest_trap ~> (_I:Instr => .) ... </instrs>
rule <instrs> spectest_trap ~> (_D:Defn => .) ... </instrs>
rule <instrs> (spectest_trap => .) ~> _M:ModuleDecl ... </instrs>
rule <instrs> (spectest_trap => .) ~> _A:Assertion ... </instrs>
rule <instrs> #import(MOD, _, #funcDesc(... id: OID, type: TIDX))
=> #func(... type: TIDX, locals: [ .ValTypes ], body: spectest_trap .Instrs, metadata: #meta(... id: OID, localIds: .Map))
...
</instrs>
requires MOD ==K #token("\"spectest\"", "WasmStringToken")
orBool MOD ==K #token("\"test\"" , "WasmStringToken")
```
Except `assert_return` and `assert_trap`, the remaining rules are directly reduced to empty. We are not planning to implement them and these rules are only used to make it easier to parse conformance tests.
*TODO:* Make use of `assert_exhaustion`, by detecting stack overflow.
```k
rule <instrs> (assert_return ACT INSTR) => ACT ~> INSTR ~> #assertAndRemoveEqual ~> #assertAndRemoveToken ... </instrs>
<valstack> VALSTACK => token : VALSTACK </valstack>
rule <instrs> (assert_return ACT) => ACT ~> #assertAndRemoveToken ... </instrs>
<valstack> VALSTACK => token : VALSTACK </valstack>
rule <instrs> (assert_return_canonical_nan _ACT) => . ... </instrs>
rule <instrs> (assert_return_arithmetic_nan _ACT) => . ... </instrs>
rule <instrs> (assert_trap ACT:Action DESC) => ACT ~> #assertTrap DESC ... </instrs>
rule <instrs> (assert_exhaustion _ACT:Action _DESC) => . ... </instrs>
rule <instrs> (assert_malformed _MOD _DESC) => . ... </instrs>
rule <instrs> (assert_invalid _MOD _DESC) => . ... </instrs>
rule <instrs> (assert_unlinkable _MOD _DESC) => . ... </instrs>
rule <instrs> (assert_trap MOD:ModuleDecl DESC) => sequenceStmts(text2abstract(MOD .Stmts)) ~> #assertTrap DESC ... </instrs>
```
And we implement some helper assertions to help testing.
```k
syntax Assertion ::= "#assertAndRemoveEqual"
| "#assertAndRemoveToken"
// --------------------------------------------
rule <instrs> #assertAndRemoveEqual => #assertTopStack V .WasmString ~> ( drop ) ... </instrs>
<valstack> V : VALSTACK => VALSTACK </valstack>
rule <instrs> #assertAndRemoveToken => . ... </instrs>
<valstack> token : VALSTACK => VALSTACK </valstack>
```
### Trap Assertion
This asserts that a `trap` was just thrown.
```k
syntax Assertion ::= "#assertTrap" WasmString
// ---------------------------------------------
rule <instrs> trap ~> #assertTrap _ => . ... </instrs>
```
### ValStack Assertions
These functions make assertions about the state of the `<valstack>` cell.
```k
syntax Assertion ::= "#assertTopStack" Val WasmString
| "#assertTopStackExactly" Val WasmString
| "#assertStack" ValStack WasmString
| "#assertStackAux" ValStack ValStack
// ---------------------------------------------------------------
rule <instrs> #assertTopStack S _ => . ... </instrs> <valstack> S : _VALSTACK </valstack>
rule <instrs> #assertTopStack < ITYPE:IValType > VAL _ => . ... </instrs> <valstack> < ITYPE > VAL' : _VALSTACK </valstack>
requires #unsigned(ITYPE, VAL) ==Int VAL'
rule <instrs> #assertTopStack < FTYPE:FValType > VAL _ => . ... </instrs> <valstack> < FTYPE > VAL' : _VALSTACK </valstack>
requires signFloat(VAL) ==Bool signFloat(VAL') andBool VAL ==Float VAL'
rule <instrs> #assertTopStackExactly A _ => . ... </instrs> <valstack> A : _VALSTACK </valstack>
rule <instrs> #assertStack S1 _ => #assertStackAux S1 S2 ... </instrs>
<valstack> S2 </valstack>
rule <instrs> #assertStackAux .ValStack _ => . ... </instrs>
rule <instrs> #assertStackAux ( V : S1') (V : S2') => #assertStackAux S1' S2' ... </instrs>
rule <instrs> #assertStackAux (< ITYPE > VAL : S1') (< ITYPE > VAL' : S2') => #assertStackAux S1' S2' ... </instrs>
requires #unsigned(ITYPE, VAL) ==Int VAL'
```
### Variables Assertions
The operator `#assertLocal`/`#assertGlobal` operators perform a check for a local/global variable's value.
```k
syntax Assertion ::= "#assertLocal" Int Val WasmString
| "#assertGlobal" Index Val WasmString
// ---------------------------------------------------------
rule <instrs> #assertLocal INDEX VALUE _ => . ... </instrs>
<locals> ... INDEX |-> VALUE ... </locals>
rule <instrs> #assertGlobal TFIDX VALUE _ => . ... </instrs>
<curModIdx> CUR </curModIdx>
<moduleInst>
<modIdx> CUR </modIdx>
<globIds> IDS </globIds>
<globalAddrs> ... #ContextLookup(IDS , TFIDX) |-> GADDR ... </globalAddrs>
...
</moduleInst>
<globals>
<globalInst>
<gAddr> GADDR </gAddr>
<gValue> VALUE </gValue>
...
</globalInst>
...
</globals>
```
### Type Assertions
`#assertType` checks whether a type is allocated to the correct index.
`#assertNextTypeIdx` checks whether the number of types are allocated correctly.
```k
syntax Assertion ::= "#assertType" Int FuncType
| "#assertNextTypeIdx" Int
// ---------------------------------------------
rule <instrs> #assertType IDX FTYPE => . ... </instrs>
<curModIdx> CUR </curModIdx>
<moduleInst>
<modIdx> CUR </modIdx>
<types> ... IDX |-> FTYPE ... </types>
...
</moduleInst>
rule <instrs> #assertNextTypeIdx IDX => . ... </instrs>
<curModIdx> CUR </curModIdx>
<moduleInst>
<modIdx> CUR </modIdx>
<nextTypeIdx> IDX </nextTypeIdx>
...
</moduleInst>
```
### Function Assertions
This simply checks that the given function exists in the `<funcs>` cell and has the given signature and local types.
```k
syntax Assertion ::= "#assertFunction" Index FuncType VecType WasmString
// ------------------------------------------------------------------------
rule <instrs> #assertFunction IDX FTYPE LTYPE _ => . ... </instrs>
<curModIdx> CUR </curModIdx>
<moduleInst>
<modIdx> CUR </modIdx>
<funcAddrs> ... IDX |-> FADDR ... </funcAddrs>
...
</moduleInst>
<funcs>
<funcDef>
<fAddr> FADDR </fAddr>
<fType> FTYPE </fType>
<fLocal> LTYPE </fLocal>
...
</funcDef>
...
</funcs>
```
### Table Assertions
This asserts related operation about tables.
```k
syntax Assertion ::= "#assertTable" Index Int OptionalInt WasmString
// --------------------------------------------------------------------
rule <instrs> #assertTable TFIDX SIZE MAX _MSG => . ... </instrs>
<curModIdx> CUR </curModIdx>
<moduleInst>
<modIdx> CUR </modIdx>
<tabIds> IDS </tabIds>
<tabAddrs> #ContextLookup(IDS, TFIDX) |-> ADDR </tabAddrs>
...
</moduleInst>
<tabs>
<tabInst>
<tAddr> ADDR </tAddr>
<tmax> MAX </tmax>
<tsize> SIZE </tsize>
...
</tabInst>
...
</tabs>
syntax Assertion ::= "#assertTableElem" "(" Int "," Int ")" WasmString
// ----------------------------------------------------------------------
rule <instrs> #assertTableElem (KEY , VAL) _MSG => . ... </instrs>
<curModIdx> CUR </curModIdx>
<moduleInst>
<modIdx> CUR </modIdx>
<tabAddrs> 0 |-> ADDR </tabAddrs>
...
</moduleInst>
<tabs>
<tabInst>
<tAddr> ADDR </tAddr>
<tdata> ... KEY |-> VAL ... </tdata>
...
</tabInst>
...
</tabs>
```
### Memory Assertions
This checks that the last allocated memory has the given size and max value.
```k
syntax Assertion ::= "#assertMemory" Index Int OptionalInt WasmString
// ---------------------------------------------------------------------
rule <instrs> #assertMemory TFIDX SIZE MAX _MSG => . ... </instrs>
<curModIdx> CUR </curModIdx>
<moduleInst>
<modIdx> CUR </modIdx>
<memIds> IDS </memIds>
<memAddrs> #ContextLookup(IDS, TFIDX) |-> ADDR </memAddrs>
...
</moduleInst>
<mems>
<memInst>
<mAddr> ADDR </mAddr>
<mmax> MAX </mmax>
<msize> SIZE </msize>
...
</memInst>
...
</mems>
syntax Assertion ::= "#assertMemoryData" "(" Int "," Int ")" WasmString
syntax Assertion ::= "#assertMemoryData" Int "(" Int "," Int ")" WasmString
// ---------------------------------------------------------------------------
rule <instrs> #assertMemoryData (KEY , VAL) MSG => #assertMemoryData CUR (KEY, VAL) MSG ... </instrs>
<curModIdx> CUR </curModIdx>
rule <instrs> #assertMemoryData MODIDX (KEY , VAL) _MSG => . ... </instrs>
<moduleInst>
<modIdx> MODIDX </modIdx>
<memAddrs> 0 |-> ADDR </memAddrs>
...
</moduleInst>
<mems>
<memInst>
<mAddr> ADDR </mAddr>
<mdata> BM </mdata>
...
</memInst>
...
</mems>
requires #getRange(BM, KEY, 1) ==Int VAL
```
### Module Assertions
These assertions test (and delete) module instances.
These assertions act on the last module defined.
```k
syntax Assertion ::= "#assertNamedModule" Identifier WasmString
// ---------------------------------------------------------------
rule <instrs> #assertNamedModule NAME _S => . ... </instrs>
<moduleIds> ... NAME |-> IDX ... </moduleIds>
<moduleInstances>
<moduleInst>
<modIdx> IDX </modIdx>
...
</moduleInst>
...
</moduleInstances>
```
The modules are cleaned all together after the test file is executed.
Registry Assertations
---------------------
We also want to be able to test that the embedder's registration function is working.
```k
syntax Assertion ::= "#assertRegistrationUnnamed" WasmString WasmString
| "#assertRegistrationNamed" WasmString Identifier WasmString
// ----------------------------------------------------------------------------------
rule <instrs> #assertRegistrationUnnamed REGNAME _ => . ... </instrs>
<modIdx> IDX </modIdx>
<moduleRegistry> ... REGNAME |-> IDX ... </moduleRegistry>
rule <instrs> #assertRegistrationNamed REGNAME _NAME _ => . ... </instrs>
<modIdx> IDX </modIdx>
<moduleRegistry> ... REGNAME |-> IDX ... </moduleRegistry>
```
```k
endmodule
```

View file

@ -0,0 +1,5 @@
(module
(memory 1)
(data (i32.const 0) "WASM")
(data (i32.const 3) "foo\AA\01")
)

View file

@ -0,0 +1,7 @@
(module
(table 10 funcref)
(elem (i32.const 0) 0 0 $foo)
(elem (i32.const 3) 1 1 $foo)
(func)
(func)
(func $foo))

View file

@ -0,0 +1,6 @@
(module
(func (export "foo"))
(func (export "fom"))
(memory (export "baz") 1 1)
(global (export "faz") (mut f64) (f64.const 0))
(table (export "bar") 1 3 funcref))

View file

@ -0,0 +1,3 @@
(module
(func (param i32 i64) (local f64))
)

View file

@ -0,0 +1,3 @@
(module
(global i64 (i64.const 0))
(global (mut i32) (i32.const 1)))

View file

@ -0,0 +1,6 @@
(module
(type (func (param i32) (result i64)))
(import "env" "foo" (func))
(import "env" "baz" (global (mut f64)))
(import "env" "faz" (memory 1 3))
(import "env" "bar" (table 1 3 funcref)))

View file

@ -0,0 +1,175 @@
(module
(memory 0)
(table 100 funcref)
(global (mut i32) (i32.const 0))
(func (param i32 i64) (local f64))
(func (local i32)
;; `unreachable` and `drop` are inserted as needed to
;; ensure correct typing.
unreachable
;; Numeric Instrs
;; --------------
;; Commented out instructions are not currently supported by py-wasm.
i32.const 0 drop
f32.const 0 drop
;; i32.extend8_s drop
;; i32.extend16_s drop
;; i64.extend32_s drop
i32.wrap_i64 drop
i64.extend_i32_s drop
i64.extend_i32_u drop
i32.trunc_f64_s drop
i64.trunc_f32_u drop
;; i32.trunc_sat_f64_s drop
;; i32.trunc_sat_f64_u drop
f32.demote_f64 drop
f64.promote_f32 drop
f64.convert_i32_s drop
f64.convert_i32_u drop
;; i32.reinterpret_f32 drop
;; f64.reinterpret_i64 drop
;; --
unreachable
;; IUnOp
;; -----
i32.clz
i32.ctz
i32.popcnt
;; --
unreachable
;; IBinOp
;; ------
i64.add
i64.sub
i64.mul
i64.div_s
i64.div_u
i64.rem_s
i64.rem_u
i64.and
i64.or
i64.xor
i64.shl
i64.shr_s
i64.shr_u
i64.rotl
i64.rotr
;; --
unreachable
;; FUnOp
;; -----
f32.abs
f32.neg
f32.sqrt
f32.ceil
f32.floor
f32.trunc
f32.nearest
;; --
unreachable
;; FBinOp
;; ------
f64.add
f64.sub
f64.mul
f64.div
f64.min
f64.max
f64.copysign
;; --
unreachable
;; ITestop
;; -------
i32.eqz
;; --
unreachable
;; IRelOp
;; ------
i32.eq
i32.ne
i32.lt_s
i32.lt_u
i32.gt_s
i32.gt_u
i32.le_s
i32.le_u
i32.ge_s
i32.ge_u
;; --
unreachable
;; FRelOp
;; ------
f64.eq drop
f64.ne drop
f64.lt drop
f64.gt drop
f64.le drop
f64.ge drop
;; --
unreachable
;; Stack Instrs
;; ------------
drop
select
;; --
unreachable
;; Variable Instrs
;; ---------------
local.get 0
local.set 0
local.tee 0
global.get 0
global.set 0
;; --
unreachable
;; Memory Instrs
;; -------------
i32.load offset=10 drop
f32.load offset=10 drop
i32.store offset=10 drop
f32.store offset=10 drop
i32.load8_s offset=10 drop
i32.load8_u offset=10 drop
i32.load16_s offset=1 drop
i32.load16_u offset=1 drop
i64.load32_s offset=1 drop
i64.load32_u offset=1 drop
i32.store8 offset=10 drop
i32.store16 offset=10 drop
i64.store32 offset=10 drop
memory.size
memory.grow
;; --
unreachable
;; Control Instrs
;; --------------
nop
unreachable
block (result i32) unreachable end
block (result i32) unreachable nop end
block end
if (result f32) unreachable else unreachable end
loop unreachable end
br 0
br_if 0
br_table 0 0 0
return
call 0
call_indirect (type 0)
;; --
unreachable
)
)

View file

@ -0,0 +1,2 @@
(module
(memory 1))

View file

@ -0,0 +1,3 @@
(module
(func $first)
(start $first))

View file

@ -0,0 +1,2 @@
(module
(table 1 funcref))

View file

@ -0,0 +1,5 @@
(module
(type (func ))
(type (func (param i32 i64 f32 f64) (result i32)))
(type $foo (func (param i32 i64 f32 f64) (result i32)))
)

View file

@ -0,0 +1,65 @@
#!/bin/bash
# Execute the script from project top directory.
# This script considers all conformance tests currently listed as unparseable or unsupported, and tries parsing and executing them in turn.
# If both steps are successfull, the confromance test is deleted from the lists.
# If either step fails, the conformance test is added to the correct list and removed from the other.
unparsefile="tests/conformance/unparseable.txt"
unparseable=$(cat $unparsefile)
backends="llvm"
# Try all unparseable files, see if any new one parses and add them to the unsupported lists.
# Keep the files sorted and without duplicates so the diff is easy to follow.
for shortname in $unparseable $unsupported; do
file=tests/wasm-tests/test/core/$shortname
echo Trying to parse: $shortname
echo ===============
make $file.parse
if [ $? -eq 0 ]
then
sed --in-place "/^$shortname\$/d" $unparsefile
for backend in $backends; do
unsuppfile="tests/conformance/unsupported-$backend.txt"
echo $shortname >> $unsuppfile
sort -u $unsuppfile -o $unsuppfile
done
else
echo $shortname >> $unparsefile
sort -u $unparsefile -o $unparsefile
for backend in $backends; do
unsuppfile="tests/conformance/unsupported-$backend.txt"
sed --in-place "/^$shortname\$/d" $unsuppfile
done
echo "Unparseable: $shortname\n"
fi
done
# Go over the unsupported files, see if any has become supported.
for backend in $backends; do
unsuppfile="tests/conformance/unsupported-$backend.txt"
unsupported=$(cat $unsuppfile)
for shortname in $unsupported; do
file=tests/wasm-tests/test/core/$shortname
echo Trying to run: $shortname
echo =============
make $file.run-term TEST_CONCRETE_BACKEND=$backend
if [ $? -eq 0 ]
then
# Now supported, remove.
sed --in-place "/^$shortname\$/d" $unparsefile
sed --in-place "/^$shortname\$/d" $unsuppfile
sort -u $unsuppfile -o $unsuppfile
echo "New supported ($backend): $shortname\n"
else
echo "Unsupported ($backend): $shortname\n"
fi
done
done

View file

@ -0,0 +1,14 @@
br_table.wast
comments.wast
conversions.wast
endianness.wast
f32_cmp.wast
f32.wast
f64_cmp.wast
f64.wast
float_exprs.wast
float_literals.wast
globals.wast
memory.wast
names.wast
skip-stack-guard-page.wast

View file

@ -0,0 +1,15 @@
address.wast
align.wast
call_indirect.wast
const.wast
data.wast
elem.wast
f32_bitwise.wast
f64_bitwise.wast
float_memory.wast
float_misc.wast
imports.wast
left-to-right.wast
memory_redundancy.wast
memory_trap.wast
traps.wast

View file

@ -0,0 +1,4 @@
tests/simple/f64_bitwise-cs.wast
tests/simple/f64_cmp-cs.wast
tests/simple/f64-cs.wast
tests/simple/fconversions-c.wast

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2015 K Team. All Rights Reserved. -->
<tests>
<test
definition="wasm.k"
programs="."
extension="wasm"
results="." >
<all-programs>
<krun-option name="--smt" value="none" />
<krun-option name="--output" value="none" />
</all-programs>
</test>
</tests>

View file

@ -0,0 +1,52 @@
// (c) 2015 Andreas Rossberg
(module
// Recursive factorial
(func (param i32) (result i32)
(local) // opt
(return
(if
(eq.i32 (getlocal 0) (const.i32 0))
(const.i32 1)
(mul.i32 (getlocal 0) (call 0 (sub.i32 (getlocal 0) (const.i32 1))))
)
)
)
// Recursive factorial with implicit return value
(func (param i32) (result i32)
(local) // opt
(if
(eq.i32 (getlocal 0) (const.i32 0))
(const.i32 1)
(mul.i32 (getlocal 0) (call 0 (sub.i32 (getlocal 0) (const.i32 1))))
)
)
// Iterative factorial
(func (param i32) (result i32)
(local i32)
(setlocal 1 (const.i32 1))
(label
(loop
(if
(eq.i32 (getlocal 0) (const.i32 0))
(break 0)
(block
(setlocal 1 (mul.i32 (getlocal 0) (getlocal 1)))
(setlocal 0 (sub.i32 (getlocal 0) (const.i32 1)))
)
)
)
)
(return (getlocal 1))
)
(export 0 1 2)
(memory 0)
)
(invoke 0 (const.i32 3))
(invoke 1 (const.i32 3))
(invoke 2 (const.i32 3))

View file

@ -0,0 +1,3 @@
6
6
6

View file

@ -0,0 +1,69 @@
// (c) 2015 Andreas Rossberg
(module
// Aligned read/write
(func (param) (result i32)
(local i32 i32 i32)
(setlocal 0 (const.i32 10))
(label
(loop
(if
(eq.i32 (getlocal 0) (const.i32 0))
(break)
)
(setlocal 2 (mul.i32 (getlocal 0) (const.i32 4)))
(setnears.i32 (getlocal 2) (getlocal 0))
(setlocal 1 (getnears.i32 (getlocal 2)))
(if
(neq.i32 (getlocal 0) (getlocal 1))
(return (const.i32 0))
)
(setlocal 0 (sub.i32 (getlocal 0) (const.i32 1)))
)
)
(return (const.i32 1))
)
// Unaligned read/write
(func (param) (result i32)
(local i32 i32 i32)
(setlocal 0 (const.i32 10))
(label
(loop
(if
(eq.i32 (getlocal 0) (const.i32 0))
(break)
)
(setlocal 2 (getlocal 0))
(setnearunaligneds.i32 (getlocal 0) (getlocal 2))
(setlocal 1 (getnearunaligneds.i32 (getlocal 0)))
(if
(neq.i32 (getlocal 2) (getlocal 1))
(return (const.i32 0))
)
(setlocal 0 (sub.i32 (getlocal 0) (const.i32 1)))
)
)
(return (const.i32 1))
)
// Memory cast
(func (param) (result f64)
(local)
(setnears.i64 (const.i32 8) (const.i64 -12345))
(if
(eq.f64 (getnear.f64 (const.i32 8)) (cast.i64.f64 (const.i64 -12345)))
(return (const.f64 0))
)
(setfarunaligneds.i16 (const.i64 3) (const.i32 -23423))
(return (getnear.f64 (const.i32 0)))
)
(export 0 1 2)
(memory 64)
)
(invoke 0)
(invoke 1)
//(invoke 2)

Some files were not shown because too many files have changed in this diff Show more