Compare commits

..

11 Commits

Author SHA1 Message Date
xermicus f6a412eef4 release resolc v0.1.0-dev.14 (#289)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-04-24 11:57:46 +02:00
xermicus 20e77cb0b5 configurable stack and heap memory size (#288)
- Allow configuration of the maximum heap and stack size via CLI flags
and JSON input settings.
- Increase the default value for the stack size to 32kb.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-04-24 10:47:38 +02:00
xermicus 357bf58868 allow dynamic configuration of the heap memory (#287)
This PR changes the implementation of the emulated EVM heap memory:
Instead of linking in a C implementation the code is emitted directly
into the contract module. Which allows making it configurable via a
compiler parameter (a follow up PR to this).

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-04-23 20:25:55 +02:00
xermicus f937188991 revive-runner: install with locked dependencies (#286)
Make the installation of  `revive-runner` easier:
- Use locked dependencies to avoid issues with downstream crates
- Make the llvm-context crate an optional dependency to the runner
- Add it to the default `test` target ensuring that this actually works

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-04-22 19:46:18 +02:00
xermicus 6e44488b4f revive-runner: add a utility binary for local contract execution (#284)
I had this in mind for a while but never implemented a standalone binary
so far because I always end up writing an integration test anyways.

However, using a standalone version of the pallet based on the
revive-runner crate is something people filing in bug reports do
anyways, for example:
https://github.com/paritytech/revive/issues/266
https://github.com/paritytech/contract-issues/issues/54
---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-04-22 15:34:51 +02:00
xermicus 5003f3e9ac llvm-context: alloca at the function entry if possible (#283)
Closes  #48

Change the code size test to no longer emit debug info as to get a more
accurate picture.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-04-15 15:22:24 +02:00
xermicus 431b5a2ce5 llvm-context: lazy handling of function arguments and immutable data (#282)
- Lazily load function arguments so that they can be passed as pointers.
- Lazily call the immutable store function to avoid storing zero sized
immutable data.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-04-14 15:54:59 +02:00
xermicus ad3315346c release resolc-0.1.0-dev.13 (#279) 2025-04-08 09:10:13 +02:00
dependabot[bot] 516f79ee0f Bump tokio from 1.43.0 to 1.44.2 (#281)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.43.0 to 1.44.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/tokio/releases">tokio's
releases</a>.</em></p>
<blockquote>
<h2>Tokio v1.44.2</h2>
<p>This release fixes a soundness issue in the broadcast channel. The
channel
accepts values that are <code>Send</code> but <code>!Sync</code>.
Previously, the channel called
<code>clone()</code> on these values without synchronizing. This release
fixes the channel
by synchronizing calls to <code>.clone()</code> (Thanks Austin Bonander
for finding and
reporting the issue).</p>
<h3>Fixed</h3>
<ul>
<li>sync: synchronize <code>clone()</code> call in broadcast channel (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7232">#7232</a>)</li>
</ul>
<p><a
href="https://redirect.github.com/tokio-rs/tokio/issues/7232">#7232</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7232">tokio-rs/tokio#7232</a></p>
<h2>Tokio v1.44.1</h2>
<h1>1.44.1 (March 13th, 2025)</h1>
<h3>Fixed</h3>
<ul>
<li>rt: skip defer queue in <code>block_in_place</code> context (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7216">#7216</a>)</li>
</ul>
<p><a
href="https://redirect.github.com/tokio-rs/tokio/issues/7216">#7216</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7216">tokio-rs/tokio#7216</a></p>
<h2>Tokio v1.44.0</h2>
<h1>1.44.0 (March 7th, 2025)</h1>
<p>This release changes the <code>from_std</code> method on sockets to
panic if a blocking socket is provided. We determined this change is not
a breaking change as Tokio is not intended to operate using blocking
sockets. Doing so results in runtime hangs and should be considered a
bug. Accidentally passing a blocking socket to Tokio is one of the most
common user mistakes. If this change causes an issue for you, please
comment on <a
href="https://redirect.github.com/tokio-rs/tokio/issues/7172">#7172</a>.</p>
<h3>Added</h3>
<ul>
<li>coop: add <code>task::coop</code> module (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7116">#7116</a>)</li>
<li>process: add <code>Command::get_kill_on_drop()</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7086">#7086</a>)</li>
<li>sync: add <code>broadcast::Sender::closed</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6685">#6685</a>,
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7090">#7090</a>)</li>
<li>sync: add <code>broadcast::WeakSender</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7100">#7100</a>)</li>
<li>sync: add <code>oneshot::Receiver::is_empty()</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7153">#7153</a>)</li>
<li>sync: add <code>oneshot::Receiver::is_terminated()</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7152">#7152</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>fs: empty reads on <code>File</code> should not start a background
read (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7139">#7139</a>)</li>
<li>process: calling <code>start_kill</code> on exited child should not
fail (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7160">#7160</a>)</li>
<li>signal: fix <code>CTRL_CLOSE</code>, <code>CTRL_LOGOFF</code>,
<code>CTRL_SHUTDOWN</code> on windows (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7122">#7122</a>)</li>
<li>sync: properly handle panic during mpsc drop (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7094">#7094</a>)</li>
</ul>
<h3>Changes</h3>
<ul>
<li>runtime: clean up magic number in registration set (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7112">#7112</a>)</li>
<li>coop: make coop yield using waker defer strategy (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7185">#7185</a>)</li>
<li>macros: make <code>select!</code> budget-aware (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7164">#7164</a>)</li>
<li>net: panic when passing a blocking socket to <code>from_std</code>
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7166">#7166</a>)</li>
<li>io: clean up buffer casts (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7142">#7142</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/tokio-rs/tokio/commit/ec4b1d7215a3e1e91797ad3fb6ba0f7c7f3d2566"><code>ec4b1d7</code></a>
chore: forward port 1.43.x</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/e3c3a56718d201fb7bb430567f05fbb64b2ef082"><code>e3c3a56</code></a>
Merge branch 'tokio-1.43.x' into forward-port-1.43.x</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/a7b658c35bd40f6811e557aeb97cbb361b612c56"><code>a7b658c</code></a>
chore: prepare Tokio v1.43.1 release</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/c1c8d1033d637d7027fdc137ec8008c5801cbc0d"><code>c1c8d10</code></a>
Merge remote-tracking branch 'origin/tokio-1.38.x' into
forward-port-1.38.x</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/aa303bc2051f7c21b48bb7bfcafe8fd4f39afd21"><code>aa303bc</code></a>
chore: prepare Tokio v1.38.2 release</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/7b6ccb515ff067151ed62db835f735e5653f8784"><code>7b6ccb5</code></a>
chore: backport CI fixes</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/4b174ce2c95fe1d1a217917db93fcc935e17e0da"><code>4b174ce</code></a>
sync: fix cloning value when receiving from broadcast channel</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/d413c9c02af8f2b4fea14b769b86484b12f46595"><code>d413c9c</code></a>
chore: prepare Tokio v1.44.1 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7217">#7217</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/addbfb9204be25a8621feb3f20b44a7c1f00edbd"><code>addbfb9</code></a>
rt: skip defer queue in <code>block_in_place</code> context (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7216">#7216</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/8182ecf2628d5e80dac52b8ed1ea466dbb0925b9"><code>8182ecf</code></a>
chore: prepare Tokio v1.44.0 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7202">#7202</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-1.43.0...tokio-1.44.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tokio&package-manager=cargo&previous-version=1.43.0&new-version=1.44.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/paritytech/revive/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: xermicus <cyrill@parity.io>
2025-04-08 08:26:06 +02:00
Alexander Samusev 9f5443b6d6 Remove path from release json and add sha256 (#280)
PR addresses
https://github.com/paritytech/revive/issues/162#issuecomment-2783173447
2025-04-08 08:18:35 +02:00
Alexander Samusev 0dafc779ce ci: update release flow and publish list.json (#276)
This pull request adds changes described in **this comment**:

- `.github/scripts/json_generator.py` - a small script that generates
several json files for different platforms.
-
[generate_versions.yml](https://github.com/paritytech/revive/compare/as-release-json?expand=1#diff-2aee05b96020ac60943e6dfcb30597e53898f31542aeb570468b970d9a13a5a6)
- the workflow that runs when release is published, creates info.json
files and pushes them into resolc-bin repo
- `js/build.js` is adjusted in order to set `RESOLC_WASM_URI` from env
variable
- ⚠️ Release workflow is changed: 
  - In PRs and main branch it'll only build artifacts
- Release will happen automatically only on the `v*` tag push. This is
needed for revive_web.js to have the necessary `RESOLC_WASM_URI`
- workflow will check that version in Cargo.toml is the same as the tag
when the new tag is pushed

cc https://github.com/paritytech/revive/issues/162
cc https://github.com/paritytech/devops/issues/3890
2025-04-07 13:39:56 +02:00
48 changed files with 926 additions and 323 deletions
+55 -9
View File
@@ -2,7 +2,6 @@ import os
import sys
import json
import requests
from datetime import datetime
def validate_github_token():
"""Validate that GITHUB_TOKEN environment variable is set."""
@@ -27,26 +26,70 @@ def fetch_release_data(repo, tag):
print(f"Error fetching release data: {e}")
sys.exit(1)
def fetch_checksum_file(release_data):
"""
Fetch the checksum.txt file from the release assets
and parse it into a dictionary mapping file names to their SHA256 checksums.
"""
checksums = {}
# Find the checksum.txt asset URL
checksum_asset = None
for asset in release_data['assets']:
if asset['name'] == 'checksums.txt':
checksum_asset = asset
break
if not checksum_asset:
print("Warning: checksum.txt file not found in release assets.")
return checksums
# Download the checksum file
headers = {
'Authorization': f"Bearer {os.environ['GITHUB_TOKEN']}",
'Accept': 'application/octet-stream'
}
try:
response = requests.get(checksum_asset['browser_download_url'], headers=headers)
response.raise_for_status()
# Parse checksum file
for line in response.text.splitlines():
if line.strip():
checksum, filename = line.strip().split(None, 1)
checksums[filename] = checksum
return checksums
except requests.RequestException as e:
print(f"Error fetching checksum file: {e}")
return checksums
except Exception as e:
print(f"Error parsing checksum file: {e}")
return checksums
def extract_build_hash(target_commitish):
"""Extract the first 8 characters of the commit hash."""
return f"commit.{target_commitish[:8]}"
def generate_asset_json(release_data, asset):
def generate_asset_json(release_data, asset, checksums):
"""Generate JSON for a specific asset."""
version = release_data['tag_name'].lstrip('v')
build = extract_build_hash(release_data['target_commitish'])
long_version = f"{version}+{build}"
path = f"{asset['name']}+{long_version}"
# Get SHA256 checksum if available
sha256 = checksums.get(asset['name'], "")
return {
"path": path,
"name": asset['name'],
"version": version,
"build": build,
"longVersion": long_version,
"url": asset['browser_download_url'],
"firstSolcVersion": os.environ["FIRST_SOLC_VERSION"],
"lastSolcVersion": os.environ["LAST_SOLC_VERSION"]
"sha256": sha256,
"firstSolcVersion": os.environ.get("FIRST_SOLC_VERSION", ""),
"lastSolcVersion": os.environ.get("LAST_SOLC_VERSION", "")
}
def save_platform_json(platform_folder, asset_json, tag):
@@ -69,14 +112,14 @@ def save_platform_json(platform_folder, asset_json, tag):
# Remove any existing entry with the same path
list_data['builds'] = [
build for build in list_data['builds']
if build['path'] != asset_json['path']
if build['version'] != asset_json['version']
]
# Add the new build
list_data['builds'].append(asset_json)
# Update releases
version = asset_json['version']
list_data['releases'][version] = asset_json['path']
list_data['releases'][version] = f"{asset_json['name']}+{asset_json['longVersion']}"
# Update latest release
list_data['latestRelease'] = version
@@ -98,6 +141,9 @@ def main():
# Fetch release data
release_data = fetch_release_data(repo, tag)
# Fetch checksums
checksums = fetch_checksum_file(release_data)
# Mapping of asset names to platform folders
platform_mapping = {
'resolc-x86_64-unknown-linux-musl': 'linux',
@@ -111,7 +157,7 @@ def main():
platform_name = platform_mapping.get(asset['name'])
if platform_name:
platform_folder = os.path.join(platform_name)
asset_json = generate_asset_json(release_data, asset)
asset_json = generate_asset_json(release_data, asset, checksums)
save_platform_json(platform_folder, asset_json, tag)
print(f"Processed {asset['name']} for {platform_name}")
+10
View File
@@ -249,6 +249,15 @@ jobs:
chmod +x resolc-x86_64-unknown-linux-musl
chmod +x resolc-universal-apple-darwin
- name: Create sha-256 checksum
run: |
shasum -a 256 resolc-x86_64-unknown-linux-musl > checksums.txt
shasum -a 256 resolc-universal-apple-darwin >> checksums.txt
shasum -a 256 resolc-x86_64-pc-windows-msvc.exe >> checksums.txt
shasum -a 256 resolc.js >> checksums.txt
shasum -a 256 resolc.wasm >> checksums.txt
shasum -a 256 resolc_web.js >> checksums.txt
- uses: actions/create-github-app-token@v1
id: app-token
with:
@@ -276,3 +285,4 @@ jobs:
resolc.js
resolc.wasm
resolc_web.js
checksums.txt
+29 -1
View File
@@ -6,6 +6,34 @@ This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added
### Changed
### Fixed
## v0.1.0-dev.14
This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added
- The `revive-runner` helper utility binary which helps to run contracts locally without a blockchain node.
- Allow configuration of the EVM heap memory size and stack size via CLI flags and JSON input settings.
### Changed
- The default PVM stack memory size was increased from 16kb to 32kb.
### Fixed
- Constructors avoid storing zero sized immutable data on exit.
## v0.1.0-dev.13
This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added
- Support for solc v0.8.29
- Decouples the solc JSON-input-output type definitions from the Solidity fronted and expose them via a dedicated crate.
@@ -14,7 +42,7 @@ Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Changed
- Storage keys and values are big endian. This was a pre-mature optimization because for the contract itself it this is a no-op and thus not observable. However we should consider the storage layout as part of the contract ABI. The endianness of transient storage values are still kept as-is.
- Runner `resolc` using webkit is no longer supported.
- Running `resolc` using webkit is no longer supported.
### Fixed
- A missing byte swap for the create2 salt value.
Generated
+20 -17
View File
@@ -4553,7 +4553,7 @@ checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
[[package]]
name = "lld-sys"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"cc",
"libc",
@@ -8266,7 +8266,7 @@ dependencies = [
[[package]]
name = "revive-benchmarks"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"alloy-primitives",
"criterion",
@@ -8278,18 +8278,18 @@ dependencies = [
[[package]]
name = "revive-build-utils"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
[[package]]
name = "revive-builtins"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"revive-build-utils",
]
[[package]]
name = "revive-common"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"anyhow",
"serde",
@@ -8299,7 +8299,7 @@ dependencies = [
[[package]]
name = "revive-differential"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"alloy-genesis",
"alloy-primitives",
@@ -8312,7 +8312,7 @@ dependencies = [
[[package]]
name = "revive-integration"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"alloy-primitives",
"alloy-sol-types",
@@ -8328,7 +8328,7 @@ dependencies = [
[[package]]
name = "revive-linker"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"anyhow",
"libc",
@@ -8340,7 +8340,7 @@ dependencies = [
[[package]]
name = "revive-llvm-builder"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"anyhow",
"assert_cmd",
@@ -8362,7 +8362,7 @@ dependencies = [
[[package]]
name = "revive-llvm-context"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"anyhow",
"hex",
@@ -8384,9 +8384,12 @@ dependencies = [
[[package]]
name = "revive-runner"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"alloy-primitives",
"anyhow",
"clap",
"env_logger 0.11.6",
"hex",
"parity-scale-codec",
"polkadot-sdk 0.1.0",
@@ -8400,7 +8403,7 @@ dependencies = [
[[package]]
name = "revive-runtime-api"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"anyhow",
"inkwell",
@@ -8410,7 +8413,7 @@ dependencies = [
[[package]]
name = "revive-solc-json-interface"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"anyhow",
"rayon",
@@ -8422,7 +8425,7 @@ dependencies = [
[[package]]
name = "revive-solidity"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"anyhow",
"clap",
@@ -8449,7 +8452,7 @@ dependencies = [
[[package]]
name = "revive-stdlib"
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
dependencies = [
"inkwell",
"revive-build-utils",
@@ -11117,9 +11120,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.43.0"
version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
+15 -15
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"]
[workspace.package]
version = "0.1.0-dev.12"
version = "0.1.0-dev.14"
authors = [
"Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>",
@@ -14,20 +14,20 @@ repository = "https://github.com/paritytech/revive"
rust-version = "1.81.0"
[workspace.dependencies]
revive-benchmarks = { version = "0.1.0-dev.12", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.12", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.12", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.12", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.12", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.12", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.12", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.12", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.12", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.12", path = "crates/runner" }
revive-solc-json-interface = { version = "0.1.0-dev.12", path = "crates/solc-json-interface" }
revive-solidity = { version = "0.1.0-dev.12", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.12", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.12", path = "crates/build-utils" }
revive-benchmarks = { version = "0.1.0-dev.14", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.14", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.14", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.14", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.14", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.14", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.14", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.14", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.14", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.14", path = "crates/runner" }
revive-solc-json-interface = { version = "0.1.0-dev.14", path = "crates/solc-json-interface" }
revive-solidity = { version = "0.1.0-dev.14", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.14", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.14", path = "crates/build-utils" }
hex = "0.4.3"
cc = "1.2"
+1 -1
View File
@@ -11,7 +11,7 @@ RUN make install-llvm-builder
RUN revive-llvm --target-env musl clone
RUN revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang
FROM messense/rust-musl-cross:x86_64-musl AS resolc-builder
FROM messense/rust-musl-cross@sha256:68b86bc7cb2867259e6b233415a665ff4469c28b57763e78c3bfea1c68091561 AS resolc-builder
WORKDIR /opt/revive
RUN apt update && \
+5 -1
View File
@@ -5,6 +5,7 @@
install-wasm \
install-llvm-builder \
install-llvm \
install-revive-runner \
format \
clippy \
machete \
@@ -39,6 +40,9 @@ install-llvm: install-llvm-builder
revive-llvm clone
revive-llvm build --llvm-projects lld --llvm-projects clang
install-revive-runner:
cargo install --path crates/runner --no-default-features --locked
format:
cargo fmt --all --check
@@ -49,7 +53,7 @@ machete:
cargo install cargo-machete
cargo machete
test: format clippy machete test-cli test-workspace
test: format clippy machete test-cli test-workspace install-revive-runner
test-integration: install-bin
cargo test --package revive-integration
+24 -24
View File
@@ -15,58 +15,58 @@
### Baseline
| | `EVM` | `PVMInterpreter` |
|:--------|:------------------------|:-------------------------------- |
| **`0`** | `3.36 us` (✅ **1.00x**) | `11.84 us` (*3.52x slower*) |
| | `EVM` | `PVMInterpreter` |
|:--------|:-------------------------|:-------------------------------- |
| **`0`** | `10.08 us` (✅ **1.00x**) | `10.32 us` (**1.02x slower**) |
### OddPorduct
| | `EVM` | `PVMInterpreter` |
|:-------------|:-------------------------|:-------------------------------- |
| **`10000`** | `3.11 ms` (✅ **1.00x**) | `1.53 ms` (🚀 **2.03x faster**) |
| **`100000`** | `30.70 ms` (✅ **1.00x**) | `15.54 ms` (🚀 **1.98x faster**) |
| **`300000`** | `92.68 ms` (✅ **1.00x**) | `45.47 ms` (🚀 **2.04x faster**) |
| | `EVM` | `PVMInterpreter` |
|:-------------|:--------------------------|:-------------------------------- |
| **`10000`** | `3.60 ms` (✅ **1.00x**) | `1.57 ms` (🚀 **2.28x faster**) |
| **`100000`** | `34.72 ms` (✅ **1.00x**) | `14.82 ms` (🚀 **2.34x faster**) |
| **`300000`** | `105.01 ms` (✅ **1.00x**) | `44.11 ms` (🚀 **2.38x faster**) |
### TriangleNumber
| | `EVM` | `PVMInterpreter` |
|:-------------|:-------------------------|:-------------------------------- |
| **`10000`** | `2.29 ms` (✅ **1.00x**) | `1.09 ms` (🚀 **2.11x faster**) |
| **`100000`** | `22.84 ms` (✅ **1.00x**) | `10.66 ms` (🚀 **2.14x faster**) |
| **`360000`** | `82.29 ms` (✅ **1.00x**) | `37.01 ms` (🚀 **2.22x faster**) |
| **`10000`** | `2.43 ms` (✅ **1.00x**) | `1.12 ms` (🚀 **2.17x faster**) |
| **`100000`** | `24.20 ms` (✅ **1.00x**) | `10.86 ms` (🚀 **2.23x faster**) |
| **`360000`** | `88.69 ms` (✅ **1.00x**) | `38.46 ms` (🚀 **2.31x faster**) |
### FibonacciRecursive
| | `EVM` | `PVMInterpreter` |
|:---------|:--------------------------|:--------------------------------- |
| **`12`** | `135.67 us` (✅ **1.00x**) | `125.02 us` (✅ **1.09x faster**) |
| **`16`** | `903.75 us` (✅ **1.00x**) | `762.79 us` (✅ **1.18x faster**) |
| **`20`** | `6.12 ms` (✅ **1.00x**) | `4.96 ms` (✅ **1.23x faster**) |
| **`24`** | `42.05 ms` (✅ **1.00x**) | `33.86 ms` (✅ **1.24x faster**) |
| **`12`** | `144.17 us` (✅ **1.00x**) | `150.85 us` (✅ **1.05x slower**) |
| **`16`** | `938.71 us` (✅ **1.00x**) | `922.11 us` (✅ **1.02x faster**) |
| **`20`** | `6.54 ms` (✅ **1.00x**) | `6.20 ms` (✅ **1.05x faster**) |
| **`24`** | `45.73 ms` (✅ **1.00x**) | `41.98 ms` (✅ **1.09x faster**) |
### FibonacciIterative
| | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- |
| **`64`** | `15.04 us` (✅ **1.00x**) | `29.45 us` (❌ *1.96x slower*) |
| **`128`** | `26.36 us` (✅ **1.00x**) | `42.19 us` (❌ *1.60x slower*) |
| **`256`** | `48.61 us` (✅ **1.00x**) | `65.71 us` (*1.35x slower*) |
| **`64`** | `23.00 us` (✅ **1.00x**) | `31.88 us` (❌ *1.39x slower*) |
| **`128`** | `35.28 us` (✅ **1.00x**) | `42.43 us` (❌ *1.20x slower*) |
| **`256`** | `60.12 us` (✅ **1.00x**) | `61.20 us` (**1.02x slower**) |
### FibonacciBinet
| | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- |
| **`64`** | `15.22 us` (✅ **1.00x**) | `41.46 us` (❌ *2.72x slower*) |
| **`128`** | `17.05 us` (✅ **1.00x**) | `42.84 us` (❌ *2.51x slower*) |
| **`256`** | `19.00 us` (✅ **1.00x**) | `44.36 us` (❌ *2.34x slower*) |
| **`64`** | `23.01 us` (✅ **1.00x**) | `47.74 us` (❌ *2.07x slower*) |
| **`128`** | `25.44 us` (✅ **1.00x**) | `49.67 us` (❌ *1.95x slower*) |
| **`256`** | `28.66 us` (✅ **1.00x**) | `53.01 us` (❌ *1.85x slower*) |
### SHA1
| | `EVM` | `PVMInterpreter` |
|:----------|:--------------------------|:--------------------------------- |
| **`1`** | `110.04 us` (✅ **1.00x**) | `216.11 us` (❌ *1.96x slower*) |
| **`64`** | `209.04 us` (✅ **1.00x**) | `309.48 us` (❌ *1.48x slower*) |
| **`512`** | `903.65 us` (✅ **1.00x**) | `980.49 us` (✅ **1.09x slower**) |
| **`1`** | `135.87 us` (✅ **1.00x**) | `243.75 us` (❌ *1.79x slower*) |
| **`64`** | `258.45 us` (✅ **1.00x**) | `355.70 us` (❌ *1.38x slower*) |
| **`512`** | `1.10 ms` (✅ **1.00x**) | `1.09 ms` (✅ **1.01x faster**) |
---
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
+8 -8
View File
@@ -1,10 +1,10 @@
{
"Baseline": 1443,
"Computation": 2788,
"DivisionArithmetics": 9748,
"ERC20": 19150,
"Events": 2201,
"FibonacciIterative": 2041,
"Flipper": 2691,
"SHA1": 8997
"Baseline": 939,
"Computation": 2282,
"DivisionArithmetics": 8849,
"ERC20": 18308,
"Events": 1640,
"FibonacciIterative": 1497,
"Flipper": 2099,
"SHA1": 8243
}
+3 -1
View File
@@ -5,6 +5,7 @@ use std::sync::OnceLock;
pub use self::debug_config::ir_type::IRType as DebugConfigIR;
pub use self::debug_config::DebugConfig;
pub use self::memory::MemoryConfig;
pub use self::optimizer::settings::size_level::SizeLevel as OptimizerSettingsSizeLevel;
pub use self::optimizer::settings::Settings as OptimizerSettings;
pub use self::optimizer::Optimizer;
@@ -28,6 +29,7 @@ pub use self::polkavm::context::function::runtime::entry::Entry as PolkaVMEntryF
pub use self::polkavm::context::function::runtime::revive::Exit as PolkaVMExitFunction;
pub use self::polkavm::context::function::runtime::revive::WordToPointer as PolkaVMWordToPointerFunction;
pub use self::polkavm::context::function::runtime::runtime_code::RuntimeCode as PolkaVMRuntimeCodeFunction;
pub use self::polkavm::context::function::runtime::sbrk::Sbrk as PolkaVMSbrkFunction;
pub use self::polkavm::context::function::runtime::FUNCTION_DEPLOY_CODE as PolkaVMFunctionDeployCode;
pub use self::polkavm::context::function::runtime::FUNCTION_ENTRY as PolkaVMFunctionEntry;
pub use self::polkavm::context::function::runtime::FUNCTION_RUNTIME_CODE as PolkaVMFunctionRuntimeCode;
@@ -60,7 +62,6 @@ pub use self::polkavm::evm::ext_code as polkavm_evm_ext_code;
pub use self::polkavm::evm::immutable as polkavm_evm_immutable;
pub use self::polkavm::evm::immutable::Load as PolkaVMLoadImmutableDataFunction;
pub use self::polkavm::evm::immutable::Store as PolkaVMStoreImmutableDataFunction;
pub use self::polkavm::evm::math as polkavm_evm_math;
pub use self::polkavm::evm::memory as polkavm_evm_memory;
pub use self::polkavm::evm::r#return as polkavm_evm_return;
@@ -75,6 +76,7 @@ pub use self::target_machine::target::Target;
pub use self::target_machine::TargetMachine;
pub(crate) mod debug_config;
pub(crate) mod memory;
pub(crate) mod optimizer;
pub(crate) mod polkavm;
pub(crate) mod target_machine;
+21
View File
@@ -0,0 +1,21 @@
//! The compile time PolkaVM memory configuration settings.
use serde::{Deserialize, Serialize};
/// The PolkaVM memory configuration.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct MemoryConfig {
/// The emulated EVM linear heap memory size in bytes.
pub heap_size: u32,
/// The PVM stack size in bytes.
pub stack_size: u32,
}
impl Default for MemoryConfig {
fn default() -> Self {
Self {
heap_size: 64 * 1024,
stack_size: 32 * 1024,
}
}
}
@@ -9,6 +9,15 @@ pub static XLEN: usize = revive_common::BIT_LENGTH_X32;
/// The calldata size global variable name.
pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize";
/// The heap size global variable name.
pub static GLOBAL_HEAP_SIZE: &str = "__heap_size";
/// The heap memory global variable name.
pub static GLOBAL_HEAP_MEMORY: &str = "__heap_memory";
/// The spill buffer global variable name.
pub static GLOBAL_ADDRESS_SPILL_BUFFER: &str = "address_spill_buffer";
/// The deployer call header size that consists of:
/// - bytecode hash (32 bytes)
pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD;
@@ -4,61 +4,98 @@
#[derive(Debug, Clone)]
pub struct Argument<'ctx> {
/// The actual LLVM operand.
pub value: inkwell::values::BasicValueEnum<'ctx>,
pub value: Value<'ctx>,
/// The original AST value. Used mostly for string literals.
pub original: Option<String>,
/// The preserved constant value, if available.
pub constant: Option<num::BigUint>,
}
/// The function argument can be either a pointer or a integer value.
/// This disambiguation allows for lazy loading of variables.
#[derive(Clone, Debug)]
pub enum Value<'ctx> {
Register(inkwell::values::BasicValueEnum<'ctx>),
Pointer {
pointer: crate::polkavm::context::Pointer<'ctx>,
id: String,
},
}
impl<'ctx> Argument<'ctx> {
/// The calldata offset argument index.
pub const ARGUMENT_INDEX_CALLDATA_OFFSET: usize = 0;
/// The calldata length argument index.
pub const ARGUMENT_INDEX_CALLDATA_LENGTH: usize = 1;
/// A shortcut constructor.
pub fn new(value: inkwell::values::BasicValueEnum<'ctx>) -> Self {
/// A shortcut constructor for register arguments.
pub fn value(value: inkwell::values::BasicValueEnum<'ctx>) -> Self {
Self {
value,
value: Value::Register(value),
original: None,
constant: None,
}
}
/// A shortcut constructor.
pub fn new_with_original(
value: inkwell::values::BasicValueEnum<'ctx>,
original: String,
) -> Self {
/// A shortcut constructor for stack arguments.
pub fn pointer(pointer: crate::polkavm::context::Pointer<'ctx>, id: String) -> Self {
Self {
value,
original: Some(original),
value: Value::Pointer { pointer, id },
original: None,
constant: None,
}
}
/// A shortcut constructor.
pub fn new_with_constant(
value: inkwell::values::BasicValueEnum<'ctx>,
constant: num::BigUint,
) -> Self {
Self {
value,
original: None,
constant: Some(constant),
}
/// Set the original decleratation value.
pub fn with_original(mut self, original: String) -> Self {
self.original = Some(original);
self
}
/// Set the constant value.
pub fn with_constant(mut self, constant: num::BigUint) -> Self {
self.constant = Some(constant);
self
}
/// Returns the inner LLVM value.
pub fn to_llvm(&self) -> inkwell::values::BasicValueEnum<'ctx> {
self.value
///
/// Panics if `self` is a pointer argument.
pub fn _to_llvm_value(&self) -> inkwell::values::BasicValueEnum<'ctx> {
match &self.value {
Value::Register(value) => *value,
Value::Pointer { .. } => unreachable!("invalid register value access"),
}
}
/// Access the underlying value.
///
/// Will emit a stack load if `self` is a pointer argument.
pub fn access<D: crate::polkavm::Dependency + Clone>(
&self,
context: &crate::polkavm::context::Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
match &self.value {
Value::Register(value) => Ok(*value),
Value::Pointer { pointer, id } => context.build_load(*pointer, id),
}
}
/// Access the underlying value.
///
/// Will emit a stack load if `self` is a pointer argument.
pub fn as_pointer<D: crate::polkavm::Dependency + Clone>(
&self,
context: &crate::polkavm::context::Context<'ctx, D>,
) -> anyhow::Result<crate::polkavm::context::Pointer<'ctx>> {
match &self.value {
Value::Register(value) => {
let pointer = context.build_alloca_at_entry(context.word_type(), "pvm_arg");
context.build_store(pointer, *value)?;
Ok(pointer)
}
Value::Pointer { pointer, .. } => Ok(*pointer),
}
}
}
impl<'ctx> From<inkwell::values::BasicValueEnum<'ctx>> for Argument<'ctx> {
fn from(value: inkwell::values::BasicValueEnum<'ctx>) -> Self {
Self::new(value)
Self::value(value)
}
}
@@ -31,6 +31,31 @@ impl Entry {
context.xlen_type().get_undef(),
);
context.set_global(
crate::polkavm::GLOBAL_HEAP_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.xlen_type().const_zero(),
);
let heap_memory_type = context
.byte_type()
.array_type(context.memory_config.heap_size);
context.set_global(
crate::polkavm::GLOBAL_HEAP_MEMORY,
heap_memory_type,
AddressSpace::Stack,
heap_memory_type.const_zero(),
);
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
context.set_global(
crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER,
address_type,
AddressSpace::Stack,
address_type.const_zero(),
);
Ok(())
}
@@ -5,6 +5,7 @@ pub mod deploy_code;
pub mod entry;
pub mod revive;
pub mod runtime_code;
pub mod sbrk;
/// The main entry function name.
pub const FUNCTION_ENTRY: &str = "__entry";
@@ -0,0 +1,144 @@
//! Emulates the linear EVM heap memory via a simulated `sbrk` system call.
use inkwell::values::BasicValue;
use crate::polkavm::context::attribute::Attribute;
use crate::polkavm::context::runtime::RuntimeFunction;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
/// Simulates the `sbrk` system call, reproducing the semantics of the EVM heap memory.
///
/// Parameters:
/// - The `offset` into the emulated EVM heap memory.
/// - The `size` of the allocation emulated EVM heap memory.
///
/// Returns:
/// - A pointer to the EVM heap memory at given `offset`.
///
/// Semantics:
/// - Traps if the offset is out of bounds.
/// - Aligns the total heap memory size to the EVM word size.
/// - Traps if the memory size would be greater than the configured EVM heap memory size.
/// - Maintains the total memory size (`msize`) in global heap size value.
pub struct Sbrk;
impl<D> RuntimeFunction<D> for Sbrk
where
D: Dependency + Clone,
{
const NAME: &'static str = "__sbrk_internal";
const ATTRIBUTES: &'static [Attribute] = &[
Attribute::NoFree,
Attribute::NoRecurse,
Attribute::WillReturn,
];
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context.llvm().ptr_type(Default::default()).fn_type(
&[context.xlen_type().into(), context.xlen_type().into()],
false,
)
}
fn emit_body<'ctx>(
&self,
context: &mut Context<'ctx, D>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let offset = Self::paramater(context, 0).into_int_value();
let size = Self::paramater(context, 1).into_int_value();
let trap_block = context.append_basic_block("trap");
let offset_in_bounds_block = context.append_basic_block("offset_in_bounds");
let is_offset_out_of_bounds = context.builder().build_int_compare(
inkwell::IntPredicate::UGE,
offset,
context.heap_size(),
"offset_out_of_bounds",
)?;
context.build_conditional_branch(
is_offset_out_of_bounds,
trap_block,
offset_in_bounds_block,
)?;
context.set_basic_block(trap_block);
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
context.build_unreachable();
context.set_basic_block(offset_in_bounds_block);
let mask = context
.xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64 - 1, false);
let total_size = context
.builder()
.build_int_add(offset, size, "total_size")?;
let memory_size = context.builder().build_and(
context.builder().build_int_add(total_size, mask, "mask")?,
context.builder().build_not(mask, "mask_not")?,
"memory_size",
)?;
let size_in_bounds_block = context.append_basic_block("size_in_bounds");
let is_size_out_of_bounds = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
memory_size,
context.heap_size(),
"size_out_of_bounds",
)?;
context.build_conditional_branch(
is_size_out_of_bounds,
trap_block,
size_in_bounds_block,
)?;
context.set_basic_block(size_in_bounds_block);
let return_block = context.append_basic_block("return_pointer");
let new_size_block = context.append_basic_block("new_size");
let is_new_size = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
memory_size,
context
.get_global_value(crate::polkavm::GLOBAL_HEAP_SIZE)?
.into_int_value(),
"is_new_size",
)?;
context.build_conditional_branch(is_new_size, new_size_block, return_block)?;
context.set_basic_block(new_size_block);
context.build_store(
context.get_global(crate::polkavm::GLOBAL_HEAP_SIZE)?.into(),
memory_size,
)?;
context.build_unconditional_branch(return_block);
context.set_basic_block(return_block);
Ok(Some(
context
.build_gep(
context
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY)?
.into(),
&[context.xlen_type().const_zero(), offset],
context.byte_type(),
"allocation_start_pointer",
)
.value
.as_basic_value_enum(),
))
}
}
impl<D> WriteLLVM<D> for Sbrk
where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::declare(self, context)
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context)
}
}
+44 -24
View File
@@ -26,6 +26,7 @@ use inkwell::debug_info::DIScope;
use inkwell::types::BasicType;
use inkwell::values::BasicValue;
use crate::memory::MemoryConfig;
use crate::optimizer::settings::Settings as OptimizerSettings;
use crate::optimizer::Optimizer;
use crate::polkavm::DebugConfig;
@@ -33,6 +34,7 @@ use crate::polkavm::Dependency;
use crate::target_machine::target::Target;
use crate::target_machine::TargetMachine;
use crate::PolkaVMLoadHeapWordFunction;
use crate::PolkaVMSbrkFunction;
use crate::PolkaVMStoreHeapWordFunction;
use self::address_space::AddressSpace;
@@ -85,6 +87,8 @@ where
loop_stack: Vec<Loop<'ctx>>,
/// The extra LLVM arguments that were used during target initialization.
llvm_arguments: &'ctx [String],
/// The PVM memory configuration.
memory_config: MemoryConfig,
/// The project dependency manager. It can be any entity implementing the trait.
/// The manager is used to get information about contracts and their dependencies during
@@ -116,9 +120,6 @@ where
/// The loop stack default capacity.
const LOOP_STACK_INITIAL_CAPACITY: usize = 16;
/// The PolkaVM minimum stack size.
const POLKAVM_STACK_SIZE: u32 = 0x4000;
/// Link in the stdlib module.
fn link_stdlib_module(
llvm: &'ctx inkwell::context::Context,
@@ -218,6 +219,7 @@ where
}
/// Initializes a new LLVM context.
#[allow(clippy::too_many_arguments)]
pub fn new(
llvm: &'ctx inkwell::context::Context,
module: inkwell::module::Module<'ctx>,
@@ -226,11 +228,12 @@ where
include_metadata_hash: bool,
debug_config: DebugConfig,
llvm_arguments: &'ctx [String],
memory_config: MemoryConfig,
) -> Self {
Self::set_data_layout(llvm, &module);
Self::link_stdlib_module(llvm, &module);
Self::link_polkavm_imports(llvm, &module);
Self::set_polkavm_stack_size(llvm, &module, Self::POLKAVM_STACK_SIZE);
Self::set_polkavm_stack_size(llvm, &module, memory_config.stack_size);
Self::set_module_flags(llvm, &module);
let intrinsics = Intrinsics::new(llvm, &module);
@@ -254,6 +257,7 @@ where
current_function: None,
loop_stack: Vec::with_capacity(Self::LOOP_STACK_INITIAL_CAPACITY),
llvm_arguments,
memory_config,
dependency_manager,
include_metadata_hash,
@@ -644,6 +648,7 @@ where
self.include_metadata_hash,
self.debug_config.clone(),
self.llvm_arguments,
self.memory_config,
)
})
}
@@ -750,7 +755,9 @@ where
address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<Pointer<'ctx>> {
let address_type = self.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer = self.build_alloca_at_entry(address_type, "address_pointer");
let address_pointer = self
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into();
let address_truncated =
self.builder()
.build_int_truncate(address, address_type, "address_truncated")?;
@@ -1082,32 +1089,40 @@ where
offset: inkwell::values::IntValue<'ctx>,
size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
Ok(self
.builder()
.build_call(
self.runtime_api_method(revive_runtime_api::polkavm_imports::SBRK),
&[offset.into(), size.into()],
"call_sbrk",
)?
let call_site_value = self.builder().build_call(
<PolkaVMSbrkFunction as RuntimeFunction<D>>::declaration(self).function_value(),
&[offset.into(), size.into()],
"alloc_start",
)?;
call_site_value.add_attribute(
inkwell::attributes::AttributeLoc::Return,
self.llvm
.create_enum_attribute(Attribute::NonNull as u32, 0),
);
call_site_value.add_attribute(
inkwell::attributes::AttributeLoc::Return,
self.llvm
.create_enum_attribute(Attribute::NoUndef as u32, 0),
);
Ok(call_site_value
.try_as_basic_value()
.left()
.expect("sbrk returns a pointer")
.unwrap_or_else(|| {
panic!(
"revive runtime function {} should return a value",
<PolkaVMSbrkFunction as RuntimeFunction<D>>::NAME,
)
})
.into_pointer_value())
}
/// Build a call to PolkaVM `msize` for querying the linear memory size.
pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
let memory_size_pointer = self
.module()
.get_global(revive_runtime_api::polkavm_imports::MEMORY_SIZE)
.expect("the memory size symbol should have been declared")
.as_pointer_value();
let memory_size_value = self.builder().build_load(
self.xlen_type(),
memory_size_pointer,
"memory_size_value",
)?;
Ok(memory_size_value.into_int_value())
Ok(self
.get_global_value(crate::polkavm::GLOBAL_HEAP_SIZE)?
.into_int_value())
}
/// Returns a pointer to `offset` into the heap, allocating
@@ -1426,4 +1441,9 @@ where
pub fn optimizer_settings(&self) -> &OptimizerSettings {
self.optimizer.settings()
}
pub fn heap_size(&self) -> inkwell::values::IntValue<'ctx> {
self.xlen_type()
.const_int(self.memory_config.heap_size as u64, false)
}
}
@@ -19,7 +19,7 @@ where
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context
.word_type()
.fn_type(&[context.word_type().into()], false)
.fn_type(&[context.llvm().ptr_type(Default::default()).into()], false)
}
fn emit_body<'ctx>(
@@ -59,7 +59,7 @@ where
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context
.word_type()
.fn_type(&[context.word_type().into()], false)
.fn_type(&[context.llvm().ptr_type(Default::default()).into()], false)
}
fn emit_body<'ctx>(
@@ -94,7 +94,10 @@ where
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type(
&[context.word_type().into(), context.word_type().into()],
&[
context.llvm().ptr_type(Default::default()).into(),
context.llvm().ptr_type(Default::default()).into(),
],
false,
)
}
@@ -138,7 +141,10 @@ where
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type(
&[context.word_type().into(), context.word_type().into()],
&[
context.llvm().ptr_type(Default::default()).into(),
context.llvm().ptr_type(Default::default()).into(),
],
false,
)
}
@@ -173,9 +179,17 @@ where
fn emit_load<'ctx, D: Dependency + Clone>(
context: &mut Context<'ctx, D>,
mut key: BasicValueEnum<'ctx>,
key: BasicValueEnum<'ctx>,
transient: bool,
) -> anyhow::Result<BasicValueEnum<'ctx>> {
let mut key = context.build_load(
super::Pointer::new(
context.word_type(),
Default::default(),
key.into_pointer_value(),
),
"key",
)?;
if !transient {
key = context.build_byte_swap(key)?;
}
@@ -217,10 +231,26 @@ fn emit_load<'ctx, D: Dependency + Clone>(
fn emit_store<'ctx, D: Dependency + Clone>(
context: &mut Context<'ctx, D>,
mut key: BasicValueEnum<'ctx>,
mut value: BasicValueEnum<'ctx>,
key: BasicValueEnum<'ctx>,
value: BasicValueEnum<'ctx>,
transient: bool,
) -> anyhow::Result<()> {
let mut key = context.build_load(
super::Pointer::new(
context.word_type(),
Default::default(),
key.into_pointer_value(),
),
"key",
)?;
let mut value = context.build_load(
super::Pointer::new(
context.word_type(),
Default::default(),
value.into_pointer_value(),
),
"key",
)?;
if !transient {
key = context.build_byte_swap(key)?;
value = context.build_byte_swap(value)?;
@@ -23,6 +23,7 @@ pub fn create_context(
true,
Default::default(),
Default::default(),
Default::default(),
)
}
+28 -14
View File
@@ -63,7 +63,6 @@ where
let non_overflow_block = context.append_basic_block("shift_left_non_overflow");
let join_block = context.append_basic_block("shift_left_join");
let result_pointer = context.build_alloca(context.word_type(), "shift_left_result_pointer");
let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
shift,
@@ -73,7 +72,6 @@ where
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
context.set_basic_block(overflow_block);
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block);
@@ -81,11 +79,17 @@ where
context
.builder()
.build_left_shift(value, shift, "shift_left_non_overflow_result")?;
context.build_store(result_pointer, value)?;
context.build_unconditional_branch(join_block);
context.set_basic_block(join_block);
context.build_load(result_pointer, "shift_left_result")
let result = context
.builder()
.build_phi(context.word_type(), "shift_left_value")?;
result.add_incoming(&[
(&value, non_overflow_block),
(&context.word_const(0), overflow_block),
]);
Ok(result.as_basic_value())
}
/// Translates the bitwise shift right.
@@ -101,7 +105,6 @@ where
let non_overflow_block = context.append_basic_block("shift_right_non_overflow");
let join_block = context.append_basic_block("shift_right_join");
let result_pointer = context.build_alloca(context.word_type(), "shift_right_result_pointer");
let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
shift,
@@ -111,7 +114,6 @@ where
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
context.set_basic_block(overflow_block);
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block);
@@ -121,11 +123,17 @@ where
false,
"shift_right_non_overflow_result",
)?;
context.build_store(result_pointer, value)?;
context.build_unconditional_branch(join_block);
context.set_basic_block(join_block);
context.build_load(result_pointer, "shift_right_result")
let result = context
.builder()
.build_phi(context.word_type(), "shift_right_value")?;
result.add_incoming(&[
(&value, non_overflow_block),
(&context.word_const(0), overflow_block),
]);
Ok(result.as_basic_value())
}
/// Translates the arithmetic bitwise shift right.
@@ -145,8 +153,6 @@ where
let non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow");
let join_block = context.append_basic_block("shift_right_arithmetic_join");
let result_pointer =
context.build_alloca(context.word_type(), "shift_right_arithmetic_result_pointer");
let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
shift,
@@ -174,11 +180,9 @@ where
)?;
context.set_basic_block(overflow_positive_block);
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block);
context.set_basic_block(overflow_negative_block);
context.build_store(result_pointer, context.word_type().const_all_ones())?;
context.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block);
@@ -188,11 +192,21 @@ where
true,
"shift_right_arithmetic_non_overflow_result",
)?;
context.build_store(result_pointer, value)?;
context.build_unconditional_branch(join_block);
context.set_basic_block(join_block);
context.build_load(result_pointer, "shift_right_arithmetic_result")
let result = context
.builder()
.build_phi(context.word_type(), "shift_arithmetic_right_value")?;
result.add_incoming(&[
(&value, non_overflow_block),
(
&context.word_type().const_all_ones(),
overflow_negative_block,
),
(&context.word_const(0), overflow_block),
]);
Ok(result.as_basic_value())
}
/// Translates the `byte` instruction, extracting the byte of `operand_2`
+16 -16
View File
@@ -2,6 +2,7 @@
use inkwell::values::BasicValue;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
@@ -49,7 +50,9 @@ where
D: Dependency + Clone,
{
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer = context.build_alloca_at_entry(address_type, "origin_address");
let address_pointer: Pointer<'_> = context
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into();
context.build_store(address_pointer, address_type.const_zero())?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::ORIGIN,
@@ -97,13 +100,13 @@ where
D: Dependency + Clone,
{
let output_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_out_ptr");
let index_ptr = context.build_alloca_at_entry(context.word_type(), "blockhash_index_ptr");
context.build_store(index_ptr, index)?;
let index_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_index_ptr");
context.build_store(index_pointer, index)?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::BLOCK_HASH,
&[
index_ptr.to_int(context).into(),
index_pointer.to_int(context).into(),
output_pointer.to_int(context).into(),
],
);
@@ -127,10 +130,9 @@ pub fn coinbase<'ctx, D>(
where
D: Dependency + Clone,
{
let pointer = context.build_alloca_at_entry(
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
"coinbase_output",
);
let pointer: Pointer<'_> = context
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into();
context.build_runtime_call(
revive_runtime_api::polkavm_imports::BLOCK_AUTHOR,
&[pointer.to_int(context).into()],
@@ -155,10 +157,9 @@ pub fn address<'ctx, D>(
where
D: Dependency + Clone,
{
let pointer = context.build_alloca_at_entry(
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
"address_output",
);
let pointer: Pointer<'_> = context
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into();
context.build_runtime_call(
revive_runtime_api::polkavm_imports::ADDRESS,
&[pointer.to_int(context).into()],
@@ -173,10 +174,9 @@ pub fn caller<'ctx, D>(
where
D: Dependency + Clone,
{
let pointer = context.build_alloca_at_entry(
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
"address_output",
);
let pointer: Pointer<'_> = context
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into();
context.build_runtime_call(
revive_runtime_api::polkavm_imports::CALLER,
&[pointer.to_int(context).into()],
+6 -10
View File
@@ -119,10 +119,8 @@ where
_ => error,
})?;
if contract_path.as_str() == parent {
return Ok(Argument::new_with_constant(
context.word_const(0).as_basic_value_enum(),
num::BigUint::zero(),
));
return Ok(Argument::value(context.word_const(0).as_basic_value_enum())
.with_constant(num::BigUint::zero()));
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime {
anyhow::bail!("type({}).runtimeCode is not supported", identifier);
}
@@ -131,7 +129,7 @@ where
let hash_value = context
.word_const_str_hex(hash_string.as_str())
.as_basic_value_enum();
Ok(Argument::new_with_original(hash_value, hash_string))
Ok(Argument::value(hash_value).with_original(hash_string))
}
/// Translates the deploy call header size instruction. the header consists of
@@ -160,10 +158,8 @@ where
_ => error,
})?;
if contract_path.as_str() == parent {
return Ok(Argument::new_with_constant(
context.word_const(0).as_basic_value_enum(),
num::BigUint::zero(),
));
return Ok(Argument::value(context.word_const(0).as_basic_value_enum())
.with_constant(num::BigUint::zero()));
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime {
anyhow::bail!("type({}).runtimeCode is not supported", identifier);
}
@@ -172,5 +168,5 @@ where
let size_value = context
.word_const(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE as u64)
.as_basic_value_enum();
Ok(Argument::new_with_constant(size_value, size_bigint))
Ok(Argument::value(size_value).with_constant(size_bigint))
}
@@ -28,7 +28,7 @@ pub fn value<'ctx, D>(
where
D: Dependency + Clone,
{
let output_pointer = context.build_alloca(context.value_type(), "value_transferred");
let output_pointer = context.build_alloca_at_entry(context.value_type(), "value_transferred");
context.build_store(output_pointer, context.word_const(0))?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::VALUE_TRANSFERRED,
@@ -46,8 +46,7 @@ where
D: Dependency + Clone,
{
let address_pointer = context.build_address_argument_store(address)?;
let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer");
let balance_pointer = context.build_alloca_at_entry(context.word_type(), "balance_pointer");
let balance = context.builder().build_ptr_to_int(
balance_pointer.value,
context.xlen_type(),
@@ -69,7 +68,7 @@ pub fn self_balance<'ctx, D>(
where
D: Dependency + Clone,
{
let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer");
let balance_pointer = context.build_alloca_at_entry(context.word_type(), "balance_pointer");
let balance = context.builder().build_ptr_to_int(
balance_pointer.value,
context.xlen_type(),
+17 -10
View File
@@ -3,6 +3,7 @@
use crate::polkavm::context::runtime::RuntimeFunction;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::PolkaVMArgument;
use crate::PolkaVMLoadStorageWordFunction;
use crate::PolkaVMLoadTransientStorageWordFunction;
use crate::PolkaVMStoreStorageWordFunction;
@@ -11,14 +12,14 @@ use crate::PolkaVMStoreTransientStorageWordFunction;
/// Translates the storage load.
pub fn load<'ctx, D>(
context: &mut Context<'ctx, D>,
position: inkwell::values::IntValue<'ctx>,
position: &PolkaVMArgument<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let name = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::declaration(context);
let arguments = [position.into()];
let arguments = [position.as_pointer(context)?.value.into()];
Ok(context
.build_call(declaration, &arguments, "storage_load")
.unwrap_or_else(|| panic!("runtime function {name} should return a value")))
@@ -27,14 +28,17 @@ where
/// Translates the storage store.
pub fn store<'ctx, D>(
context: &mut Context<'ctx, D>,
position: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>,
position: &PolkaVMArgument<'ctx>,
value: &PolkaVMArgument<'ctx>,
) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
let declaration = <PolkaVMStoreStorageWordFunction as RuntimeFunction<D>>::declaration(context);
let arguments = [position.into(), value.into()];
let arguments = [
position.as_pointer(context)?.value.into(),
value.as_pointer(context)?.value.into(),
];
context.build_call(declaration, &arguments, "storage_store");
Ok(())
}
@@ -42,13 +46,13 @@ where
/// Translates the transient storage load.
pub fn transient_load<'ctx, D>(
context: &mut Context<'ctx, D>,
position: inkwell::values::IntValue<'ctx>,
position: &PolkaVMArgument<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let name = <PolkaVMLoadTransientStorageWordFunction as RuntimeFunction<D>>::NAME;
let arguments = [position.into()];
let arguments = [position.as_pointer(context)?.value.into()];
let declaration =
<PolkaVMLoadTransientStorageWordFunction as RuntimeFunction<D>>::declaration(context);
Ok(context
@@ -59,15 +63,18 @@ where
/// Translates the transient storage store.
pub fn transient_store<'ctx, D>(
context: &mut Context<'ctx, D>,
position: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>,
position: &PolkaVMArgument<'ctx>,
value: &PolkaVMArgument<'ctx>,
) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
let declaration =
<PolkaVMStoreTransientStorageWordFunction as RuntimeFunction<D>>::declaration(context);
let arguments = [position.into(), value.into()];
let arguments = [
position.as_pointer(context)?.value.into(),
value.as_pointer(context)?.value.into(),
];
context.build_call(declaration, &arguments, "transient_storage_store");
Ok(())
}
+3
View File
@@ -7,6 +7,7 @@ pub mod evm;
pub use self::r#const::*;
use crate::debug_config::DebugConfig;
use crate::memory::MemoryConfig;
use crate::optimizer::settings::Settings as OptimizerSettings;
use anyhow::Context as AnyhowContext;
@@ -90,6 +91,7 @@ pub trait Dependency {
include_metadata_hash: bool,
debug_config: DebugConfig,
llvm_arguments: &[String],
memory_config: MemoryConfig,
) -> anyhow::Result<String>;
/// Resolves a full contract path.
@@ -111,6 +113,7 @@ impl Dependency for DummyDependency {
_include_metadata_hash: bool,
_debug_config: DebugConfig,
_llvm_arguments: &[String],
_memory_config: MemoryConfig,
) -> anyhow::Result<String> {
Ok(String::new())
}
+9 -2
View File
@@ -10,12 +10,19 @@ description = "Execute revive contracts in a simulated blockchain runtime"
[package.metadata.cargo-machete]
ignored = ["codec", "scale-info"]
[[bin]]
name = "revive-runner"
path = "src/main.rs"
[features]
std = ["polkadot-sdk/std"]
default = ["solidity"]
solidity = ["revive-solidity", "revive-differential"]
solidity = ["revive-solidity", "revive-differential", "revive-llvm-context"]
[dependencies]
env_logger = { workspace = true }
clap = { workspace = true, features = ["help", "std", "derive"] }
anyhow = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
hex = { workspace = true, features = ["serde"] }
@@ -34,4 +41,4 @@ polkadot-sdk.features = [
revive-solidity = { workspace = true, optional = true }
revive-differential = { workspace = true, optional = true }
revive-llvm-context = { workspace = true }
revive-llvm-context = { workspace = true, optional = true }
+26
View File
@@ -0,0 +1,26 @@
# revive-runner
The revive runner is a helper utility aiding in contract debugging.
Given a PVM contract blob, it will upload, deploy and call that contract using a local, stand-alone un-blockchained pallet revive (which is our execution layer).
This is somewhat similar to the geth `evm` utility binary.
## Installation
The `revive-runner` does not depend on the compiler itself, hence installing this utility does not depend on LLVM, so no LLVM build is required.
Inside the root `revive` repository directory, execute:
```bash
make install-revive-runner
```
Which will install the `revive-runner` using `cargo`.
## Usage
Set the `RUST_LOG` environment varibale to the `trace` level to see the full PolkaVM execution trace. For example:
```bash
RUST_LOG=trace revive-runner -f mycontract.pvm -c a9059cbb000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac0000000000000000000000000000000000000000000000000000000000000000
+93
View File
@@ -0,0 +1,93 @@
use std::path::PathBuf;
use clap::Parser;
use revive_runner::{Code, OptionalHex, Specs, SpecsAction::*, TestAddress};
/// Execute revive PolkaVM contracts locally.
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Arguments {
/// The hex encoded calldata for the contract call.
#[arg(short, long)]
calldata: Option<String>,
/// The hex encoded calldata for the contract deployment.
#[arg(short, long)]
deploy_calldata: Option<String>,
/// The hex encoded contract code blob to instantiate and execute.
#[arg(short, long)]
blob: Option<String>,
/// The contract code to instantiate and execute.
#[arg(short, long)]
file: Option<PathBuf>,
/// The origin account used to initiate the deploy and call transactions.
#[arg(short, long)]
origin: Option<TestAddress>,
/// The value the call transaction is endowed with.
#[arg(short, long)]
value: Option<u128>,
/// The value the deploy transaction is endowed with.
#[arg(long)]
deploy_value: Option<u128>,
}
fn main() -> anyhow::Result<()> {
env_logger::init();
let arguments = Arguments::parse();
let code = match (arguments.blob, arguments.file) {
(Some(blob), None) => hex::decode(blob)
.map_err(|error| anyhow::anyhow!("expected hex encoded PVM blob: {error}"))?,
(None, Some(file)) => std::fs::read(&file).map_err(|error| {
anyhow::anyhow!("unable to read PVM file {}: {error}", file.display())
})?,
_ => anyhow::bail!("should either provide a PVM blob or a PVM file"),
};
let calldata = match arguments.calldata {
Some(calldata) => hex::decode(calldata)
.map_err(|error| anyhow::anyhow!("expected hex encoded calldata: {error}"))?,
None => vec![],
};
let deploy_calldata = match arguments.deploy_calldata {
Some(calldata) => hex::decode(calldata)
.map_err(|error| anyhow::anyhow!("expected hex encoded calldata: {error}"))?,
None => vec![],
};
let origin = arguments.origin.unwrap_or(TestAddress::Alice);
let actions = vec![
Instantiate {
origin: origin.clone(),
value: arguments.deploy_value.unwrap_or(0),
gas_limit: None,
storage_deposit_limit: None,
code: Code::Bytes(code),
data: deploy_calldata,
salt: OptionalHex::default(),
},
Call {
origin,
dest: TestAddress::Instantiated(0),
value: arguments.value.unwrap_or(0),
gas_limit: None,
storage_deposit_limit: None,
data: calldata,
},
];
Specs {
actions,
differential: false,
..Default::default()
}
.run();
Ok(())
}
+38 -2
View File
@@ -1,9 +1,11 @@
use std::time::Instant;
use std::{str::FromStr, time::Instant};
use serde::{Deserialize, Serialize};
use crate::*;
use alloy_primitives::{keccak256, Address};
use alloy_primitives::keccak256;
#[cfg(feature = "revive-solidity")]
use alloy_primitives::Address;
#[cfg(feature = "revive-solidity")]
use revive_differential::{Evm, EvmLog};
#[cfg(feature = "revive-solidity")]
@@ -156,6 +158,39 @@ impl TestAddress {
}
}
impl FromStr for TestAddress {
type Err = &'static str;
fn from_str(value: &str) -> Result<Self, Self::Err> {
value.try_into()
}
}
impl TryFrom<&str> for TestAddress {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"alice" => Ok(Self::Alice),
"bob" => Ok(Self::Bob),
"charlie" => Ok(Self::Charlie),
value => {
if let Ok(value) = value.parse() {
return Ok(Self::Instantiated(value));
}
if let Ok(value) = hex::decode(value) {
if value.len() == 20 {
return Ok(Self::AccountId(H160(value.try_into().unwrap())));
}
}
Err("can not parse into test address")
}
}
}
}
/// Specs for a contract test
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
@@ -212,6 +247,7 @@ impl Specs {
/// Helper to allow not specifying the code bytes or path directly in the runner.json
/// - Replace `Code::Bytes(bytes)` if `bytes` are empty: read `contract_file`
/// - Replace `Code::Solidity{ path, ..}` if `path` is not provided: replace `path` with `contract_file`
#[allow(unused_variables)]
pub fn replace_empty_code(&mut self, contract_name: &str, contract_path: &str) {
for action in self.actions.iter_mut() {
let code = match action {
-28
View File
@@ -5,34 +5,6 @@
// Missing builtins
#define EVM_WORD_SIZE 32
#define ALIGN(size) ((size + EVM_WORD_SIZE - 1) & ~(EVM_WORD_SIZE - 1))
#define MAX_MEMORY_SIZE (64 * 1024)
char __memory[MAX_MEMORY_SIZE];
uint32_t __memory_size = 0;
void * __sbrk_internal(uint32_t offset, uint32_t size) {
if (offset >= MAX_MEMORY_SIZE || size > MAX_MEMORY_SIZE) {
POLKAVM_TRAP();
}
uint32_t new_size = ALIGN(offset + size);
if (new_size > MAX_MEMORY_SIZE) {
POLKAVM_TRAP();
}
if (new_size > __memory_size) {
__memory_size = new_size;
}
return (void *)&__memory[offset];
}
void * memset(void *b, int c, size_t len) {
uint8_t *dest = b;
while (len-- > 0) *dest++ = c;
return b;
}
void * memcpy(void *dst, const void *_src, size_t len) {
uint8_t *dest = dst;
const uint8_t *src = _src;
+1 -10
View File
@@ -2,14 +2,6 @@ use inkwell::{context::Context, memory_buffer::MemoryBuffer, module::Module, sup
include!(concat!(env!("OUT_DIR"), "/polkavm_imports.rs"));
/// The emulated EVM heap memory global symbol.
pub static MEMORY: &str = "__memory";
/// The emulated EVM heap memory size global symbol.
pub static MEMORY_SIZE: &str = "__memory_size";
pub static SBRK: &str = "__sbrk_internal";
pub static ADDRESS: &str = "address";
pub static BALANCE: &str = "balance";
@@ -78,8 +70,7 @@ pub static WEIGHT_TO_FEE: &str = "weight_to_fee";
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 34] = [
SBRK,
pub static IMPORTS: [&str; 33] = [
ADDRESS,
BALANCE,
BALANCE_OF,
+11
View File
@@ -55,6 +55,7 @@ pub fn yul<T: Compiler>(
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<Build> {
let path = match input_files.len() {
1 => input_files.first().expect("Always exists"),
@@ -80,6 +81,7 @@ pub fn yul<T: Compiler>(
include_metadata_hash,
debug_config,
llvm_arguments,
memory_config,
)?;
Ok(build)
@@ -92,6 +94,7 @@ pub fn llvm_ir(
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<Build> {
let path = match input_files.len() {
1 => input_files.first().expect("Always exists"),
@@ -109,6 +112,7 @@ pub fn llvm_ir(
include_metadata_hash,
debug_config,
llvm_arguments,
memory_config,
)?;
Ok(build)
@@ -131,6 +135,7 @@ pub fn standard_output<T: Compiler>(
suppressed_warnings: Option<Vec<ResolcWarning>>,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<Build> {
let solc_version = solc.version()?;
@@ -189,12 +194,14 @@ pub fn standard_output<T: Compiler>(
include_metadata_hash,
debug_config,
llvm_arguments,
memory_config,
)?;
Ok(build)
}
/// Runs the standard JSON mode.
#[allow(clippy::too_many_arguments)]
pub fn standard_json<T: Compiler>(
solc: &mut T,
detect_missing_libraries: bool,
@@ -203,6 +210,7 @@ pub fn standard_json<T: Compiler>(
allow_paths: Option<String>,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<()> {
let solc_version = solc.version()?;
@@ -250,6 +258,7 @@ pub fn standard_json<T: Compiler>(
include_metadata_hash,
debug_config,
llvm_arguments,
memory_config,
)?;
build.write_to_standard_json(&mut solc_output, &solc_version)?;
}
@@ -277,6 +286,7 @@ pub fn combined_json<T: Compiler>(
output_directory: Option<PathBuf>,
overwrite: bool,
llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<()> {
let build = standard_output(
input_files,
@@ -293,6 +303,7 @@ pub fn combined_json<T: Compiler>(
suppressed_warnings,
debug_config,
llvm_arguments,
memory_config,
)?;
let mut combined_json = solc.combined_json(input_files, format.as_str())?;
+4
View File
@@ -22,6 +22,8 @@ pub struct Input {
pub debug_config: revive_llvm_context::DebugConfig,
/// The extra LLVM arguments give used for manual control.
pub llvm_arguments: Vec<String>,
/// The PVM memory configuration.
pub memory_config: revive_llvm_context::MemoryConfig,
}
impl Input {
@@ -33,6 +35,7 @@ impl Input {
optimizer_settings: revive_llvm_context::OptimizerSettings,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: Vec<String>,
memory_config: revive_llvm_context::MemoryConfig,
) -> Self {
Self {
contract,
@@ -41,6 +44,7 @@ impl Input {
optimizer_settings,
debug_config,
llvm_arguments,
memory_config,
}
}
}
+1
View File
@@ -50,6 +50,7 @@ pub trait Process {
input.include_metadata_hash,
input.debug_config,
&input.llvm_arguments,
input.memory_config,
);
match result {
@@ -78,6 +78,7 @@ impl Contract {
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<ContractBuild> {
let llvm = inkwell::context::Context::create();
let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings);
@@ -123,6 +124,7 @@ impl Contract {
include_metadata_hash,
debug_config,
llvm_arguments,
memory_config,
);
context.set_solidity_data(revive_llvm_context::PolkaVMContextSolidityData::default());
match self.ir {
+4
View File
@@ -67,6 +67,7 @@ impl Project {
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<Build> {
let project = self.clone();
#[cfg(feature = "parallel")]
@@ -83,6 +84,7 @@ impl Project {
optimizer_settings.clone(),
debug_config.clone(),
llvm_arguments.to_vec(),
memory_config,
);
let process_output = {
#[cfg(target_os = "emscripten")]
@@ -319,6 +321,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<String> {
let contract_path = project.resolve_path(identifier)?;
let contract = project
@@ -339,6 +342,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
include_metadata_hash,
debug_config,
llvm_arguments,
memory_config,
)
.map_err(|error| {
anyhow::anyhow!(
+33 -1
View File
@@ -167,9 +167,41 @@ pub struct Arguments {
#[arg(long = "recursive-process-input")]
pub recursive_process_input: Option<String>,
#[arg(long = "llvm-arg")]
/// These are passed to LLVM as the command line to allow manual control.
#[arg(long = "llvm-arg")]
pub llvm_arguments: Vec<String>,
/// The emulated EVM linear heap memory static buffer size in bytes.
///
/// Unlike the EVM, due to the lack of dynamic memory metering, PVM contracts emulate
/// the EVM heap memory with a static buffer. Consequentially, instead of infinite
/// memory with exponentially growing gas costs, PVM contracts have a finite amount
/// of memory with constant gas costs available.
///
/// If the contract uses more heap memory than configured, it will compile fine but
/// eventually revert execution at runtime!
///
/// You are incentiviced to keep this value as small as possible:
/// 1.Increasing the heap size will increase startup costs.
/// 2.The heap size contributes to the total memory size a contract can use,
/// which includes the contracts code size
#[arg(long = "heap-size", default_value = "65536")]
pub heap_size: u32,
/// The contracts total stack size in bytes.
///
/// PVM is a register machine with a traditional stack memory space for local
/// variables. This controls the total amount of stack space the contract can use.
///
/// If the contract uses more stack memory than configured, it will compile fine but
/// eventually revert execution at runtime!
///
/// You are incentiviced to keep this value as small as possible:
/// 1.Increasing the heap size will increase startup costs.
/// 2.The stack size contributes to the total memory size a contract can use,
/// which includes the contracts code size
#[arg(long = "stack-size", default_value = "32768")]
pub stack_size: u32,
}
impl Arguments {
+10
View File
@@ -148,6 +148,11 @@ fn main_inner() -> anyhow::Result<()> {
None => true,
};
let memory_config = revive_llvm_context::MemoryConfig {
heap_size: arguments.heap_size,
stack_size: arguments.stack_size,
};
let build = if arguments.yul {
revive_solidity::yul(
input_files.as_slice(),
@@ -156,6 +161,7 @@ fn main_inner() -> anyhow::Result<()> {
include_metadata_hash,
debug_config,
&arguments.llvm_arguments,
memory_config,
)
} else if arguments.llvm_ir {
revive_solidity::llvm_ir(
@@ -164,6 +170,7 @@ fn main_inner() -> anyhow::Result<()> {
include_metadata_hash,
debug_config,
&arguments.llvm_arguments,
memory_config,
)
} else if arguments.standard_json {
revive_solidity::standard_json(
@@ -174,6 +181,7 @@ fn main_inner() -> anyhow::Result<()> {
arguments.allow_paths,
debug_config,
&arguments.llvm_arguments,
memory_config,
)?;
return Ok(());
} else if let Some(format) = arguments.combined_json {
@@ -195,6 +203,7 @@ fn main_inner() -> anyhow::Result<()> {
arguments.output_directory,
arguments.overwrite,
&arguments.llvm_arguments,
memory_config,
)?;
return Ok(());
} else {
@@ -213,6 +222,7 @@ fn main_inner() -> anyhow::Result<()> {
suppressed_warnings,
debug_config,
&arguments.llvm_arguments,
memory_config,
)
}?;
+21 -5
View File
@@ -92,7 +92,7 @@ pub fn build_solidity_with_options(
SolcStandardJsonInputSettingsSelection::new_required(),
SolcStandardJsonInputSettingsOptimizer::new(
solc_optimizer_enabled,
None,
optimizer_settings.middle_end_as_string().chars().last(),
&solc_version.default,
false,
),
@@ -102,16 +102,26 @@ pub fn build_solidity_with_options(
let mut output = solc.standard_json(input, None, vec![], None)?;
let debug_config = revive_llvm_context::DebugConfig::new(
None,
optimizer_settings.middle_end_as_string() != "z",
);
let project = Project::try_from_standard_json_output(
&output,
sources,
libraries,
&solc_version,
&DEBUG_CONFIG,
&debug_config,
)?;
let build: crate::Build =
project.compile(optimizer_settings, false, DEBUG_CONFIG, Default::default())?;
let build: crate::Build = project.compile(
optimizer_settings,
false,
debug_config,
Default::default(),
Default::default(),
)?;
build.write_to_standard_json(&mut output, &solc_version)?;
Ok(output)
@@ -238,7 +248,13 @@ pub fn build_yul(source_code: &str) -> anyhow::Result<()> {
source_code,
None,
)?;
let _build = project.compile(optimizer_settings, false, DEBUG_CONFIG, Default::default())?;
let _build = project.compile(
optimizer_settings,
false,
DEBUG_CONFIG,
Default::default(),
Default::default(),
)?;
Ok(())
}
@@ -139,13 +139,14 @@ where
identifier.inner,
)
})?;
context.build_store(pointer, value.to_llvm())?;
context.build_store(pointer, value.access(context)?)?;
return Ok(());
}
let llvm_type = value.to_llvm().into_struct_value().get_type();
let value = value.access(context)?;
let llvm_type = value.into_struct_value().get_type();
let tuple_pointer = context.build_alloca(llvm_type, "assignment_pointer");
context.build_store(tuple_pointer, value.to_llvm())?;
context.build_store(tuple_pointer, value)?;
for (index, binding) in self.bindings.into_iter().enumerate() {
context.set_debug_location(self.location.line, 0, None)?;
@@ -128,7 +128,10 @@ impl FunctionCall {
Name::UserDefined(name) => {
let mut values = Vec::with_capacity(self.arguments.len());
for argument in self.arguments.into_iter().rev() {
let value = argument.into_llvm(context)?.expect("Always exists").value;
let value = argument
.into_llvm(context)?
.expect("Always exists")
.access(context)?;
values.push(value);
}
values.reverse();
@@ -461,36 +464,29 @@ impl FunctionCall {
}
Name::SLoad => {
let arguments = self.pop_arguments_llvm::<D, 1>(context)?;
revive_llvm_context::polkavm_evm_storage::load(
context,
arguments[0].into_int_value(),
)
.map(Some)
let arguments = self.pop_arguments::<D, 1>(context)?;
revive_llvm_context::polkavm_evm_storage::load(context, &arguments[0]).map(Some)
}
Name::SStore => {
let arguments = self.pop_arguments_llvm::<D, 2>(context)?;
let arguments = self.pop_arguments::<D, 2>(context)?;
revive_llvm_context::polkavm_evm_storage::store(
context,
arguments[0].into_int_value(),
arguments[1].into_int_value(),
&arguments[0],
&arguments[1],
)
.map(|_| None)
}
Name::TLoad => {
let arguments = self.pop_arguments_llvm::<D, 1>(context)?;
revive_llvm_context::polkavm_evm_storage::transient_load(
context,
arguments[0].into_int_value(),
)
.map(Some)
let arguments = self.pop_arguments::<D, 1>(context)?;
revive_llvm_context::polkavm_evm_storage::transient_load(context, &arguments[0])
.map(Some)
}
Name::TStore => {
let arguments = self.pop_arguments_llvm::<D, 2>(context)?;
let arguments = self.pop_arguments::<D, 2>(context)?;
revive_llvm_context::polkavm_evm_storage::transient_store(
context,
arguments[0].into_int_value(),
arguments[1].into_int_value(),
&arguments[0],
&arguments[1],
)
.map(|_| None)
}
@@ -514,7 +510,7 @@ impl FunctionCall {
let offset = context.solidity_mut().allocate_immutable(key.as_str())
/ revive_common::BYTE_LENGTH_WORD;
let index = context.xlen_type().const_int(offset as u64, false);
let value = arguments[2].value.into_int_value();
let value = arguments[2].access(context)?.into_int_value();
revive_llvm_context::polkavm_evm_immutable::store(context, index, value)
.map(|_| None)
}
@@ -720,13 +716,13 @@ impl FunctionCall {
Name::Call => {
let arguments = self.pop_arguments::<D, 7>(context)?;
let gas = arguments[0].value.into_int_value();
let address = arguments[1].value.into_int_value();
let value = arguments[2].value.into_int_value();
let input_offset = arguments[3].value.into_int_value();
let input_size = arguments[4].value.into_int_value();
let output_offset = arguments[5].value.into_int_value();
let output_size = arguments[6].value.into_int_value();
let gas = arguments[0].access(context)?.into_int_value();
let address = arguments[1].access(context)?.into_int_value();
let value = arguments[2].access(context)?.into_int_value();
let input_offset = arguments[3].access(context)?.into_int_value();
let input_size = arguments[4].access(context)?.into_int_value();
let output_offset = arguments[5].access(context)?.into_int_value();
let output_size = arguments[6].access(context)?.into_int_value();
let simulation_address: Vec<Option<num::BigUint>> = arguments
.into_iter()
@@ -750,12 +746,12 @@ impl FunctionCall {
Name::StaticCall => {
let arguments = self.pop_arguments::<D, 6>(context)?;
let gas = arguments[0].value.into_int_value();
let address = arguments[1].value.into_int_value();
let input_offset = arguments[2].value.into_int_value();
let input_size = arguments[3].value.into_int_value();
let output_offset = arguments[4].value.into_int_value();
let output_size = arguments[5].value.into_int_value();
let gas = arguments[0].access(context)?.into_int_value();
let address = arguments[1].access(context)?.into_int_value();
let input_offset = arguments[2].access(context)?.into_int_value();
let input_size = arguments[3].access(context)?.into_int_value();
let output_offset = arguments[4].access(context)?.into_int_value();
let output_size = arguments[5].access(context)?.into_int_value();
let simulation_address: Vec<Option<num::BigUint>> = arguments
.into_iter()
@@ -779,12 +775,12 @@ impl FunctionCall {
Name::DelegateCall => {
let arguments = self.pop_arguments::<D, 6>(context)?;
let gas = arguments[0].value.into_int_value();
let address = arguments[1].value.into_int_value();
let input_offset = arguments[2].value.into_int_value();
let input_size = arguments[3].value.into_int_value();
let output_offset = arguments[4].value.into_int_value();
let output_size = arguments[5].value.into_int_value();
let gas = arguments[0].access(context)?.into_int_value();
let address = arguments[1].access(context)?.into_int_value();
let input_offset = arguments[2].access(context)?.into_int_value();
let input_size = arguments[3].access(context)?.into_int_value();
let output_offset = arguments[4].access(context)?.into_int_value();
let output_size = arguments[5].access(context)?.into_int_value();
let simulation_address: Vec<Option<num::BigUint>> = arguments
.into_iter()
@@ -845,7 +841,8 @@ impl FunctionCall {
})?;
revive_llvm_context::polkavm_evm_create::contract_hash(context, identifier)
.map(|argument| Some(argument.value))
.and_then(|argument| argument.access(context))
.map(Some)
}
Name::DataSize => {
let mut arguments = self.pop_arguments::<D, 1>(context)?;
@@ -855,7 +852,8 @@ impl FunctionCall {
})?;
revive_llvm_context::polkavm_evm_create::header_size(context, identifier)
.map(|argument| Some(argument.value))
.and_then(|argument| argument.access(context))
.map(Some)
}
Name::DataCopy => {
let arguments = self.pop_arguments_llvm::<D, 3>(context)?;
@@ -989,7 +987,12 @@ impl FunctionCall {
{
let mut arguments = Vec::with_capacity(N);
for expression in self.arguments.drain(0..N).rev() {
arguments.push(expression.into_llvm(context)?.expect("Always exists").value);
arguments.push(
expression
.into_llvm(context)?
.expect("Always exists")
.access(context)?,
);
}
arguments.reverse();
@@ -97,9 +97,7 @@ impl Literal {
BooleanLiteral::True => num::BigUint::one(),
};
Ok(revive_llvm_context::PolkaVMArgument::new_with_constant(
value, constant,
))
Ok(revive_llvm_context::PolkaVMArgument::value(value).with_constant(constant))
}
LexicalLiteral::Integer(inner) => {
let r#type = self.yul_type.unwrap_or_default().into_llvm(context);
@@ -127,9 +125,7 @@ impl Literal {
}
.expect("Always valid");
Ok(revive_llvm_context::PolkaVMArgument::new_with_constant(
value, constant,
))
Ok(revive_llvm_context::PolkaVMArgument::value(value).with_constant(constant))
}
LexicalLiteral::String(inner) => {
let string = inner.inner;
@@ -200,10 +196,10 @@ impl Literal {
};
if hex_string.len() > revive_common::BYTE_LENGTH_WORD * 2 {
return Ok(revive_llvm_context::PolkaVMArgument::new_with_original(
return Ok(revive_llvm_context::PolkaVMArgument::value(
r#type.const_zero().as_basic_value_enum(),
string,
));
)
.with_original(string));
}
if hex_string.len() < revive_common::BYTE_LENGTH_WORD * 2 {
@@ -220,9 +216,7 @@ impl Literal {
)
.expect("The value is valid")
.as_basic_value_enum();
Ok(revive_llvm_context::PolkaVMArgument::new_with_original(
value, string,
))
Ok(revive_llvm_context::PolkaVMArgument::value(value).with_original(string))
}
}
}
@@ -119,36 +119,28 @@ impl Expression {
})
.map(Some),
Self::Identifier(identifier) => {
let id = identifier.inner;
let pointer = context
.current_function()
.borrow()
.get_stack_pointer(identifier.inner.as_str())
.get_stack_pointer(&id)
.ok_or_else(|| {
anyhow::anyhow!(
"{} Undeclared variable `{}`",
identifier.location,
identifier.inner,
)
anyhow::anyhow!("{} Undeclared variable `{}`", identifier.location, id)
})?;
let constant = context
.current_function()
.borrow()
.yul()
.get_constant(identifier.inner.as_str());
let constant = context.current_function().borrow().yul().get_constant(&id);
let value = context.build_load(pointer, identifier.inner.as_str())?;
let argument = revive_llvm_context::PolkaVMArgument::pointer(pointer, id);
match constant {
Some(constant) => Ok(Some(
revive_llvm_context::PolkaVMArgument::new_with_constant(value, constant),
)),
None => Ok(Some(value.into())),
}
Ok(Some(match constant {
Some(constant) => argument.with_constant(constant),
_ => argument,
}))
}
Self::FunctionCall(call) => Ok(call
.into_llvm(context)?
.map(revive_llvm_context::PolkaVMArgument::new)),
.map(revive_llvm_context::PolkaVMArgument::value)),
}
}
}
@@ -78,7 +78,7 @@ where
.condition
.into_llvm(context)?
.expect("Always exists")
.to_llvm()
.access(context)?
.into_int_value();
let condition = context.builder().build_int_z_extend_or_bit_cast(
condition,
@@ -57,7 +57,7 @@ where
.condition
.into_llvm(context)?
.expect("Always exists")
.to_llvm()
.access(context)?
.into_int_value();
let condition = context.builder().build_int_z_extend_or_bit_cast(
condition,
@@ -209,6 +209,8 @@ where
revive_llvm_context::PolkaVMRemainderFunction.declare(context)?;
revive_llvm_context::PolkaVMSignedRemainderFunction.declare(context)?;
revive_llvm_context::PolkaVMSbrkFunction.declare(context)?;
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
entry.declare(context)?;
@@ -261,6 +263,8 @@ where
revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMSignedRemainderFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;
Ok(())
}
@@ -137,7 +137,7 @@ where
let mut branches = Vec::with_capacity(self.cases.len());
for (index, case) in self.cases.into_iter().enumerate() {
let constant = case.literal.into_llvm(context)?.to_llvm();
let constant = case.literal.into_llvm(context)?.access(context)?;
let expression_block = context
.append_basic_block(format!("switch_case_branch_{}_block", index + 1).as_str());
@@ -161,7 +161,10 @@ where
context.set_basic_block(current_block);
context.builder().build_switch(
scrutinee.expect("Always exists").to_llvm().into_int_value(),
scrutinee
.expect("Always exists")
.access(context)?
.into_int_value(),
default_block,
branches.as_slice(),
)?;
@@ -121,7 +121,7 @@ where
.insert_constant(identifier.inner.clone(), constant);
}
value.to_llvm()
value.access(context)?
}
None => r#type.const_zero().as_basic_value_enum(),
}
@@ -175,7 +175,8 @@ where
.collect::<Vec<inkwell::types::BasicTypeEnum<'ctx>>>()
.as_slice(),
);
if expression.value.get_type() != llvm_type.as_basic_type_enum() {
let value = expression.access(context)?;
if value.get_type() != llvm_type.as_basic_type_enum() {
anyhow::bail!(
"{} Assignment to {:?} received an invalid number of arguments",
location,
@@ -183,7 +184,7 @@ where
);
}
let pointer = context.build_alloca(llvm_type, "bindings_pointer");
context.build_store(pointer, expression.to_llvm())?;
context.build_store(pointer, value)?;
for (index, binding) in self.bindings.into_iter().enumerate() {
let pointer = context.build_gep(