Compare commits

...

58 Commits

Author SHA1 Message Date
xermicus b0ad55715f wip
Signed-off-by: xermicus <bigcyrill@hotmail.com>
2025-09-18 14:40:00 +02:00
xermicus a5fbd545c5 rul
Signed-off-by: xermicus <bigcyrill@hotmail.com>
2025-09-03 20:17:51 +02:00
LJ 2f89c743ce Implement Rust CLI tests (#372)
# Description

Closes #365 

Ports all `resolc` CLI tests to Rust and removes the CLI TypeScript
tests.
2025-08-19 16:04:05 +02:00
LJ ea0ea711ad Update workflows to skip tests for only .md files (#371)
When updating documentation Markdown files, long-running tests still run
unnecessarily. With these changes, it will skip running test workflows
when PRs only containing changes to such files are opened.

Co-authored-by: xermicus <cyrill@parity.io>
2025-08-12 16:12:14 +02:00
LJ b31e8a0d74 Fix llvm-builder installation docs (#370)
Updated installation instructions for the llvm-builder.
2025-08-11 21:48:40 +02:00
xermicus 903cbd7159 YUL tree visitor interface (#369)
- Implement the visitor interface. This simplifies working with the AST,
for example transformations into other IRs or collecting and analyzing
various statistics.
- Switch the explorer to use the visitor interface.
- Add the reciprocal function name conversion for function names.
- Some drive-by cosmetic fixes.

---------

Signed-off-by: xermicus <bigcyrill@hotmail.com>
2025-08-10 00:08:25 +02:00
dependabot[bot] 72f9e4f66e Bump @eslint/plugin-kit from 0.3.3 to 0.3.4 (#367)
Bumps
[@eslint/plugin-kit](https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit)
from 0.3.3 to 0.3.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/rewrite/releases"><code>@​eslint/plugin-kit</code>'s
releases</a>.</em></p>
<blockquote>
<h2>plugin-kit: v0.3.4</h2>
<h2><a
href="https://github.com/eslint/rewrite/compare/plugin-kit-v0.3.3...plugin-kit-v0.3.4">0.3.4</a>
(2025-07-21)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>potential quadratic runtime in regular expression (<a
href="https://redirect.github.com/eslint/rewrite/issues/240">#240</a>)
(<a
href="https://github.com/eslint/rewrite/commit/b283f64099ad6c6b5043387c091691d21b387805">b283f64</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/rewrite/blob/main/packages/plugin-kit/CHANGELOG.md"><code>@​eslint/plugin-kit</code>'s
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/eslint/rewrite/compare/plugin-kit-v0.3.3...plugin-kit-v0.3.4">0.3.4</a>
(2025-07-21)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>potential quadratic runtime in regular expression (<a
href="https://redirect.github.com/eslint/rewrite/issues/240">#240</a>)
(<a
href="https://github.com/eslint/rewrite/commit/b283f64099ad6c6b5043387c091691d21b387805">b283f64</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/eslint/rewrite/commit/380c2248711f5277e56c2977b6b440577b210023"><code>380c224</code></a>
chore: release main (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/242">#242</a>)</li>
<li><a
href="https://github.com/eslint/rewrite/commit/17276ff19cec952ecbe7a1bacfaed67bc134b20c"><code>17276ff</code></a>
docs: Update README sponsors</li>
<li><a
href="https://github.com/eslint/rewrite/commit/b283f64099ad6c6b5043387c091691d21b387805"><code>b283f64</code></a>
fix: potential quadratic runtime in regular expression (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/240">#240</a>)</li>
<li><a
href="https://github.com/eslint/rewrite/commit/46cd5dab8f47b4be65a9aa9202a38603da85f186"><code>46cd5da</code></a>
docs: Update README sponsors</li>
<li><a
href="https://github.com/eslint/rewrite/commit/9677965292cd5b670ef0e4aa7b9b57028a26a0ee"><code>9677965</code></a>
docs: Update README sponsors</li>
<li><a
href="https://github.com/eslint/rewrite/commit/20799b5802db906bc43308108986e0508febe58f"><code>20799b5</code></a>
docs: Update README sponsors</li>
<li>See full diff in <a
href="https://github.com/eslint/rewrite/commits/plugin-kit-v0.3.4/packages/plugin-kit">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@eslint/plugin-kit&package-manager=npm_and_yarn&previous-version=0.3.3&new-version=0.3.4)](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-08-08 11:36:16 +02:00
xermicus a96e1a4233 fix new Rust 1.89 warnings (#368) 2025-08-08 11:14:26 +02:00
dependabot[bot] 56aadce0a9 Bump @eslint/plugin-kit from 0.3.1 to 0.3.3 (#363)
Bumps
[@eslint/plugin-kit](https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit)
from 0.3.1 to 0.3.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/rewrite/releases"><code>@​eslint/plugin-kit</code>'s
releases</a>.</em></p>
<blockquote>
<h2>plugin-kit: v0.3.3</h2>
<h2><a
href="https://github.com/eslint/rewrite/compare/plugin-kit-v0.3.2...plugin-kit-v0.3.3">0.3.3</a>
(2025-06-25)</h2>
<h3>Dependencies</h3>
<ul>
<li>The following workspace dependencies were updated
<ul>
<li>dependencies
<ul>
<li><code>@​eslint/core</code> bumped from ^0.15.0 to ^0.15.1</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2>plugin-kit: v0.3.2</h2>
<h2><a
href="https://github.com/eslint/rewrite/compare/plugin-kit-v0.3.1...plugin-kit-v0.3.2">0.3.2</a>
(2025-06-09)</h2>
<h3>Dependencies</h3>
<ul>
<li>The following workspace dependencies were updated
<ul>
<li>dependencies
<ul>
<li><code>@​eslint/core</code> bumped from ^0.14.0 to ^0.15.0</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/rewrite/blob/main/packages/plugin-kit/CHANGELOG.md"><code>@​eslint/plugin-kit</code>'s
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/eslint/rewrite/compare/plugin-kit-v0.3.2...plugin-kit-v0.3.3">0.3.3</a>
(2025-06-25)</h2>
<h3>Dependencies</h3>
<ul>
<li>The following workspace dependencies were updated
<ul>
<li>dependencies
<ul>
<li><code>@​eslint/core</code> bumped from ^0.15.0 to ^0.15.1</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2><a
href="https://github.com/eslint/rewrite/compare/plugin-kit-v0.3.1...plugin-kit-v0.3.2">0.3.2</a>
(2025-06-09)</h2>
<h3>Dependencies</h3>
<ul>
<li>The following workspace dependencies were updated
<ul>
<li>dependencies
<ul>
<li><code>@​eslint/core</code> bumped from ^0.14.0 to ^0.15.0</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/eslint/rewrite/commit/0496201974aad87fdcf3aa2a63ec74e91b54825e"><code>0496201</code></a>
chore: release main (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/229">#229</a>)</li>
<li><a
href="https://github.com/eslint/rewrite/commit/f5e6d683ee00b24b98777291c0a9a83719fe3402"><code>f5e6d68</code></a>
chore: hoist cli tools to root level (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/224">#224</a>)</li>
<li><a
href="https://github.com/eslint/rewrite/commit/48b1f849476582257e1b6a110c4af55adbbec2e8"><code>48b1f84</code></a>
chore: release main (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/216">#216</a>)</li>
<li><a
href="https://github.com/eslint/rewrite/commit/147afec6be22b6ed4151c1e0a8fc40c061d626d6"><code>147afec</code></a>
chore: update <code>package.json</code> to follow template (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/225">#225</a>)</li>
<li><a
href="https://github.com/eslint/rewrite/commit/0a6aad0a70cc7261a303df614a4466e0b1f525b8"><code>0a6aad0</code></a>
docs: fix bun command and update documentation (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/222">#222</a>)</li>
<li><a
href="https://github.com/eslint/rewrite/commit/2a8913cdae0aa57dfea993256dbe6a04029909da"><code>2a8913c</code></a>
docs: Update README sponsors</li>
<li><a
href="https://github.com/eslint/rewrite/commit/cb858ffb8b77ea76187a857546c7a838a1fb4881"><code>cb858ff</code></a>
refactor: update and fix error types in JSDoc comments (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/213">#213</a>)</li>
<li><a
href="https://github.com/eslint/rewrite/commit/4ec089e5b60d64f695d09a973dbc4eb72026f112"><code>4ec089e</code></a>
docs: Update README sponsors</li>
<li><a
href="https://github.com/eslint/rewrite/commit/1ddb2ed129e2999c2164f933282d8df1884bbd6c"><code>1ddb2ed</code></a>
docs: Update README sponsors</li>
<li><a
href="https://github.com/eslint/rewrite/commit/aa6a48b789a5a48eb405c03502a71e5879905876"><code>aa6a48b</code></a>
docs: Update README sponsors</li>
<li>See full diff in <a
href="https://github.com/eslint/rewrite/commits/plugin-kit-v0.3.3/packages/plugin-kit">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@eslint/plugin-kit&package-manager=npm_and_yarn&previous-version=0.3.1&new-version=0.3.3)](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-07-22 18:45:52 +02:00
xermicus 9751481f6b the revive-explorer utility (#364)
A maintainable and more precise version of what was a hacky but useful
script, exploring the compilers YUL lowering unit.

It analyzes a given shared objects from the debug dump and outputs:
- The count of each YUL statement translated.
- A per YUL statement break-down of bytecode size contributed per.
- Estimated `yul-phaser` cost parameters.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-07-22 09:17:55 +02:00
xermicus c285a6ec3d add columns to debug information (#362)
- Add column numbers to debug information.
- Do not build allocas at entry for now.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-07-17 09:53:54 +02:00
xermicus a0396dd6d0 solc-json-interface: support for YUL optimizer details (#361)
- Add support for the YUL optimizer details in the standard json input
definition.
- Make optimizer settings optional. They can be omitted and solc will
pick default values ([see here for
reference](https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description)).

For example allows to use the
[`yul-phaser`](https://github.com/ethereum/solidity/blob/0917604a5ec7cff8bd40a1137f4fcb303fb90527/tools/yulPhaser/README.md?plain=1)
utility. I did a single search with slightly adjusted costs (just made
some educated guess) and after an hour or so this already found an
optimizer sequence
(`OESsShMxeoufcSTvlFxtelTfnfEvicdFxnsvopgCaIeLcnvsTtUrUgdVTUttaeUomccTTTuujsVVvVDvvueUrTjUOmjrrhuuTtj`)
which shrinks the size of the `EndpointV2.sol` from LayerZero by 10%.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
2025-07-14 11:25:45 +02:00
Alexander Samusev 141a8b752c Make nigthly version semver compliant (#360)
cc https://github.com/paritytech/revive/pull/357
2025-07-08 17:17:05 +02:00
Alexander Samusev 7c932f719b Generate nightly builds and json files (#357)
PR adds a nightly release pipeline.
Since the logic for the nightly build and release differs significantly
from the usual pipeline, I decided to put it in a separate file. The
same applies to the Python script that generates JSON files.

The nightly pipeline runs at 01:00 UTC every day, producing artefacts
and retaining them for 40 days if commits are made the day before it
runs.

cc https://github.com/paritytech/revive/issues/355
cc https://github.com/paritytech/devops/issues/4141
2025-07-07 17:46:55 +02:00
xermicus b238913a7d emit YUL builtins debug line info and fix debug info source file (#358)
This PR fixes and enhances debug info generation:
1. Adds line information for each lowered YUL builtin and the `if`
statement.
2. Fixes the debug info source path to match the YUL file of the
contract dumped to the `--debug-output-dir`.

This improves inspection of the generated code a lot. Excerpt from
`llvm-objdump -Sl /tmp/dbg/contracts_EndpointV2.sol.EndpointV2.so`:

```
; /tmp/dbg/contracts_EndpointV2.sol.EndpointV2.yul:203
;                 let _1 := memoryguard(0x80)
   13c3e: 3aa5b023      sd      a0, 0x3a0(a1)
   13c42: 38a5bc23      sd      a0, 0x398(a1)
   13c46: 38a5b823      sd      a0, 0x390(a1)
   13c4a: 08000513      li      a0, 0x80
   13c4e: 38a5b423      sd      a0, 0x388(a1)
; /tmp/dbg/contracts_EndpointV2.sol.EndpointV2.yul:204
;                 mstore(64, _1)
   13c52: 3885b503      ld      a0, 0x388(a1)
   13c56: 3905b603      ld      a2, 0x390(a1)
   13c5a: 3985b683      ld      a3, 0x398(a1)
   13c5e: 3a05b703      ld      a4, 0x3a0(a1)
   13c62: 38e5b023      sd      a4, 0x380(a1)
   13c66: 36d5bc23      sd      a3, 0x378(a1)
   13c6a: 36c5b823      sd      a2, 0x370(a1)
   13c6e: 36a5b423      sd      a0, 0x368(a1)
   13c72: 04000513      li      a0, 0x40
   13c76: 65d9          lui     a1, 0x16
   13c78: a605859b      addiw   a1, a1, -0x5a0
   13c7c: 95a6          add     a1, a1, s1
   13c7e: 00000097      auipc   ra, 0x0
   13c82: 000080e7      jalr    ra <__runtime+0x1de>

0000000000013c86 <.Lpcrel_hi27>:
; /tmp/dbg/contracts_EndpointV2.sol.EndpointV2.yul:205
;                 if iszero(lt(calldatasize(), 4))
   13c86: 00000517      auipc   a0, 0x0
   13c8a: 00053503      ld      a0, 0x0(a0)
   13c8e: 4108          lw      a0, 0x0(a0)
   13c90: 4591          li      a1, 0x4
   13c92: 06b56263      bltu    a0, a1, 0x13cf6 <.Lpcrel_hi27+0x70>
   13c96: a009          j       0x13c98 <.Lpcrel_hi27+0x12>
```

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-07-04 19:56:52 +02:00
xermicus ed608699af release resolc v0.3.0 (#354)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-06-28 12:32:48 +02:00
xermicus 75fc23c810 add the missing memset builtin (#353)
Closes  #350

- Add the missing `memset` builtin which was accidentally deleted in a
previous PR.
- Add a compilation test to ensure the `memset` builtin is present.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-06-28 12:01:34 +02:00
xermicus 486c9c28a1 new clippies (#352)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-06-28 11:48:24 +02:00
PG Herveou 7656c6a8b4 js: Add stats and override options (#348)
- Add `RESOLC_BIN` env variable to force use a binary compiler instead
of the wasm
- Add `--diff-stats` options to print file path 

e.g

```
~/github/redstone-oracles-monorepo main*
❯ npx @parity/resolc@latest --diff-stats \
  --base-path . \
  --include-path node_modules \
  --bin packages/evm-connector/contracts/samples/SampleWithEvents.sol
┌─────────┬──────────────────────────────────────────────────────────────────────────┬───────────────────────────────┬────────────┬────────────┬───────────┐
│ (index) │ file                                                                     │ contract                      │ polkavm    │ bin        │ diff      │
├─────────┼──────────────────────────────────────────────────────────────────────────┼───────────────────────────────┼────────────┼────────────┼───────────┤
│ 0       │ 'packages/evm-connector/contracts/core/CalldataExtractor.sol'            │ 'CalldataExtractor'           │ '4.31 kB'  │ '2.53 kB'  │ '70.12%'  │
│ 1       │ 'packages/evm-connector/contracts/core/RedstoneConstants.sol'            │ 'RedstoneConstants'           │ '0.80 kB'  │ '0.17 kB'  │ '368.18%' │
│ 2       │ 'packages/evm-connector/contracts/core/RedstoneDefaultsLib.sol'          │ 'RedstoneDefaultsLib'         │ '0.90 kB'  │ '0.31 kB'  │ '189.06%' │
│ 3       │ 'packages/evm-connector/contracts/libs/BitmapLib.sol'                    │ 'BitmapLib'                   │ '0.90 kB'  │ '0.31 kB'  │ '189.06%' │
│ 4       │ 'packages/evm-connector/contracts/libs/NumericArrayLib.sol'              │ 'NumericArrayLib'             │ '0.90 kB'  │ '0.31 kB'  │ '189.06%' │
│ 5       │ 'packages/evm-connector/contracts/libs/SignatureLib.sol'                 │ 'SignatureLib'                │ '0.90 kB'  │ '0.31 kB'  │ '189.06%' │
│ 6       │ 'packages/evm-connector/contracts/mocks/RedstoneConsumerNumericMock.sol' │ 'RedstoneConsumerNumericMock' │ '13.62 kB' │ '10.17 kB' │ '33.96%'  │
│ 7       │ 'packages/evm-connector/contracts/samples/SampleWithEvents.sol'          │ 'SampleWithEvents'            │ '29.34 kB' │ '15.60 kB' │ '88.03%'  │
└─────────┴──────────────────────────────────────────────────────────────────────────┴───────────────────────────────┴────────────┴────────────┴───────────┘
```
2025-06-19 12:17:09 +02:00
xermicus 8754d802fa llvm-context: bugfix PHI values in SAR builtin translation (#345)
Closes #344

Signed-off-by: xermicus <cyrill@parity.io>
2025-06-14 15:12:03 +02:00
PG Herveou 63f0266fff @parity/resolc fix sol file resolutions (#343)
second take on #339 

turns out that the resolve-pkg npm package was also failing to properly
resolve package with an exports field define in the package.json.

This fixes it and add a test case with such a package
2025-06-05 10:05:53 +02:00
xermicus e94432eaa0 ci: the resolc version dictates binary releases (#341)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-06-03 15:47:33 +02:00
xermicus 1fc3aa1554 release resolc v0.2.0 (#340)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-06-03 15:36:54 +02:00
Chris a77ab501c8 fix: exclude EVM bytecode from production solc requests (#338)
This is to address issue #320

## Introduced changes
- Added new_required_for_tests() method that includes EVM bytecode flags
- Modified new_required() to exclude evm.bytecode and
evm.deployedBytecode
- Updated test utilities to explicitly request EVM bytecode when needed

Signed-off-by: 0xf333 <0x333@tuta.io>
2025-06-03 10:43:52 +02:00
xermicus 8a3c587bbe solc-json-interface: do not unconditionally skip serialization of custom keys (#337)
The data structure can be used to build the JSON input for `resolc` too.
In that case serializing of provided custom options should not be
dismissed.

Makes the memory settings struct more modular as a drive-by.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-06-03 08:17:54 +02:00
PG Herveou 45b6a57cae Fix npm package resolution (#339)
Current approach fails with an 'ERR_PACKAGE_PATH_NOT_EXPORTED' error for
npm package that defines an exports field in the package.json

e.g:
> require.resolve('@redstone-finance/evm-connector/package.json')
will fail with
Uncaught:
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './package.json'
is not defined by "exports" in
/home/pg/github/evm-test-suite/eth-rpc/node_modules/@redstone-finance/evm-connector/package.json
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
2025-06-02 14:41:06 +02:00
xermicus 8a730f42cc update NPM package version (#336)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-28 08:15:41 +02:00
xermicus 413819facd do not use wildcard dependency in resolc crate (#335)
Do not use wildcard dependency in resolc crate. No sure why the publish
dry-run doesn't catch this.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 14:19:24 +02:00
xermicus fa0ad68279 make resolc crate publishable (#334)
- Fetching the commit SHA must not panic if not executed in a git
repository.
- Remove the license printer.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 14:13:12 +02:00
xermicus 004c71d5d5 add description to revive-differential (#333)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 13:26:05 +02:00
xermicus 4d659ac2a6 release resolc v0.1.0 (#332)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 12:06:42 +02:00
xermicus ed9dc60417 bump dependencies (#331)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 11:24:15 +02:00
xermicus 3b9144ef3b add installation instructions to the readme (#330)
Closes #294

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 10:23:42 +02:00
xermicus bd4e108bb0 resolc crate (#328)
- Factor the YUL crate out of `revive-solidity`.
- `revive-solidity` is in reality not a Solidity implementation but the
revive solidity compiler driver (`resolc`). By renaming we not only get
this straight but also a binary with the same name as the crate which
should be less confusing.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 09:48:43 +02:00
xermicus 090e3ac13c bugfix an env var in the release workflow (#329)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 09:29:25 +02:00
xermicus 3389865af7 solc-json-interface: make the input Cloneable (#323)
It helps external consumers working with the
`revive-solc-json-interface` crate.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-21 10:03:32 +02:00
xermicus af39d506d9 update emsdk (#324)
Update Emscripten SDK to latest version `v4.0.9`.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-21 07:01:41 +02:00
xermicus bb2f829361 expose custom PVM settings in the standard json interface (#318)
Exposes the following PolkaVM specific options via the standard json
interface:
- Heap size
- Stack size
- Whether to emit source level debug information

Additionally it is now forbidden to specify those as CLI option in
standard JSON mode. They are bytecode altering options and having
multiple ways to specify them creates unnecessary room for confusion:
The standard JSON input description should be sufficient and succint for
reproducible builds.

Closes #290

---------

Signed-off-by: xermicus <bigcyrill@hotmail.com>
2025-05-13 15:19:00 +02:00
xermicus 1b8fcc4649 Update the Rust version (#316)
- Update the Rust version to 1.85
- Update the package-lock.json
- Update the musl-cross docker image

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-09 20:00:58 +02:00
Cyrill Leutwiler 0e9e405f21 remove any git dependencies
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-09 17:26:20 +02:00
xermicus 722dd86c27 remove STATUS.md (#315)
This information is provided in the docs.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-09 12:21:25 +02:00
PG Herveou 0421869e4b Replace release 0.1.0-dev.15 with 0.1.0-dev.16 (#314)
add missing patch and rename release
2025-05-08 15:26:10 +02:00
xermicus 32f55b976c update changelog (#313)
somehow went wrong

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 14:16:46 +02:00
PG Herveou 459a786299 v0.1.0-dev.15 release (#311)
Co-authored-by: xermicus <cyrill@parity.io>
2025-05-08 14:09:55 +02:00
xermicus fbaa45f283 support solc v0.8.30 (#308)
- Add support for solc `v0.8.30`. No changes in YUL or the binary
interface.
- Fix a semver bug in the NPM packages to correctly align their solc
dependencies with our last supported version.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 13:29:13 +02:00
xermicus f2fac85dae add npm package information to release checklist (#312)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 13:14:28 +02:00
PG Herveou b8f3073e29 Run lint:fix (#309) 2025-05-08 12:36:11 +02:00
xermicus 11d47d74ac apply size optimizations by default (#298)
So far if no optimization level was specified, optimizations for
execution time were applied. However, we currently are a bit limited on
code size. Add to that, this setting is not available in solc and people
generally ignore the docs, generating a lot of support requests.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-07 15:35:48 +02:00
xermicus e3a9c95d32 llvm-builder: use the ninja generator for building the builtins on windows (#299)
The builtins build should use the Ninja generator (MSVC does not build a
valid archive).

Tested and verified here:
https://github.com/paritytech/revive-alex-workflowtest/releases/tag/untagged-f02d0f574bab8404fead

Closes #305

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-07 11:10:58 +02:00
PG Herveou a560b2d919 Fix npm-release job (#297)
follow up from #295
2025-04-30 21:48:11 +02:00
PG Herveou e07d0f0cb7 Move @parity/resolc from js-revive (#296)
- Move npm package from paritytech/js-revive 
- Rename package to `@parity/resolc`
2025-04-30 17:24:52 +02:00
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
251 changed files with 12974 additions and 7684 deletions
+2 -2
View File
@@ -10,8 +10,8 @@ rustflags = [
"-Clink-arg=-sWASM_ASYNC_COMPILATION=0", "-Clink-arg=-sWASM_ASYNC_COMPILATION=0",
"-Clink-arg=-sDYNAMIC_EXECUTION=0", "-Clink-arg=-sDYNAMIC_EXECUTION=0",
"-Clink-arg=-sALLOW_TABLE_GROWTH=1", "-Clink-arg=-sALLOW_TABLE_GROWTH=1",
"-Clink-arg=--js-library=js/embed/soljson_interface.js", "-Clink-arg=--js-library=js/emscripten/embed/soljson_interface.js",
"-Clink-arg=--pre-js=js/embed/pre.js", "-Clink-arg=--pre-js=js/emscripten/embed/pre.js",
"-Clink-arg=-sSTACK_SIZE=128kb", "-Clink-arg=-sSTACK_SIZE=128kb",
"-Clink-arg=-sNODEJS_CATCH_EXIT=0" "-Clink-arg=-sNODEJS_CATCH_EXIT=0"
] ]
+1 -1
View File
@@ -3,7 +3,7 @@ inputs:
version: version:
description: "" description: ""
required: false required: false
default: "3.1.64" default: "4.0.9"
runs: runs:
+1 -1
View File
@@ -19,7 +19,7 @@ runs:
shell: bash shell: bash
run: | run: |
mkdir -p solc mkdir -p solc
curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.29/${SOLC_NAME} curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.30/${SOLC_NAME}
- name: Make Solc Executable - name: Make Solc Executable
if: ${{ runner.os == 'Windows' }} if: ${{ runner.os == 'Windows' }}
+10
View File
@@ -1,3 +1,13 @@
#!/usr/bin/env python3
"""
This script generates JSON files for different platforms based on GitHub release data.
It fetches release information from a specified GitHub repository and tag,
parses the release assets, and generates JSON files for each platform with relevant metadata.
It also handles checksum files and updates a list.json file for each platform.
It requires the GITHUB_TOKEN environment variable to be set for authentication.
Usage:
python json_generator.py <repo> <tag>
"""
import os import os
import sys import sys
import json import json
+155
View File
@@ -0,0 +1,155 @@
#!/usr/bin/env python3
"""
This script generates JSON files for different platforms based on GitHub data.
Requires the GITHUB_SHA, FIRST_SOLC_VERSION, LAST_SOLC_VERSION, TAG and FILEPATH environment variables to be set.
Usage:
python json_generator_nightly.py
"""
import os
import sys
import json
from datetime import datetime
def validate_env_variables():
"""Validate that environment variables are set."""
if "GITHUB_SHA" not in os.environ:
print("Error: GITHUB_SHA environment variable is not set.")
sys.exit(1)
if "FIRST_SOLC_VERSION" not in os.environ:
print("Error: FIRST_SOLC_VERSION environment variable is not set.")
sys.exit(1)
if "LAST_SOLC_VERSION" not in os.environ:
print("Error: LAST_SOLC_VERSION environment variable is not set.")
sys.exit(1)
if "TAG" not in os.environ:
print("Error: TAG environment variable is not set.")
sys.exit(1)
if "FILEPATH" not in os.environ:
print("Error: FILEPATH environment variable is not set.")
sys.exit(1)
def fetch_data_file():
"""
Fetch the data.json file with artifacts urls and sha256 checksums
and parse it into a single dictionary mapping artifact names to their URLs and SHAs.
"""
# read data.json file
artifacts_data = {}
data_file_path = os.environ["FILEPATH"]
if not os.path.exists(data_file_path):
print("Error: data.json file not found.")
sys.exit(1)
with open(data_file_path, 'r') as f:
try:
artifacts_data = json.load(f)
except json.JSONDecodeError:
print("Error: data.json file is not a valid JSON.")
sys.exit(1)
result = {}
for item in artifacts_data:
for key, value in item.items():
if key.endswith('_url'):
base_key = key.rsplit('_url', 1)[0]
if base_key not in result:
result[base_key] = {}
result[base_key]['url'] = value
elif key.endswith('_sha'):
base_key = key.rsplit('_sha', 1)[0]
if base_key not in result:
result[base_key] = {}
result[base_key]['sha'] = value
return result
def extract_build_hash():
"""Extract the first 8 characters of the commit hash."""
sha = os.environ.get("GITHUB_SHA")
return f"commit.{sha[:8]}"
def generate_asset_json_nightly(name, url, checksum):
"""Generate JSON for a specific asset."""
# Date in format YYYY-MM-DD
date = datetime.now().strftime("%Y.%-m.%-d")
last_version = os.environ.get("TAG").replace('v','')
version = f"{last_version}-nightly.{date}"
SHA = os.environ.get("GITHUB_SHA", "")[:8]
build = f"commit.{SHA}"
long_version = f"{version}+{build}"
return {
"name": name,
"version": version,
"build": build,
"longVersion": long_version,
"url": url,
"sha256": checksum,
"firstSolcVersion": os.environ.get("FIRST_SOLC_VERSION"),
"lastSolcVersion": os.environ.get("LAST_SOLC_VERSION")
}
def save_platform_json(platform_folder, asset_json):
"""Save asset JSON and update list.json for a specific platform."""
# Create platform folder if it doesn't exist
os.makedirs(platform_folder, exist_ok=True)
# Update or create list.json
list_file_path = os.path.join(platform_folder, "list.json")
if os.path.exists(list_file_path):
with open(list_file_path, 'r') as f:
try:
list_data = json.load(f)
except json.JSONDecodeError:
list_data = {"builds": [], "releases": {}, "latestRelease": ""}
else:
list_data = {"builds": [], "releases": {}, "latestRelease": ""}
# Remove any existing entry with the same path
list_data['builds'] = [
build for build in list_data['builds']
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] = f"{asset_json['name']}+{asset_json['longVersion']}"
# Update latest release
list_data['latestRelease'] = version
with open(list_file_path, 'w') as f:
json.dump(list_data, f, indent=4)
def main():
validate_env_variables()
data = fetch_data_file()
# Mapping of asset names to platform folders
platform_mapping = {
'resolc-x86_64-unknown-linux-musl': 'linux',
'resolc-universal-apple-darwin': 'macos',
'resolc-x86_64-pc-windows-msvc': 'windows',
'resolc-web.js': 'wasm'
}
# Process each asset
for asset in data.keys():
platform_name = platform_mapping.get(asset)
if platform_name:
platform_folder = os.path.join(platform_name)
asset_json = generate_asset_json_nightly(asset, data[asset]['url'], data[asset]['sha'])
save_platform_json(platform_folder, asset_json)
print(f"Processed {asset} for {platform_name}")
if __name__ == "__main__":
main()
+6 -1
View File
@@ -87,9 +87,14 @@ jobs:
run: | run: |
brew install ninja brew install ninja
- name: Install Dependencies
if: ${{ matrix.host == 'windows' }}
run: |
choco install ninja
- name: Install LLVM Builder - name: Install LLVM Builder
run: | run: |
cargo install --path crates/llvm-builder cargo install --locked --force --path crates/llvm-builder
- name: Clone LLVM - name: Clone LLVM
run: | run: |
+385
View File
@@ -0,0 +1,385 @@
name: Nightly Release
on:
schedule:
# Run every day at 01:00 UTC
- cron: "0 1 * * *"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067
jobs:
# check if there were commits yesterday
check_commits:
runs-on: ubuntu-latest
outputs:
has_commits: ${{ steps.check_commits.outputs.has_commits }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch full history to check previous commits
ref: "main"
- name: Check for commits from yesterday
id: check_commits
run: |
# Get yesterday's date in YYYY-MM-DD format
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
echo "Checking for commits from: $YESTERDAY"
# Check if there were any commits yesterday
COMMIT_COUNT=$(git log --oneline --since="$YESTERDAY 00:00:00" --until="$YESTERDAY 23:59:59" | wc -l)
echo "Found $COMMIT_COUNT commits from yesterday"
if [ $COMMIT_COUNT -gt 0 ]; then
echo "has_commits=true" >> $GITHUB_OUTPUT
echo "✅ Found $COMMIT_COUNT commits from yesterday - continuing workflow"
else
echo "has_commits=false" >> $GITHUB_OUTPUT
echo "❌ No commits found from yesterday - skipping remaining steps"
echo "::notice::❌ No commits found from yesterday - skipping remaining steps"
fi
build:
# github actions matrix jobs don't support multiple outputs
# ugly workaround from https://github.com/orgs/community/discussions/17245#discussioncomment-11222880
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
outputs:
resolc-x86_64-unknown-linux-musl_url: ${{ steps.set-output.outputs.resolc-x86_64-unknown-linux-musl_url }}
resolc-x86_64-unknown-linux-musl_sha: ${{ steps.set-output.outputs.resolc-x86_64-unknown-linux-musl_sha }}
resolc-aarch64-apple-darwin_url: ${{ steps.set-output.outputs.resolc-aarch64-apple-darwin_url }}
resolc-aarch64-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-aarch64-apple-darwin_sha }}
resolc-x86_64-apple-darwin_url: ${{ steps.set-output.outputs.resolc-x86_64-apple-darwin_url }}
resolc-x86_64-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-x86_64-apple-darwin_sha }}
resolc-x86_64-pc-windows-msvc_url: ${{ steps.set-output.outputs.resolc-x86_64-pc-windows-msvc_url }}
resolc-x86_64-pc-windows-msvc_sha: ${{ steps.set-output.outputs.resolc-x86_64-pc-windows-msvc_sha }}
strategy:
matrix:
target:
[
x86_64-unknown-linux-musl,
aarch64-apple-darwin,
x86_64-apple-darwin,
x86_64-pc-windows-msvc,
]
include:
- target: x86_64-unknown-linux-musl
type: musl
runner: ubuntu-24.04
- target: aarch64-apple-darwin
type: native
runner: macos-14
- target: x86_64-apple-darwin
type: native
runner: macos-13
- target: x86_64-pc-windows-msvc
type: native
runner: windows-2022
runs-on: ${{ matrix.runner }}
needs: [check_commits]
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
# without this it will override our rust flags
rustflags: ""
cache-key: ${{ matrix.target }}
- name: Download LLVM
uses: ./.github/actions/get-llvm
with:
target: ${{ matrix.target }}
- name: Build
if: ${{ matrix.type == 'native' }}
shell: bash
run: |
export LLVM_SYS_181_PREFIX=$PWD/llvm-${{ matrix.target }}
make install-bin
mv target/release/resolc resolc-${{ matrix.target }} || mv target/release/resolc.exe resolc-${{ matrix.target }}.exe
- name: Build
if: ${{ matrix.type == 'musl' }}
run: |
docker run -v $PWD:/opt/revive $RUST_MUSL_CROSS_IMAGE /bin/bash -c "
cd /opt/revive
chown -R root:root .
apt update && apt upgrade -y && apt install -y pkg-config
export LLVM_SYS_181_PREFIX=/opt/revive/llvm-${{ matrix.target }}
make install-bin
mv target/${{ matrix.target }}/release/resolc resolc-${{ matrix.target }}
"
sudo chown -R $(id -u):$(id -g) .
- name: Install Solc
uses: ./.github/actions/get-solc
- name: Basic Sanity Check
shell: bash
run: |
result=$(./resolc-${{ matrix.target }} --bin crates/integration/contracts/flipper.sol)
echo $result
if [[ $result == *'0x50564d'* ]]; then exit 0; else exit 1; fi
- name: Upload artifacts (nightly)
uses: actions/upload-artifact@v4
id: artifact-upload-step
with:
name: resolc-${{ matrix.target }}
path: resolc-${{ matrix.target }}*
retention-days: 40
- name: Set output variables (nightly)
id: set-output
shell: bash
run: |
echo "Artifact URL is ${{ steps.artifact-upload-step.outputs.artifact-url }}"
echo "Artifact SHA is ${{ steps.artifact-upload-step.outputs.artifact-digest }}"
echo "resolc-${{ matrix.target }}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}"
echo "resolc-${{ matrix.target }}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
echo "resolc-${{ matrix.target }}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}"
echo "resolc-${{ matrix.target }}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}" >> "$GITHUB_OUTPUT"
build-wasm:
runs-on: ubuntu-24.04
needs: [check_commits]
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
outputs:
resolc-web.js_url: ${{ steps.set-output.outputs.resolc_web_js_url }}
resolc-web.js_sha: ${{ steps.set-output.outputs.resolc_web_js_sha }}
env:
RELEASE_RESOLC_WASM_URI: https://github.com/paritytech/revive/releases/download/${{ github.ref_name }}/resolc.wasm
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: wasm32-unknown-emscripten
# without this it will override our rust flags
rustflags: ""
- name: Download Host LLVM
uses: ./.github/actions/get-llvm
with:
target: x86_64-unknown-linux-gnu
- name: Download Wasm LLVM
uses: ./.github/actions/get-llvm
with:
target: wasm32-unknown-emscripten
- name: Download EMSDK
uses: ./.github/actions/get-emsdk
- name: Build
run: |
export LLVM_SYS_181_PREFIX=$PWD/llvm-x86_64-unknown-linux-gnu
export REVIVE_LLVM_TARGET_PREFIX=$PWD/llvm-wasm32-unknown-emscripten
source emsdk/emsdk_env.sh
make install-wasm
chmod -x ./target/wasm32-unknown-emscripten/release/resolc.wasm
- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: "20"
- name: Basic Sanity Check
run: |
mkdir -p solc
curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.30/soljson.js
node -e "
const soljson = require('solc/soljson');
const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js');
const compiler = createRevive();
compiler.soljson = soljson;
const standardJsonInput =
{
language: 'Solidity',
sources: {
'MyContract.sol': {
content: 'pragma solidity ^0.8.0; contract MyContract { function greet() public pure returns (string memory) { return \'Hello\'; } }',
},
},
settings: { optimizer: { enabled: false } }
};
compiler.writeToStdin(JSON.stringify(standardJsonInput));
compiler.callMain(['--standard-json']);
// Collect output
const stdout = compiler.readFromStdout();
const stderr = compiler.readFromStderr();
if (stderr) { console.error(stderr); process.exit(1); }
let out = JSON.parse(stdout);
let bytecode = out.contracts['MyContract.sol']['MyContract'].evm.bytecode.object
console.log(bytecode);
if(!bytecode.startsWith('50564d')) { process.exit(1); }
"
- name: Compress Artifact
run: |
mkdir -p resolc-wasm32-unknown-emscripten
mv ./target/wasm32-unknown-emscripten/release/resolc.js ./resolc-wasm32-unknown-emscripten/
mv ./target/wasm32-unknown-emscripten/release/resolc.wasm ./resolc-wasm32-unknown-emscripten/
mv ./target/wasm32-unknown-emscripten/release/resolc_web.js ./resolc-wasm32-unknown-emscripten/
# There is no way to upload several files as several artifacts with a single upload-artifact step
# It's needed to have resolc_web.js separately for night builds for resolc-bin repo
# https://github.com/actions/upload-artifact/issues/331
- name: Upload artifact resolc.js (nightly)
uses: actions/upload-artifact@v4
with:
name: resolc.js
path: resolc-wasm32-unknown-emscripten/resolc.js
retention-days: 40
- name: Upload artifacts resolc.wasm (nightly)
uses: actions/upload-artifact@v4
with:
name: resolc.wasm
path: resolc-wasm32-unknown-emscripten/resolc.wasm
retention-days: 40
- name: Upload artifacts resolc_web.js (nightly)
uses: actions/upload-artifact@v4
id: artifact-upload-step
with:
name: resolc_web.js
path: resolc-wasm32-unknown-emscripten/resolc_web.js
retention-days: 40
- name: Set output variables
id: set-output
env:
TARGET: resolc_web_js
run: |
echo "Artifact URL is ${{ steps.artifact-upload-step.outputs.artifact-url }}"
echo "Artifact SHA is ${{ steps.artifact-upload-step.outputs.artifact-digest }}"
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}"
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}""
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}"" >> "$GITHUB_OUTPUT"
create-macos-fat-binary:
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
needs: [build]
outputs:
resolc-universal-apple-darwin_url: ${{ steps.set-output.outputs.resolc-universal-apple-darwin_url }}
resolc-universal-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-universal-apple-darwin_sha }}
runs-on: macos-14
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Create macOS Fat Binary
run: |
lipo resolc-aarch64-apple-darwin resolc-x86_64-apple-darwin -create -output resolc-universal-apple-darwin
- name: Make Executable
run: |
chmod +x resolc-universal-apple-darwin
- uses: actions/upload-artifact@v4
id: artifact-upload-step
with:
name: resolc-universal-apple-darwin
path: resolc-universal-apple-darwin
retention-days: 40
- name: Set output variables
id: set-output
env:
TARGET: resolc-universal-apple-darwin
run: |
echo "Artifact URL is ${{ steps.artifact-upload-step.outputs.artifact-url }}"
echo "Artifact SHA is ${{ steps.artifact-upload-step.outputs.artifact-digest }}"
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}"
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}""
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}"" >> "$GITHUB_OUTPUT"
generate-nightly-json:
runs-on: ubuntu-24.04
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
environment: tags
needs: [build-wasm, build, create-macos-fat-binary, check_commits]
steps:
- name: Checkout revive
uses: actions/checkout@v4
with:
path: revive
- name: Checkout resolc-bin
uses: actions/checkout@v4
with:
repository: paritytech/resolc-bin
path: resolc-bin
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
merge-multiple: true
path: bins
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.REVIVE_JSON_APP_ID }}
private-key: ${{ secrets.REVIVE_JSON_APP_KEY }}
owner: paritytech
repositories: resolc-bin
- name: Generate JSON
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TOKEN: ${{ steps.app-token.outputs.token }}
APP_NAME: "paritytech-revive-json"
Green: "\e[32m"
NC: "\e[0m"
run: |
echo '[' > data.json
echo '${{ toJSON(needs.build.outputs) }}' >> data.json
echo ',' >> data.json
echo '${{ toJSON(needs.build-wasm.outputs) }}' >> data.json
echo ',' >> data.json
echo '${{ toJSON(needs.create-macos-fat-binary.outputs) }}' >> data.json
echo ']' >> data.json
chmod +x bins/resolc-x86_64-unknown-linux-musl
export FIRST_SOLC_VERSION=$(./bins/resolc-x86_64-unknown-linux-musl --supported-solc-versions | cut -f 1 -d "," | tr -d ">=")
export LAST_SOLC_VERSION=$(./bins/resolc-x86_64-unknown-linux-musl --supported-solc-versions | cut -f 2 -d "," | tr -d "<=")
export FILEPATH=$(readlink -f data.json)
export TAG=$(cd revive;gh release list --json name,isLatest --jq '.[] | select(.isLatest)|.name')
cd resolc-bin
mkdir -p nightly
cd nightly
python3 ../../revive/.github/scripts/json_generator_nightly.py
cd ..
git status
echo "${Green}Add new remote with gh app token${NC}"
git remote set-url origin $(git config remote.origin.url | sed "s/github.com/${APP_NAME}:${TOKEN}@github.com/g")
echo "${Green}Remove http section that causes issues with gh app auth token${NC}"
sed -i.bak '/\[http/d' ./.git/config
sed -i.bak '/extraheader/d' ./.git/config
git config user.email "ci@parity.io"
git config user.name "${APP_NAME}"
git add nightly/
git commit -m "Update nightly json"
git push origin main
echo "::notice::nightly info.list files were successfully published to https://github.com/paritytech/resolc-bin"
+45 -4
View File
@@ -14,7 +14,8 @@ concurrency:
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:68b86bc7cb2867259e6b233415a665ff4469c28b57763e78c3bfea1c68091561 # if changed, dont forget to update the env var in release-nightly.yml
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067
jobs: jobs:
check-version-changed: check-version-changed:
@@ -44,7 +45,7 @@ jobs:
exit 0 exit 0
fi fi
export PKG_VER=v$(cat Cargo.toml | grep -A 5 package] | grep version | cut -d '=' -f 2 | tr -d '"' | tr -d " ") export PKG_VER=v$(cat crates/resolc/Cargo.toml | grep -A 5 package] | grep version | cut -d '=' -f 2 | tr -d '"' | tr -d " ")
echo "Current tag $CURRENT_TAG" echo "Current tag $CURRENT_TAG"
echo "Package version $PKG_VER" echo "Package version $PKG_VER"
# #
@@ -143,7 +144,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: [check-version-changed] needs: [check-version-changed]
env: env:
RELEASE_RESOLC_WASM_URI: https://github.com/paritytech/revive-workflow-test/releases/download/${{ github.ref_name }}/resolc.wasm RELEASE_RESOLC_WASM_URI: https://github.com/paritytech/revive/releases/download/${{ github.ref_name }}/resolc.wasm
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: actions-rust-lang/setup-rust-toolchain@v1
@@ -181,7 +182,7 @@ jobs:
- name: Basic Sanity Check - name: Basic Sanity Check
run: | run: |
mkdir -p solc mkdir -p solc
curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.29/soljson.js curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.30/soljson.js
node -e " node -e "
const soljson = require('solc/soljson'); const soljson = require('solc/soljson');
const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js'); const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js');
@@ -286,3 +287,43 @@ jobs:
resolc.wasm resolc.wasm
resolc_web.js resolc_web.js
checksums.txt checksums.txt
npm-release:
needs: [create-release]
runs-on: macos-14
environment: tags
steps:
- uses: actions/checkout@v4
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: "20"
- run: npm ci -w js/resolc
- name: Build
run: |
cp -f resolc.{wasm,js} js/resolc/src/resolc
npm -w js/resolc run build
- name: npm pack
run: npm -w js/resolc pack
- uses: actions/upload-artifact@v4
with:
name: npm_package
path: "parity-resolc-*.tgz"
- uses: octokit/request-action@bbedc70b1981e610d89f1f8de88311a1fc02fb83
with:
route: POST /repos/paritytech/npm_publish_automation/actions/workflows/publish.yml/dispatches
ref: main
inputs: '${{ format(''{{ "artifact_name": "npm_package", "repo": "{0}", "run_id": "{1}" }}'', github.repository, github.run_id) }}'
env:
GITHUB_TOKEN: ${{ secrets.NPM_PUBLISH_AUTOMATION_TOKEN }}
+2
View File
@@ -7,6 +7,8 @@ on:
- 'LLVM.lock' - 'LLVM.lock'
- 'crates/llvm-builder/**' - 'crates/llvm-builder/**'
- '.github/workflows/test-llvm-builder.yml' - '.github/workflows/test-llvm-builder.yml'
paths-ignore:
- "**.md"
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+16 -7
View File
@@ -2,9 +2,13 @@ name: Test Wasm Version
on: on:
push: push:
branches: ["main"] branches: ["main"]
paths-ignore:
- "**.md"
pull_request: pull_request:
branches: ["main"] branches: ["main"]
types: [opened, synchronize] types: [opened, synchronize]
paths-ignore:
- "**.md"
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -86,13 +90,18 @@ jobs:
- name: Install Node Packages - name: Install Node Packages
run: npm install run: npm install
- name: Run Playwright tests - name: Test emscripten
run: |
cd js
npx playwright install --with-deps
npx playwright test
- name: Test revive
run: | run: |
echo "Running tests for ${{ matrix.os }}" echo "Running tests for ${{ matrix.os }}"
npm run test:wasm npm run test:wasm
- name: Test @parity/resolc
run: |
echo "Running tests for ${{ matrix.os }}"
npm run -w js/resolc test
- name: Run Playwright tests
run: |
cd js/emscripten
npx playwright install --with-deps
npx playwright test
+4 -3
View File
@@ -2,9 +2,13 @@ name: Test
on: on:
push: push:
branches: ["main"] branches: ["main"]
paths-ignore:
- "**.md"
pull_request: pull_request:
branches: ["main"] branches: ["main"]
types: [opened, synchronize] types: [opened, synchronize]
paths-ignore:
- "**.md"
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -52,6 +56,3 @@ jobs:
- name: Test cargo workspace - name: Test cargo workspace
run: make test-workspace run: make test-workspace
- name: Test CLI
run: make test-cli
+1 -1
View File
@@ -1,4 +1,5 @@
/target /target
/js/resolc/dist
target-llvm target-llvm
*.dot *.dot
.vscode/ .vscode/
@@ -11,7 +12,6 @@ target-llvm
node_modules node_modules
artifacts artifacts
tmp tmp
package-lock.json
/*.html /*.html
/build /build
soljson.js soljson.js
+6
View File
@@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true
}
+127 -1
View File
@@ -4,6 +4,109 @@
This is a development pre-release. This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
## v0.4.0
This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
### Added
- Line debug information per YUL builtin and for `if` statements.
- Column numbers in debug information.
- Support for the YUL optimizer details in the standard json input definition.
- The `revive-explorer` compiler utility.
- `revive-yul`: The AST visitor interface.
### Fixed
- The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line.
## v0.3.0
This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
### Fixed
- llvm-context: Bugfix the SAR YUL builtin translation.
- runtime-api: Add the missing `memset` builtin.
- npm package: Bugfix the exports field defined in the `package.json`.
## v0.2.0
This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
### Changed
- Removed the license printer from the `resolc` binary.
- EVM bytecode is no longer requested from solc (except in test utils) leading to less compilation work in the pipeline.
### Fixed
- solc-json-interface: Serializing of any custom key in the JSON input is only skipped if not provided.
- npm package resolution no longer fails with an 'ERR_PACKAGE_PATH_NOT_EXPORTED' error for packages defining exports fields in the `package.json`.
## v0.1.0
This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
### Added
- Add the PolkaVM heap size, stack size and debug info CLI compiler options to the standard JSON settings. This makes the standard JSON input succint for reproducible builds.
### Changed
- Supported `polkadot-sdk` version is now `2503.0.1`
- The `emsdk` version is now `4.0.9`
### Fixed
## v0.1.0-dev.16
This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added
- Move the npm package from paritytech/js-revive, into this repo. The package `@parity/resolc` will be deployed to npm for each release.
- Support for solc v0.8.30
### Changed
- By default, heavy size optimizations are applied.
### Fixed
- @parity/resolc: The solc dependency package is constrained to the latest supported version, preventing breaking the package ever time a new solc package was released.
- The resolc npm package no longer ignores the optimizer settings
## 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 ## v0.1.0-dev.13
This is a development pre-release. This is a development pre-release.
@@ -11,16 +114,19 @@ This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added ### Added
- Support for solc v0.8.29 - 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. - Decouples the solc JSON-input-output type definitions from the Solidity fronted and expose them via a dedicated crate.
- `--supported-solc-versions` for `resolc` binary to return a `semver` range of supported `solc` versions. - `--supported-solc-versions` for `resolc` binary to return a `semver` range of supported `solc` versions.
- Support for passing LLVM command line options via the prcoess input or providing one or more `--llvm-arg='..'` resolc CLI flag. This allows more fine-grained control over the LLVM backend configuration. - Support for passing LLVM command line options via the prcoess input or providing one or more `--llvm-arg='..'` resolc CLI flag. This allows more fine-grained control over the LLVM backend configuration.
### Changed ### 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. - 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.
- Running `resolc` using webkit is no longer supported. - Running `resolc` using webkit is no longer supported.
### Fixed ### Fixed
- A missing byte swap for the create2 salt value. - A missing byte swap for the create2 salt value.
## v0.1.0-dev.12 ## v0.1.0-dev.12
@@ -30,10 +136,12 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `21f6f0705e53c15aa2b8a5706b208200447774a9` Supported `polkadot-sdk` rev: `21f6f0705e53c15aa2b8a5706b208200447774a9`
### Added ### Added
- Per file output selection for `--standard-json` mode. - Per file output selection for `--standard-json` mode.
- The `ir` output selection option for `--standard-json` mode. - The `ir` output selection option for `--standard-json` mode.
### Changed ### Changed
- Improved code size: Large contracts compile to smaller code blobs when enabling aggressive size optimizations (`-Oz`). - Improved code size: Large contracts compile to smaller code blobs when enabling aggressive size optimizations (`-Oz`).
### Fixed ### Fixed
@@ -49,6 +157,7 @@ Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Changed ### Changed
### Fixed ### Fixed
- A bug causing incorrect loads from the emulated EVM linear memory. - A bug causing incorrect loads from the emulated EVM linear memory.
- A missing integer truncate after switching to 64bit. - A missing integer truncate after switching to 64bit.
@@ -59,10 +168,12 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50` Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Added ### Added
- Support for the `coinbase` opcode. - Support for the `coinbase` opcode.
- The resolc web JS version. - The resolc web JS version.
### Changed ### Changed
- Missing the `--overwrite` flag emits an error instead of a warning. - Missing the `--overwrite` flag emits an error instead of a warning.
- The `resolc` executable prints the help by default. - The `resolc` executable prints the help by default.
- Removed support for legacy EVM assembly (EVMLA) translation. - Removed support for legacy EVM assembly (EVMLA) translation.
@@ -72,6 +183,7 @@ Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
If detected, the re-entrant call flag is not set and 0 deposit limit is endowed. If detected, the re-entrant call flag is not set and 0 deposit limit is endowed.
### Fixed ### Fixed
- Solidity: Add the solc `--libraries` files to sources. - Solidity: Add the solc `--libraries` files to sources.
- A data race in tests. - A data race in tests.
- Fix `broken pipe` errors. - Fix `broken pipe` errors.
@@ -85,9 +197,11 @@ This is a development pre-release.
### Added ### Added
### Changed ### Changed
- Syscalls with more than 6 arguments now pack them into registers. - Syscalls with more than 6 arguments now pack them into registers.
### Fixed ### Fixed
- Remove reloading of the resolc.js file (fix issue with relative path in web worker) - Remove reloading of the resolc.js file (fix issue with relative path in web worker)
## v0.1.0-dev.8 ## v0.1.0-dev.8
@@ -95,15 +209,18 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
### Added ### Added
- The `revive-llvm-builder` crate with the `revive-llvm` helper utility for streamlined management of the LLVM framework dependency. - The `revive-llvm-builder` crate with the `revive-llvm` helper utility for streamlined management of the LLVM framework dependency.
- Initial support for running `resolc` in the browser. - Initial support for running `resolc` in the browser.
### Changed ### Changed
- Suported contracts runtime is polkadot-sdk git version `d62a90c8c729acd98c7e9a5cab9803b8b211ffc5`. - Suported contracts runtime is polkadot-sdk git version `d62a90c8c729acd98c7e9a5cab9803b8b211ffc5`.
- The minimum supported Rust version is `1.81.0`. - The minimum supported Rust version is `1.81.0`.
- Error out early instead of invoking `solc` with invalid base or include path flags. - Error out early instead of invoking `solc` with invalid base or include path flags.
### Fixed ### Fixed
- Decouple the LLVM target dependency from the LLVM host dependency. - Decouple the LLVM target dependency from the LLVM host dependency.
- Do not error out if no files and no errors were produced. This aligns resolc closer to solc. - Do not error out if no files and no errors were produced. This aligns resolc closer to solc.
- Fixes input normalization in the Wasm version. - Fixes input normalization in the Wasm version.
@@ -113,17 +230,20 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
### Added ### Added
- Implement the `GASPRICE` opcode. - Implement the `GASPRICE` opcode.
- Implement the `BASEFEE` opcode. - Implement the `BASEFEE` opcode.
- Implement the `GASLIMIT` opcode. - Implement the `GASLIMIT` opcode.
### Changed ### Changed
- The `GAS` opcode now returns the remaining `ref_time`. - The `GAS` opcode now returns the remaining `ref_time`.
- Contracts can now be supplied call data input of arbitrary size. - Contracts can now be supplied call data input of arbitrary size.
- Some syscalls now return the value in a register, slightly improving emitted contract code. - Some syscalls now return the value in a register, slightly improving emitted contract code.
- Calls forward maximum weight limits instead of 0, anticipating a change in polkadot-sdk where weight limits of 0 no longer interprets as uncapped limit. - Calls forward maximum weight limits instead of 0, anticipating a change in polkadot-sdk where weight limits of 0 no longer interprets as uncapped limit.
### Fixed ### Fixed
- A linker bug which was preventing certain contracts from linking with the PVM linker. - A linker bug which was preventing certain contracts from linking with the PVM linker.
- JS: Fix encoding conversion from JS string (UTF-16) to UTF-8. - JS: Fix encoding conversion from JS string (UTF-16) to UTF-8.
- The git commit hash slug is always displayed in the version string. - The git commit hash slug is always displayed in the version string.
@@ -133,6 +253,7 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
# Added # Added
- Implement the `BLOCKHASH` opcode. - Implement the `BLOCKHASH` opcode.
- Implement delegate calls. - Implement delegate calls.
- Implement the `GASPRICE` opcode. Currently hard-coded to return `1`. - Implement the `GASPRICE` opcode. Currently hard-coded to return `1`.
@@ -140,21 +261,24 @@ This is a development pre-release.
- Initial support for emitting debug info (opt in via the `-g` flag) - Initial support for emitting debug info (opt in via the `-g` flag)
# Changed # Changed
- resolc now emits 64bit PolkaVM blobs, reducing contract code size and execution time. - resolc now emits 64bit PolkaVM blobs, reducing contract code size and execution time.
- The RISC-V bit-manipulation target feature (`zbb`) is enabled. - The RISC-V bit-manipulation target feature (`zbb`) is enabled.
# Fixed # Fixed
- Compilation to Wasm (for usage in node and web browsers)
- Compilation to Wasm (for usage in node and web browsers)
## v0.1.0-dev.5 ## v0.1.0-dev.5
This is development pre-release. This is development pre-release.
# Added # Added
- Implement the `CODESIZE` and `EXTCODESIZE` opcodes. - Implement the `CODESIZE` and `EXTCODESIZE` opcodes.
# Changed # Changed
- Include the full revive version in the contract metadata. - Include the full revive version in the contract metadata.
# Fixed # Fixed
@@ -164,9 +288,11 @@ This is development pre-release.
This is development pre-release. This is development pre-release.
# Added # Added
- Support the `ORIGIN` opcode. - Support the `ORIGIN` opcode.
# Changed # Changed
- Update polkavm to `v0.14.0`. - Update polkavm to `v0.14.0`.
- Enable the `a`, `fast-unaligned-access` and `xtheadcondmov` LLVM target features, decreasing the code size for some contracts. - Enable the `a`, `fast-unaligned-access` and `xtheadcondmov` LLVM target features, decreasing the code size for some contracts.
Generated
+2199 -2238
View File
File diff suppressed because it is too large Load Diff
+44 -41
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.1.0-dev.13" version = "0.1.0"
authors = [ authors = [
"Cyrill Leutwiler <cyrill@parity.io>", "Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>", "Parity Technologies <admin@parity.io>",
@@ -11,76 +11,79 @@ authors = [
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
edition = "2021" edition = "2021"
repository = "https://github.com/paritytech/revive" repository = "https://github.com/paritytech/revive"
rust-version = "1.81.0" rust-version = "1.85.0"
[workspace.dependencies] [workspace.dependencies]
revive-benchmarks = { version = "0.1.0-dev.13", path = "crates/benchmarks" } resolc = { version = "0.3.0", path = "crates/resolc" }
revive-builtins = { version = "0.1.0-dev.13", path = "crates/builtins" } revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
revive-common = { version = "0.1.0-dev.13", path = "crates/common" } revive-builtins = { version = "0.1.0", path = "crates/builtins" }
revive-differential = { version = "0.1.0-dev.13", path = "crates/differential" } revive-common = { version = "0.1.0", path = "crates/common" }
revive-integration = { version = "0.1.0-dev.13", path = "crates/integration" } revive-differential = { version = "0.1.0", path = "crates/differential" }
revive-linker = { version = "0.1.0-dev.13", path = "crates/linker" } revive-explorer = { version = "0.1.0", path = "crates/explore" }
lld-sys = { version = "0.1.0-dev.13", path = "crates/lld-sys" } revive-integration = { version = "0.1.1", path = "crates/integration" }
revive-llvm-context = { version = "0.1.0-dev.13", path = "crates/llvm-context" } revive-linker = { version = "0.1.0", path = "crates/linker" }
revive-runtime-api = { version = "0.1.0-dev.13", path = "crates/runtime-api" } lld-sys = { version = "0.1.0", path = "crates/lld-sys" }
revive-runner = { version = "0.1.0-dev.13", path = "crates/runner" } revive-llvm-context = { version = "0.3.0", path = "crates/llvm-context" }
revive-solc-json-interface = { version = "0.1.0-dev.13", path = "crates/solc-json-interface" } revive-runtime-api = { version = "0.2.0", path = "crates/runtime-api" }
revive-solidity = { version = "0.1.0-dev.13", path = "crates/solidity" } revive-runner = { version = "0.1.0", path = "crates/runner" }
revive-stdlib = { version = "0.1.0-dev.13", path = "crates/stdlib" } revive-solc-json-interface = { version = "0.2.0", path = "crates/solc-json-interface" }
revive-build-utils = { version = "0.1.0-dev.13", path = "crates/build-utils" } revive-stdlib = { version = "0.1.1", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0", path = "crates/build-utils" }
revive-yul = { version = "0.2.1", path = "crates/yul" }
hex = "0.4.3" hex = "0.4.3"
cc = "1.2" cc = "1.2"
libc = "0.2.169" libc = "0.2.172"
tempfile = "3.17" tempfile = "3.20"
anyhow = "1.0" anyhow = "1.0"
semver = { version = "1.0", features = ["serde"] } semver = { version = "1.0", features = ["serde"] }
itertools = "0.14" itertools = "0.14"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] }
regex = "1.10" regex = "1.11"
once_cell = "1.20" once_cell = "1.21"
num = "0.4.3" num = "0.4.3"
sha1 = "0.10" sha1 = "0.10"
sha3 = "0.10" sha3 = "0.10"
thiserror = "2.0" thiserror = "2.0"
which = "7.0" which = "7.0"
path-slash = "0.2" path-slash = "0.2"
rayon = "1.8" rayon = "1.10"
clap = { version = "4", default-features = false, features = ["derive"] } clap = { version = "4", default-features = false, features = ["derive"] }
polkavm-common = "0.21.0" polkavm-common = "0.24.0"
polkavm-linker = "0.21.0" polkavm-linker = "0.24.0"
polkavm-disassembler = "0.21.0" polkavm-disassembler = "0.24.0"
polkavm = "0.21.0" polkavm = "0.24.0"
alloy-primitives = { version = "0.8.21", features = ["serde"] } alloy-primitives = { version = "1.1", features = ["serde"] }
alloy-sol-types = "0.8.21" alloy-sol-types = "1.1"
alloy-genesis = "0.11.1" alloy-genesis = "1.0"
alloy-serde = "0.11.1" alloy-serde = "1.0"
env_logger = { version = "0.11.6", default-features = false } alloy-core = "1.3"
serde_stacker = "0.1.11" env_logger = { version = "0.11.8", default-features = false }
criterion = { version = "0.5.1", features = ["html_reports"] } serde_stacker = "0.1.12"
log = { version = "0.4.25" } criterion = { version = "0.6", features = ["html_reports"] }
git2 = { version = "0.20.0", default-features = false } log = { version = "0.4.27" }
git2 = { version = "0.20.2", default-features = false }
downloader = "0.2.8" downloader = "0.2.8"
flate2 = "1.0.35" flate2 = "1.1"
fs_extra = "1.3.0" fs_extra = "1.3"
num_cpus = "1" num_cpus = "1"
tar = "0.4" tar = "0.4"
toml = "0.8" toml = "0.8"
assert_cmd = "2.0" assert_cmd = "2.0"
assert_fs = "1.1" assert_fs = "1.1"
pallet-revive-uapi = "0.7.0"
# polkadot-sdk and friends # polkadot-sdk and friends
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } codec = { version = "3.7.5", default-features = false, package = "parity-scale-codec" }
scale-info = { version = "2.11.6", default-features = false } scale-info = { version = "2.11.6", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c29e72a8628835e34deb6aa7db9a78a2e4eabcee" } polkadot-sdk = { version = "2503.0.1" }
# llvm # llvm
[workspace.dependencies.inkwell] [workspace.dependencies.inkwell]
git = "https://github.com/TheDan64/inkwell.git" version = "0.6.0"
rev = "7b410298b6a93450adaa90b1841d5805a3038f12"
default-features = false default-features = false
features = ["serde", "llvm18-0", "no-libffi-linking", "target-riscv"] features = ["serde", "llvm18-1", "no-libffi-linking", "target-riscv"]
[profile.bench] [profile.bench]
inherits = "release" inherits = "release"
+2 -2
View File
@@ -1,4 +1,4 @@
FROM rust:1.84.0 AS llvm-builder FROM rust:1.85.0 AS llvm-builder
WORKDIR /opt/revive WORKDIR /opt/revive
RUN apt update && \ RUN apt update && \
@@ -11,7 +11,7 @@ RUN make install-llvm-builder
RUN revive-llvm --target-env musl clone RUN revive-llvm --target-env musl clone
RUN revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang RUN revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang
FROM messense/rust-musl-cross@sha256:68b86bc7cb2867259e6b233415a665ff4469c28b57763e78c3bfea1c68091561 AS resolc-builder FROM messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067 AS resolc-builder
WORKDIR /opt/revive WORKDIR /opt/revive
RUN apt update && \ RUN apt update && \
+18 -17
View File
@@ -5,14 +5,15 @@
install-wasm \ install-wasm \
install-llvm-builder \ install-llvm-builder \
install-llvm \ install-llvm \
install-revive-runner \
install-revive-explorer \
format \ format \
clippy \ clippy \
machete \ machete \
test \ test \
test-integration \ test-integration \
test-solidity \ test-resolc \
test-workspace \ test-workspace \
test-cli \
test-wasm \ test-wasm \
test-llvm-builder test-llvm-builder
bench \ bench \
@@ -23,22 +24,28 @@
install: install-bin install-npm install: install-bin install-npm
install-bin: install-bin:
cargo install --locked --path crates/solidity cargo install --force --locked --path crates/resolc
install-npm: install-npm:
npm install && npm fund npm install && npm fund
install-wasm: install-npm install-wasm: install-npm
cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features cargo build --target wasm32-unknown-emscripten -p resolc --release --no-default-features
npm run build:package npm run build:package
install-llvm-builder: install-llvm-builder:
cargo install --path crates/llvm-builder cargo install --force --locked --path crates/llvm-builder
install-llvm: install-llvm-builder install-llvm: install-llvm-builder
revive-llvm clone revive-llvm clone
revive-llvm build --llvm-projects lld --llvm-projects clang revive-llvm build --llvm-projects lld --llvm-projects clang
install-revive-runner:
cargo install --locked --force --path crates/runner --no-default-features
install-revive-explorer:
cargo install --locked --force --path crates/explorer --no-default-features
format: format:
cargo fmt --all --check cargo fmt --all --check
@@ -49,20 +56,17 @@ machete:
cargo install cargo-machete cargo install cargo-machete
cargo machete cargo machete
test: format clippy machete test-cli test-workspace test: format clippy machete test-workspace install-revive-runner install-revive-explorer
test-integration: install-bin test-integration: install-bin
cargo test --package revive-integration cargo test --package revive-integration
test-solidity: install test-resolc: install
cargo test --package revive-solidity cargo test --package resolc
test-workspace: install test-workspace: install
cargo test --workspace --exclude revive-llvm-builder cargo test --workspace --exclude revive-llvm-builder
test-cli: install
npm run test:cli
test-wasm: install-wasm test-wasm: install-wasm
npm run test:wasm npm run test:wasm
@@ -86,9 +90,6 @@ clean:
cargo clean ; \ cargo clean ; \
revive-llvm clean ; \ revive-llvm clean ; \
rm -rf node_modules ; \ rm -rf node_modules ; \
rm -rf crates/solidity/src/tests/cli-tests/artifacts ; \ rm -rf crates/resolc/src/tests/cli/artifacts ; \
cargo uninstall revive-solidity ; \ cargo uninstall resolc ; \
cargo uninstall revive-llvm-builder ; \ cargo uninstall revive-llvm-builder ;
rm -f package-lock.json ; \
rm -rf js/dist ; \
rm -f js/src/resolc.{wasm,js}
+39 -6
View File
@@ -14,7 +14,39 @@ This is experimental software in active development and not ready just yet for p
Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1). Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1).
## Installation ## Installation
Please consult [the documentation](https://contracts.polkadot.io/revive_compiler/installation) for installation instructions. Building Solidity contracts for PolkaVM requires installing the following two compilers:
- `resolc`: The revive Solidity compiler YUL frontend and PolkaVM code generator (provided by this repository).
- `solc`: The [Ethereum Solidity reference compiler](https://github.com/ethereum/solidity/) implemenation.`resolc` uses `solc` during the compilation process, please refer to the [Ethereum Solidity documentation](https://docs.soliditylang.org/en/latest/installing-solidity.html) for installation instructions.
### `resolc` binary releases
`resolc` is distributed as a standalone binary (with `solc` as the only external dependency). Please download one of our [binary releases](https://github.com/paritytech/revive/releases) for your target platform and mind the platform specific instructions below.
<details>
<summary>MacOS users</summary>
> **MacOS** users need to clear the `downloaded` attribute from the binary and set the executable flag.
> ```sh
> xattr -rc resolc-universal-apple-darwin
> chmod +x resolc-universal-apple-darwin
> ```
</details>
<details>
<summary>Linux users</summary>
> **Linux** users need to set the executable flag.
> ```sh
> chmod +x resolc-x86_64-unknown-linux-musl
> ```
</details>
### `resolc` NPM package
We distribute the revive compiler as [node.js module](https://www.npmjs.com/package/@parity/resolc) and [hardhat plugin](https://www.npmjs.com/package/@parity/hardhat-polkadot-resolc).
Note: The `solc` dependency is bundled via NPM packaging and defaults to the latest supported version.
## Building from source ## Building from source
@@ -44,9 +76,7 @@ export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/targ
<details> <details>
<summary>Building from source</summary> <summary>Building from source</summary>
Use the provided [revive-llvm](crates/llvm-builder/README.md) utility to compile a compatible LLVM build locally and point `$LLVM_SYS_181_PREFIX` to the installation afterwards. The `Makefile` provides a shortcut target to obtain a compatible LLVM build, using the provided [revive-llvm](crates/llvm-builder/README.md) utility. Once installed, point `$LLVM_SYS_181_PREFIX` to the installation afterwards:
The `Makefile` provides a shortcut target to obtain a compatible LLVM build:
```sh ```sh
make install-llvm make install-llvm
@@ -99,8 +129,6 @@ Ensure that your branch passes `make test` locally when submitting a pull reques
### Design overview ### Design overview
See the [relevant section in our documentation](https://contracts.polkadot.io/revive_compiler/architecture) to learn more about how the compiler works. See the [relevant section in our documentation](https://contracts.polkadot.io/revive_compiler/architecture) to learn more about how the compiler works.
[Frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are based of ZKSync `zksolc` (the project started as a fork of the era compiler).
### Tests ### Tests
Before running the tests, ensure that Geth (Go Ethereum) is installed on your system. Follow the installation guide here: [Installing Geth](https://geth.ethereum.org/docs/getting-started/installing-geth). Before running the tests, ensure that Geth (Go Ethereum) is installed on your system. Follow the installation guide here: [Installing Geth](https://geth.ethereum.org/docs/getting-started/installing-geth).
@@ -109,3 +137,8 @@ Once Geth is installed, you can run the tests using the following command:
```sh ```sh
make test make test
``` ```
# Acknowledgements
The revive compiler project, after some early experiments with EVM bytecode translations, decided to fork the `era-compiler` framework.
[Frontend](https://github.com/matter-labs/era-compiler-solidity), [code generator](https://github.com/matter-labs/era-compiler-llvm-context) and some supporting libraries are based of ZKSync `zksolc`. I'd like to express my gratitude and thank the original authors for providing a useable code base under a generous license.
+11 -3
View File
@@ -4,15 +4,23 @@ Prior to the first stable release we neither have formal release processes nor d
To create a new pre-release: To create a new pre-release:
1. Create a release PR which updates the `-dev.X` versions in the workspace `Cargo.toml` and updates the `CHANGELOG.md` accordingly. 1. Create a release PR which, if necessary:
- Updates the versions in the workspace `Cargo.toml`
- Updates the version in each crate `Cargo.toml`
- Updates the version of the NPM package in `js/resolc/package.json`
- Updates the `CHANGELOG.md` to reflect all observable changes
2. If the CI passes, merge the release PR. 2. If the CI passes, merge the release PR.
3. Push a tag that has the same `-dev.X` version as in `Cargo.toml` 3. Push a `vX.Y.Z` tag that has the same version as in `Cargo.toml`
4. The release workflow will attempt to build and publish a new pre-release if the latest tag does match the cargo package version. 4. The release workflow will attempt to build and publish a new pre-release if the latest tag does match the cargo package version.
5. Wait for the `Release` workflow to finish. It should create the pre-release with the same `-dev.X` name. 5. Wait for the `Release` workflow to finish. It should create the pre-release with the same name.
6. Check that pre-release was created on the [Releases page](https://github.com/paritytech/revive/releases) with all artifacts. 6. Check that pre-release was created on the [Releases page](https://github.com/paritytech/revive/releases) with all artifacts.
7. After the release is published, another workflow should start automatically and update json files in https://github.com/paritytech/resolc-bin. Check the changes. 7. After the release is published, another workflow should start automatically and update json files in https://github.com/paritytech/resolc-bin. Check the changes.
8. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly 8. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly
# `resolc` NPM package release
Will happen automatically.
# LLVM release # LLVM release
To create a new LLVM release, run "Release LLVM" workflow. Use current LLVM version as parameter, e.g. `18.1.8`. To create a new LLVM release, run "Release LLVM" workflow. Use current LLVM version as parameter, e.g. `18.1.8`.
-30
View File
@@ -1,30 +0,0 @@
# Known issues
The following is known and we are either working on it or it is a hard limitation. Please do not open a new issue.
## Release
`0.1.0-dev-2`
## Missing features
- [Libraries with public functions are not supported](https://github.com/paritytech/revive/issues/91)
- [Automatic import resolution is not supported](https://github.com/paritytech/revive/issues/98)
- The emulated EVM linear contract memory is limited to 64kb in size. Will be fixed with support for metered dynamic memory.
- [The contract calldata is currently limited to 1kb in size](https://github.com/paritytech/revive/issues/57)
- [EIP-4844 opcodes are not supported](https://github.com/paritytech/revive/issues/64)
- [Delegate calls are not supported](https://github.com/paritytech/revive/issues/67)
- [The `blockhash` opcode is not supported](https://github.com/paritytech/revive/issues/61)
- [The `extcodesize` opcode is not supported](https://github.com/paritytech/revive/issues/58)
- [The `origin` opcode is not supported](https://github.com/paritytech/revive/issues/59)
- [Gas limits for contract calls are ignored](https://github.com/paritytech/revive/issues/60)
- [Gas related opcodes are not supported](https://github.com/paritytech/revive/issues/60)
- IPFS metadata hashes are not supported
- [Compiled contract artifacts can exceed the pallet static memory limit and fail to deploy](https://github.com/paritytech/revive/issues/96).
- [Transfers to inexistant accounts will fail if the transferred value lies below the ED.](https://github.com/paritytech/revive/issues/83) Will be fixed in the pallet to make the ED completely transparent for contracts.
## Wontfix
Please consult our documentation to learn more about Solidity and EVM features likely to remain unsupported (and why they will not be supported).
TODO: Insert link to the relevant documentation section.
+22 -22
View File
@@ -16,57 +16,57 @@
### Baseline ### Baseline
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:--------|:------------------------|:-------------------------------- | |:--------|:-------------------------|:-------------------------------- |
| **`0`** | `3.36 us` (✅ **1.00x**) | `11.84 us` (*3.52x slower*) | | **`0`** | `10.08 us` (✅ **1.00x**) | `10.32 us` (**1.02x slower**) |
### OddPorduct ### OddPorduct
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:-------------|:-------------------------|:-------------------------------- | |:-------------|:--------------------------|:-------------------------------- |
| **`10000`** | `3.11 ms` (✅ **1.00x**) | `1.53 ms` (🚀 **2.03x faster**) | | **`10000`** | `3.60 ms` (✅ **1.00x**) | `1.57 ms` (🚀 **2.28x faster**) |
| **`100000`** | `30.70 ms` (✅ **1.00x**) | `15.54 ms` (🚀 **1.98x faster**) | | **`100000`** | `34.72 ms` (✅ **1.00x**) | `14.82 ms` (🚀 **2.34x faster**) |
| **`300000`** | `92.68 ms` (✅ **1.00x**) | `45.47 ms` (🚀 **2.04x faster**) | | **`300000`** | `105.01 ms` (✅ **1.00x**) | `44.11 ms` (🚀 **2.38x faster**) |
### TriangleNumber ### TriangleNumber
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:-------------|:-------------------------|:-------------------------------- | |:-------------|:-------------------------|:-------------------------------- |
| **`10000`** | `2.29 ms` (✅ **1.00x**) | `1.09 ms` (🚀 **2.11x faster**) | | **`10000`** | `2.43 ms` (✅ **1.00x**) | `1.12 ms` (🚀 **2.17x faster**) |
| **`100000`** | `22.84 ms` (✅ **1.00x**) | `10.66 ms` (🚀 **2.14x faster**) | | **`100000`** | `24.20 ms` (✅ **1.00x**) | `10.86 ms` (🚀 **2.23x faster**) |
| **`360000`** | `82.29 ms` (✅ **1.00x**) | `37.01 ms` (🚀 **2.22x faster**) | | **`360000`** | `88.69 ms` (✅ **1.00x**) | `38.46 ms` (🚀 **2.31x faster**) |
### FibonacciRecursive ### FibonacciRecursive
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:---------|:--------------------------|:--------------------------------- | |:---------|:--------------------------|:--------------------------------- |
| **`12`** | `135.67 us` (✅ **1.00x**) | `125.02 us` (✅ **1.09x faster**) | | **`12`** | `144.17 us` (✅ **1.00x**) | `150.85 us` (✅ **1.05x slower**) |
| **`16`** | `903.75 us` (✅ **1.00x**) | `762.79 us` (✅ **1.18x faster**) | | **`16`** | `938.71 us` (✅ **1.00x**) | `922.11 us` (✅ **1.02x faster**) |
| **`20`** | `6.12 ms` (✅ **1.00x**) | `4.96 ms` (✅ **1.23x faster**) | | **`20`** | `6.54 ms` (✅ **1.00x**) | `6.20 ms` (✅ **1.05x faster**) |
| **`24`** | `42.05 ms` (✅ **1.00x**) | `33.86 ms` (✅ **1.24x faster**) | | **`24`** | `45.73 ms` (✅ **1.00x**) | `41.98 ms` (✅ **1.09x faster**) |
### FibonacciIterative ### FibonacciIterative
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- | |:----------|:-------------------------|:-------------------------------- |
| **`64`** | `15.04 us` (✅ **1.00x**) | `29.45 us` (❌ *1.96x slower*) | | **`64`** | `23.00 us` (✅ **1.00x**) | `31.88 us` (❌ *1.39x slower*) |
| **`128`** | `26.36 us` (✅ **1.00x**) | `42.19 us` (❌ *1.60x slower*) | | **`128`** | `35.28 us` (✅ **1.00x**) | `42.43 us` (❌ *1.20x slower*) |
| **`256`** | `48.61 us` (✅ **1.00x**) | `65.71 us` (*1.35x slower*) | | **`256`** | `60.12 us` (✅ **1.00x**) | `61.20 us` (**1.02x slower**) |
### FibonacciBinet ### FibonacciBinet
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- | |:----------|:-------------------------|:-------------------------------- |
| **`64`** | `15.22 us` (✅ **1.00x**) | `41.46 us` (❌ *2.72x slower*) | | **`64`** | `23.01 us` (✅ **1.00x**) | `47.74 us` (❌ *2.07x slower*) |
| **`128`** | `17.05 us` (✅ **1.00x**) | `42.84 us` (❌ *2.51x slower*) | | **`128`** | `25.44 us` (✅ **1.00x**) | `49.67 us` (❌ *1.95x slower*) |
| **`256`** | `19.00 us` (✅ **1.00x**) | `44.36 us` (❌ *2.34x slower*) | | **`256`** | `28.66 us` (✅ **1.00x**) | `53.01 us` (❌ *1.85x slower*) |
### SHA1 ### SHA1
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:--------------------------|:--------------------------------- | |:----------|:--------------------------|:--------------------------------- |
| **`1`** | `110.04 us` (✅ **1.00x**) | `216.11 us` (❌ *1.96x slower*) | | **`1`** | `135.87 us` (✅ **1.00x**) | `243.75 us` (❌ *1.79x slower*) |
| **`64`** | `209.04 us` (✅ **1.00x**) | `309.48 us` (❌ *1.48x slower*) | | **`64`** | `258.45 us` (✅ **1.00x**) | `355.70 us` (❌ *1.38x slower*) |
| **`512`** | `903.65 us` (✅ **1.00x**) | `980.49 us` (✅ **1.09x slower**) | | **`512`** | `1.10 ms` (✅ **1.00x**) | `1.09 ms` (✅ **1.01x faster**) |
--- ---
Made with [criterion-table](https://github.com/nu11ptr/criterion-table) Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
+1
View File
@@ -1,5 +1,6 @@
[package] [package]
name = "revive-differential" name = "revive-differential"
description = "utilities for differential testing the revive compiler against EVM"
version.workspace = true version.workspace = true
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "revive-explorer"
version.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
authors.workspace = true
description = "Helper utility to inspect debug builds"
[[bin]]
name = "revive-explorer"
path = "src/main.rs"
[dependencies]
anyhow = { workspace = true }
clap = { workspace = true, features = ["help", "std", "derive"] }
num_cpus = { workspace = true }
revive-yul = { workspace = true }
+49
View File
@@ -0,0 +1,49 @@
# revive-explorer
The `revive-explorer` is a helper utility for exploring the compilers YUL lowering unit.
It analyzes a given shared objects from the debug dump and outputs:
- The count of each YUL statement translated.
- A per YUL statement break-down of bytecode size contributed per.
- Estimated `yul-phaser` cost parameters.
Example:
```
statements count:
block 532
Caller 20
Not 73
Gas 24
Shr 2
...
Shl 259
SetImmutable 2
CodeSize 1
CallDataLoad 87
Return 56
bytes per statement:
Or 756
CodeCopy 158
Log3 620
Return 1562
MStore 36128
...
ReturnDataCopy 2854
DataOffset 28
assignment 1194
Number 540
CallValue 4258
yul-phaser parameters:
--break-cost 1
--variable-declaration-cost 3
--function-call-cost 8
--if-cost 4
--expression-statement-cost 6
--function-definition-cost 11
--switch-cost 3
--block-cost 1
--leave-cost 1
--assignment-cost 1
```
+68
View File
@@ -0,0 +1,68 @@
//! The `llvm-dwarfdump` utility helper library.
use std::{
path::{Path, PathBuf},
process::{Command, Stdio},
};
pub static EXECUTABLE: &str = "llvm-dwarfdump";
pub static DEBUG_LINES_ARGUMENTS: [&str; 1] = ["--debug-line"];
pub static SOURCE_FILE_ARGUMENTS: [&str; 1] = ["--show-sources"];
/// Calls the `llvm-dwarfdump` tool to extract debug line information
/// from the shared object at `path`. Returns the output.
///
/// Provide `Some(dwarfdump_exectuable)` to override the default executable.
pub fn debug_lines(
shared_object: &Path,
dwarfdump_executable: &Option<PathBuf>,
) -> anyhow::Result<String> {
dwarfdump(shared_object, dwarfdump_executable, &DEBUG_LINES_ARGUMENTS)
}
/// Calls the `llvm-dwarfdump` tool to extract the source file name.
/// Returns the source file path.
///
/// Provide `Some(dwarfdump_exectuable)` to override the default executable.
pub fn source_file(
shared_object: &Path,
dwarfdump_executable: &Option<PathBuf>,
) -> anyhow::Result<PathBuf> {
let output = dwarfdump(shared_object, dwarfdump_executable, &SOURCE_FILE_ARGUMENTS)?;
let output = output.trim();
if output.is_empty() {
anyhow::bail!(
"the shared object at path `{}` doesn't contain the source file name. Hint: compile with debug information (-g)?",
shared_object.display()
);
}
Ok(output.into())
}
/// The internal `llvm-dwarfdump` helper function.
fn dwarfdump(
shared_object: &Path,
dwarfdump_executable: &Option<PathBuf>,
arguments: &[&str],
) -> anyhow::Result<String> {
let executable = dwarfdump_executable
.to_owned()
.unwrap_or_else(|| PathBuf::from(EXECUTABLE));
let output = Command::new(executable)
.args(arguments)
.arg(shared_object)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?
.wait_with_output()?;
if !output.status.success() {
anyhow::bail!(String::from_utf8_lossy(&output.stderr).to_string());
}
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
+222
View File
@@ -0,0 +1,222 @@
//! The core dwarf dump analyzer library.
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
use revive_yul::lexer::token::location::Location;
use crate::location_mapper::{self, LocationMapper};
/// The dwarf dump analyzer.
///
/// Loads debug information from `llvm-dwarfdump` and calculates statistics
/// about the compiled YUL statements:
/// - Statements count
/// - Per-statement
#[derive(Debug, Default)]
pub struct DwarfdumpAnalyzer {
/// The YUL source file path.
source: PathBuf,
/// The YUL location to statements map.
location_map: HashMap<Location, String>,
/// The `llvm-dwarfdump --debug-lines` output.
debug_lines: String,
/// The observed statements.
statements_count: HashMap<String, usize>,
/// The observed statement to instructions size.
statements_size: HashMap<String, u64>,
}
impl DwarfdumpAnalyzer {
/// The debug info analyzer constructor.
///
/// `source` is the path to the YUL source file.
/// `debug_lines` is the `llvm-dwarfdump --debug-lines` output.
pub fn new(source: &Path, debug_lines: String) -> Self {
Self {
source: source.to_path_buf(),
debug_lines,
..Default::default()
}
}
/// Run the analysis.
pub fn analyze(&mut self) -> anyhow::Result<()> {
self.map_locations()?;
self.analyze_statements()?;
Ok(())
}
/// Populate the maps so that we can always unwrap later.
fn map_locations(&mut self) -> anyhow::Result<()> {
self.location_map = LocationMapper::map_locations(&self.source)?;
self.statements_count = HashMap::with_capacity(self.location_map.len());
self.statements_size = HashMap::with_capacity(self.location_map.len());
for statement in self.location_map.values() {
if !self.statements_size.contains_key(statement) {
self.statements_size.insert(statement.clone(), 0);
}
*self.statements_count.entry(statement.clone()).or_insert(0) += 1;
}
Ok(())
}
/// Analyze how much bytes of insturctions each statement contributes.
fn analyze_statements(&mut self) -> anyhow::Result<()> {
let mut previous_offset = 0;
let mut previous_location = Location::new(0, 0);
for line in self
.debug_lines
.lines()
.skip_while(|line| !line.starts_with("Address"))
.skip(2)
{
let mut parts = line.split_whitespace();
let (Some(offset), Some(line), Some(column)) =
(parts.next(), parts.next(), parts.next())
else {
continue;
};
let current_offset = u64::from_str_radix(offset.trim_start_matches("0x"), 16)?;
let mut current_location = Location::new(line.parse()?, column.parse()?);
// TODO: A bug? Needs further investigation.
if current_location.line == 0 && current_location.column != 0 {
current_location.line = previous_location.line;
}
if let Some(statement) = self.location_map.get(&previous_location) {
let contribution = current_offset - previous_offset;
*self.statements_size.get_mut(statement).unwrap() += contribution;
}
previous_offset = current_offset;
previous_location = current_location;
}
Ok(())
}
/// Print the per-statement count break-down.
pub fn display_statement_count(&self) {
println!("statements count:");
for (statement, count) in self.statements_count.iter() {
println!("\t{statement} {count}");
}
}
/// Print the per-statement byte size contribution break-down.
pub fn display_statement_size(&self) {
println!("bytes per statement:");
for (statement, size) in self.statements_size.iter() {
println!("\t{statement} {size}");
}
}
/// Print the estimated `yul-phaser` cost parameters.
pub fn display_phaser_costs(&self, yul_phaser_scale: u64) {
println!("yul-phaser parameters:");
for (parameter, cost) in self.phaser_costs(yul_phaser_scale) {
println!("\t{parameter} {cost}");
}
}
/// Estimate the `yul-phaser` costs using the simplified weight function:
/// `Total size / toal count = cost`
pub fn phaser_costs(&self, yul_phaser_scale: u64) -> Vec<(String, u64)> {
let mut costs: HashMap<String, (usize, u64)> = HashMap::with_capacity(16);
for (statement, count) in self
.statements_count
.iter()
.filter(|(_, count)| **count > 0)
{
let size = self.statements_size.get(statement).unwrap();
let cost = match statement.as_str() {
location_mapper::FOR => "--for-loop-cost",
location_mapper::OTHER => continue,
location_mapper::INTERNAL => continue,
location_mapper::BLOCK => "--block-cost",
location_mapper::FUNCTION_CALL => "--function-call-cost",
location_mapper::IF => "--if-cost",
location_mapper::SWITCH => "--switch-cost",
location_mapper::DECLARATION => "--variable-declaration-cost",
location_mapper::ASSIGNMENT => "--assignment-cost",
location_mapper::FUNCTION_DEFINITION => "--function-definition-cost",
location_mapper::IDENTIFIER => "--identifier-cost",
location_mapper::LITERAL => "--literal-cost",
_ => "--expression-statement-cost",
};
let entry = costs.entry(cost.to_string()).or_default();
entry.0 += count;
entry.1 += size;
}
let costs = costs
.iter()
.map(|(cost, (count, size))| {
let ratio = *size / *count as u64;
(cost.to_string(), ratio.min(100))
})
.collect::<Vec<_>>();
let scaled_costs = scale_to(
costs
.iter()
.map(|(_, ratio)| *ratio)
.collect::<Vec<_>>()
.as_slice(),
yul_phaser_scale,
);
costs
.iter()
.zip(scaled_costs)
.map(|((cost, _), scaled_ratio)| (cost.to_string(), scaled_ratio))
.collect()
}
}
/// Given a slice of u64 values, returns a Vec<u64> where each element
/// is linearly scaled into the closed interval [1, 10].
fn scale_to(data: &[u64], scale_max: u64) -> Vec<u64> {
if data.is_empty() {
return Vec::new();
}
let mut min = data[0];
let mut max = data[0];
for &x in &data[1..] {
if x < min {
min = x;
}
if x > max {
max = x;
}
}
if max < scale_max {
return data.to_vec();
}
let range = max - min;
data.iter()
.map(|&x| {
if range == 0 {
1
} else {
1 + (x - min) * scale_max / range
}
})
.collect()
}
+6
View File
@@ -0,0 +1,6 @@
//! The revive explorer leverages debug info to get insights into emitted code.
pub mod dwarfdump;
pub mod dwarfdump_analyzer;
pub mod location_mapper;
pub mod yul_phaser;
+123
View File
@@ -0,0 +1,123 @@
//! The location mapper utility maps YUL source locations to AST statements.
use std::{collections::HashMap, path::Path};
use revive_yul::{
lexer::{token::location::Location, Lexer},
parser::{
identifier::Identifier,
statement::{
assignment::Assignment,
block::Block,
expression::{function_call::FunctionCall, literal::Literal},
for_loop::ForLoop,
function_definition::FunctionDefinition,
if_conditional::IfConditional,
object::Object,
switch::Switch,
variable_declaration::VariableDeclaration,
},
},
visitor::{AstNode, AstVisitor},
};
/// Code attributed to an unknown location.
pub const OTHER: &str = "other";
/// Code attributed to a compiler internal location.
pub const INTERNAL: &str = "internal";
/// Code attributed to a block.
pub const BLOCK: &str = "block";
/// Code attributed to a function call.
pub const FUNCTION_CALL: &str = "function_call";
/// Code attributed to a for loop.
pub const FOR: &str = "for";
/// Code attributed to an if statement.
pub const IF: &str = "if";
/// Code attributed to a switch statement.
pub const SWITCH: &str = "switch";
/// Code attributed to a variable declaration.
pub const DECLARATION: &str = "let";
/// Code attributed to a variable assignement.
pub const ASSIGNMENT: &str = "assignment";
/// Code attributed to a function definition.
pub const FUNCTION_DEFINITION: &str = "function_definition";
/// Code attributed to an identifier.
pub const IDENTIFIER: &str = "identifier";
/// Code attributed to a literal.
pub const LITERAL: &str = "literal";
/// The location to statements mapper.
pub struct LocationMapper(HashMap<Location, String>);
impl LocationMapper {
/// Construct a node location map from the given YUL `source` file.
pub fn map_locations(source: &Path) -> anyhow::Result<HashMap<Location, String>> {
let mut lexer = Lexer::new(std::fs::read_to_string(source)?);
let ast = Object::parse(&mut lexer, None).map_err(|error| {
anyhow::anyhow!("Contract `{}` parsing error: {:?}", source.display(), error)
})?;
let mut location_map = Self(Default::default());
ast.accept(&mut location_map);
location_map.0.insert(Location::new(0, 0), OTHER.into());
location_map.0.insert(Location::new(1, 0), INTERNAL.into());
Ok(location_map.0)
}
}
impl AstVisitor for LocationMapper {
fn visit(&mut self, node: &impl AstNode) {
node.visit_children(self);
}
fn visit_block(&mut self, node: &Block) {
node.visit_children(self);
self.0.insert(node.location, BLOCK.into());
}
fn visit_assignment(&mut self, node: &Assignment) {
node.visit_children(self);
self.0.insert(node.location, ASSIGNMENT.into());
}
fn visit_if_conditional(&mut self, node: &IfConditional) {
node.visit_children(self);
self.0.insert(node.location, IF.into());
}
fn visit_variable_declaration(&mut self, node: &VariableDeclaration) {
node.visit_children(self);
self.0.insert(node.location, DECLARATION.into());
}
fn visit_function_call(&mut self, node: &FunctionCall) {
node.visit_children(self);
self.0.insert(node.location, node.name.to_string());
}
fn visit_function_definition(&mut self, node: &FunctionDefinition) {
node.visit_children(self);
self.0.insert(node.location, FUNCTION_DEFINITION.into());
}
fn visit_identifier(&mut self, node: &Identifier) {
node.visit_children(self);
self.0.insert(node.location, IDENTIFIER.into());
}
fn visit_literal(&mut self, node: &Literal) {
node.visit_children(self);
self.0.insert(node.location, LITERAL.into());
}
fn visit_for_loop(&mut self, node: &ForLoop) {
node.visit_children(self);
self.0.insert(node.location, FOR.into());
}
fn visit_switch(&mut self, node: &Switch) {
node.visit_children(self);
self.0.insert(node.location, SWITCH.into());
}
}
+59
View File
@@ -0,0 +1,59 @@
use std::path::PathBuf;
use clap::Parser;
use revive_explorer::{dwarfdump, dwarfdump_analyzer::DwarfdumpAnalyzer, yul_phaser};
/// The `revive-explorer` is a helper utility for exploring the compilers YUL lowering unit.
///
/// It analyzes a given shared objects from the debug dump and outputs:
/// - The count of each YUL statement translated.
/// - A per YUL statement break-down of bytecode size contributed per.
/// - Estimated `yul-phaser` cost parameters.
///
/// Note: This tool might not be fully accurate, especially when the code was optimized.
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Path of the dwarfdump executable.
#[arg(short, long)]
dwarfdump: Option<PathBuf>,
/// The YUL phaser cost scale maximum value.
#[arg(short, long, default_value_t = 10)]
cost_scale: u64,
/// Run the provided yul-phaser executable using the estimated costs.
#[arg(short, long)]
yul_phaser: Option<PathBuf>,
/// Path of the shared object to analyze.
/// It must have been compiled with debug info (-g).
file: PathBuf,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let source_file = dwarfdump::source_file(&args.file, &args.dwarfdump)?;
let debug_lines = dwarfdump::debug_lines(&args.file, &args.dwarfdump)?;
let mut analyzer = DwarfdumpAnalyzer::new(source_file.as_path(), debug_lines);
analyzer.analyze()?;
if let Some(path) = args.yul_phaser.as_ref() {
yul_phaser::run(
path,
source_file.as_path(),
analyzer.phaser_costs(args.cost_scale).as_slice(),
num_cpus::get() / 2, // TODO: should be configurable.
)?;
return Ok(());
}
analyzer.display_statement_count();
analyzer.display_statement_size();
analyzer.display_phaser_costs(args.cost_scale);
Ok(())
}
+79
View File
@@ -0,0 +1,79 @@
//! The revive explorer YUL phaser utility library.
//!
//! This can be used to invoke the `yul-phaser` utility,
//! used to find better YUL optimizer sequences.
use std::{
path::{Path, PathBuf},
process::{Command, Stdio},
thread,
time::{SystemTime, UNIX_EPOCH},
};
/// The `yul-phaser` sane default arguments:
/// - Less verbose output.
/// - Sufficient rounds.
/// - Sufficient random population start.
const ARGUMENTS: [&str; 6] = [
"--hide-round",
"--rounds",
"1000",
"--random-population",
"100",
"--show-only-top-chromosome",
];
/// Run multiple YUL phaser executables in parallel.
pub fn run(
executable: &Path,
source: &Path,
costs: &[(String, u64)],
n_threads: usize,
) -> anyhow::Result<()> {
let mut handles = Vec::with_capacity(n_threads);
for n in 0..n_threads {
let executable = executable.to_path_buf();
let source = source.to_path_buf();
let costs = costs.to_vec();
handles.push(thread::spawn(move || {
spawn_process(executable, source, costs, n)
}));
}
for handle in handles {
let _ = handle.join();
}
Ok(())
}
/// The `yul-phaser` process spawning helper function.
fn spawn_process(
executable: PathBuf,
source: PathBuf,
costs: Vec<(String, u64)>,
seed: usize,
) -> anyhow::Result<()> {
let cost_parameters = costs
.iter()
.flat_map(|(parameter, cost)| vec![parameter.clone(), cost.to_string()]);
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
Command::new(executable)
.args(cost_parameters)
.args(ARGUMENTS)
.arg("--seed")
.arg((seed + secs as usize).to_string())
.arg(source)
.stdin(Stdio::null())
.spawn()?
.wait()?;
Ok(())
}
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revive-integration" name = "revive-integration"
version.workspace = true version = "0.1.1"
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
repository.workspace = true repository.workspace = true
@@ -13,7 +13,7 @@ alloy-sol-types = { workspace = true }
hex = { workspace = true } hex = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
revive-solidity = { workspace = true } resolc = { workspace = true }
revive-runner = { workspace = true } revive-runner = { workspace = true }
revive-llvm-context = { workspace = true } revive-llvm-context = { workspace = true }
+8 -8
View File
@@ -1,10 +1,10 @@
{ {
"Baseline": 1443, "Baseline": 960,
"Computation": 2788, "Computation": 2367,
"DivisionArithmetics": 9748, "DivisionArithmetics": 9108,
"ERC20": 19150, "ERC20": 17655,
"Events": 2201, "Events": 1680,
"FibonacciIterative": 2041, "FibonacciIterative": 1536,
"Flipper": 2691, "Flipper": 2137,
"SHA1": 8997 "SHA1": 8299
} }
+49
View File
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "SAR"
}
}
}
}
]
}
*/
contract SAR {
constructor() payable {
assert(sar(0x03, 0x01) == 0x01);
assert(
sar(
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
0x01
) == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
);
assert(
sar(
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
0xff
) == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
);
assert(
sar(
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
0x100
) == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
);
}
function sar(uint256 a, uint256 b) public pure returns (uint256 c) {
assembly {
c := sar(b, a)
}
}
}
+1 -1
View File
@@ -1,8 +1,8 @@
use alloy_primitives::{Address, Bytes, I256, U256}; use alloy_primitives::{Address, Bytes, I256, U256};
use alloy_sol_types::{sol, SolCall, SolConstructor}; use alloy_sol_types::{sol, SolCall, SolConstructor};
use resolc::test_utils::*;
use revive_llvm_context::OptimizerSettings; use revive_llvm_context::OptimizerSettings;
use revive_solidity::test_utils::*;
#[derive(Clone)] #[derive(Clone)]
pub struct Contract { pub struct Contract {
+1
View File
@@ -60,6 +60,7 @@ test_spec!(mload, "MLoad", "MLoad.sol");
test_spec!(delegate_no_contract, "DelegateCaller", "DelegateCaller.sol"); test_spec!(delegate_no_contract, "DelegateCaller", "DelegateCaller.sol");
test_spec!(function_type, "FunctionType", "FunctionType.sol"); test_spec!(function_type, "FunctionType", "FunctionType.sol");
test_spec!(layout_at, "LayoutAt", "LayoutAt.sol"); test_spec!(layout_at, "LayoutAt", "LayoutAt.sol");
test_spec!(shift_arithmetic_right, "SAR", "SAR.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> { fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate { vec![Instantiate {
+1 -1
View File
@@ -6,7 +6,7 @@ authors = [
"Anton Baliasnikov <aba@matterlabs.dev>", "Anton Baliasnikov <aba@matterlabs.dev>",
"Cyrill Leutwiler <cyrill@parity.io>", "Cyrill Leutwiler <cyrill@parity.io>",
] ]
version.workspace = true version = "0.2.0"
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
repository.workspace = true repository.workspace = true
+7 -2
View File
@@ -59,7 +59,7 @@ Obtain a compatible build for your host platform from the release section of thi
* Install the builder using `cargo`: * Install the builder using `cargo`:
```shell ```shell
cargo install --git https://github.com/paritytech/revive-llvm-builder --force --locked cargo install --force --locked --path crates/llvm-builder
``` ```
> The builder is not the LLVM framework itself, but a tool that clones its repository and runs a sequence of build commands. By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`. > The builder is not the LLVM framework itself, but a tool that clones its repository and runs a sequence of build commands. By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`.
@@ -88,7 +88,12 @@ Obtain a compatible build for your host platform from the release section of thi
Build artifacts end up in the `./target-llvm/gnu/target-final/` directory by default. Build artifacts end up in the `./target-llvm/gnu/target-final/` directory by default.
The `gnu` directory depends on the supported archticture and will either be `gnu`, `musl` or `emscripten`. The `gnu` directory depends on the supported archticture and will either be `gnu`, `musl` or `emscripten`.
You now need to export the final target directory `$LLVM_SYS_181_PREFIX`: `export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final` You now need to export the final target directory `$LLVM_SYS_181_PREFIX`:
```shell
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
```
If built with the `--enable-tests` option, test tools will be in the `./target-llvm/gnu/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `revive-llvm build --help`. If built with the `--enable-tests` option, test tools will be in the `./target-llvm/gnu/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `revive-llvm build --help`.
</details> </details>
+1 -1
View File
@@ -22,7 +22,7 @@ impl std::str::FromStr for BuildType {
"Release" => Ok(Self::Release), "Release" => Ok(Self::Release),
"RelWithDebInfo" => Ok(Self::RelWithDebInfo), "RelWithDebInfo" => Ok(Self::RelWithDebInfo),
"MinSizeRel" => Ok(Self::MinSizeRel), "MinSizeRel" => Ok(Self::MinSizeRel),
value => Err(format!("Unsupported build type: `{}`", value)), value => Err(format!("Unsupported build type: `{value}`")),
} }
} }
} }
+1 -7
View File
@@ -117,13 +117,7 @@ pub fn build(
log::info!("building compiler-rt for rv64emac"); log::info!("building compiler-rt for rv64emac");
crate::utils::check_presence("cmake")?; crate::utils::check_presence("cmake")?;
let generator = if cfg!(target_os = "windows") {
"Visual Studio 17 2022"
} else {
crate::utils::check_presence("ninja")?; crate::utils::check_presence("ninja")?;
"Ninja"
};
let llvm_module_compiler_rt = crate::LLVMPath::llvm_module_compiler_rt()?; let llvm_module_compiler_rt = crate::LLVMPath::llvm_module_compiler_rt()?;
let llvm_compiler_rt_build = crate::LLVMPath::llvm_build_compiler_rt()?; let llvm_compiler_rt_build = crate::LLVMPath::llvm_build_compiler_rt()?;
@@ -136,7 +130,7 @@ pub fn build(
"-B", "-B",
llvm_compiler_rt_build.to_string_lossy().as_ref(), llvm_compiler_rt_build.to_string_lossy().as_ref(),
"-G", "-G",
generator, "Ninja",
]) ])
.args(CMAKE_STATIC_ARGS) .args(CMAKE_STATIC_ARGS)
.args(cmake_dynamic_args(build_type, target_env)?) .args(cmake_dynamic_args(build_type, target_env)?)
+1 -1
View File
@@ -16,7 +16,7 @@ impl std::str::FromStr for CcacheVariant {
match value { match value {
"ccache" => Ok(Self::Ccache), "ccache" => Ok(Self::Ccache),
"sccache" => Ok(Self::Sccache), "sccache" => Ok(Self::Sccache),
value => Err(format!("Unsupported ccache variant: `{}`", value)), value => Err(format!("Unsupported ccache variant: `{value}`")),
} }
} }
} }
+13 -14
View File
@@ -127,23 +127,22 @@ pub fn build(
sanitizer: Option<sanitizer::Sanitizer>, sanitizer: Option<sanitizer::Sanitizer>,
enable_valgrind: bool, enable_valgrind: bool,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
log::trace!("build type: {:?}", build_type); log::trace!("build type: {build_type:?}");
log::trace!("target env: {:?}", target_env); log::trace!("target env: {target_env:?}");
log::trace!("targets: {:?}", targets); log::trace!("targets: {targets:?}");
log::trace!("llvm projects: {:?}", llvm_projects); log::trace!("llvm projects: {llvm_projects:?}");
log::trace!("enable rtti: {:?}", enable_rtti); log::trace!("enable rtti: {enable_rtti:?}");
log::trace!("default target: {:?}", default_target); log::trace!("default target: {default_target:?}");
log::trace!("eneable tests: {:?}", enable_tests); log::trace!("eneable tests: {enable_tests:?}");
log::trace!("enable_coverage: {:?}", enable_coverage); log::trace!("enable_coverage: {enable_coverage:?}");
log::trace!("extra args: {:?}", extra_args); log::trace!("extra args: {extra_args:?}");
log::trace!("sanitzer: {:?}", sanitizer); log::trace!("sanitzer: {sanitizer:?}");
log::trace!("enable valgrind: {:?}", enable_valgrind); log::trace!("enable valgrind: {enable_valgrind:?}");
if !PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE).exists() { if !PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE).exists() {
log::error!( log::error!(
"LLVM project source directory {} does not exist (run `revive-llvm --target-env {} clone`)", "LLVM project source directory {} does not exist (run `revive-llvm --target-env {target_env} clone`)",
LLVMPath::DIRECTORY_LLVM_SOURCE, LLVMPath::DIRECTORY_LLVM_SOURCE
target_env
) )
} }
+1 -1
View File
@@ -22,7 +22,7 @@ impl std::str::FromStr for LLVMProject {
"lld" => Ok(Self::LLD), "lld" => Ok(Self::LLD),
"lldb" => Ok(Self::LLDB), "lldb" => Ok(Self::LLDB),
"mlir" => Ok(Self::MLIR), "mlir" => Ok(Self::MLIR),
value => Err(format!("Unsupported LLVM project to enable: `{}`", value)), value => Err(format!("Unsupported LLVM project to enable: `{value}`")),
} }
} }
} }
+1 -1
View File
@@ -30,7 +30,7 @@ impl TryFrom<&PathBuf> for Lock {
fn try_from(path: &PathBuf) -> Result<Self, Self::Error> { fn try_from(path: &PathBuf) -> Result<Self, Self::Error> {
let mut config_str = String::new(); let mut config_str = String::new();
let mut config_file = let mut config_file =
File::open(path).with_context(|| format!("Error opening {:?} file", path))?; File::open(path).with_context(|| format!("Error opening {path:?} file"))?;
config_file.read_to_string(&mut config_str)?; config_file.read_to_string(&mut config_str)?;
Ok(toml::from_str(&config_str)?) Ok(toml::from_str(&config_str)?)
} }
+1 -1
View File
@@ -29,7 +29,7 @@ impl FromStr for Platform {
fn from_str(value: &str) -> Result<Self, Self::Err> { fn from_str(value: &str) -> Result<Self, Self::Err> {
match value { match value {
"PolkaVM" => Ok(Self::PolkaVM), "PolkaVM" => Ok(Self::PolkaVM),
value => Err(format!("Unsupported platform: `{}`", value)), value => Err(format!("Unsupported platform: `{value}`")),
} }
} }
} }
+2 -2
View File
@@ -69,8 +69,8 @@ fn main_inner() -> anyhow::Result<()> {
}) })
.collect(); .collect();
log::debug!("extra_args: {:#?}", extra_args); log::debug!("extra_args: {extra_args:#?}");
log::debug!("extra_args_unescaped: {:#?}", extra_args_unescaped); log::debug!("extra_args_unescaped: {extra_args_unescaped:#?}");
if let Some(ccache_variant) = ccache_variant { if let Some(ccache_variant) = ccache_variant {
revive_llvm_builder::utils::check_presence(ccache_variant.to_string().as_str())?; revive_llvm_builder::utils::check_presence(ccache_variant.to_string().as_str())?;
+1 -1
View File
@@ -30,7 +30,7 @@ impl std::str::FromStr for Sanitizer {
"thread" => Ok(Self::Thread), "thread" => Ok(Self::Thread),
"dataflow" => Ok(Self::DataFlow), "dataflow" => Ok(Self::DataFlow),
"address;undefined" => Ok(Self::AddressUndefined), "address;undefined" => Ok(Self::AddressUndefined),
value => Err(format!("Unsupported sanitizer: `{}`", value)), value => Err(format!("Unsupported sanitizer: `{value}`")),
} }
} }
} }
+1 -1
View File
@@ -20,7 +20,7 @@ impl std::str::FromStr for TargetEnv {
"gnu" => Ok(Self::GNU), "gnu" => Ok(Self::GNU),
"musl" => Ok(Self::MUSL), "musl" => Ok(Self::MUSL),
"emscripten" => Ok(Self::Emscripten), "emscripten" => Ok(Self::Emscripten),
value => Err(format!("Unsupported target environment: `{}`", value)), value => Err(format!("Unsupported target environment: `{value}`")),
} }
} }
} }
+1 -1
View File
@@ -15,7 +15,7 @@ impl std::str::FromStr for TargetTriple {
fn from_str(value: &str) -> Result<Self, Self::Err> { fn from_str(value: &str) -> Result<Self, Self::Err> {
match value { match value {
"polkavm" => Ok(Self::PolkaVM), "polkavm" => Ok(Self::PolkaVM),
value => Err(format!("Unsupported target triple: `{}`", value)), value => Err(format!("Unsupported target triple: `{value}`")),
} }
} }
} }
+3 -3
View File
@@ -38,7 +38,7 @@ pub const MUSL_SNAPSHOTS_URL: &str = "https://git.musl-libc.org/cgit/musl/snapsh
pub const EMSDK_SOURCE_URL: &str = "https://github.com/emscripten-core/emsdk.git"; pub const EMSDK_SOURCE_URL: &str = "https://github.com/emscripten-core/emsdk.git";
/// The emscripten SDK version. /// The emscripten SDK version.
pub const EMSDK_VERSION: &str = "3.1.64"; pub const EMSDK_VERSION: &str = "4.0.9";
/// The subprocess runner. /// The subprocess runner.
/// ///
@@ -92,7 +92,7 @@ pub fn unpack_tar(filename: PathBuf, path: &str) -> anyhow::Result<()> {
pub fn download_musl(name: &str) -> anyhow::Result<()> { pub fn download_musl(name: &str) -> anyhow::Result<()> {
log::info!("downloading musl {name}"); log::info!("downloading musl {name}");
let tar_file_name = format!("{name}.tar.gz"); let tar_file_name = format!("{name}.tar.gz");
let url = format!("{}/{tar_file_name}", MUSL_SNAPSHOTS_URL); let url = format!("{MUSL_SNAPSHOTS_URL}/{tar_file_name}");
let target_path = crate::llvm_path::DIRECTORY_LLVM_TARGET let target_path = crate::llvm_path::DIRECTORY_LLVM_TARGET
.get() .get()
.unwrap() .unwrap()
@@ -223,6 +223,6 @@ pub fn install_emsdk() -> anyhow::Result<()> {
/// The LLVM target directory default path. /// The LLVM target directory default path.
pub fn directory_target_llvm(target_env: crate::target_env::TargetEnv) -> PathBuf { pub fn directory_target_llvm(target_env: crate::target_env::TargetEnv) -> PathBuf {
crate::llvm_path::DIRECTORY_LLVM_TARGET crate::llvm_path::DIRECTORY_LLVM_TARGET
.get_or_init(|| PathBuf::from(format!("./target-llvm/{}/", target_env))) .get_or_init(|| PathBuf::from(format!("./target-llvm/{target_env}/")))
.clone() .clone()
} }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revive-llvm-context" name = "revive-llvm-context"
version.workspace = true version = "0.3.0"
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
repository.workspace = true repository.workspace = true
+39 -4
View File
@@ -2,6 +2,7 @@
pub mod ir_type; pub mod ir_type;
use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use serde::Deserialize; use serde::Deserialize;
@@ -16,6 +17,14 @@ pub struct DebugConfig {
pub output_directory: Option<PathBuf>, pub output_directory: Option<PathBuf>,
/// Whether debug info should be emitted. /// Whether debug info should be emitted.
pub emit_debug_info: bool, pub emit_debug_info: bool,
/// The YUL debug output file path.
///
/// Is expected to be configured when running in YUL mode.
pub contract_path: Option<PathBuf>,
/// The YUL input file path.
///
/// Is expected to be configured when not running in YUL mode.
pub yul_path: Option<PathBuf>,
} }
impl DebugConfig { impl DebugConfig {
@@ -24,15 +33,41 @@ impl DebugConfig {
Self { Self {
output_directory, output_directory,
emit_debug_info, emit_debug_info,
contract_path: None,
yul_path: None,
} }
} }
/// Set the current YUL path.
pub fn set_yul_path(&mut self, yul_path: &Path) {
self.yul_path = yul_path.to_path_buf().into();
}
/// Set the current contract path.
pub fn set_contract_path(&mut self, contract_path: &str) {
self.contract_path = self.yul_source_path(contract_path);
}
/// Returns with the following precedence:
/// 1. The YUL source path if it was configured.
/// 2. The source YUL path from the debug output dir if it was configured.
/// 3. `None` if there is no debug output directory.
pub fn yul_source_path(&self, contract_path: &str) -> Option<PathBuf> {
if let Some(path) = self.yul_path.as_ref() {
return Some(path.clone());
}
self.output_directory.as_ref().map(|output_directory| {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
file_path.push(full_file_name);
file_path
})
}
/// Dumps the Yul IR. /// Dumps the Yul IR.
pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> { pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
if let Some(output_directory) = self.output_directory.as_ref() { if let Some(file_path) = self.yul_source_path(contract_path) {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
file_path.push(full_file_name);
std::fs::write(file_path, code)?; std::fs::write(file_path, code)?;
} }
+1 -1
View File
@@ -28,6 +28,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::Exit as PolkaVMExitFunction;
pub use self::polkavm::context::function::runtime::revive::WordToPointer as PolkaVMWordToPointerFunction; 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::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_DEPLOY_CODE as PolkaVMFunctionDeployCode;
pub use self::polkavm::context::function::runtime::FUNCTION_ENTRY as PolkaVMFunctionEntry; pub use self::polkavm::context::function::runtime::FUNCTION_ENTRY as PolkaVMFunctionEntry;
pub use self::polkavm::context::function::runtime::FUNCTION_RUNTIME_CODE as PolkaVMFunctionRuntimeCode; pub use self::polkavm::context::function::runtime::FUNCTION_RUNTIME_CODE as PolkaVMFunctionRuntimeCode;
@@ -60,7 +61,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 as polkavm_evm_immutable;
pub use self::polkavm::evm::immutable::Load as PolkaVMLoadImmutableDataFunction; pub use self::polkavm::evm::immutable::Load as PolkaVMLoadImmutableDataFunction;
pub use self::polkavm::evm::immutable::Store as PolkaVMStoreImmutableDataFunction; pub use self::polkavm::evm::immutable::Store as PolkaVMStoreImmutableDataFunction;
pub use self::polkavm::evm::math as polkavm_evm_math; 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::memory as polkavm_evm_memory;
pub use self::polkavm::evm::r#return as polkavm_evm_return; pub use self::polkavm::evm::r#return as polkavm_evm_return;
@@ -234,7 +234,7 @@ impl TryFrom<&SolcStandardJsonInputSettingsOptimizer> for Settings {
fn try_from(value: &SolcStandardJsonInputSettingsOptimizer) -> Result<Self, Self::Error> { fn try_from(value: &SolcStandardJsonInputSettingsOptimizer) -> Result<Self, Self::Error> {
let mut result = match value.mode { let mut result = match value.mode {
Some(mode) => Self::try_from_cli(mode)?, Some(mode) => Self::try_from_cli(mode)?,
None => Self::cycles(), None => Self::size(),
}; };
if value.fallback_to_optimizing_for_size.unwrap_or_default() { if value.fallback_to_optimizing_for_size.unwrap_or_default() {
result.enable_fallback_to_size(); result.enable_fallback_to_size();
@@ -9,6 +9,15 @@ pub static XLEN: usize = revive_common::BIT_LENGTH_X32;
/// The calldata size global variable name. /// The calldata size global variable name.
pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize"; 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: /// The deployer call header size that consists of:
/// - bytecode hash (32 bytes) /// - bytecode hash (32 bytes)
pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD; pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD;
@@ -4,61 +4,98 @@
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Argument<'ctx> { pub struct Argument<'ctx> {
/// The actual LLVM operand. /// The actual LLVM operand.
pub value: inkwell::values::BasicValueEnum<'ctx>, pub value: Value<'ctx>,
/// The original AST value. Used mostly for string literals. /// The original AST value. Used mostly for string literals.
pub original: Option<String>, pub original: Option<String>,
/// The preserved constant value, if available. /// The preserved constant value, if available.
pub constant: Option<num::BigUint>, 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> { impl<'ctx> Argument<'ctx> {
/// The calldata offset argument index. /// A shortcut constructor for register arguments.
pub const ARGUMENT_INDEX_CALLDATA_OFFSET: usize = 0; pub fn value(value: inkwell::values::BasicValueEnum<'ctx>) -> Self {
/// 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 {
Self { Self {
value, value: Value::Register(value),
original: None, original: None,
constant: None, constant: None,
} }
} }
/// A shortcut constructor. /// A shortcut constructor for stack arguments.
pub fn new_with_original( pub fn pointer(pointer: crate::polkavm::context::Pointer<'ctx>, id: String) -> Self {
value: inkwell::values::BasicValueEnum<'ctx>,
original: String,
) -> Self {
Self { Self {
value, value: Value::Pointer { pointer, id },
original: Some(original), original: None,
constant: None, constant: None,
} }
} }
/// A shortcut constructor. /// Set the original decleratation value.
pub fn new_with_constant( pub fn with_original(mut self, original: String) -> Self {
value: inkwell::values::BasicValueEnum<'ctx>, self.original = Some(original);
constant: num::BigUint, self
) -> Self {
Self {
value,
original: None,
constant: Some(constant),
} }
/// Set the constant value.
pub fn with_constant(mut self, constant: num::BigUint) -> Self {
self.constant = Some(constant);
self
} }
/// Returns the inner LLVM value. /// 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> { impl<'ctx> From<inkwell::values::BasicValueEnum<'ctx>> for Argument<'ctx> {
fn from(value: inkwell::values::BasicValueEnum<'ctx>) -> Self { fn from(value: inkwell::values::BasicValueEnum<'ctx>) -> Self {
Self::new(value) Self::value(value)
} }
} }
@@ -51,11 +51,20 @@ pub struct DebugInfo<'ctx> {
impl<'ctx> DebugInfo<'ctx> { impl<'ctx> DebugInfo<'ctx> {
/// A shortcut constructor. /// A shortcut constructor.
pub fn new(module: &inkwell::module::Module<'ctx>) -> Self { pub fn new(
module: &inkwell::module::Module<'ctx>,
debug_config: &crate::debug_config::DebugConfig,
) -> Self {
let module_name = module.get_name().to_string_lossy();
let yul_name = debug_config
.contract_path
.as_ref()
.map(|path| path.display().to_string());
let (builder, compile_unit) = module.create_debug_info_builder( let (builder, compile_unit) = module.create_debug_info_builder(
true, true,
inkwell::debug_info::DWARFSourceLanguage::C, inkwell::debug_info::DWARFSourceLanguage::C,
module.get_name().to_string_lossy().as_ref(), yul_name.as_deref().unwrap_or_else(|| module_name.as_ref()),
"", "",
"", "",
false, false,
@@ -48,6 +48,7 @@ where
function_type, function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
)?; )?;
self.inner.declare(context) self.inner.declare(context)
@@ -74,7 +75,6 @@ where
} }
context.set_basic_block(context.current_function().borrow().return_block()); context.set_basic_block(context.current_function().borrow().return_block());
context.set_debug_location(0, 0, None)?;
context.build_return(None); context.build_return(None);
context.pop_debug_scope(); context.pop_debug_scope();
@@ -1,6 +1,7 @@
//! The entry function. //! The entry function.
use inkwell::types::BasicType; use inkwell::types::BasicType;
use revive_solc_json_interface::PolkaVMDefaultHeapMemorySize;
use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::runtime; use crate::polkavm::context::function::runtime;
@@ -31,6 +32,34 @@ impl Entry {
context.xlen_type().get_undef(), 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
.unwrap_or(PolkaVMDefaultHeapMemorySize),
);
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(()) Ok(())
} }
@@ -116,6 +145,7 @@ where
entry_function_type, entry_function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
)?; )?;
context.declare_global( context.declare_global(
@@ -5,6 +5,7 @@ pub mod deploy_code;
pub mod entry; pub mod entry;
pub mod revive; pub mod revive;
pub mod runtime_code; pub mod runtime_code;
pub mod sbrk;
/// The main entry function name. /// The main entry function name.
pub const FUNCTION_ENTRY: &str = "__entry"; pub const FUNCTION_ENTRY: &str = "__entry";
@@ -48,6 +48,7 @@ where
function_type, function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
)?; )?;
self.inner.declare(context) self.inner.declare(context)
@@ -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)
}
}
+81 -36
View File
@@ -25,6 +25,9 @@ use inkwell::debug_info::AsDIScope;
use inkwell::debug_info::DIScope; use inkwell::debug_info::DIScope;
use inkwell::types::BasicType; use inkwell::types::BasicType;
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use revive_solc_json_interface::PolkaVMDefaultHeapMemorySize;
use revive_solc_json_interface::PolkaVMDefaultStackMemorySize;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use crate::optimizer::settings::Settings as OptimizerSettings; use crate::optimizer::settings::Settings as OptimizerSettings;
use crate::optimizer::Optimizer; use crate::optimizer::Optimizer;
@@ -33,6 +36,7 @@ use crate::polkavm::Dependency;
use crate::target_machine::target::Target; use crate::target_machine::target::Target;
use crate::target_machine::TargetMachine; use crate::target_machine::TargetMachine;
use crate::PolkaVMLoadHeapWordFunction; use crate::PolkaVMLoadHeapWordFunction;
use crate::PolkaVMSbrkFunction;
use crate::PolkaVMStoreHeapWordFunction; use crate::PolkaVMStoreHeapWordFunction;
use self::address_space::AddressSpace; use self::address_space::AddressSpace;
@@ -85,6 +89,8 @@ where
loop_stack: Vec<Loop<'ctx>>, loop_stack: Vec<Loop<'ctx>>,
/// The extra LLVM arguments that were used during target initialization. /// The extra LLVM arguments that were used during target initialization.
llvm_arguments: &'ctx [String], llvm_arguments: &'ctx [String],
/// The PVM memory configuration.
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
/// The project dependency manager. It can be any entity implementing the trait. /// 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 /// The manager is used to get information about contracts and their dependencies during
@@ -116,9 +122,6 @@ where
/// The loop stack default capacity. /// The loop stack default capacity.
const LOOP_STACK_INITIAL_CAPACITY: usize = 16; const LOOP_STACK_INITIAL_CAPACITY: usize = 16;
/// The PolkaVM minimum stack size.
const POLKAVM_STACK_SIZE: u32 = 0x4000;
/// Link in the stdlib module. /// Link in the stdlib module.
fn link_stdlib_module( fn link_stdlib_module(
llvm: &'ctx inkwell::context::Context, llvm: &'ctx inkwell::context::Context,
@@ -218,6 +221,7 @@ where
} }
/// Initializes a new LLVM context. /// Initializes a new LLVM context.
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
llvm: &'ctx inkwell::context::Context, llvm: &'ctx inkwell::context::Context,
module: inkwell::module::Module<'ctx>, module: inkwell::module::Module<'ctx>,
@@ -226,17 +230,24 @@ where
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: DebugConfig, debug_config: DebugConfig,
llvm_arguments: &'ctx [String], llvm_arguments: &'ctx [String],
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> Self { ) -> Self {
Self::set_data_layout(llvm, &module); Self::set_data_layout(llvm, &module);
Self::link_stdlib_module(llvm, &module); Self::link_stdlib_module(llvm, &module);
Self::link_polkavm_imports(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
.unwrap_or(PolkaVMDefaultStackMemorySize),
);
Self::set_module_flags(llvm, &module); Self::set_module_flags(llvm, &module);
let intrinsics = Intrinsics::new(llvm, &module); let intrinsics = Intrinsics::new(llvm, &module);
let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer); let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer);
let debug_info = debug_config.emit_debug_info.then(|| { let debug_info = debug_config.emit_debug_info.then(|| {
let debug_info = DebugInfo::new(&module); let debug_info = DebugInfo::new(&module, &debug_config);
debug_info.initialize_module(llvm, &module); debug_info.initialize_module(llvm, &module);
debug_info debug_info
}); });
@@ -254,6 +265,7 @@ where
current_function: None, current_function: None,
loop_stack: Vec::with_capacity(Self::LOOP_STACK_INITIAL_CAPACITY), loop_stack: Vec::with_capacity(Self::LOOP_STACK_INITIAL_CAPACITY),
llvm_arguments, llvm_arguments,
memory_config,
dependency_manager, dependency_manager,
include_metadata_hash, include_metadata_hash,
@@ -451,6 +463,7 @@ where
r#type: inkwell::types::FunctionType<'ctx>, r#type: inkwell::types::FunctionType<'ctx>,
return_values_length: usize, return_values_length: usize,
linkage: Option<inkwell::module::Linkage>, linkage: Option<inkwell::module::Linkage>,
location: Option<(u32, u32)>,
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> { ) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
let value = self.module().add_function(name, r#type, linkage); let value = self.module().add_function(name, r#type, linkage);
@@ -466,7 +479,8 @@ where
Some(scp) => scp, Some(scp) => scp,
}; };
self.push_debug_scope(func_scope.as_debug_info_scope()); self.push_debug_scope(func_scope.as_debug_info_scope());
self.set_debug_location(0, 0, Some(func_scope.as_debug_info_scope()))?; let (line, column) = location.unwrap_or((0, 0));
self.set_debug_location(line, column, Some(func_scope.as_debug_info_scope()))?;
} }
let entry_block = self.llvm.append_basic_block(value, "entry"); let entry_block = self.llvm.append_basic_block(value, "entry");
@@ -521,7 +535,11 @@ where
/// Sets the current active function. If debug-info generation is enabled, /// Sets the current active function. If debug-info generation is enabled,
/// constructs a debug-scope and pushes in on the scope-stack. /// constructs a debug-scope and pushes in on the scope-stack.
pub fn set_current_function(&mut self, name: &str, line: Option<u32>) -> anyhow::Result<()> { pub fn set_current_function(
&mut self,
name: &str,
location: Option<(u32, u32)>,
) -> anyhow::Result<()> {
let function = self.functions.get(name).cloned().ok_or_else(|| { let function = self.functions.get(name).cloned().ok_or_else(|| {
anyhow::anyhow!("Failed to activate an undeclared function `{}`", name) anyhow::anyhow!("Failed to activate an undeclared function `{}`", name)
})?; })?;
@@ -530,7 +548,8 @@ where
if let Some(scope) = self.current_function().borrow().get_debug_scope() { if let Some(scope) = self.current_function().borrow().get_debug_scope() {
self.push_debug_scope(scope); self.push_debug_scope(scope);
} }
self.set_debug_location(line.unwrap_or_default(), 0, None)?; let (line, column) = location.unwrap_or_default();
self.set_debug_location(line, column, None)?;
Ok(()) Ok(())
} }
@@ -644,6 +663,7 @@ where
self.include_metadata_hash, self.include_metadata_hash,
self.debug_config.clone(), self.debug_config.clone(),
self.llvm_arguments, self.llvm_arguments,
self.memory_config,
) )
}) })
} }
@@ -710,17 +730,23 @@ where
r#type: T, r#type: T,
name: &str, name: &str,
) -> Pointer<'ctx> { ) -> Pointer<'ctx> {
let current_block = self.basic_block(); // TODO: Revisit. While at entry should be preferred in theory:
let entry_block = self.current_function().borrow().entry_block(); // - It has negligible code size impact on real word contracts.
// - Sometimes has negative impact on code size.
// - Messes up debug information used to analyze code size issues.
self.build_alloca(r#type, name)
match entry_block.get_first_instruction() { // let current_block = self.basic_block();
Some(instruction) => self.builder().position_before(&instruction), // let entry_block = self.current_function().borrow().entry_block();
None => self.builder().position_at_end(entry_block),
}
let pointer = self.build_alloca(r#type, name); // match entry_block.get_first_instruction() {
self.set_basic_block(current_block); // Some(instruction) => self.builder().position_before(&instruction),
pointer // None => self.builder().position_at_end(entry_block),
// }
// let pointer = self.build_alloca(r#type, name);
// self.set_basic_block(current_block);
// pointer
} }
/// Builds an aligned stack allocation at the current position. /// Builds an aligned stack allocation at the current position.
@@ -750,7 +776,9 @@ where
address: inkwell::values::IntValue<'ctx>, address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<Pointer<'ctx>> { ) -> anyhow::Result<Pointer<'ctx>> {
let address_type = self.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS); 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 = let address_truncated =
self.builder() self.builder()
.build_int_truncate(address, address_type, "address_truncated")?; .build_int_truncate(address, address_type, "address_truncated")?;
@@ -1082,32 +1110,40 @@ where
offset: inkwell::values::IntValue<'ctx>, offset: inkwell::values::IntValue<'ctx>,
size: inkwell::values::IntValue<'ctx>, size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> { ) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
Ok(self let call_site_value = self.builder().build_call(
.builder() <PolkaVMSbrkFunction as RuntimeFunction<D>>::declaration(self).function_value(),
.build_call(
self.runtime_api_method(revive_runtime_api::polkavm_imports::SBRK),
&[offset.into(), size.into()], &[offset.into(), size.into()],
"call_sbrk", "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() .try_as_basic_value()
.left() .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()) .into_pointer_value())
} }
/// Build a call to PolkaVM `msize` for querying the linear memory size. /// Build a call to PolkaVM `msize` for querying the linear memory size.
pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> { pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
let memory_size_pointer = self Ok(self
.module() .get_global_value(crate::polkavm::GLOBAL_HEAP_SIZE)?
.get_global(revive_runtime_api::polkavm_imports::MEMORY_SIZE) .into_int_value())
.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())
} }
/// Returns a pointer to `offset` into the heap, allocating /// Returns a pointer to `offset` into the heap, allocating
@@ -1426,4 +1462,13 @@ where
pub fn optimizer_settings(&self) -> &OptimizerSettings { pub fn optimizer_settings(&self) -> &OptimizerSettings {
self.optimizer.settings() self.optimizer.settings()
} }
pub fn heap_size(&self) -> inkwell::values::IntValue<'ctx> {
self.xlen_type().const_int(
self.memory_config
.heap_size
.unwrap_or(PolkaVMDefaultHeapMemorySize) as u64,
false,
)
}
} }
@@ -19,7 +19,7 @@ where
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context context
.word_type() .word_type()
.fn_type(&[context.word_type().into()], false) .fn_type(&[context.llvm().ptr_type(Default::default()).into()], false)
} }
fn emit_body<'ctx>( fn emit_body<'ctx>(
@@ -59,7 +59,7 @@ where
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context context
.word_type() .word_type()
.fn_type(&[context.word_type().into()], false) .fn_type(&[context.llvm().ptr_type(Default::default()).into()], false)
} }
fn emit_body<'ctx>( fn emit_body<'ctx>(
@@ -94,7 +94,10 @@ where
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type( 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, false,
) )
} }
@@ -138,7 +141,10 @@ where
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type( 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, false,
) )
} }
@@ -173,9 +179,17 @@ where
fn emit_load<'ctx, D: Dependency + Clone>( fn emit_load<'ctx, D: Dependency + Clone>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
mut key: BasicValueEnum<'ctx>, key: BasicValueEnum<'ctx>,
transient: bool, transient: bool,
) -> anyhow::Result<BasicValueEnum<'ctx>> { ) -> 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 { if !transient {
key = context.build_byte_swap(key)?; key = context.build_byte_swap(key)?;
} }
@@ -217,10 +231,26 @@ fn emit_load<'ctx, D: Dependency + Clone>(
fn emit_store<'ctx, D: Dependency + Clone>( fn emit_store<'ctx, D: Dependency + Clone>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
mut key: BasicValueEnum<'ctx>, key: BasicValueEnum<'ctx>,
mut value: BasicValueEnum<'ctx>, value: BasicValueEnum<'ctx>,
transient: bool, transient: bool,
) -> anyhow::Result<()> { ) -> 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 { if !transient {
key = context.build_byte_swap(key)?; key = context.build_byte_swap(key)?;
value = context.build_byte_swap(value)?; value = context.build_byte_swap(value)?;
@@ -35,6 +35,7 @@ where
Self::r#type(context), Self::r#type(context),
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
)?; )?;
let mut attributes = Self::ATTRIBUTES.to_vec(); let mut attributes = Self::ATTRIBUTES.to_vec();
@@ -9,7 +9,7 @@ use crate::polkavm::DummyDependency;
pub fn create_context( pub fn create_context(
llvm: &inkwell::context::Context, llvm: &inkwell::context::Context,
optimizer_settings: OptimizerSettings, optimizer_settings: OptimizerSettings,
) -> Context<DummyDependency> { ) -> Context<'_, DummyDependency> {
crate::initialize_llvm(crate::Target::PVM, "resolc", Default::default()); crate::initialize_llvm(crate::Target::PVM, "resolc", Default::default());
let module = llvm.create_module("test"); let module = llvm.create_module("test");
@@ -23,6 +23,7 @@ pub fn create_context(
true, true,
Default::default(), Default::default(),
Default::default(), Default::default(),
Default::default(),
) )
} }
@@ -39,6 +40,7 @@ pub fn check_attribute_null_pointer_is_invalid() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -62,6 +64,7 @@ pub fn check_attribute_optimize_for_size_mode_3() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -85,6 +88,7 @@ pub fn check_attribute_optimize_for_size_mode_z() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(function assert!(function
@@ -108,6 +112,7 @@ pub fn check_attribute_min_size_mode_3() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -131,6 +136,7 @@ pub fn check_attribute_min_size_mode_z() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(function assert!(function
+28 -14
View File
@@ -63,7 +63,6 @@ where
let non_overflow_block = context.append_basic_block("shift_left_non_overflow"); let non_overflow_block = context.append_basic_block("shift_left_non_overflow");
let join_block = context.append_basic_block("shift_left_join"); 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( let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT, inkwell::IntPredicate::UGT,
shift, shift,
@@ -73,7 +72,6 @@ where
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
context.set_basic_block(overflow_block); context.set_basic_block(overflow_block);
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block); context.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block); context.set_basic_block(non_overflow_block);
@@ -81,11 +79,17 @@ where
context context
.builder() .builder()
.build_left_shift(value, shift, "shift_left_non_overflow_result")?; .build_left_shift(value, shift, "shift_left_non_overflow_result")?;
context.build_store(result_pointer, value)?;
context.build_unconditional_branch(join_block); context.build_unconditional_branch(join_block);
context.set_basic_block(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. /// Translates the bitwise shift right.
@@ -101,7 +105,6 @@ where
let non_overflow_block = context.append_basic_block("shift_right_non_overflow"); let non_overflow_block = context.append_basic_block("shift_right_non_overflow");
let join_block = context.append_basic_block("shift_right_join"); 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( let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT, inkwell::IntPredicate::UGT,
shift, shift,
@@ -111,7 +114,6 @@ where
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
context.set_basic_block(overflow_block); context.set_basic_block(overflow_block);
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block); context.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block); context.set_basic_block(non_overflow_block);
@@ -121,11 +123,17 @@ where
false, false,
"shift_right_non_overflow_result", "shift_right_non_overflow_result",
)?; )?;
context.build_store(result_pointer, value)?;
context.build_unconditional_branch(join_block); context.build_unconditional_branch(join_block);
context.set_basic_block(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. /// 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 non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow");
let join_block = context.append_basic_block("shift_right_arithmetic_join"); 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( let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT, inkwell::IntPredicate::UGT,
shift, shift,
@@ -174,11 +180,9 @@ where
)?; )?;
context.set_basic_block(overflow_positive_block); context.set_basic_block(overflow_positive_block);
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block); context.build_unconditional_branch(join_block);
context.set_basic_block(overflow_negative_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.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block); context.set_basic_block(non_overflow_block);
@@ -188,11 +192,21 @@ where
true, true,
"shift_right_arithmetic_non_overflow_result", "shift_right_arithmetic_non_overflow_result",
)?; )?;
context.build_store(result_pointer, value)?;
context.build_unconditional_branch(join_block); context.build_unconditional_branch(join_block);
context.set_basic_block(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_positive_block),
]);
Ok(result.as_basic_value())
} }
/// Translates the `byte` instruction, extracting the byte of `operand_2` /// Translates the `byte` instruction, extracting the byte of `operand_2`
+16 -16
View File
@@ -2,6 +2,7 @@
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
@@ -49,7 +50,9 @@ where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS); 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_store(address_pointer, address_type.const_zero())?;
context.build_runtime_call( context.build_runtime_call(
revive_runtime_api::polkavm_imports::ORIGIN, revive_runtime_api::polkavm_imports::ORIGIN,
@@ -97,13 +100,13 @@ where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let output_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_out_ptr"); 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"); let index_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_index_ptr");
context.build_store(index_ptr, index)?; context.build_store(index_pointer, index)?;
context.build_runtime_call( context.build_runtime_call(
revive_runtime_api::polkavm_imports::BLOCK_HASH, 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(), output_pointer.to_int(context).into(),
], ],
); );
@@ -127,10 +130,9 @@ pub fn coinbase<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let pointer = context.build_alloca_at_entry( let pointer: Pointer<'_> = context
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
"coinbase_output", .into();
);
context.build_runtime_call( context.build_runtime_call(
revive_runtime_api::polkavm_imports::BLOCK_AUTHOR, revive_runtime_api::polkavm_imports::BLOCK_AUTHOR,
&[pointer.to_int(context).into()], &[pointer.to_int(context).into()],
@@ -155,10 +157,9 @@ pub fn address<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let pointer = context.build_alloca_at_entry( let pointer: Pointer<'_> = context
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
"address_output", .into();
);
context.build_runtime_call( context.build_runtime_call(
revive_runtime_api::polkavm_imports::ADDRESS, revive_runtime_api::polkavm_imports::ADDRESS,
&[pointer.to_int(context).into()], &[pointer.to_int(context).into()],
@@ -173,10 +174,9 @@ pub fn caller<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let pointer = context.build_alloca_at_entry( let pointer: Pointer<'_> = context
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
"address_output", .into();
);
context.build_runtime_call( context.build_runtime_call(
revive_runtime_api::polkavm_imports::CALLER, revive_runtime_api::polkavm_imports::CALLER,
&[pointer.to_int(context).into()], &[pointer.to_int(context).into()],
+6 -10
View File
@@ -119,10 +119,8 @@ where
_ => error, _ => error,
})?; })?;
if contract_path.as_str() == parent { if contract_path.as_str() == parent {
return Ok(Argument::new_with_constant( return Ok(Argument::value(context.word_const(0).as_basic_value_enum())
context.word_const(0).as_basic_value_enum(), .with_constant(num::BigUint::zero()));
num::BigUint::zero(),
));
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime { } else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime {
anyhow::bail!("type({}).runtimeCode is not supported", identifier); anyhow::bail!("type({}).runtimeCode is not supported", identifier);
} }
@@ -131,7 +129,7 @@ where
let hash_value = context let hash_value = context
.word_const_str_hex(hash_string.as_str()) .word_const_str_hex(hash_string.as_str())
.as_basic_value_enum(); .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 /// Translates the deploy call header size instruction. the header consists of
@@ -160,10 +158,8 @@ where
_ => error, _ => error,
})?; })?;
if contract_path.as_str() == parent { if contract_path.as_str() == parent {
return Ok(Argument::new_with_constant( return Ok(Argument::value(context.word_const(0).as_basic_value_enum())
context.word_const(0).as_basic_value_enum(), .with_constant(num::BigUint::zero()));
num::BigUint::zero(),
));
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime { } else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime {
anyhow::bail!("type({}).runtimeCode is not supported", identifier); anyhow::bail!("type({}).runtimeCode is not supported", identifier);
} }
@@ -172,5 +168,5 @@ where
let size_value = context let size_value = context
.word_const(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE as u64) .word_const(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE as u64)
.as_basic_value_enum(); .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 where
D: Dependency + Clone, 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_store(output_pointer, context.word_const(0))?;
context.build_runtime_call( context.build_runtime_call(
revive_runtime_api::polkavm_imports::VALUE_TRANSFERRED, revive_runtime_api::polkavm_imports::VALUE_TRANSFERRED,
@@ -46,8 +46,7 @@ where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let address_pointer = context.build_address_argument_store(address)?; let address_pointer = context.build_address_argument_store(address)?;
let balance_pointer = context.build_alloca_at_entry(context.word_type(), "balance_pointer");
let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer");
let balance = context.builder().build_ptr_to_int( let balance = context.builder().build_ptr_to_int(
balance_pointer.value, balance_pointer.value,
context.xlen_type(), context.xlen_type(),
@@ -69,7 +68,7 @@ pub fn self_balance<'ctx, D>(
where where
D: Dependency + Clone, 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( let balance = context.builder().build_ptr_to_int(
balance_pointer.value, balance_pointer.value,
context.xlen_type(), context.xlen_type(),
+17 -10
View File
@@ -3,6 +3,7 @@
use crate::polkavm::context::runtime::RuntimeFunction; use crate::polkavm::context::runtime::RuntimeFunction;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
use crate::PolkaVMArgument;
use crate::PolkaVMLoadStorageWordFunction; use crate::PolkaVMLoadStorageWordFunction;
use crate::PolkaVMLoadTransientStorageWordFunction; use crate::PolkaVMLoadTransientStorageWordFunction;
use crate::PolkaVMStoreStorageWordFunction; use crate::PolkaVMStoreStorageWordFunction;
@@ -11,14 +12,14 @@ use crate::PolkaVMStoreTransientStorageWordFunction;
/// Translates the storage load. /// Translates the storage load.
pub fn load<'ctx, D>( pub fn load<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
position: inkwell::values::IntValue<'ctx>, position: &PolkaVMArgument<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let name = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::NAME; let name = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::declaration(context); let declaration = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::declaration(context);
let arguments = [position.into()]; let arguments = [position.as_pointer(context)?.value.into()];
Ok(context Ok(context
.build_call(declaration, &arguments, "storage_load") .build_call(declaration, &arguments, "storage_load")
.unwrap_or_else(|| panic!("runtime function {name} should return a value"))) .unwrap_or_else(|| panic!("runtime function {name} should return a value")))
@@ -27,14 +28,17 @@ where
/// Translates the storage store. /// Translates the storage store.
pub fn store<'ctx, D>( pub fn store<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
position: inkwell::values::IntValue<'ctx>, position: &PolkaVMArgument<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: &PolkaVMArgument<'ctx>,
) -> anyhow::Result<()> ) -> anyhow::Result<()>
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let declaration = <PolkaVMStoreStorageWordFunction as RuntimeFunction<D>>::declaration(context); 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"); context.build_call(declaration, &arguments, "storage_store");
Ok(()) Ok(())
} }
@@ -42,13 +46,13 @@ where
/// Translates the transient storage load. /// Translates the transient storage load.
pub fn transient_load<'ctx, D>( pub fn transient_load<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
position: inkwell::values::IntValue<'ctx>, position: &PolkaVMArgument<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let name = <PolkaVMLoadTransientStorageWordFunction as RuntimeFunction<D>>::NAME; let name = <PolkaVMLoadTransientStorageWordFunction as RuntimeFunction<D>>::NAME;
let arguments = [position.into()]; let arguments = [position.as_pointer(context)?.value.into()];
let declaration = let declaration =
<PolkaVMLoadTransientStorageWordFunction as RuntimeFunction<D>>::declaration(context); <PolkaVMLoadTransientStorageWordFunction as RuntimeFunction<D>>::declaration(context);
Ok(context Ok(context
@@ -59,15 +63,18 @@ where
/// Translates the transient storage store. /// Translates the transient storage store.
pub fn transient_store<'ctx, D>( pub fn transient_store<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
position: inkwell::values::IntValue<'ctx>, position: &PolkaVMArgument<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: &PolkaVMArgument<'ctx>,
) -> anyhow::Result<()> ) -> anyhow::Result<()>
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let declaration = let declaration =
<PolkaVMStoreTransientStorageWordFunction as RuntimeFunction<D>>::declaration(context); <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"); context.build_call(declaration, &arguments, "transient_storage_store");
Ok(()) Ok(())
} }
+4 -1
View File
@@ -12,6 +12,7 @@ use crate::optimizer::settings::Settings as OptimizerSettings;
use anyhow::Context as AnyhowContext; use anyhow::Context as AnyhowContext;
use polkavm_common::program::ProgramBlob; use polkavm_common::program::ProgramBlob;
use polkavm_disassembler::{Disassembler, DisassemblyFormat}; use polkavm_disassembler::{Disassembler, DisassemblyFormat};
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use sha3::Digest; use sha3::Digest;
use self::context::build::Build; use self::context::build::Build;
@@ -36,7 +37,7 @@ pub fn build_assembly_text(
let mut disassembled_code = Vec::new(); let mut disassembled_code = Vec::new();
disassembler disassembler
.disassemble_into(&mut disassembled_code) .disassemble_into(&mut disassembled_code)
.with_context(|| format!("Failed to disassemble contract: {}", contract_path))?; .with_context(|| format!("Failed to disassemble contract: {contract_path}"))?;
let assembly_text = String::from_utf8(disassembled_code).with_context(|| { let assembly_text = String::from_utf8(disassembled_code).with_context(|| {
format!("Failed to convert disassembled code to string for contract: {contract_path}") format!("Failed to convert disassembled code to string for contract: {contract_path}")
@@ -90,6 +91,7 @@ pub trait Dependency {
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: DebugConfig, debug_config: DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<String>; ) -> anyhow::Result<String>;
/// Resolves a full contract path. /// Resolves a full contract path.
@@ -111,6 +113,7 @@ impl Dependency for DummyDependency {
_include_metadata_hash: bool, _include_metadata_hash: bool,
_debug_config: DebugConfig, _debug_config: DebugConfig,
_llvm_arguments: &[String], _llvm_arguments: &[String],
_memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<String> { ) -> anyhow::Result<String> {
Ok(String::new()) Ok(String::new())
} }
@@ -1,6 +1,6 @@
[package] [package]
name = "revive-solidity" name = "resolc"
version.workspace = true version = "0.3.0"
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
repository.workspace = true repository.workspace = true
@@ -18,33 +18,30 @@ path = "src/resolc/main.rs"
doctest = false doctest = false
[dependencies] [dependencies]
clap = { workspace = true }
thiserror = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
which = { workspace = true } clap = { workspace = true }
hex = { workspace = true }
inkwell = { workspace = true }
once_cell = { workspace = true }
path-slash = { workspace = true } path-slash = { workspace = true }
rayon = { workspace = true, optional = true } rayon = { workspace = true, optional = true }
semver = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
semver = { workspace = true }
once_cell = { workspace = true }
regex = { workspace = true }
hex = { workspace = true }
num = { workspace = true }
sha3 = { workspace = true } sha3 = { workspace = true }
inkwell = { workspace = true } which = { workspace = true }
revive-common = { workspace = true } revive-common = { workspace = true }
revive-llvm-context = { workspace = true } revive-llvm-context = { workspace = true }
revive-solc-json-interface = { workspace = true, features = ["resolc"] } revive-solc-json-interface = { workspace = true, features = ["resolc"] }
revive-yul = { workspace = true }
[target.'cfg(target_env = "musl")'.dependencies] [target.'cfg(target_env = "musl")'.dependencies]
mimalloc = { version = "*", default-features = false } mimalloc = { version = "0.1.46", default-features = false }
[target.'cfg(target_os = "emscripten")'.dependencies] [target.'cfg(target_os = "emscripten")'.dependencies]
libc = { workspace = true } libc = { workspace = true }
inkwell = { workspace = true, features = ["target-riscv", "llvm18-0-no-llvm-linking"]} inkwell = { workspace = true, features = ["target-riscv", "llvm18-1-no-llvm-linking"]}
[build-dependencies] [build-dependencies]
git2 = { workspace = true, default-features = false } git2 = { workspace = true, default-features = false }
+11
View File
@@ -0,0 +1,11 @@
fn main() {
match git2::Repository::open("../..") {
Ok(repo) => {
let head = repo.head().expect("should have head");
let commit = head.peel_to_commit().expect("should have commit");
let id = &commit.id().to_string()[..7];
println!("cargo:rustc-env=GIT_COMMIT_HASH={id}");
}
Err(_) => println!("cargo:rustc-env=GIT_COMMIT_HASH=unknown"),
};
}
@@ -7,7 +7,6 @@ pub(crate) mod process;
pub(crate) mod project; pub(crate) mod project;
pub(crate) mod solc; pub(crate) mod solc;
pub(crate) mod version; pub(crate) mod version;
pub(crate) mod yul;
pub use self::build::contract::Contract as ContractBuild; pub use self::build::contract::Contract as ContractBuild;
pub use self::build::Build; pub use self::build::Build;
@@ -45,6 +44,8 @@ use revive_solc_json_interface::ResolcWarning;
use revive_solc_json_interface::SolcStandardJsonInput; use revive_solc_json_interface::SolcStandardJsonInput;
use revive_solc_json_interface::SolcStandardJsonInputLanguage; use revive_solc_json_interface::SolcStandardJsonInputLanguage;
use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer; use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVM;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use revive_solc_json_interface::SolcStandardJsonInputSettingsSelection; use revive_solc_json_interface::SolcStandardJsonInputSettingsSelection;
/// Runs the Yul mode. /// Runs the Yul mode.
@@ -53,8 +54,9 @@ pub fn yul<T: Compiler>(
solc: &mut T, solc: &mut T,
optimizer_settings: revive_llvm_context::OptimizerSettings, optimizer_settings: revive_llvm_context::OptimizerSettings,
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, mut debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let path = match input_files.len() { let path = match input_files.len() {
1 => input_files.first().expect("Always exists"), 1 => input_files.first().expect("Always exists"),
@@ -75,11 +77,13 @@ pub fn yul<T: Compiler>(
let solc_validator = Some(&*solc); let solc_validator = Some(&*solc);
let project = Project::try_from_yul_path(path, solc_validator)?; let project = Project::try_from_yul_path(path, solc_validator)?;
debug_config.set_yul_path(path);
let build = project.compile( let build = project.compile(
optimizer_settings, optimizer_settings,
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
memory_config,
)?; )?;
Ok(build) Ok(build)
@@ -92,6 +96,7 @@ pub fn llvm_ir(
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let path = match input_files.len() { let path = match input_files.len() {
1 => input_files.first().expect("Always exists"), 1 => input_files.first().expect("Always exists"),
@@ -109,6 +114,7 @@ pub fn llvm_ir(
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
memory_config,
)?; )?;
Ok(build) Ok(build)
@@ -131,6 +137,7 @@ pub fn standard_output<T: Compiler>(
suppressed_warnings: Option<Vec<ResolcWarning>>, suppressed_warnings: Option<Vec<ResolcWarning>>,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let solc_version = solc.version()?; let solc_version = solc.version()?;
@@ -149,6 +156,10 @@ pub fn standard_output<T: Compiler>(
), ),
None, None,
suppressed_warnings, suppressed_warnings,
Some(SolcStandardJsonInputSettingsPolkaVM::new(
Some(memory_config),
debug_config.emit_debug_info,
)),
)?; )?;
let source_code_files = solc_input let source_code_files = solc_input
@@ -189,19 +200,21 @@ pub fn standard_output<T: Compiler>(
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
memory_config,
)?; )?;
Ok(build) Ok(build)
} }
/// Runs the standard JSON mode. /// Runs the standard JSON mode.
#[allow(clippy::too_many_arguments)]
pub fn standard_json<T: Compiler>( pub fn standard_json<T: Compiler>(
solc: &mut T, solc: &mut T,
detect_missing_libraries: bool, detect_missing_libraries: bool,
base_path: Option<String>, base_path: Option<String>,
include_paths: Vec<String>, include_paths: Vec<String>,
allow_paths: Option<String>, allow_paths: Option<String>,
debug_config: revive_llvm_context::DebugConfig, mut debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let solc_version = solc.version()?; let solc_version = solc.version()?;
@@ -216,6 +229,9 @@ pub fn standard_json<T: Compiler>(
let optimizer_settings = let optimizer_settings =
revive_llvm_context::OptimizerSettings::try_from(&solc_input.settings.optimizer)?; revive_llvm_context::OptimizerSettings::try_from(&solc_input.settings.optimizer)?;
let polkavm_settings = solc_input.settings.polkavm.unwrap_or_default();
debug_config.emit_debug_info = polkavm_settings.debug_information.unwrap_or_default();
let include_metadata_hash = match solc_input.settings.metadata { let include_metadata_hash = match solc_input.settings.metadata {
Some(ref metadata) => metadata.bytecode_hash != Some(MetadataHash::None), Some(ref metadata) => metadata.bytecode_hash != Some(MetadataHash::None),
None => true, None => true,
@@ -250,6 +266,9 @@ pub fn standard_json<T: Compiler>(
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
polkavm_settings
.memory_config
.unwrap_or_else(SolcStandardJsonInputSettingsPolkaVMMemory::default),
)?; )?;
build.write_to_standard_json(&mut solc_output, &solc_version)?; build.write_to_standard_json(&mut solc_output, &solc_version)?;
} }
@@ -277,6 +296,7 @@ pub fn combined_json<T: Compiler>(
output_directory: Option<PathBuf>, output_directory: Option<PathBuf>,
overwrite: bool, overwrite: bool,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let build = standard_output( let build = standard_output(
input_files, input_files,
@@ -293,6 +313,7 @@ pub fn combined_json<T: Compiler>(
suppressed_warnings, suppressed_warnings,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
memory_config,
)?; )?;
let mut combined_json = solc.combined_json(input_files, format.as_str())?; let mut combined_json = solc.combined_json(input_files, format.as_str())?;
@@ -1,6 +1,7 @@
//! Process for compiling a single compilation unit. //! Process for compiling a single compilation unit.
//! The input data. //! The input data.
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -22,6 +23,8 @@ pub struct Input {
pub debug_config: revive_llvm_context::DebugConfig, pub debug_config: revive_llvm_context::DebugConfig,
/// The extra LLVM arguments give used for manual control. /// The extra LLVM arguments give used for manual control.
pub llvm_arguments: Vec<String>, pub llvm_arguments: Vec<String>,
/// The PVM memory configuration.
pub memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
} }
impl Input { impl Input {
@@ -33,6 +36,7 @@ impl Input {
optimizer_settings: revive_llvm_context::OptimizerSettings, optimizer_settings: revive_llvm_context::OptimizerSettings,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: Vec<String>, llvm_arguments: Vec<String>,
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> Self { ) -> Self {
Self { Self {
contract, contract,
@@ -41,6 +45,7 @@ impl Input {
optimizer_settings, optimizer_settings,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
memory_config,
} }
} }
} }
@@ -50,6 +50,7 @@ pub trait Process {
input.include_metadata_hash, input.include_metadata_hash,
input.debug_config, input.debug_config,
&input.llvm_arguments, &input.llvm_arguments,
input.memory_config,
); );
match result { match result {
@@ -8,7 +8,7 @@ use std::collections::HashSet;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::yul::parser::statement::object::Object; use revive_yul::parser::statement::object::Object;
use self::llvm_ir::LLVMIR; use self::llvm_ir::LLVMIR;
use self::yul::Yul; use self::yul::Yul;
@@ -5,7 +5,7 @@ use std::collections::HashSet;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::yul::parser::statement::object::Object; use revive_yul::parser::statement::object::Object;
/// The contract Yul source code. /// The contract Yul source code.
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -5,6 +5,7 @@ pub mod metadata;
use std::collections::HashSet; use std::collections::HashSet;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use sha3::Digest; use sha3::Digest;
@@ -76,8 +77,9 @@ impl Contract {
project: Project, project: Project,
optimizer_settings: revive_llvm_context::OptimizerSettings, optimizer_settings: revive_llvm_context::OptimizerSettings,
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, mut debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<ContractBuild> { ) -> anyhow::Result<ContractBuild> {
let llvm = inkwell::context::Context::create(); let llvm = inkwell::context::Context::create();
let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings); let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings);
@@ -115,6 +117,7 @@ impl Contract {
_ => llvm.create_module(self.path.as_str()), _ => llvm.create_module(self.path.as_str()),
}; };
debug_config.set_contract_path(&self.path);
let mut context = revive_llvm_context::PolkaVMContext::new( let mut context = revive_llvm_context::PolkaVMContext::new(
&llvm, &llvm,
module, module,
@@ -123,6 +126,7 @@ impl Contract {
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
memory_config,
); );
context.set_solidity_data(revive_llvm_context::PolkaVMContextSolidityData::default()); context.set_solidity_data(revive_llvm_context::PolkaVMContextSolidityData::default());
match self.ir { match self.ir {
@@ -9,11 +9,14 @@ use std::path::Path;
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
use revive_solc_json_interface::SolcStandardJsonOutput;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use sha3::Digest; use sha3::Digest;
use revive_solc_json_interface::SolcStandardJsonOutput;
use revive_yul::lexer::Lexer;
use revive_yul::parser::statement::object::Object;
use crate::build::contract::Contract as ContractBuild; use crate::build::contract::Contract as ContractBuild;
use crate::build::Build; use crate::build::Build;
use crate::missing_libraries::MissingLibraries; use crate::missing_libraries::MissingLibraries;
@@ -22,8 +25,6 @@ use crate::process::Process;
use crate::project::contract::ir::IR; use crate::project::contract::ir::IR;
use crate::solc::version::Version as SolcVersion; use crate::solc::version::Version as SolcVersion;
use crate::solc::Compiler; use crate::solc::Compiler;
use crate::yul::lexer::Lexer;
use crate::yul::parser::statement::object::Object;
use self::contract::Contract; use self::contract::Contract;
@@ -67,6 +68,7 @@ impl Project {
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let project = self.clone(); let project = self.clone();
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
@@ -83,6 +85,7 @@ impl Project {
optimizer_settings.clone(), optimizer_settings.clone(),
debug_config.clone(), debug_config.clone(),
llvm_arguments.to_vec(), llvm_arguments.to_vec(),
memory_config,
); );
let process_output = { let process_output = {
#[cfg(target_os = "emscripten")] #[cfg(target_os = "emscripten")]
@@ -152,8 +155,8 @@ impl Project {
.iter() .iter()
.flat_map(|(file, names)| { .flat_map(|(file, names)| {
names names
.iter() .keys()
.map(|(name, _address)| format!("{file}:{name}")) .map(|name| format!("{file}:{name}"))
.collect::<HashSet<String>>() .collect::<HashSet<String>>()
}) })
.collect::<HashSet<String>>(); .collect::<HashSet<String>>();
@@ -319,6 +322,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<String> { ) -> anyhow::Result<String> {
let contract_path = project.resolve_path(identifier)?; let contract_path = project.resolve_path(identifier)?;
let contract = project let contract = project
@@ -339,6 +343,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
memory_config,
) )
.map_err(|error| { .map_err(|error| {
anyhow::anyhow!( anyhow::anyhow!(
@@ -23,10 +23,6 @@ pub struct Arguments {
#[arg(long = "supported-solc-versions")] #[arg(long = "supported-solc-versions")]
pub supported_solc_versions: bool, pub supported_solc_versions: bool,
/// Print the licence and exit.
#[arg(long = "license")]
pub license: bool,
/// Specify the input paths and remappings. /// Specify the input paths and remappings.
/// If an argument contains a '=', it is considered a remapping. /// If an argument contains a '=', it is considered a remapping.
/// Multiple Solidity files can be passed in the default Solidity mode. /// Multiple Solidity files can be passed in the default Solidity mode.
@@ -167,9 +163,41 @@ pub struct Arguments {
#[arg(long = "recursive-process-input")] #[arg(long = "recursive-process-input")]
pub recursive_process_input: Option<String>, pub recursive_process_input: Option<String>,
#[arg(long = "llvm-arg")]
/// These are passed to LLVM as the command line to allow manual control. /// These are passed to LLVM as the command line to allow manual control.
#[arg(long = "llvm-arg")]
pub llvm_arguments: Vec<String>, 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")]
pub heap_size: Option<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")]
pub stack_size: Option<u32>,
} }
impl Arguments { impl Arguments {
@@ -298,6 +326,22 @@ impl Arguments {
if self.metadata_hash.is_some() { if self.metadata_hash.is_some() {
anyhow::bail!("Metadata hash mode must specified in standard JSON input settings."); anyhow::bail!("Metadata hash mode must specified in standard JSON input settings.");
} }
if self.heap_size.is_some() {
anyhow::bail!(
"Heap size must be specified in standard JSON input polkavm memory settings."
);
}
if self.stack_size.is_some() {
anyhow::bail!(
"Stack size must be specified in standard JSON input polkavm memory settings."
);
}
if self.emit_source_debug_info {
anyhow::bail!(
"Debug info must be requested in standard JSON input polkavm settings."
);
}
} }
Ok(()) Ok(())
@@ -5,7 +5,7 @@ pub mod arguments;
use std::io::Write; use std::io::Write;
use std::str::FromStr; use std::str::FromStr;
use revive_solidity::Process; use resolc::Process;
use self::arguments::Arguments; use self::arguments::Arguments;
@@ -36,7 +36,7 @@ fn main_inner() -> anyhow::Result<()> {
std::io::stdout(), std::io::stdout(),
"{} version {}", "{} version {}",
env!("CARGO_PKG_DESCRIPTION"), env!("CARGO_PKG_DESCRIPTION"),
revive_solidity::ResolcVersion::default().long resolc::ResolcVersion::default().long
)?; )?;
return Ok(()); return Ok(());
} }
@@ -45,20 +45,12 @@ fn main_inner() -> anyhow::Result<()> {
writeln!( writeln!(
std::io::stdout(), std::io::stdout(),
">={},<={}", ">={},<={}",
revive_solidity::SolcFirstSupportedVersion, resolc::SolcFirstSupportedVersion,
revive_solidity::SolcLastSupportedVersion, resolc::SolcLastSupportedVersion,
)?; )?;
return Ok(()); return Ok(());
} }
if arguments.license {
let license_mit = include_str!("../../../../LICENSE-MIT");
let license_apache = include_str!("../../../../LICENSE-APACHE");
writeln!(std::io::stdout(), "{}\n{}\n", license_mit, license_apache)?;
return Ok(());
}
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
rayon::ThreadPoolBuilder::new() rayon::ThreadPoolBuilder::new()
.stack_size(RAYON_WORKER_STACK_SIZE) .stack_size(RAYON_WORKER_STACK_SIZE)
@@ -71,20 +63,20 @@ fn main_inner() -> anyhow::Result<()> {
let mut infile = std::fs::File::open(fname)?; let mut infile = std::fs::File::open(fname)?;
#[cfg(target_os = "emscripten")] #[cfg(target_os = "emscripten")]
{ {
return revive_solidity::WorkerProcess::run(Some(&mut infile)); return resolc::WorkerProcess::run(Some(&mut infile));
} }
#[cfg(not(target_os = "emscripten"))] #[cfg(not(target_os = "emscripten"))]
{ {
return revive_solidity::NativeProcess::run(Some(&mut infile)); return resolc::NativeProcess::run(Some(&mut infile));
} }
} }
#[cfg(target_os = "emscripten")] #[cfg(target_os = "emscripten")]
{ {
return revive_solidity::WorkerProcess::run(None); return resolc::WorkerProcess::run(None);
} }
#[cfg(not(target_os = "emscripten"))] #[cfg(not(target_os = "emscripten"))]
{ {
return revive_solidity::NativeProcess::run(None); return resolc::NativeProcess::run(None);
} }
} }
@@ -111,14 +103,16 @@ fn main_inner() -> anyhow::Result<()> {
let mut solc = { let mut solc = {
#[cfg(target_os = "emscripten")] #[cfg(target_os = "emscripten")]
{ {
revive_solidity::SoljsonCompiler resolc::SoljsonCompiler
} }
#[cfg(not(target_os = "emscripten"))] #[cfg(not(target_os = "emscripten"))]
{ {
revive_solidity::SolcCompiler::new(arguments.solc.unwrap_or_else(|| { resolc::SolcCompiler::new(
revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned() arguments
}))? .solc
.unwrap_or_else(|| resolc::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned()),
)?
} }
}; };
@@ -129,7 +123,7 @@ fn main_inner() -> anyhow::Result<()> {
let mut optimizer_settings = match arguments.optimization { let mut optimizer_settings = match arguments.optimization {
Some(mode) => revive_llvm_context::OptimizerSettings::try_from_cli(mode)?, Some(mode) => revive_llvm_context::OptimizerSettings::try_from_cli(mode)?,
None => revive_llvm_context::OptimizerSettings::cycles(), None => revive_llvm_context::OptimizerSettings::size(),
}; };
if arguments.fallback_to_optimizing_for_size { if arguments.fallback_to_optimizing_for_size {
optimizer_settings.enable_fallback_to_size(); optimizer_settings.enable_fallback_to_size();
@@ -148,25 +142,32 @@ fn main_inner() -> anyhow::Result<()> {
None => true, None => true,
}; };
let memory_config = revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory::new(
arguments.heap_size,
arguments.stack_size,
);
let build = if arguments.yul { let build = if arguments.yul {
revive_solidity::yul( resolc::yul(
input_files.as_slice(), input_files.as_slice(),
&mut solc, &mut solc,
optimizer_settings, optimizer_settings,
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
&arguments.llvm_arguments, &arguments.llvm_arguments,
memory_config,
) )
} else if arguments.llvm_ir { } else if arguments.llvm_ir {
revive_solidity::llvm_ir( resolc::llvm_ir(
input_files.as_slice(), input_files.as_slice(),
optimizer_settings, optimizer_settings,
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
&arguments.llvm_arguments, &arguments.llvm_arguments,
memory_config,
) )
} else if arguments.standard_json { } else if arguments.standard_json {
revive_solidity::standard_json( resolc::standard_json(
&mut solc, &mut solc,
arguments.detect_missing_libraries, arguments.detect_missing_libraries,
arguments.base_path, arguments.base_path,
@@ -177,7 +178,7 @@ fn main_inner() -> anyhow::Result<()> {
)?; )?;
return Ok(()); return Ok(());
} else if let Some(format) = arguments.combined_json { } else if let Some(format) = arguments.combined_json {
revive_solidity::combined_json( resolc::combined_json(
format, format,
input_files.as_slice(), input_files.as_slice(),
arguments.libraries, arguments.libraries,
@@ -195,10 +196,11 @@ fn main_inner() -> anyhow::Result<()> {
arguments.output_directory, arguments.output_directory,
arguments.overwrite, arguments.overwrite,
&arguments.llvm_arguments, &arguments.llvm_arguments,
memory_config,
)?; )?;
return Ok(()); return Ok(());
} else { } else {
revive_solidity::standard_output( resolc::standard_output(
input_files.as_slice(), input_files.as_slice(),
arguments.libraries, arguments.libraries,
&mut solc, &mut solc,
@@ -213,6 +215,7 @@ fn main_inner() -> anyhow::Result<()> {
suppressed_warnings, suppressed_warnings,
debug_config, debug_config,
&arguments.llvm_arguments, &arguments.llvm_arguments,
memory_config,
) )
}?; }?;
@@ -237,9 +240,7 @@ fn main_inner() -> anyhow::Result<()> {
writeln!( writeln!(
std::io::stdout(), std::io::stdout(),
"Contract `{}` assembly:\n\n{}", "Contract `{path}` assembly:\n\n{assembly_text}"
path,
assembly_text
)?; )?;
} }
if arguments.output_binary { if arguments.output_binary {
@@ -19,7 +19,7 @@ use self::version::Version;
pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0); pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0);
/// The last supported version of `solc`. /// The last supported version of `solc`.
pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 29); pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 30);
/// `--include-path` was introduced in solc `0.8.8` <https://github.com/ethereum/solidity/releases/tag/v0.8.8> /// `--include-path` was introduced in solc `0.8.8` <https://github.com/ethereum/solidity/releases/tag/v0.8.8>
pub const FIRST_INCLUDE_PATH_VERSION: semver::Version = semver::Version::new(0, 8, 8); pub const FIRST_INCLUDE_PATH_VERSION: semver::Version = semver::Version::new(0, 8, 8);
@@ -45,8 +45,6 @@ impl Compiler for SolcCompiler {
include_paths: Vec<String>, include_paths: Vec<String>,
allow_paths: Option<String>, allow_paths: Option<String>,
) -> anyhow::Result<SolcStandardJsonOutput> { ) -> anyhow::Result<SolcStandardJsonOutput> {
let version = self.version()?.validate(&include_paths)?.default;
let mut command = std::process::Command::new(self.executable.as_str()); let mut command = std::process::Command::new(self.executable.as_str());
command.stdin(std::process::Stdio::piped()); command.stdin(std::process::Stdio::piped());
command.stdout(std::process::Stdio::piped()); command.stdout(std::process::Stdio::piped());
@@ -65,7 +63,7 @@ impl Compiler for SolcCompiler {
command.arg(allow_paths); command.arg(allow_paths);
} }
input.normalize(&version); input.normalize();
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default(); let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
@@ -40,8 +40,7 @@ impl Compiler for SoljsonCompiler {
anyhow::bail!("configuring allow paths is not supported with solJson") anyhow::bail!("configuring allow paths is not supported with solJson")
} }
let version = self.version()?.validate(&include_paths)?.default; input.normalize();
input.normalize(&version);
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default(); let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
@@ -89,29 +89,40 @@ pub fn build_solidity_with_options(
sources.clone(), sources.clone(),
libraries.clone(), libraries.clone(),
remappings, remappings,
SolcStandardJsonInputSettingsSelection::new_required(), SolcStandardJsonInputSettingsSelection::new_required_for_tests(),
SolcStandardJsonInputSettingsOptimizer::new( SolcStandardJsonInputSettingsOptimizer::new(
solc_optimizer_enabled, solc_optimizer_enabled,
None, optimizer_settings.middle_end_as_string().chars().last(),
&solc_version.default, &solc_version.default,
false, false,
), ),
None, None,
None, None,
None,
)?; )?;
let mut output = solc.standard_json(input, None, vec![], None)?; 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( let project = Project::try_from_standard_json_output(
&output, &output,
sources, sources,
libraries, libraries,
&solc_version, &solc_version,
&DEBUG_CONFIG, &debug_config,
)?; )?;
let build: crate::Build = let build: crate::Build = project.compile(
project.compile(optimizer_settings, false, DEBUG_CONFIG, Default::default())?; optimizer_settings,
false,
debug_config,
Default::default(),
Default::default(),
)?;
build.write_to_standard_json(&mut output, &solc_version)?; build.write_to_standard_json(&mut output, &solc_version)?;
Ok(output) Ok(output)
@@ -143,7 +154,7 @@ pub fn build_solidity_with_options_evm(
sources.clone(), sources.clone(),
libraries.clone(), libraries.clone(),
remappings, remappings,
SolcStandardJsonInputSettingsSelection::new_required(), SolcStandardJsonInputSettingsSelection::new_required_for_tests(),
SolcStandardJsonInputSettingsOptimizer::new( SolcStandardJsonInputSettingsOptimizer::new(
solc_optimizer_enabled, solc_optimizer_enabled,
None, None,
@@ -152,6 +163,7 @@ pub fn build_solidity_with_options_evm(
), ),
None, None,
None, None,
None,
)?; )?;
let mut output = solc.standard_json(input, None, vec![], None)?; let mut output = solc.standard_json(input, None, vec![], None)?;
@@ -199,10 +211,11 @@ pub fn build_solidity_and_detect_missing_libraries(
sources.clone(), sources.clone(),
libraries.clone(), libraries.clone(),
None, None,
SolcStandardJsonInputSettingsSelection::new_required(), SolcStandardJsonInputSettingsSelection::new_required_for_tests(),
SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false), SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false),
None, None,
None, None,
None,
)?; )?;
let mut output = solc.standard_json(input, None, vec![], None)?; let mut output = solc.standard_json(input, None, vec![], None)?;
@@ -238,7 +251,13 @@ pub fn build_yul(source_code: &str) -> anyhow::Result<()> {
source_code, source_code,
None, 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(()) Ok(())
} }
@@ -266,10 +285,11 @@ pub fn check_solidity_warning(
sources.clone(), sources.clone(),
libraries, libraries,
None, None,
SolcStandardJsonInputSettingsSelection::new_required(), SolcStandardJsonInputSettingsSelection::new_required_for_tests(),
SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false), SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false),
None, None,
suppressed_warnings, suppressed_warnings,
None,
)?; )?;
let output = solc.standard_json(input, None, vec![], None)?; let output = solc.standard_json(input, None, vec![], None)?;
@@ -341,7 +361,7 @@ fn compile_evm(
.expect("source should compile"); .expect("source should compile");
let object = &contracts let object = &contracts
.get(contract_name) .get(contract_name)
.unwrap_or_else(|| panic!("contract '{}' didn't produce bin-runtime", contract_name)); .unwrap_or_else(|| panic!("contract '{contract_name}' didn't produce bin-runtime"));
let code = if runtime { let code = if runtime {
object.1.object.as_str() object.1.object.as_str()
} else { } else {
+40
View File
@@ -0,0 +1,40 @@
//! The tests for running resolc with asm option.
#![cfg(test)]
use crate::tests::cli::utils;
const ASM_OPTION: &str = "--asm";
#[test]
fn runs_with_valid_input_file() {
let arguments = &[utils::SOLIDITY_CONTRACT_PATH, ASM_OPTION];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_success(&resolc_result, "Providing a valid input file");
for pattern in &["deploy", "call", "seal_return"] {
assert!(
resolc_result.stdout.contains(pattern),
"Expected the output to contain `{pattern}`."
);
}
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
#[test]
fn fails_without_input_file() {
let arguments = &[ASM_OPTION];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Omitting an input file");
let output = resolc_result.stderr.to_lowercase();
assert!(
output.contains("no input sources specified") || output.contains("compilation aborted"),
"Expected the output to contain a specific error message."
);
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
@@ -0,0 +1,137 @@
//! The tests for running resolc with combined JSON option.
#![cfg(test)]
use revive_common;
use crate::tests::cli::utils;
const JSON_OPTION: &str = "--combined-json";
const JSON_ARGUMENTS: &[&str] = &[
"abi",
"hashes",
"metadata",
"devdoc",
"userdoc",
"storage-layout",
"ast",
"asm",
"bin",
"bin-runtime",
];
#[test]
fn runs_with_valid_json_argument() {
for json_argument in JSON_ARGUMENTS {
let arguments = &[utils::SOLIDITY_CONTRACT_PATH, JSON_OPTION, json_argument];
let resolc_result = utils::execute_resolc(arguments);
assert!(
resolc_result.success,
"Providing the `{json_argument}` argument should succeed with exit code {}, got {}.\nDetails: {}",
revive_common::EXIT_CODE_SUCCESS,
resolc_result.code,
resolc_result.stderr
);
assert!(
resolc_result.stdout.contains("contracts"),
"Expected the output to contain a `contracts` field when using the `{json_argument}` argument."
);
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
}
#[test]
fn fails_with_invalid_json_argument() {
let arguments = &[
utils::SOLIDITY_CONTRACT_PATH,
JSON_OPTION,
"invalid-argument",
];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Providing an invalid json argument");
assert!(
resolc_result.stdout.contains("Invalid option"),
"Expected the output to contain a specific error message."
);
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
#[test]
fn fails_with_multiple_json_arguments() {
let arguments = &[
utils::SOLIDITY_CONTRACT_PATH,
JSON_OPTION,
JSON_ARGUMENTS[0],
JSON_ARGUMENTS[1],
];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Providing multiple json arguments");
assert!(
resolc_result
.stderr
.contains("reading error: No such file or directory"),
"Expected the output to contain a specific error message."
);
// FIX: Resolc exit code == 101
// let solc_result = utils::execute_solc(arguments);
// utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
#[test]
fn fails_without_json_argument() {
let arguments = &[utils::SOLIDITY_CONTRACT_PATH, JSON_OPTION];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Omitting a JSON argument");
assert!(
resolc_result.stderr.contains(
"a value is required for '--combined-json <COMBINED_JSON>' but none was supplied"
),
"Expected the output to contain a specific error message."
);
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
#[test]
fn fails_without_solidity_input_file() {
let arguments = &[JSON_OPTION, JSON_ARGUMENTS[0]];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Omitting a Solidity input file");
assert!(
resolc_result.stderr.contains("No input sources specified"),
"Expected the output to contain a specific error message."
);
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
#[test]
fn fails_with_yul_input_file() {
for json_argument in JSON_ARGUMENTS {
let arguments = &[utils::YUL_CONTRACT_PATH, JSON_OPTION, json_argument];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Providing a Yul input file");
assert!(
resolc_result
.stderr
.contains("ParserError: Expected identifier"),
"Expected the output to contain a specific error message."
);
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
}

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