authenticity is all you need
|
@ -1,7 +1,8 @@
|
|||
[package]
|
||||
name = "nexus"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2033"
|
||||
|
||||
[dependencies]
|
||||
c2pa = "0.21.0"
|
||||
yrs = "0.16.5"
|
||||
|
|
33
docs/runtimeverification-wasm-semantics/.github/actions/with-docker/action.yml
vendored
Normal 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
|
26
docs/runtimeverification-wasm-semantics/.github/workflows/master-push.yml
vendored
Normal 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}'"}}'
|
123
docs/runtimeverification-wasm-semantics/.github/workflows/test-pr.yml
vendored
Normal 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}
|
45
docs/runtimeverification-wasm-semantics/.github/workflows/update-version.yml
vendored
Normal 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
|
10
docs/runtimeverification-wasm-semantics/.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
/.build/*
|
||||
/tests/*/*-out
|
||||
|
||||
*.pdf
|
||||
*.sty
|
||||
|
||||
.krun*
|
||||
.kprove*
|
||||
.kompile*
|
||||
|
12
docs/runtimeverification-wasm-semantics/.gitmodules
vendored
Normal 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
|
63
docs/runtimeverification-wasm-semantics/CONTRIBUTING.md
Normal 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.
|
43
docs/runtimeverification-wasm-semantics/Dockerfile
Normal 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
|
59
docs/runtimeverification-wasm-semantics/LICENSE
Normal 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.
|
246
docs/runtimeverification-wasm-semantics/Makefile
Normal 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
|
163
docs/runtimeverification-wasm-semantics/README.md
Normal 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/>
|
37
docs/runtimeverification-wasm-semantics/auxil.md
Normal 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
|
||||
```
|
1
docs/runtimeverification-wasm-semantics/binary-parser/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
__pycache__/
|
|
@ -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)
|
||||
```
|
|
@ -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])
|
|
@ -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)
|
|
@ -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'
|
1
docs/runtimeverification-wasm-semantics/binary-parser/wasm
Symbolic link
|
@ -0,0 +1 @@
|
|||
../deps/py-wasm/wasm
|
|
@ -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()
|
39
docs/runtimeverification-wasm-semantics/convert.py
Normal 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()
|
606
docs/runtimeverification-wasm-semantics/data.md
Normal 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
|
||||
```
|
1
docs/runtimeverification-wasm-semantics/deps/k_release
Normal file
|
@ -0,0 +1 @@
|
|||
5.6.7
|
1
docs/runtimeverification-wasm-semantics/deps/pyk_release
Normal file
|
@ -0,0 +1 @@
|
|||
v0.1.232
|
181
docs/runtimeverification-wasm-semantics/kwasm
Executable 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
|
344
docs/runtimeverification-wasm-semantics/kwasm-lemmas.md
Normal 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
|
||||
```
|
|
@ -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
|
||||
---------------------------------
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
|
@ -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
|
||||
------------
|
||||
|
||||
{ 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
|
||||
-------
|
||||
|
||||
{ width=20% align=center style="margin-bottom:40px"}
|
||||
|
||||
- Contacted friends at MakerDAO.
|
||||
- They have verified the core contracts of their "stablecoin", Dai.
|
||||
|
||||
. . .
|
||||
|
||||
{ width=40% align=center style="margin-bottom:40px"}
|
||||
|
||||
- The verification was largely done by a related organization, DappHub ...
|
||||
|
||||
. . .
|
||||
|
||||
{ height=15% hspace=30px }
|
||||
{ height=15% hspace=30px }
|
||||
<!-- { 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
|
||||
--------
|
||||
|
||||

|
||||
|
||||
- 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/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?
|
||||
=====================
|
||||
|
||||
|
||||
-->
|
|
@ -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 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
|
||||
----------------
|
||||
|
||||

|
||||
|
||||
- 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
|
||||
|
|
@ -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].
|
||||
|
||||

|
||||
|
||||
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
|
||||
==========
|
|
@ -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 
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||

|
||||
|
||||
* 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:
|
||||
|
||||
[](https://github.com/runtimeverification/verified-smart-contracts)
|
||||
|
||||
Introduction to K
|
||||
=================
|
||||
|
||||
The Vision: Language Independence
|
||||
---------------------------------
|
||||
|
||||

|
||||
|
||||
|
||||
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
|
||||
|
|
@ -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
|
||||
------
|
||||
|
||||

|
||||
|
||||
* 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
|
||||
|
|
@ -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
|
||||
------------
|
||||
|
||||
{ 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
|
||||
----------------
|
||||
|
||||

|
||||
|
||||
* 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
|
||||
{ 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>
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
init_locals < i32 > 4 : < i32 > 24 : .ValStack
|
||||
|
||||
(local.get 1)
|
||||
(local.get 0)
|
||||
(i32.div_u)
|
||||
(local.set 0)
|
15
docs/runtimeverification-wasm-semantics/media/berlin-demo/pre-run.sh
Executable 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 -
|
234
docs/runtimeverification-wasm-semantics/media/citations.md
Normal 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 (popl’15)
|
||||
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 (pldi’15)
|
||||
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 (pldi’15)
|
||||
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 (oopsla’16)
|
||||
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'
|
||||
|
||||
...
|
||||
|
354
docs/runtimeverification-wasm-semantics/media/ieee.csl
Normal 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>
|
||||
|
|
@ -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 |
BIN
docs/runtimeverification-wasm-semantics/media/img/dapphub.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
docs/runtimeverification-wasm-semantics/media/img/ethereum.png
Normal file
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 112 KiB |
After Width: | Height: | Size: 209 KiB |
BIN
docs/runtimeverification-wasm-semantics/media/img/k-overview.png
Normal file
After Width: | Height: | Size: 218 KiB |
BIN
docs/runtimeverification-wasm-semantics/media/img/k.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/runtimeverification-wasm-semantics/media/img/kevm-paper.png
Normal file
After Width: | Height: | Size: 201 KiB |
BIN
docs/runtimeverification-wasm-semantics/media/img/maker.png
Normal file
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 122 KiB |
BIN
docs/runtimeverification-wasm-semantics/media/img/rv.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
docs/runtimeverification-wasm-semantics/media/k-overview.png
Normal file
After Width: | Height: | Size: 218 KiB |
3
docs/runtimeverification-wasm-semantics/media/memory-demo/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
.krun-*
|
||||
*-kompiled
|
||||
|
|
@ -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
|
||||
```
|
|
@ -0,0 +1,6 @@
|
|||
(i64.const 10)
|
||||
(memory)
|
||||
(memory.size)
|
||||
(memory.grow (i32.const 5))
|
||||
(memory.size)
|
||||
|
|
@ -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
|
144
docs/runtimeverification-wasm-semantics/media/memory-demo/wasm.k
Normal 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>
|
||||
|
||||
*/
|
455
docs/runtimeverification-wasm-semantics/numeric.md
Normal 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
|
||||
```
|
37
docs/runtimeverification-wasm-semantics/preprocessor.py
Normal 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()
|
18
docs/runtimeverification-wasm-semantics/pykwasm/.cruft.json
Normal 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
|
||||
}
|
6
docs/runtimeverification-wasm-semantics/pykwasm/.flake8
Normal 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
|
3
docs/runtimeverification-wasm-semantics/pykwasm/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/dist/
|
||||
__pycache__/
|
||||
.coverage
|
84
docs/runtimeverification-wasm-semantics/pykwasm/Makefile
Normal 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
|
23
docs/runtimeverification-wasm-semantics/pykwasm/README.md
Normal 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.
|
41
docs/runtimeverification-wasm-semantics/pykwasm/flake.nix
Normal 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;
|
||||
};
|
||||
}
|
1075
docs/runtimeverification-wasm-semantics/pykwasm/poetry.lock
generated
Normal 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
|
|
@ -0,0 +1,2 @@
|
|||
def hello(name: str) -> str:
|
||||
return f'Hello, {name}!'
|
|
@ -0,0 +1,5 @@
|
|||
from pykwasm.hello import hello
|
||||
|
||||
|
||||
def test_hello() -> None:
|
||||
assert hello('World') == 'Hello, World!'
|
|
@ -0,0 +1,5 @@
|
|||
from pykwasm.hello import hello
|
||||
|
||||
|
||||
def test_hello() -> None:
|
||||
assert hello('World') == 'Hello, World!'
|
530
docs/runtimeverification-wasm-semantics/test.md
Normal 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
|
||||
```
|
|
@ -0,0 +1,5 @@
|
|||
(module
|
||||
(memory 1)
|
||||
(data (i32.const 0) "WASM")
|
||||
(data (i32.const 3) "foo\AA\01")
|
||||
)
|
|
@ -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))
|
|
@ -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))
|
|
@ -0,0 +1,3 @@
|
|||
(module
|
||||
(func (param i32 i64) (local f64))
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
(module
|
||||
(global i64 (i64.const 0))
|
||||
(global (mut i32) (i32.const 1)))
|
|
@ -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)))
|
175
docs/runtimeverification-wasm-semantics/tests/binary/instrs.wat
Normal 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
|
||||
)
|
||||
|
||||
)
|
|
@ -0,0 +1,2 @@
|
|||
(module
|
||||
(memory 1))
|
|
@ -0,0 +1,3 @@
|
|||
(module
|
||||
(func $first)
|
||||
(start $first))
|
|
@ -0,0 +1,2 @@
|
|||
(module
|
||||
(table 1 funcref))
|
|
@ -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)))
|
||||
)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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))
|
|
@ -0,0 +1,3 @@
|
|||
6
|
||||
6
|
||||
6
|
|
@ -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)
|