Compare commits

...

41 Commits

Author SHA1 Message Date
Cyrill Leutwiler a57352f9c4 release resolc 0.4.0
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-10-06 08:51:39 +02:00
xermicus b560d72139 Prevent frontend function confusion (#383)
Function symbols can clash as we compile multiple YUL `object`
definition into the same `LLVM` module.
- Disambiguate via unique function symbols based its location (runtime
or deploy code).
- Use `LinkOnceODR` linkage for compiler builtin helpers.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-09-29 23:05:57 +02:00
xermicus 6858cb9a61 mark internal functions linker private (#381)
Prevents unused functions in the emitted ELF object. Drive-by add a
missing test case (which misses a relocation under `-Oz` when all
internal functions are marked as private).

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-09-29 17:55:29 +02:00
xermicus 1cc4f967b4 vet cargo docs on CI (#380)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-09-29 13:39:09 +02:00
xermicus 713b7e5409 Nightly release workflow fix (#378)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-09-28 13:12:10 +02:00
dependabot[bot] ffc13697ed Bump brace-expansion (#377)
Bumps and
[brace-expansion](https://github.com/juliangruber/brace-expansion).
These dependencies needed to be updated together.
Updates `brace-expansion` from 1.1.11 to 1.1.12
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/juliangruber/brace-expansion/releases">brace-expansion's
releases</a>.</em></p>
<blockquote>
<h2>v1.1.12</h2>
<ul>
<li>pkg: publish on tag 1.x  c460dbd</li>
<li>fmt  ccb8ac6</li>
<li>Fix potential ReDoS Vulnerability or Inefficient Regular Expression
(<a
href="https://redirect.github.com/juliangruber/brace-expansion/issues/65">#65</a>)
c3c73c8</li>
</ul>
<hr />
<p><a
href="https://github.com/juliangruber/brace-expansion/compare/v1.1.11...v1.1.12">https://github.com/juliangruber/brace-expansion/compare/v1.1.11...v1.1.12</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/juliangruber/brace-expansion/commit/44f33b47c5c6a965d507421af43e86cf5971d711"><code>44f33b4</code></a>
1.1.12</li>
<li><a
href="https://github.com/juliangruber/brace-expansion/commit/c460dbd68e428d147b2080622d8ce126c7a08570"><code>c460dbd</code></a>
pkg: publish on tag 1.x</li>
<li><a
href="https://github.com/juliangruber/brace-expansion/commit/ccb8ac6d4292b7661b677fe048ba6690c877f51f"><code>ccb8ac6</code></a>
fmt</li>
<li><a
href="https://github.com/juliangruber/brace-expansion/commit/c3c73c8b088defc70851843be88ccc3af08e7217"><code>c3c73c8</code></a>
Fix potential ReDoS Vulnerability or Inefficient Regular Expression (<a
href="https://redirect.github.com/juliangruber/brace-expansion/issues/65">#65</a>)</li>
<li>See full diff in <a
href="https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `brace-expansion` from 2.0.1 to 2.0.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/juliangruber/brace-expansion/releases">brace-expansion's
releases</a>.</em></p>
<blockquote>
<h2>v1.1.12</h2>
<ul>
<li>pkg: publish on tag 1.x  c460dbd</li>
<li>fmt  ccb8ac6</li>
<li>Fix potential ReDoS Vulnerability or Inefficient Regular Expression
(<a
href="https://redirect.github.com/juliangruber/brace-expansion/issues/65">#65</a>)
c3c73c8</li>
</ul>
<hr />
<p><a
href="https://github.com/juliangruber/brace-expansion/compare/v1.1.11...v1.1.12">https://github.com/juliangruber/brace-expansion/compare/v1.1.11...v1.1.12</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/juliangruber/brace-expansion/commit/44f33b47c5c6a965d507421af43e86cf5971d711"><code>44f33b4</code></a>
1.1.12</li>
<li><a
href="https://github.com/juliangruber/brace-expansion/commit/c460dbd68e428d147b2080622d8ce126c7a08570"><code>c460dbd</code></a>
pkg: publish on tag 1.x</li>
<li><a
href="https://github.com/juliangruber/brace-expansion/commit/ccb8ac6d4292b7661b677fe048ba6690c877f51f"><code>ccb8ac6</code></a>
fmt</li>
<li><a
href="https://github.com/juliangruber/brace-expansion/commit/c3c73c8b088defc70851843be88ccc3af08e7217"><code>c3c73c8</code></a>
Fix potential ReDoS Vulnerability or Inefficient Regular Expression (<a
href="https://redirect.github.com/juliangruber/brace-expansion/issues/65">#65</a>)</li>
<li>See full diff in <a
href="https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12">compare
view</a></li>
</ul>
</details>
<br />


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>
2025-09-28 13:06:55 +02:00
xermicus 94ec34c4d5 Separate compilation and linker phases (#376)
Separate between compilation and linker phases to allow deploy time
linking and back-porting era compiler changes to fix #91. Unlinked
contract binaries (caused by missing libraries or missing factory
dependencies in turn) are emitted as raw ELF object.

Few drive by fixes:
- #98
- A compiler panic on missing libraries definitions.
- Fixes some incosistent type forwarding in JSON output (empty string
vs. null object).
- Remove the unused fallback for size optimization setting.
- Remove the broken `--lvm-ir`  mode.
- CI workflow fixes.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
2025-09-27 20:52:22 +02:00
xermicus 13faedf08a link to readme in the build utils llvm error message more (#373)
Signed-off-by: xermicus <cyrill@parity.io>
2025-09-02 14:21:54 +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
277 changed files with 12822 additions and 14563 deletions
+3
View File
@@ -15,3 +15,6 @@ rustflags = [
"-Clink-arg=-sSTACK_SIZE=128kb", "-Clink-arg=-sSTACK_SIZE=128kb",
"-Clink-arg=-sNODEJS_CATCH_EXIT=0" "-Clink-arg=-sNODEJS_CATCH_EXIT=0"
] ]
[build]
rustdocflags = ["-D", "warnings"]
+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()
+1 -1
View File
@@ -94,7 +94,7 @@ jobs:
- 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 == *'50564d'* ]]; 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"
+4 -3
View File
@@ -14,6 +14,7 @@ concurrency:
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
# if changed, dont forget to update the env var in release-nightly.yml
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067 RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067
jobs: jobs:
@@ -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"
# #
@@ -131,7 +132,7 @@ jobs:
run: | run: |
result=$(./resolc-${{ matrix.target }} --bin crates/integration/contracts/flipper.sol) result=$(./resolc-${{ matrix.target }} --bin crates/integration/contracts/flipper.sol)
echo $result echo $result
if [[ $result == *'0x50564d'* ]]; then exit 0; else exit 1; fi if [[ $result == *'50564d'* ]]; then exit 0; else exit 1; fi
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
@@ -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
+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 }}
+4
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 }}
+7 -2
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 }}
@@ -22,6 +26,7 @@ jobs:
with: with:
# without this it will override our rust flags # without this it will override our rust flags
rustflags: "" rustflags: ""
components: rustfmt, clippy
- name: Install Solc - name: Install Solc
uses: ./.github/actions/get-solc uses: ./.github/actions/get-solc
@@ -53,5 +58,5 @@ jobs:
- name: Test cargo workspace - name: Test cargo workspace
run: make test-workspace run: make test-workspace
- name: Test CLI - name: Test docs
run: make test-cli run: make doc
+54
View File
@@ -6,6 +6,60 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1` Supported `polkadot-sdk` rev: `2503.0.1`
## v0.4.0
This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
### Changed
- Remove the broken `--llvm-ir` mode.
- Remove the unused fallback for size optimization setting.
- Unlinked contract binaries are emitted as raw ELF objects.
### 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.
- The `--link` deploy time linking mode.
### Fixed
- The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line.
- Incosistent type forwarding in JSON output (empty string vs. null object).
- The solc automatic import resolution.
- Compiler panic on missing libraries definition.
## 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 ## v0.1.0
This is a development pre-release. This is a development pre-release.
Generated
+978 -567
View File
File diff suppressed because it is too large Load Diff
+38 -35
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.1.0-dev.16" 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>",
@@ -14,64 +14,67 @@ repository = "https://github.com/paritytech/revive"
rust-version = "1.85.0" rust-version = "1.85.0"
[workspace.dependencies] [workspace.dependencies]
revive-benchmarks = { version = "0.1.0-dev.16", path = "crates/benchmarks" } lld-sys = { version = "0.1.0", path = "crates/lld-sys" }
revive-builtins = { version = "0.1.0-dev.16", path = "crates/builtins" } resolc = { version = "0.4.0", path = "crates/resolc", default-features = false }
revive-common = { version = "0.1.0-dev.16", path = "crates/common" } revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
revive-differential = { version = "0.1.0-dev.16", path = "crates/differential" } revive-build-utils = { version = "0.2.0", path = "crates/build-utils" }
revive-integration = { version = "0.1.0-dev.16", path = "crates/integration" } revive-builtins = { version = "0.1.0", path = "crates/builtins" }
revive-linker = { version = "0.1.0-dev.16", path = "crates/linker" } revive-common = { version = "0.2.0", path = "crates/common" }
lld-sys = { version = "0.1.0-dev.16", path = "crates/lld-sys" } revive-differential = { version = "0.2.0", path = "crates/differential" }
revive-llvm-context = { version = "0.1.0-dev.16", path = "crates/llvm-context" } revive-explorer = { version = "0.1.0", path = "crates/explore" }
revive-runtime-api = { version = "0.1.0-dev.16", path = "crates/runtime-api" } revive-integration = { version = "0.2.0", path = "crates/integration" }
revive-runner = { version = "0.1.0-dev.16", path = "crates/runner" } revive-linker = { version = "0.2.0", path = "crates/linker" }
revive-solc-json-interface = { version = "0.1.0-dev.16", path = "crates/solc-json-interface" } revive-llvm-context = { version = "0.4.0", path = "crates/llvm-context" }
revive-solidity = { version = "0.1.0-dev.16", path = "crates/solidity" } revive-runner = { version = "0.2.0", path = "crates/runner" }
revive-stdlib = { version = "0.1.0-dev.16", path = "crates/stdlib" } revive-runtime-api = { version = "0.3.0", path = "crates/runtime-api" }
revive-build-utils = { version = "0.1.0-dev.16", path = "crates/build-utils" } revive-solc-json-interface = { version = "0.3.0", path = "crates/solc-json-interface", default-features = false }
revive-stdlib = { version = "0.2.0", path = "crates/stdlib" }
revive-yul = { version = "0.3.0", path = "crates/yul" }
hex = "0.4.3" hex = "0.4.3"
cc = "1.2" cc = "1.2"
libc = "0.2.172" 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 } env_logger = { version = "0.11.8", default-features = false }
serde_stacker = "0.1.11" serde_stacker = "0.1.12"
criterion = { version = "0.5.1", features = ["html_reports"] } criterion = { version = "0.6", features = ["html_reports"] }
log = { version = "0.4.25" } log = { version = "0.4.27" }
git2 = { version = "0.20.0", default-features = false } 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"
normpath = "1.3"
# 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 = { version = "2503.0.1" } polkadot-sdk = { version = "2503.0.1" }
+18 -14
View File
@@ -6,14 +6,15 @@
install-llvm-builder \ install-llvm-builder \
install-llvm \ install-llvm \
install-revive-runner \ install-revive-runner \
install-revive-explorer \
format \ format \
clippy \ clippy \
doc \
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 \
@@ -24,24 +25,27 @@
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: install-revive-runner:
cargo install --path crates/runner --no-default-features --locked 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,24 +53,24 @@ format:
clippy: clippy:
cargo clippy --all-features --workspace --tests --benches -- --deny warnings cargo clippy --all-features --workspace --tests --benches -- --deny warnings
doc:
cargo doc --all-features --workspace --document-private-items --no-deps
machete: machete:
cargo install cargo-machete cargo install cargo-machete
cargo machete cargo machete
test: format clippy machete test-cli test-workspace install-revive-runner test: format clippy machete test-workspace install-revive-runner install-revive-explorer doc
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
@@ -90,6 +94,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 ;
+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.
+7 -3
View File
@@ -4,11 +4,15 @@ 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
+1
View File
@@ -0,0 +1 @@
large-error-threshold = 192
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revive-build-utils" name = "revive-build-utils"
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
+5 -3
View File
@@ -6,6 +6,10 @@ pub const REVIVE_LLVM_HOST_PREFIX: &str = "LLVM_SYS_181_PREFIX";
/// The revive LLVM target dependency directory prefix environment variable. /// The revive LLVM target dependency directory prefix environment variable.
pub const REVIVE_LLVM_TARGET_PREFIX: &str = "REVIVE_LLVM_TARGET_PREFIX"; pub const REVIVE_LLVM_TARGET_PREFIX: &str = "REVIVE_LLVM_TARGET_PREFIX";
/// The revive LLVM host tool help link.
pub const REVIVE_LLVM_BUILDER_HELP_LINK: &str =
"https://github.com/paritytech/revive?tab=readme-ov-file#building-from-source";
/// Constructs a path to the LLVM tool `name`. /// Constructs a path to the LLVM tool `name`.
/// ///
/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable. /// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
@@ -13,9 +17,7 @@ pub fn llvm_host_tool(name: &str) -> std::path::PathBuf {
std::env::var_os(REVIVE_LLVM_HOST_PREFIX) std::env::var_os(REVIVE_LLVM_HOST_PREFIX)
.map(Into::<std::path::PathBuf>::into) .map(Into::<std::path::PathBuf>::into)
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!( panic!("install LLVM using the revive-llvm builder and export '{REVIVE_LLVM_HOST_PREFIX}'; see also: {REVIVE_LLVM_BUILDER_HELP_LINK}")
"install LLVM using the revive-llvm builder and export {REVIVE_LLVM_HOST_PREFIX}",
)
}) })
.join("bin") .join("bin")
.join(name) .join(name)
+3 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revive-common" name = "revive-common"
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
@@ -15,6 +15,8 @@ doctest = false
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
hex = { workspace = true }
sha3 = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = [ "arbitrary_precision", "unbounded_depth" ] } serde_json = { workspace = true, features = [ "arbitrary_precision", "unbounded_depth" ] }
serde_stacker = { workspace = true } serde_stacker = { workspace = true }
+33
View File
@@ -0,0 +1,33 @@
//! The contract identifier helper library.
use serde::{Deserialize, Serialize};
/// This structure simplifies passing the contract identifiers through the compilation pipeline.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractIdentifier {
/// The absolute file path.
pub path: String,
/// The contract name.
/// Is set for Solidity contracts only. Otherwise it would be equal to the file name.
pub name: Option<String>,
/// The full contract identifier.
/// For Solidity, The format is `<absolute file path>:<contract name>`.
/// For other languages, `<absolute file path>`.
pub full_path: String,
}
impl ContractIdentifier {
/// A shortcut constructor.
pub fn new(path: String, name: Option<String>) -> Self {
let full_path = match name {
Some(ref name) => format!("{path}:{name}"),
None => path.clone(),
};
Self {
path,
name,
full_path,
}
}
}
+1 -1
View File
@@ -37,4 +37,4 @@ pub static EXTENSION_POLKAVM_ASSEMBLY: &str = "pvmasm";
pub static EXTENSION_POLKAVM_BINARY: &str = "pvm"; pub static EXTENSION_POLKAVM_BINARY: &str = "pvm";
/// The ELF shared object file extension. /// The ELF shared object file extension.
pub static EXTENSION_SHARED_OBJECT: &str = "so"; pub static EXTENSION_OBJECT: &str = "o";
+68
View File
@@ -0,0 +1,68 @@
//! Keccak-256 hash utilities.
use serde::{Deserialize, Serialize};
use sha3::digest::FixedOutput;
use sha3::Digest;
pub const DIGEST_BYTES: usize = 32;
/// Keccak-256 hash utilities.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Keccak256 {
/// Binary representation.
bytes: [u8; DIGEST_BYTES],
/// Hexadecimal string representation.
string: String,
}
impl Keccak256 {
/// Computes the `keccak256` hash for `preimage`.
pub fn from_slice(preimage: &[u8]) -> Self {
let bytes = sha3::Keccak256::digest(preimage).into();
let string = format!("0x{}", hex::encode(bytes));
Self { bytes, string }
}
/// Computes the `keccak256` hash for an array of `preimages`.
pub fn from_slices<R: AsRef<[u8]>>(preimages: &[R]) -> Self {
let mut hasher = sha3::Keccak256::new();
for preimage in preimages.iter() {
hasher.update(preimage);
}
let bytes: [u8; DIGEST_BYTES] = hasher.finalize_fixed().into();
let string = format!("0x{}", hex::encode(bytes));
Self { bytes, string }
}
/// Returns a reference to the 32-byte SHA-3 hash.
pub fn as_bytes(&self) -> &[u8] {
self.bytes.as_slice()
}
/// Returns a reference to the hexadecimal string representation.
pub fn as_str(&self) -> &str {
self.string.as_str()
}
/// Extracts the binary representation.
pub fn to_vec(&self) -> Vec<u8> {
self.bytes.to_vec()
}
}
impl std::fmt::Display for Keccak256 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
#[test]
fn hash_and_stringify_works() {
assert_eq!(
super::Keccak256::from_slices(&["foo".as_bytes(), "bar".as_bytes(),]).as_str(),
"0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e"
);
}
}
+8
View File
@@ -3,9 +3,13 @@
pub(crate) mod base; pub(crate) mod base;
pub(crate) mod bit_length; pub(crate) mod bit_length;
pub(crate) mod byte_length; pub(crate) mod byte_length;
pub(crate) mod contract_identifier;
pub(crate) mod evm_version; pub(crate) mod evm_version;
pub(crate) mod exit_code; pub(crate) mod exit_code;
pub(crate) mod extension; pub(crate) mod extension;
pub(crate) mod keccak256;
pub(crate) mod metadata;
pub(crate) mod object;
pub(crate) mod utils; pub(crate) mod utils;
pub use self::base::*; pub use self::base::*;
@@ -14,4 +18,8 @@ pub use self::byte_length::*;
pub use self::evm_version::EVMVersion; pub use self::evm_version::EVMVersion;
pub use self::exit_code::*; pub use self::exit_code::*;
pub use self::extension::*; pub use self::extension::*;
pub use self::keccak256::*;
pub use self::metadata::*;
pub use self::object::*;
pub use self::utils::*; pub use self::utils::*;
pub use contract_identifier::*;
+42
View File
@@ -0,0 +1,42 @@
//! The metadata hash type.
use std::str::FromStr;
use serde::{Deserialize, Serialize};
/// The metadata hash type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MetadataHash {
/// Do not include bytecode hash.
#[serde(rename = "none")]
None,
/// Include the `ipfs` hash.
#[serde(rename = "ipfs")]
IPFS,
/// Include the `keccak256`` hash.
#[serde(rename = "keccak256")]
Keccak256,
}
impl FromStr for MetadataHash {
type Err = anyhow::Error;
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
"none" => Ok(Self::None),
"ipfs" => Ok(Self::IPFS),
"keccak256" => Ok(Self::Keccak256),
string => anyhow::bail!("unknown bytecode hash mode: `{string}`"),
}
}
}
impl std::fmt::Display for MetadataHash {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::None => write!(f, "none"),
Self::IPFS => write!(f, "ipfs"),
Self::Keccak256 => write!(f, "keccak256"),
}
}
}
+61
View File
@@ -0,0 +1,61 @@
//! The revive binary object helper module.
use std::str::FromStr;
use serde::{Deserialize, Serialize};
/// The binary object format.
///
/// Unlinked contracts are stored in a different object format
/// than final (linked) contract blobs.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ObjectFormat {
/// The unlinked ELF object format.
ELF,
/// The fully linked PVM format.
PVM,
}
impl ObjectFormat {
pub const PVM_MAGIC: [u8; 4] = [b'P', b'V', b'M', b'\0'];
pub const ELF_MAGIC: [u8; 4] = [0x7f, b'E', b'L', b'F'];
}
impl FromStr for ObjectFormat {
type Err = anyhow::Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"ELF" => Ok(Self::ELF),
"PVM" => Ok(Self::PVM),
_ => anyhow::bail!(
"Unknown object format: {value}. Supported formats: {}, {}",
Self::ELF.to_string(),
Self::PVM.to_string()
),
}
}
}
impl TryFrom<&[u8]> for ObjectFormat {
type Error = &'static str;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.starts_with(&Self::PVM_MAGIC) {
return Ok(Self::PVM);
}
if value.starts_with(&Self::ELF_MAGIC) {
return Ok(Self::ELF);
}
Err("expected a contract object")
}
}
impl std::fmt::Display for ObjectFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ELF => write!(f, "ELF"),
Self::PVM => write!(f, "PVM"),
}
}
}
+26 -6
View File
@@ -1,25 +1,45 @@
//! The compiler common utils. //! The compiler common utils.
/// Deserializes a `serde_json` object from slice with the recursion limit disabled. /// Deserializes a `serde_json` object from slice with the recursion limit disabled.
///
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit. /// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
pub fn deserialize_from_slice<O>(input: &[u8]) -> anyhow::Result<O> pub fn deserialize_from_slice<O>(input: &[u8]) -> anyhow::Result<O>
where where
O: serde::de::DeserializeOwned, O: serde::de::DeserializeOwned,
{ {
let mut deserializer = serde_json::Deserializer::from_slice(input); let deserializer = serde_json::Deserializer::from_slice(input);
deserializer.disable_recursion_limit(); deserialize(deserializer)
let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
let result = O::deserialize(deserializer)?;
Ok(result)
} }
/// Deserializes a `serde_json` object from string with the recursion limit disabled. /// Deserializes a `serde_json` object from string with the recursion limit disabled.
///
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit. /// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
pub fn deserialize_from_str<O>(input: &str) -> anyhow::Result<O> pub fn deserialize_from_str<O>(input: &str) -> anyhow::Result<O>
where where
O: serde::de::DeserializeOwned, O: serde::de::DeserializeOwned,
{ {
let mut deserializer = serde_json::Deserializer::from_str(input); let deserializer = serde_json::Deserializer::from_str(input);
deserialize(deserializer)
}
/// Deserializes a `serde_json` object from reader with the recursion limit disabled.
///
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
pub fn deserialize_from_reader<R, O>(reader: R) -> anyhow::Result<O>
where
R: std::io::Read,
O: serde::de::DeserializeOwned,
{
let deserializer = serde_json::Deserializer::from_reader(reader);
deserialize(deserializer)
}
/// Runs the generic deserializer.
pub fn deserialize<'de, R, O>(mut deserializer: serde_json::Deserializer<R>) -> anyhow::Result<O>
where
R: serde_json::de::Read<'de>,
O: serde::de::DeserializeOwned,
{
deserializer.disable_recursion_limit(); deserializer.disable_recursion_limit();
let deserializer = serde_stacker::Deserializer::new(&mut deserializer); let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
let result = O::deserialize(deserializer)?; let result = O::deserialize(deserializer)?;
+2 -1
View File
@@ -1,6 +1,7 @@
[package] [package]
name = "revive-differential" name = "revive-differential"
version.workspace = true description = "utilities for differential testing the revive compiler against EVM"
version = "0.2.0"
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
authors.workspace = true authors.workspace = true
+2 -2
View File
@@ -3,8 +3,8 @@ use std::time::Duration;
/// Parse a go formatted duration. /// Parse a go formatted duration.
/// ///
/// Sources: /// Sources:
/// - https://crates.io/crates/go-parse-duration (fixed an utf8 bug) /// - <https://crates.io/crates/go-parse-duration> (fixed an utf8 bug)
/// - https://github.com/golang/go/blob/master/src/time/format.go /// - <https://github.com/golang/go/blob/master/src/time/format.go>
pub fn parse_go_duration(value: &str) -> Result<Duration, String> { pub fn parse_go_duration(value: &str) -> Result<Duration, String> {
parse_duration(value).map(|ns| Duration::from_nanos(ns.unsigned_abs())) parse_duration(value).map(|ns| Duration::from_nanos(ns.unsigned_abs()))
} }
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "revive-explorer"
version = "0.1.0"
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.2.0"
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": 939, "Baseline": 932,
"Computation": 2282, "Computation": 2313,
"DivisionArithmetics": 8849, "DivisionArithmetics": 9103,
"ERC20": 18308, "ERC20": 17479,
"Events": 1640, "Events": 1692,
"FibonacciIterative": 1497, "FibonacciIterative": 1508,
"Flipper": 2099, "Flipper": 2098,
"SHA1": 8243 "SHA1": 8176
} }
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "AddModMulMod"
}
}
}
},
{
"Instantiate": {
"value": 123123,
"code": {
"Solidity": {
"contract": "AddModMulModTester"
}
}
}
},
{
"VerifyCall": {
"success": true
}
}
]
}
*/
contract AddModMulMod {
function test() public returns (uint256) {
// Note that this only works because computation on literals is done using
// unbounded integers.
if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 1;
if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 2;
return 0;
}
function f(uint256 d) public pure returns (uint256) {
addmod(1, 2, d);
return 2;
}
function g(uint256 d) public pure returns (uint256) {
mulmod(1, 2, d);
return 2;
}
function h() public pure returns (uint256) {
mulmod(0, 1, 2);
mulmod(1, 0, 2);
addmod(0, 1, 2);
addmod(1, 0, 2);
return 2;
}
}
contract AddModMulModTester {
constructor() payable {
AddModMulMod c = new AddModMulMod();
assert(c.test() == 0);
try c.f(0) returns (uint m) { revert(); } catch Panic(uint errorCode) {
assert(errorCode == 0x12);
}
try c.g(0) returns (uint m) { revert(); } catch Panic(uint errorCode) {
assert(errorCode == 0x12);
}
assert(c.h() == 2);
}
}
+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)
}
}
}
+21 -21
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 {
@@ -12,6 +12,26 @@ pub struct Contract {
pub calldata: Vec<u8>, pub calldata: Vec<u8>,
} }
impl Contract {
pub fn build(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
Self {
name,
evm_runtime: compile_evm_bin_runtime(name, code),
pvm_runtime: compile_blob(name, code),
calldata,
}
}
pub fn build_size_opt(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
Self {
name,
evm_runtime: compile_evm_bin_runtime(name, code),
pvm_runtime: compile_blob_with_options(name, code, true, OptimizerSettings::size()),
calldata,
}
}
}
macro_rules! case { macro_rules! case {
// Arguments: // Arguments:
// 1. The file name, expect to live under "../contracts/" // 1. The file name, expect to live under "../contracts/"
@@ -261,26 +281,6 @@ sol!(
case!("AddressPredictor.sol", Predicted, constructorCall, predicted_constructor, salt: U256); case!("AddressPredictor.sol", Predicted, constructorCall, predicted_constructor, salt: U256);
case!("AddressPredictor.sol", AddressPredictor, constructorCall, address_predictor_constructor, salt: U256, bytecode: Bytes); case!("AddressPredictor.sol", AddressPredictor, constructorCall, address_predictor_constructor, salt: U256, bytecode: Bytes);
impl Contract {
pub fn build(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
Self {
name,
evm_runtime: compile_evm_bin_runtime(name, code),
pvm_runtime: compile_blob(name, code),
calldata,
}
}
pub fn build_size_opt(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
Self {
name,
evm_runtime: compile_evm_bin_runtime(name, code),
pvm_runtime: compile_blob_with_options(name, code, true, OptimizerSettings::size()),
calldata,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
+2
View File
@@ -60,6 +60,8 @@ 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");
test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> { fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate { vec![Instantiate {
+5 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revive-linker" name = "revive-linker"
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
@@ -8,10 +8,10 @@ authors.workspace = true
description = "revive compiler linker utils" description = "revive compiler linker utils"
[dependencies] [dependencies]
tempfile = { workspace = true }
polkavm-linker = { workspace = true }
libc = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
libc = { workspace = true }
polkavm-linker = { workspace = true }
tempfile = { workspace = true }
revive-builtins = { workspace = true }
lld-sys = { workspace = true } lld-sys = { workspace = true }
revive-builtins = { workspace = true }
+114
View File
@@ -0,0 +1,114 @@
//! The revive ELF object linker library.
use std::{ffi::CString, fs, path::PathBuf, sync::Mutex};
use lld_sys::LLDELFLink;
use tempfile::TempDir;
use revive_builtins::COMPILER_RT;
static GUARD: Mutex<()> = Mutex::new(());
/// The revive ELF object linker.
pub struct ElfLinker {
temporary_directory: TempDir,
output_path: PathBuf,
object_path: PathBuf,
symbols_path: PathBuf,
linker_script_path: PathBuf,
}
impl ElfLinker {
const LINKER_SCRIPT: &str = r#"
SECTIONS {
.text : { KEEP(*(.text.polkavm_export)) *(.text .text.*) }
}"#;
const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv64.a";
const BUILTINS_LIB_NAME: &str = "clang_rt.builtins-riscv64";
/// The setup routine prepares a temporary working directory.
pub fn setup() -> anyhow::Result<Self> {
let temporary_directory = TempDir::new()?;
let object_path = temporary_directory.path().join("obj.o");
let output_path = temporary_directory.path().join("out.o");
let symbols_path = temporary_directory.path().join("sym.o");
let linker_script_path = temporary_directory.path().join("linker.ld");
fs::write(&linker_script_path, Self::LINKER_SCRIPT)
.map_err(|message| anyhow::anyhow!("{message} {linker_script_path:?}",))?;
let compiler_rt_path = temporary_directory.path().join(Self::BUILTINS_ARCHIVE_FILE);
fs::write(&compiler_rt_path, COMPILER_RT)
.map_err(|message| anyhow::anyhow!("{message} {compiler_rt_path:?}"))?;
Ok(Self {
temporary_directory,
output_path,
object_path,
symbols_path,
linker_script_path,
})
}
/// Link `input` with `symbols` and the `compiler_rt` via `LLD`.
pub fn link<T: AsRef<[u8]>>(self, input: T, symbols: T) -> anyhow::Result<Vec<u8>> {
fs::write(&self.object_path, input)
.map_err(|message| anyhow::anyhow!("{message} {:?}", self.object_path))?;
fs::write(&self.symbols_path, symbols)
.map_err(|message| anyhow::anyhow!("{message} {:?}", self.symbols_path))?;
if lld(self
.create_arguments()
.into_iter()
.map(|v| v.to_string())
.collect())
{
return Err(anyhow::anyhow!("ld.lld failed"));
}
Ok(fs::read(&self.output_path)?)
}
/// The argument creation helper function.
fn create_arguments(&self) -> Vec<String> {
[
"ld.lld",
"--error-limit=0",
"--relocatable",
"--emit-relocs",
"--no-relax",
"--unique",
"--gc-sections",
self.linker_script_path.to_str().expect("should be utf8"),
"-o",
self.output_path.to_str().expect("should be utf8"),
self.object_path.to_str().expect("should be utf8"),
self.symbols_path.to_str().expect("should be utf8"),
"--library-path",
self.temporary_directory
.path()
.to_str()
.expect("should be utf8"),
"--library",
Self::BUILTINS_LIB_NAME,
]
.iter()
.map(ToString::to_string)
.collect()
}
}
/// The thread-safe LLD helper function.
fn lld(arguments: Vec<String>) -> bool {
let c_strings = arguments
.into_iter()
.map(|arg| CString::new(arg).expect("ld.lld args should not contain null bytes"))
.collect::<Vec<_>>();
let args: Vec<*const libc::c_char> = c_strings.iter().map(|arg| arg.as_ptr()).collect();
let _lock = GUARD.lock().expect("ICE: linker mutex should not poison");
unsafe { LLDELFLink(args.as_ptr(), args.len()) == 0 }
}
+3 -75
View File
@@ -1,76 +1,4 @@
use std::{env, ffi::CString, fs}; //! The revive ELF object to PVM blob linker library.
use lld_sys::LLDELFLink; pub mod elf;
use revive_builtins::COMPILER_RT; pub mod pvm;
const LINKER_SCRIPT: &str = r#"
SECTIONS {
.text : { KEEP(*(.text.polkavm_export)) *(.text .text.*) }
}"#;
const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv64.a";
const BUILTINS_LIB_NAME: &str = "clang_rt.builtins-riscv64";
fn invoke_lld(cmd_args: &[&str]) -> bool {
let c_strings = cmd_args
.iter()
.map(|arg| CString::new(*arg).expect("ld.lld args should not contain null bytes"))
.collect::<Vec<_>>();
let args: Vec<*const libc::c_char> = c_strings.iter().map(|arg| arg.as_ptr()).collect();
unsafe { LLDELFLink(args.as_ptr(), args.len()) == 0 }
}
pub fn polkavm_linker<T: AsRef<[u8]>>(code: T, strip_binary: bool) -> anyhow::Result<Vec<u8>> {
let mut config = polkavm_linker::Config::default();
config.set_strip(strip_binary);
config.set_optimize(true);
polkavm_linker::program_from_elf(config, code.as_ref())
.map_err(|reason| anyhow::anyhow!("polkavm linker failed: {}", reason))
}
pub fn link<T: AsRef<[u8]>>(input: T) -> anyhow::Result<Vec<u8>> {
let dir = tempfile::tempdir().expect("failed to create temp directory for linking");
let output_path = dir.path().join("out.so");
let object_path = dir.path().join("out.o");
let linker_script_path = dir.path().join("linker.ld");
let compiler_rt_path = dir.path().join(BUILTINS_ARCHIVE_FILE);
fs::write(&object_path, input).map_err(|msg| anyhow::anyhow!("{msg} {object_path:?}"))?;
if env::var("PVM_LINKER_DUMP_OBJ").is_ok() {
fs::copy(&object_path, "/tmp/out.o")?;
}
fs::write(&linker_script_path, LINKER_SCRIPT)
.map_err(|msg| anyhow::anyhow!("{msg} {linker_script_path:?}"))?;
fs::write(&compiler_rt_path, COMPILER_RT)
.map_err(|msg| anyhow::anyhow!("{msg} {compiler_rt_path:?}"))?;
let ld_args = [
"ld.lld",
"--error-limit=0",
"--relocatable",
"--emit-relocs",
"--no-relax",
"--unique",
"--gc-sections",
"--library-path",
dir.path().to_str().expect("should be utf8"),
"--library",
BUILTINS_LIB_NAME,
linker_script_path.to_str().expect("should be utf8"),
object_path.to_str().expect("should be utf8"),
"-o",
output_path.to_str().expect("should be utf8"),
];
if invoke_lld(&ld_args) {
return Err(anyhow::anyhow!("ld.lld failed"));
}
Ok(fs::read(&output_path)?)
}
+10
View File
@@ -0,0 +1,10 @@
//! The revive PVM blob linker library.
pub fn polkavm_linker<T: AsRef<[u8]>>(code: T, strip_binary: bool) -> anyhow::Result<Vec<u8>> {
let mut config = polkavm_linker::Config::default();
config.set_strip(strip_binary);
config.set_optimize(true);
polkavm_linker::program_from_elf(config, code.as_ref())
.map_err(|reason| anyhow::anyhow!("polkavm linker failed: {}", reason))
}
+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.3.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 -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}`")),
} }
} }
} }
+2 -2
View File
@@ -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 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revive-llvm-context" name = "revive-llvm-context"
version.workspace = true version = "0.4.0"
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true
repository.workspace = true repository.workspace = true
@@ -20,7 +20,6 @@ itertools = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
num = { workspace = true } num = { workspace = true }
hex = { workspace = true } hex = { workspace = true }
sha3 = { workspace = true }
inkwell = { workspace = true } inkwell = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
polkavm-disassembler = { workspace = true } polkavm-disassembler = { workspace = true }
@@ -1,5 +1,9 @@
//! The debug IR type. //! The debug IR type.
use revive_common::{
EXTENSION_LLVM_SOURCE, EXTENSION_OBJECT, EXTENSION_POLKAVM_ASSEMBLY, EXTENSION_YUL,
};
/// The debug IR type. /// The debug IR type.
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -11,22 +15,17 @@ pub enum IRType {
/// Whether to dump the assembly code. /// Whether to dump the assembly code.
Assembly, Assembly,
/// Whether to dump the ELF shared object /// Whether to dump the ELF shared object
SO, Object,
/// Whether to jump JSON
#[cfg(debug_assertions)]
JSON,
} }
impl IRType { impl IRType {
/// Returns the file extension for the specified IR. /// Returns the file extension for the specified IR.
pub fn file_extension(&self) -> &'static str { pub fn file_extension(&self) -> &'static str {
match self { match self {
Self::Yul => revive_common::EXTENSION_YUL, Self::Yul => EXTENSION_YUL,
Self::LLVM => revive_common::EXTENSION_LLVM_SOURCE, Self::LLVM => EXTENSION_LLVM_SOURCE,
Self::Assembly => revive_common::EXTENSION_POLKAVM_ASSEMBLY, Self::Assembly => EXTENSION_POLKAVM_ASSEMBLY,
#[cfg(debug_assertions)] Self::Object => EXTENSION_OBJECT,
Self::JSON => revive_common::EXTENSION_JSON,
Self::SO => revive_common::EXTENSION_SHARED_OBJECT,
} }
} }
} }
+21 -25
View File
@@ -1,7 +1,5 @@
//! The debug configuration. //! The debug configuration.
pub mod ir_type;
use std::path::PathBuf; use std::path::PathBuf;
use serde::Deserialize; use serde::Deserialize;
@@ -9,6 +7,8 @@ use serde::Serialize;
use self::ir_type::IRType; use self::ir_type::IRType;
pub mod ir_type;
/// The debug configuration. /// The debug configuration.
#[derive(Debug, Default, Serialize, Deserialize, Clone)] #[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct DebugConfig { pub struct DebugConfig {
@@ -16,6 +16,8 @@ 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.
pub contract_path: Option<PathBuf>,
} }
impl DebugConfig { impl DebugConfig {
@@ -24,15 +26,27 @@ impl DebugConfig {
Self { Self {
output_directory, output_directory,
emit_debug_info, emit_debug_info,
contract_path: None,
} }
} }
/// Set the current contract path.
pub fn set_contract_path(&mut self, contract_path: &str) {
self.contract_path = self.yul_source_path(contract_path);
}
pub fn yul_source_path(&self, contract_path: &str) -> Option<PathBuf> {
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)?;
} }
@@ -93,7 +107,7 @@ impl DebugConfig {
pub fn dump_object(&self, contract_path: &str, code: &[u8]) -> anyhow::Result<()> { pub fn dump_object(&self, contract_path: &str, code: &[u8]) -> anyhow::Result<()> {
if let Some(output_directory) = self.output_directory.as_ref() { if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned(); let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::SO); let full_file_name = Self::full_file_name(contract_path, None, IRType::Object);
file_path.push(full_file_name); file_path.push(full_file_name);
std::fs::write(file_path, code)?; std::fs::write(file_path, code)?;
} }
@@ -101,24 +115,6 @@ impl DebugConfig {
Ok(()) Ok(())
} }
/// Dumps the stage output as a json file suitable for use with --recursive-process
#[cfg(debug_assertions)]
pub fn dump_stage_output(
&self,
contract_path: &str,
contract_suffix: Option<&str>,
stage_json: &Vec<u8>,
) -> anyhow::Result<()> {
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, contract_suffix, IRType::JSON);
file_path.push(full_file_name);
std::fs::write(file_path, stage_json)?;
}
Ok(())
}
/// Creates a full file name, given the contract full path, suffix, and extension. /// Creates a full file name, given the contract full path, suffix, and extension.
fn full_file_name(contract_path: &str, suffix: Option<&str>, ir_type: IRType) -> String { fn full_file_name(contract_path: &str, suffix: Option<&str>, ir_type: IRType) -> String {
let mut full_file_name = contract_path.replace('/', "_").replace(':', "."); let mut full_file_name = contract_path.replace('/', "_").replace(':', ".");
+10 -7
View File
@@ -1,5 +1,7 @@
//! The LLVM context library. //! The LLVM context library.
#![allow(clippy::too_many_arguments)]
use std::ffi::CString; use std::ffi::CString;
use std::sync::OnceLock; use std::sync::OnceLock;
@@ -8,7 +10,7 @@ pub use self::debug_config::DebugConfig;
pub use self::optimizer::settings::size_level::SizeLevel as OptimizerSettingsSizeLevel; pub use self::optimizer::settings::size_level::SizeLevel as OptimizerSettingsSizeLevel;
pub use self::optimizer::settings::Settings as OptimizerSettings; pub use self::optimizer::settings::Settings as OptimizerSettings;
pub use self::optimizer::Optimizer; pub use self::optimizer::Optimizer;
pub use self::polkavm::build_assembly_text as polkavm_build_assembly_text; pub use self::polkavm::build as polkavm_build;
pub use self::polkavm::context::address_space::AddressSpace as PolkaVMAddressSpace; pub use self::polkavm::context::address_space::AddressSpace as PolkaVMAddressSpace;
pub use self::polkavm::context::argument::Argument as PolkaVMArgument; pub use self::polkavm::context::argument::Argument as PolkaVMArgument;
pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute; pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute;
@@ -46,6 +48,7 @@ pub use self::polkavm::context::r#loop::Loop as PolkaVMLoop;
pub use self::polkavm::context::solidity_data::SolidityData as PolkaVMContextSolidityData; pub use self::polkavm::context::solidity_data::SolidityData as PolkaVMContextSolidityData;
pub use self::polkavm::context::yul_data::YulData as PolkaVMContextYulData; pub use self::polkavm::context::yul_data::YulData as PolkaVMContextYulData;
pub use self::polkavm::context::Context as PolkaVMContext; pub use self::polkavm::context::Context as PolkaVMContext;
pub use self::polkavm::disassemble as polkavm_disassemble;
pub use self::polkavm::evm::arithmetic as polkavm_evm_arithmetic; pub use self::polkavm::evm::arithmetic as polkavm_evm_arithmetic;
pub use self::polkavm::evm::bitwise as polkavm_evm_bitwise; pub use self::polkavm::evm::bitwise as polkavm_evm_bitwise;
pub use self::polkavm::evm::call as polkavm_evm_call; pub use self::polkavm::evm::call as polkavm_evm_call;
@@ -66,13 +69,13 @@ 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;
pub use self::polkavm::evm::return_data as polkavm_evm_return_data; pub use self::polkavm::evm::return_data as polkavm_evm_return_data;
pub use self::polkavm::evm::storage as polkavm_evm_storage; pub use self::polkavm::evm::storage as polkavm_evm_storage;
pub use self::polkavm::hash as polkavm_hash;
pub use self::polkavm::link as polkavm_link;
pub use self::polkavm::r#const as polkavm_const; pub use self::polkavm::r#const as polkavm_const;
pub use self::polkavm::Dependency as PolkaVMDependency;
pub use self::polkavm::DummyDependency as PolkaVMDummyDependency;
pub use self::polkavm::DummyLLVMWritable as PolkaVMDummyLLVMWritable; pub use self::polkavm::DummyLLVMWritable as PolkaVMDummyLLVMWritable;
pub use self::polkavm::WriteLLVM as PolkaVMWriteLLVM; pub use self::polkavm::WriteLLVM as PolkaVMWriteLLVM;
pub use self::target_machine::target::Target; pub use self::target_machine::target::Target as PolkaVMTarget;
pub use self::target_machine::TargetMachine; pub use self::target_machine::TargetMachine as PolkaVMTargetMachine;
pub(crate) mod debug_config; pub(crate) mod debug_config;
pub(crate) mod optimizer; pub(crate) mod optimizer;
@@ -86,7 +89,7 @@ static DID_INITIALIZE: OnceLock<()> = OnceLock::new();
/// This is a no-op if called subsequentially. /// This is a no-op if called subsequentially.
/// ///
/// `llvm_arguments` are passed as-is to the LLVM CL options parser. /// `llvm_arguments` are passed as-is to the LLVM CL options parser.
pub fn initialize_llvm(target: Target, name: &str, llvm_arguments: &[String]) { pub fn initialize_llvm(target: PolkaVMTarget, name: &str, llvm_arguments: &[String]) {
let Ok(_) = DID_INITIALIZE.set(()) else { let Ok(_) = DID_INITIALIZE.set(()) else {
return; // Tests don't go through a recursive process return; // Tests don't go through a recursive process
}; };
@@ -109,6 +112,6 @@ pub fn initialize_llvm(target: Target, name: &str, llvm_arguments: &[String]) {
inkwell::support::enable_llvm_pretty_stack_trace(); inkwell::support::enable_llvm_pretty_stack_trace();
match target { match target {
Target::PVM => inkwell::targets::Target::initialize_riscv(&Default::default()), PolkaVMTarget::PVM => inkwell::targets::Target::initialize_riscv(&Default::default()),
} }
} }
+2 -2
View File
@@ -1,7 +1,5 @@
//! The LLVM optimizing tools. //! The LLVM optimizing tools.
pub mod settings;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -9,6 +7,8 @@ use crate::target_machine::TargetMachine;
use self::settings::Settings; use self::settings::Settings;
pub mod settings;
/// The LLVM optimizing tools. /// The LLVM optimizing tools.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Optimizer { pub struct Optimizer {
@@ -1,8 +1,5 @@
//! The LLVM optimizer settings. //! The LLVM optimizer settings.
pub mod size_level;
use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -10,6 +7,8 @@ use itertools::Itertools;
use self::size_level::SizeLevel; use self::size_level::SizeLevel;
pub mod size_level;
/// The LLVM optimizer and code-gen settings. /// The LLVM optimizer and code-gen settings.
#[derive(Debug, Serialize, Deserialize, Clone, Eq)] #[derive(Debug, Serialize, Deserialize, Clone, Eq)]
pub struct Settings { pub struct Settings {
@@ -20,9 +19,6 @@ pub struct Settings {
/// The back-end optimization level. /// The back-end optimization level.
pub level_back_end: inkwell::OptimizationLevel, pub level_back_end: inkwell::OptimizationLevel,
/// Fallback to optimizing for size if the bytecode is too large.
pub is_fallback_to_size_enabled: bool,
/// Whether the LLVM `verify each` option is enabled. /// Whether the LLVM `verify each` option is enabled.
pub is_verify_each_enabled: bool, pub is_verify_each_enabled: bool,
/// Whether the LLVM `debug logging` option is enabled. /// Whether the LLVM `debug logging` option is enabled.
@@ -41,8 +37,6 @@ impl Settings {
level_middle_end_size, level_middle_end_size,
level_back_end, level_back_end,
is_fallback_to_size_enabled: false,
is_verify_each_enabled: false, is_verify_each_enabled: false,
is_debug_logging_enabled: false, is_debug_logging_enabled: false,
} }
@@ -62,8 +56,6 @@ impl Settings {
level_middle_end_size, level_middle_end_size,
level_back_end, level_back_end,
is_fallback_to_size_enabled: false,
is_verify_each_enabled, is_verify_each_enabled,
is_debug_logging_enabled, is_debug_logging_enabled,
} }
@@ -197,16 +189,6 @@ impl Settings {
combinations combinations
} }
/// Sets the fallback to optimizing for size if the bytecode is too large.
pub fn enable_fallback_to_size(&mut self) {
self.is_fallback_to_size_enabled = true;
}
/// Whether the fallback to optimizing for size is enabled.
pub fn is_fallback_to_size_enabled(&self) -> bool {
self.is_fallback_to_size_enabled
}
} }
impl PartialEq for Settings { impl PartialEq for Settings {
@@ -227,18 +209,3 @@ impl std::fmt::Display for Settings {
) )
} }
} }
impl TryFrom<&SolcStandardJsonInputSettingsOptimizer> for Settings {
type Error = anyhow::Error;
fn try_from(value: &SolcStandardJsonInputSettingsOptimizer) -> Result<Self, Self::Error> {
let mut result = match value.mode {
Some(mode) => Self::try_from_cli(mode)?,
None => Self::size(),
};
if value.fallback_to_optimizing_for_size.unwrap_or_default() {
result.enable_fallback_to_size();
}
Ok(result)
}
}
+4 -2
View File
@@ -1,10 +1,12 @@
//! The LLVM context constants. //! The LLVM context constants.
use revive_common::{BIT_LENGTH_X32, BYTE_LENGTH_WORD};
/// The LLVM framework version. /// The LLVM framework version.
pub const LLVM_VERSION: semver::Version = semver::Version::new(18, 1, 4); pub const LLVM_VERSION: semver::Version = semver::Version::new(18, 1, 4);
/// The pointer width sized type. /// The pointer width sized type.
pub static XLEN: usize = revive_common::BIT_LENGTH_X32; pub static XLEN: usize = 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";
@@ -20,4 +22,4 @@ 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 = BYTE_LENGTH_WORD;
@@ -66,9 +66,9 @@ impl<'ctx> Argument<'ctx> {
/// Access the underlying value. /// Access the underlying value.
/// ///
/// Will emit a stack load if `self` is a pointer argument. /// Will emit a stack load if `self` is a pointer argument.
pub fn access<D: crate::polkavm::Dependency + Clone>( pub fn access(
&self, &self,
context: &crate::polkavm::context::Context<'ctx, D>, context: &crate::polkavm::context::Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> { ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
match &self.value { match &self.value {
Value::Register(value) => Ok(*value), Value::Register(value) => Ok(*value),
@@ -79,9 +79,9 @@ impl<'ctx> Argument<'ctx> {
/// Access the underlying value. /// Access the underlying value.
/// ///
/// Will emit a stack load if `self` is a pointer argument. /// Will emit a stack load if `self` is a pointer argument.
pub fn as_pointer<D: crate::polkavm::Dependency + Clone>( pub fn as_pointer(
&self, &self,
context: &crate::polkavm::context::Context<'ctx, D>, context: &crate::polkavm::context::Context<'ctx>,
) -> anyhow::Result<crate::polkavm::context::Pointer<'ctx>> { ) -> anyhow::Result<crate::polkavm::context::Pointer<'ctx>> {
match &self.value { match &self.value {
Value::Register(value) => { Value::Register(value) => {
@@ -1,7 +1,6 @@
//! The LLVM module build. //! The LLVM module build.
use std::collections::BTreeMap; use revive_common::BYTE_LENGTH_WORD;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -9,31 +8,23 @@ use serde::Serialize;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Build { pub struct Build {
/// The PolkaVM text assembly. /// The PolkaVM text assembly.
pub assembly_text: String, pub assembly_text: Option<String>,
/// The metadata hash. /// The metadata hash.
pub metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]>, pub metadata_hash: Option<[u8; BYTE_LENGTH_WORD]>,
/// The PolkaVM binary bytecode. /// The PolkaVM binary bytecode.
pub bytecode: Vec<u8>, pub bytecode: Vec<u8>,
/// The PolkaVM bytecode hash. /// The PolkaVM bytecode hash. Unlinked builds don't have a hash yet.
pub bytecode_hash: String, pub bytecode_hash: Option<[u8; BYTE_LENGTH_WORD]>,
/// The hash-to-full-path mapping of the contract factory dependencies.
pub factory_dependencies: BTreeMap<String, String>,
} }
impl Build { impl Build {
/// A shortcut constructor. /// A shortcut constructor.
pub fn new( pub fn new(metadata_hash: Option<[u8; BYTE_LENGTH_WORD]>, bytecode: Vec<u8>) -> Self {
assembly_text: String,
metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]>,
bytecode: Vec<u8>,
bytecode_hash: String,
) -> Self {
Self { Self {
assembly_text, assembly_text: None,
metadata_hash, metadata_hash,
bytecode, bytecode,
bytecode_hash, bytecode_hash: None,
factory_dependencies: BTreeMap::new(),
} }
} }
} }
@@ -2,6 +2,8 @@
use std::cell::RefCell; use std::cell::RefCell;
use revive_common::BIT_LENGTH_WORD;
use inkwell::debug_info::AsDIScope; use inkwell::debug_info::AsDIScope;
use inkwell::debug_info::DIScope; use inkwell::debug_info::DIScope;
@@ -51,11 +53,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,
@@ -155,7 +166,7 @@ impl<'ctx> DebugInfo<'ctx> {
&self, &self,
flags: Option<inkwell::debug_info::DIFlags>, flags: Option<inkwell::debug_info::DIFlags>,
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> { ) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
self.create_primitive_type(revive_common::BIT_LENGTH_WORD, flags) self.create_primitive_type(BIT_LENGTH_WORD, flags)
} }
/// Return the DIBuilder. /// Return the DIBuilder.
@@ -64,24 +64,13 @@ impl<'ctx> LLVMRuntime<'ctx> {
} }
} }
/// Declares an LLVM runtime function in the `module`,
pub fn declare(
module: &inkwell::module::Module<'ctx>,
name: &str,
r#type: inkwell::types::FunctionType<'ctx>,
linkage: Option<inkwell::module::Linkage>,
) -> FunctionDeclaration<'ctx> {
let value = module.add_function(name, r#type, linkage);
FunctionDeclaration::new(r#type, value)
}
/// Create the function definition from an existing symbol. /// Create the function definition from an existing symbol.
pub fn define( pub fn define(
module: &inkwell::module::Module<'ctx>, module: &inkwell::module::Module<'ctx>,
name: &str, name: &str,
) -> Option<FunctionDeclaration<'ctx>> { ) -> Option<FunctionDeclaration<'ctx>> {
let value = module.get_function(name)?; let value = module.get_function(name)?;
value.set_linkage(inkwell::module::Linkage::External); value.set_linkage(inkwell::module::Linkage::Private);
FunctionDeclaration::new(value.get_type(), value).into() FunctionDeclaration::new(value.get_type(), value).into()
} }
} }
@@ -1,12 +1,5 @@
//! The LLVM IR generator function. //! The LLVM IR generator function.
pub mod declaration;
pub mod intrinsics;
pub mod llvm_runtime;
pub mod r#return;
pub mod runtime;
pub mod yul_data;
use std::collections::HashMap; use std::collections::HashMap;
use inkwell::debug_info::AsDIScope; use inkwell::debug_info::AsDIScope;
@@ -20,6 +13,13 @@ use self::declaration::Declaration;
use self::r#return::Return; use self::r#return::Return;
use self::yul_data::YulData; use self::yul_data::YulData;
pub mod declaration;
pub mod intrinsics;
pub mod llvm_runtime;
pub mod r#return;
pub mod runtime;
pub mod yul_data;
/// The LLVM IR generator function. /// The LLVM IR generator function.
#[derive(Debug)] #[derive(Debug)]
pub struct Function<'ctx> { pub struct Function<'ctx> {
@@ -75,15 +75,6 @@ impl<'ctx> Function<'ctx> {
self.name.as_str() self.name.as_str()
} }
/// Checks whether the function is defined outside of the front-end.
pub fn is_name_external(name: &str) -> bool {
name.starts_with("llvm.")
|| (name.starts_with("__")
&& name != self::runtime::FUNCTION_ENTRY
&& name != self::runtime::FUNCTION_DEPLOY_CODE
&& name != self::runtime::FUNCTION_RUNTIME_CODE)
}
/// Returns the LLVM function declaration. /// Returns the LLVM function declaration.
pub fn declaration(&self) -> Declaration<'ctx> { pub fn declaration(&self) -> Declaration<'ctx> {
self.declaration self.declaration
@@ -223,30 +214,11 @@ impl<'ctx> Function<'ctx> {
self.stack.get(name).copied() self.stack.get(name).copied()
} }
/// Removes the pointer to a stack variable.
pub fn remove_stack_pointer(&mut self, name: &str) {
self.stack.remove(name);
}
/// Returns the return entity representation. /// Returns the return entity representation.
pub fn r#return(&self) -> Return<'ctx> { pub fn r#return(&self) -> Return<'ctx> {
self.r#return self.r#return
} }
/// Returns the pointer to the function return value.
/// # Panics
/// If the pointer has not been set yet.
pub fn return_pointer(&self) -> Option<Pointer<'ctx>> {
self.r#return.return_pointer()
}
/// Returns the return data size in bytes, based on the default stack alignment.
/// # Panics
/// If the pointer has not been set yet.
pub fn return_data_size(&self) -> usize {
self.r#return.return_data_size()
}
/// Returns the function entry block. /// Returns the function entry block.
pub fn entry_block(&self) -> inkwell::basic_block::BasicBlock<'ctx> { pub fn entry_block(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.entry_block self.entry_block
@@ -1,22 +1,19 @@
//! Translates the arithmetic operations. //! Translates the arithmetic operations.
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use revive_common::BIT_LENGTH_WORD;
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::WriteLLVM; use crate::polkavm::WriteLLVM;
/// Implements the division operator according to the EVM specification. /// Implements the division operator according to the EVM specification.
pub struct Division; pub struct Division;
impl<D> RuntimeFunction<D> for Division impl RuntimeFunction for Division {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_division"; const NAME: &'static str = "__revive_division";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.word_type().fn_type( context.word_type().fn_type(
&[context.word_type().into(), context.word_type().into()], &[context.word_type().into(), context.word_type().into()],
false, false,
@@ -25,7 +22,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let operand_1 = Self::paramater(context, 0).into_int_value(); let operand_1 = Self::paramater(context, 0).into_int_value();
let operand_2 = Self::paramater(context, 1).into_int_value(); let operand_2 = Self::paramater(context, 1).into_int_value();
@@ -39,29 +36,23 @@ where
} }
} }
impl<D> WriteLLVM<D> for Division impl WriteLLVM for Division {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Implements the signed division operator according to the EVM specification. /// Implements the signed division operator according to the EVM specification.
pub struct SignedDivision; pub struct SignedDivision;
impl<D> RuntimeFunction<D> for SignedDivision impl RuntimeFunction for SignedDivision {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_signed_division"; const NAME: &'static str = "__revive_signed_division";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.word_type().fn_type( context.word_type().fn_type(
&[context.word_type().into(), context.word_type().into()], &[context.word_type().into(), context.word_type().into()],
false, false,
@@ -70,7 +61,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let operand_1 = Self::paramater(context, 0).into_int_value(); let operand_1 = Self::paramater(context, 0).into_int_value();
let operand_2 = Self::paramater(context, 1).into_int_value(); let operand_2 = Self::paramater(context, 1).into_int_value();
@@ -96,9 +87,7 @@ where
context.set_basic_block(block_overflow); context.set_basic_block(block_overflow);
let max_uint = context.builder().build_int_z_extend( let max_uint = context.builder().build_int_z_extend(
context context.integer_type(BIT_LENGTH_WORD - 1).const_all_ones(),
.integer_type(revive_common::BIT_LENGTH_WORD - 1)
.const_all_ones(),
context.word_type(), context.word_type(),
"max_uint", "max_uint",
)?; )?;
@@ -121,29 +110,23 @@ where
} }
} }
impl<D> WriteLLVM<D> for SignedDivision impl WriteLLVM for SignedDivision {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Implements the remainder operator according to the EVM specification. /// Implements the remainder operator according to the EVM specification.
pub struct Remainder; pub struct Remainder;
impl<D> RuntimeFunction<D> for Remainder impl RuntimeFunction for Remainder {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_remainder"; const NAME: &'static str = "__revive_remainder";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.word_type().fn_type( context.word_type().fn_type(
&[context.word_type().into(), context.word_type().into()], &[context.word_type().into(), context.word_type().into()],
false, false,
@@ -152,7 +135,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let operand_1 = Self::paramater(context, 0).into_int_value(); let operand_1 = Self::paramater(context, 0).into_int_value();
let operand_2 = Self::paramater(context, 1).into_int_value(); let operand_2 = Self::paramater(context, 1).into_int_value();
@@ -166,29 +149,23 @@ where
} }
} }
impl<D> WriteLLVM<D> for Remainder impl WriteLLVM for Remainder {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Implements the signed remainder operator according to the EVM specification. /// Implements the signed remainder operator according to the EVM specification.
pub struct SignedRemainder; pub struct SignedRemainder;
impl<D> RuntimeFunction<D> for SignedRemainder impl RuntimeFunction for SignedRemainder {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_signed_remainder"; const NAME: &'static str = "__revive_signed_remainder";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.word_type().fn_type( context.word_type().fn_type(
&[context.word_type().into(), context.word_type().into()], &[context.word_type().into(), context.word_type().into()],
false, false,
@@ -197,7 +174,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let operand_1 = Self::paramater(context, 0).into_int_value(); let operand_1 = Self::paramater(context, 0).into_int_value();
let operand_2 = Self::paramater(context, 1).into_int_value(); let operand_2 = Self::paramater(context, 1).into_int_value();
@@ -211,16 +188,13 @@ where
} }
} }
impl<D> WriteLLVM<D> for SignedRemainder impl WriteLLVM for SignedRemainder {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
@@ -231,13 +205,12 @@ where
/// ///
/// The result is either the calculated quotient or zero, /// The result is either the calculated quotient or zero,
/// selected at runtime. /// selected at runtime.
fn wrapped_division<'ctx, D, F, T>( fn wrapped_division<'ctx, F, T>(
context: &Context<'ctx, D>, context: &Context<'ctx>,
denominator: inkwell::values::IntValue<'ctx>, denominator: inkwell::values::IntValue<'ctx>,
f: F, f: F,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where where
D: Dependency + Clone,
F: FnOnce() -> anyhow::Result<T>, F: FnOnce() -> anyhow::Result<T>,
T: inkwell::values::IntMathValue<'ctx>, T: inkwell::values::IntMathValue<'ctx>,
{ {
@@ -1,63 +1,52 @@
//! The deploy code function. //! The deploy code function.
use std::marker::PhantomData;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime; use crate::polkavm::context::function::runtime;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM; use crate::polkavm::WriteLLVM;
/// The deploy code function. /// The deploy code function.
/// Is a special function that is only used by the front-end generated code. /// Is a special function that is only used by the front-end generated code.
#[derive(Debug)] #[derive(Debug)]
pub struct DeployCode<B, D> pub struct DeployCode<B>
where where
B: WriteLLVM<D>, B: WriteLLVM,
D: Dependency + Clone,
{ {
/// The deploy code AST representation. /// The deploy code AST representation.
inner: B, inner: B,
/// The `D` phantom data.
_pd: PhantomData<D>,
} }
impl<B, D> DeployCode<B, D> impl<B> DeployCode<B>
where where
B: WriteLLVM<D>, B: WriteLLVM,
D: Dependency + Clone,
{ {
/// A shortcut constructor. /// A shortcut constructor.
pub fn new(inner: B) -> Self { pub fn new(inner: B) -> Self {
Self { Self { inner }
inner,
_pd: PhantomData,
}
} }
} }
impl<B, D> WriteLLVM<D> for DeployCode<B, D> impl<B> WriteLLVM for DeployCode<B>
where where
B: WriteLLVM<D>, B: WriteLLVM,
D: Dependency + Clone,
{ {
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> { fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
let function_type = context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0); let function_type = context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0);
context.add_function( context.add_function(
runtime::FUNCTION_DEPLOY_CODE, runtime::FUNCTION_DEPLOY_CODE,
function_type, function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::Private),
None,
false,
)?; )?;
self.inner.declare(context) self.inner.declare(context)
} }
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
context.set_current_function(runtime::FUNCTION_DEPLOY_CODE, None)?; context.set_current_function(runtime::FUNCTION_DEPLOY_CODE, None, false)?;
context.set_basic_block(context.current_function().borrow().entry_block()); context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Deploy);
self.inner.into_llvm(context)?; self.inner.into_llvm(context)?;
context.set_debug_location(0, 0, None)?; context.set_debug_location(0, 0, None)?;
@@ -74,7 +63,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,11 +1,16 @@
//! The entry function. //! The entry function.
use inkwell::types::BasicType; use inkwell::types::BasicType;
use revive_common::BIT_LENGTH_ETH_ADDRESS;
use revive_runtime_api::immutable_data::{
GLOBAL_IMMUTABLE_DATA_POINTER, GLOBAL_IMMUTABLE_DATA_SIZE,
};
use revive_runtime_api::polkavm_imports::CALL_DATA_SIZE;
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;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM; use crate::polkavm::WriteLLVM;
/// The entry function. /// The entry function.
@@ -20,10 +25,7 @@ impl Entry {
/// Initializes the global variables. /// Initializes the global variables.
/// The pointers are not initialized, because it's not possible to create a null pointer. /// The pointers are not initialized, because it's not possible to create a null pointer.
pub fn initialize_globals<D>(context: &mut Context<D>) -> anyhow::Result<()> pub fn initialize_globals(context: &mut Context) -> anyhow::Result<()> {
where
D: Dependency + Clone,
{
context.set_global( context.set_global(
crate::polkavm::GLOBAL_CALLDATA_SIZE, crate::polkavm::GLOBAL_CALLDATA_SIZE,
context.xlen_type(), context.xlen_type(),
@@ -38,9 +40,12 @@ impl Entry {
context.xlen_type().const_zero(), context.xlen_type().const_zero(),
); );
let heap_memory_type = context let heap_memory_type = context.byte_type().array_type(
.byte_type() context
.array_type(context.memory_config.heap_size); .memory_config
.heap_size
.unwrap_or(PolkaVMDefaultHeapMemorySize),
);
context.set_global( context.set_global(
crate::polkavm::GLOBAL_HEAP_MEMORY, crate::polkavm::GLOBAL_HEAP_MEMORY,
heap_memory_type, heap_memory_type,
@@ -48,7 +53,7 @@ impl Entry {
heap_memory_type.const_zero(), heap_memory_type.const_zero(),
); );
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS); let address_type = context.integer_type(BIT_LENGTH_ETH_ADDRESS);
context.set_global( context.set_global(
crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER, crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER,
address_type, address_type,
@@ -60,16 +65,13 @@ impl Entry {
} }
/// Populate the calldata size global value. /// Populate the calldata size global value.
pub fn load_calldata_size<D>(context: &mut Context<D>) -> anyhow::Result<()> pub fn load_calldata_size(context: &mut Context) -> anyhow::Result<()> {
where
D: Dependency + Clone,
{
let call_data_size_pointer = context let call_data_size_pointer = context
.get_global(crate::polkavm::GLOBAL_CALLDATA_SIZE)? .get_global(crate::polkavm::GLOBAL_CALLDATA_SIZE)?
.value .value
.as_pointer_value(); .as_pointer_value();
let call_data_size_value = context let call_data_size_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::CALL_DATA_SIZE, &[]) .build_runtime_call(CALL_DATA_SIZE, &[])
.expect("the call_data_size syscall method should return a value") .expect("the call_data_size syscall method should return a value")
.into_int_value(); .into_int_value();
let call_data_size_value = context.builder().build_int_truncate( let call_data_size_value = context.builder().build_int_truncate(
@@ -86,10 +88,7 @@ impl Entry {
/// Calls the deploy code if the first function argument was `1`. /// Calls the deploy code if the first function argument was `1`.
/// Calls the runtime code otherwise. /// Calls the runtime code otherwise.
pub fn leave_entry<D>(context: &mut Context<D>) -> anyhow::Result<()> pub fn leave_entry(context: &mut Context) -> anyhow::Result<()> {
where
D: Dependency + Clone,
{
context.set_debug_location(0, 0, None)?; context.set_debug_location(0, 0, None)?;
let is_deploy = context let is_deploy = context
@@ -129,11 +128,8 @@ impl Entry {
} }
} }
impl<D> WriteLLVM<D> for Entry impl WriteLLVM for Entry {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let entry_arguments = vec![context.bool_type().as_basic_type_enum()]; let entry_arguments = vec![context.bool_type().as_basic_type_enum()];
let entry_function_type = context.function_type(entry_arguments, 0); let entry_function_type = context.function_type(entry_arguments, 0);
context.add_function( context.add_function(
@@ -141,16 +137,18 @@ where
entry_function_type, entry_function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None,
false,
)?; )?;
context.declare_global( context.declare_global(
revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER, GLOBAL_IMMUTABLE_DATA_POINTER,
context.word_type().array_type(0), context.word_type().array_type(0),
AddressSpace::Stack, AddressSpace::Stack,
); );
context.declare_global( context.declare_global(
revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_SIZE, GLOBAL_IMMUTABLE_DATA_SIZE,
context.xlen_type(), context.xlen_type(),
AddressSpace::Stack, AddressSpace::Stack,
); );
@@ -161,9 +159,9 @@ where
/// Instead of a single entrypoint, the runtime expects two exports: `call ` and `deploy`. /// Instead of a single entrypoint, the runtime expects two exports: `call ` and `deploy`.
/// `call` and `deploy` directly call `entry`, signaling a deploy if the first arg is `1`. /// `call` and `deploy` directly call `entry`, signaling a deploy if the first arg is `1`.
/// The `entry` function loads calldata, sets globals and calls the runtime or deploy code. /// The `entry` function loads calldata, sets globals and calls the runtime or deploy code.
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
let entry = context let entry = context
.get_function(runtime::FUNCTION_ENTRY) .get_function(runtime::FUNCTION_ENTRY, false)
.expect("the entry function should already be declared") .expect("the entry function should already be declared")
.borrow() .borrow()
.declaration; .declaration;
@@ -174,7 +172,7 @@ where
true, true,
); );
context.set_current_function(runtime::FUNCTION_ENTRY, None)?; context.set_current_function(runtime::FUNCTION_ENTRY, None, false)?;
context.set_basic_block(context.current_function().borrow().entry_block()); context.set_basic_block(context.current_function().borrow().entry_block());
Self::initialize_globals(context)?; Self::initialize_globals(context)?;
@@ -5,7 +5,6 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::function::Attribute; use crate::polkavm::context::function::Attribute;
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::WriteLLVM; use crate::polkavm::WriteLLVM;
/// Pointers are represented as opaque 256 bit integer values in EVM. /// Pointers are represented as opaque 256 bit integer values in EVM.
@@ -15,10 +14,7 @@ use crate::polkavm::WriteLLVM;
/// (but wrong) pointers when truncated. /// (but wrong) pointers when truncated.
pub struct WordToPointer; pub struct WordToPointer;
impl<D> RuntimeFunction<D> for WordToPointer impl RuntimeFunction for WordToPointer {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_int_truncate"; const NAME: &'static str = "__revive_int_truncate";
const ATTRIBUTES: &'static [Attribute] = &[ const ATTRIBUTES: &'static [Attribute] = &[
@@ -27,7 +23,7 @@ where
Attribute::AlwaysInline, Attribute::AlwaysInline,
]; ];
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context context
.xlen_type() .xlen_type()
.fn_type(&[context.word_type().into()], false) .fn_type(&[context.word_type().into()], false)
@@ -35,7 +31,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let value = Self::paramater(context, 0).into_int_value(); let value = Self::paramater(context, 0).into_int_value();
let truncated = let truncated =
@@ -67,26 +63,20 @@ where
} }
} }
impl<D> WriteLLVM<D> for WordToPointer impl WriteLLVM for WordToPointer {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// The revive runtime exit function. /// The revive runtime exit function.
pub struct Exit; pub struct Exit;
impl<D> RuntimeFunction<D> for Exit impl RuntimeFunction for Exit {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_exit"; const NAME: &'static str = "__revive_exit";
const ATTRIBUTES: &'static [Attribute] = &[ const ATTRIBUTES: &'static [Attribute] = &[
@@ -95,7 +85,7 @@ where
Attribute::AlwaysInline, Attribute::AlwaysInline,
]; ];
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type( context.void_type().fn_type(
&[ &[
context.xlen_type().into(), context.xlen_type().into(),
@@ -108,7 +98,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let flags = Self::paramater(context, 0).into_int_value(); let flags = Self::paramater(context, 0).into_int_value();
let offset = Self::paramater(context, 1).into_int_value(); let offset = Self::paramater(context, 1).into_int_value();
@@ -133,15 +123,12 @@ where
} }
} }
impl<D> WriteLLVM<D> for Exit impl WriteLLVM for Exit {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
@@ -1,63 +1,52 @@
//! The runtime code function. //! The runtime code function.
use std::marker::PhantomData;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime; use crate::polkavm::context::function::runtime;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM; use crate::polkavm::WriteLLVM;
/// The runtime code function. /// The runtime code function.
/// Is a special function that is only used by the front-end generated code. /// Is a special function that is only used by the front-end generated code.
#[derive(Debug)] #[derive(Debug)]
pub struct RuntimeCode<B, D> pub struct RuntimeCode<B>
where where
B: WriteLLVM<D>, B: WriteLLVM,
D: Dependency + Clone,
{ {
/// The runtime code AST representation. /// The runtime code AST representation.
inner: B, inner: B,
/// The `D` phantom data.
_pd: PhantomData<D>,
} }
impl<B, D> RuntimeCode<B, D> impl<B> RuntimeCode<B>
where where
B: WriteLLVM<D>, B: WriteLLVM,
D: Dependency + Clone,
{ {
/// A shortcut constructor. /// A shortcut constructor.
pub fn new(inner: B) -> Self { pub fn new(inner: B) -> Self {
Self { Self { inner }
inner,
_pd: PhantomData,
}
} }
} }
impl<B, D> WriteLLVM<D> for RuntimeCode<B, D> impl<B> WriteLLVM for RuntimeCode<B>
where where
B: WriteLLVM<D>, B: WriteLLVM,
D: Dependency + Clone,
{ {
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> { fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
let function_type = context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0); let function_type = context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0);
context.add_function( context.add_function(
runtime::FUNCTION_RUNTIME_CODE, runtime::FUNCTION_RUNTIME_CODE,
function_type, function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::Private),
None,
false,
)?; )?;
self.inner.declare(context) self.inner.declare(context)
} }
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
context.set_current_function(runtime::FUNCTION_RUNTIME_CODE, None)?; context.set_current_function(runtime::FUNCTION_RUNTIME_CODE, None, false)?;
context.set_basic_block(context.current_function().borrow().entry_block()); context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Runtime);
self.inner.into_llvm(context)?; self.inner.into_llvm(context)?;
context.set_debug_location(0, 0, None)?; context.set_debug_location(0, 0, None)?;
@@ -1,11 +1,11 @@
//! Emulates the linear EVM heap memory via a simulated `sbrk` system call. //! Emulates the linear EVM heap memory via a simulated `sbrk` system call.
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use revive_common::BYTE_LENGTH_WORD;
use crate::polkavm::context::attribute::Attribute; use crate::polkavm::context::attribute::Attribute;
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::WriteLLVM; use crate::polkavm::WriteLLVM;
/// Simulates the `sbrk` system call, reproducing the semantics of the EVM heap memory. /// Simulates the `sbrk` system call, reproducing the semantics of the EVM heap memory.
@@ -24,10 +24,7 @@ use crate::polkavm::WriteLLVM;
/// - Maintains the total memory size (`msize`) in global heap size value. /// - Maintains the total memory size (`msize`) in global heap size value.
pub struct Sbrk; pub struct Sbrk;
impl<D> RuntimeFunction<D> for Sbrk impl RuntimeFunction for Sbrk {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__sbrk_internal"; const NAME: &'static str = "__sbrk_internal";
const ATTRIBUTES: &'static [Attribute] = &[ const ATTRIBUTES: &'static [Attribute] = &[
@@ -36,7 +33,7 @@ where
Attribute::WillReturn, Attribute::WillReturn,
]; ];
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.llvm().ptr_type(Default::default()).fn_type( context.llvm().ptr_type(Default::default()).fn_type(
&[context.xlen_type().into(), context.xlen_type().into()], &[context.xlen_type().into(), context.xlen_type().into()],
false, false,
@@ -45,7 +42,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let offset = Self::paramater(context, 0).into_int_value(); let offset = Self::paramater(context, 0).into_int_value();
let size = Self::paramater(context, 1).into_int_value(); let size = Self::paramater(context, 1).into_int_value();
@@ -71,7 +68,7 @@ where
context.set_basic_block(offset_in_bounds_block); context.set_basic_block(offset_in_bounds_block);
let mask = context let mask = context
.xlen_type() .xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64 - 1, false); .const_int(BYTE_LENGTH_WORD as u64 - 1, false);
let total_size = context let total_size = context
.builder() .builder()
.build_int_add(offset, size, "total_size")?; .build_int_add(offset, size, "total_size")?;
@@ -130,15 +127,12 @@ where
} }
} }
impl<D> WriteLLVM<D> for Sbrk impl WriteLLVM for Sbrk {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
@@ -5,7 +5,6 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::PolkaVMDependency;
/// The LLVM global value. /// The LLVM global value.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@@ -18,15 +17,14 @@ pub struct Global<'ctx> {
impl<'ctx> Global<'ctx> { impl<'ctx> Global<'ctx> {
/// A shortcut constructor. /// A shortcut constructor.
pub fn new<D, T, V>( pub fn new<T, V>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
r#type: T, r#type: T,
address_space: AddressSpace, address_space: AddressSpace,
initializer: V, initializer: V,
name: &str, name: &str,
) -> Self ) -> Self
where where
D: PolkaVMDependency + Clone,
T: BasicType<'ctx>, T: BasicType<'ctx>,
V: BasicValue<'ctx>, V: BasicValue<'ctx>,
{ {
@@ -53,14 +51,13 @@ impl<'ctx> Global<'ctx> {
} }
/// Construct an external global. /// Construct an external global.
pub fn declare<D, T>( pub fn declare<T>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
r#type: T, r#type: T,
address_space: AddressSpace, address_space: AddressSpace,
name: &str, name: &str,
) -> Self ) -> Self
where where
D: PolkaVMDependency + Clone,
T: BasicType<'ctx>, T: BasicType<'ctx>,
{ {
let r#type = r#type.as_basic_type_enum(); let r#type = r#type.as_basic_type_enum();
+116 -168
View File
@@ -1,22 +1,5 @@
//! The LLVM IR generator context. //! The LLVM IR generator context.
pub mod address_space;
pub mod argument;
pub mod attribute;
pub mod build;
pub mod code_type;
pub mod debug_info;
pub mod function;
pub mod global;
pub mod r#loop;
pub mod pointer;
pub mod runtime;
pub mod solidity_data;
pub mod yul_data;
#[cfg(test)]
mod tests;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@@ -25,12 +8,13 @@ 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 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;
use crate::polkavm::DebugConfig; use crate::polkavm::DebugConfig;
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;
@@ -56,13 +40,27 @@ use self::runtime::RuntimeFunction;
use self::solidity_data::SolidityData; use self::solidity_data::SolidityData;
use self::yul_data::YulData; use self::yul_data::YulData;
pub mod address_space;
pub mod argument;
pub mod attribute;
pub mod build;
pub mod code_type;
pub mod debug_info;
pub mod function;
pub mod global;
pub mod r#loop;
pub mod pointer;
pub mod runtime;
pub mod solidity_data;
pub mod yul_data;
#[cfg(test)]
mod tests;
/// The LLVM IR generator context. /// The LLVM IR generator context.
/// It is a not-so-big god-like object glueing all the compilers' complexity and act as an adapter /// It is a not-so-big god-like object glueing all the compilers' complexity and act as an adapter
/// and a superstructure over the inner `inkwell` LLVM context. /// and a superstructure over the inner `inkwell` LLVM context.
pub struct Context<'ctx, D> pub struct Context<'ctx> {
where
D: Dependency + Clone,
{
/// The inner LLVM context. /// The inner LLVM context.
llvm: &'ctx inkwell::context::Context, llvm: &'ctx inkwell::context::Context,
/// The inner LLVM context builder. /// The inner LLVM context builder.
@@ -85,17 +83,9 @@ where
current_function: Option<Rc<RefCell<Function<'ctx>>>>, current_function: Option<Rc<RefCell<Function<'ctx>>>>,
/// The loop context stack. /// The loop context stack.
loop_stack: Vec<Loop<'ctx>>, loop_stack: Vec<Loop<'ctx>>,
/// The extra LLVM arguments that were used during target initialization.
llvm_arguments: &'ctx [String],
/// The PVM memory configuration. /// The PVM memory configuration.
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
/// 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 multi-threaded compilation process.
dependency_manager: Option<D>,
/// Whether to append the metadata hash at the end of bytecode.
include_metadata_hash: bool,
/// The debug info of the current module. /// The debug info of the current module.
debug_info: Option<DebugInfo<'ctx>>, debug_info: Option<DebugInfo<'ctx>>,
/// The debug configuration telling whether to dump the needed IRs. /// The debug configuration telling whether to dump the needed IRs.
@@ -107,10 +97,7 @@ where
yul_data: Option<YulData>, yul_data: Option<YulData>,
} }
impl<'ctx, D> Context<'ctx, D> impl<'ctx> Context<'ctx> {
where
D: Dependency + Clone,
{
/// The functions hashmap default capacity. /// The functions hashmap default capacity.
const FUNCTIONS_HASHMAP_INITIAL_CAPACITY: usize = 64; const FUNCTIONS_HASHMAP_INITIAL_CAPACITY: usize = 64;
@@ -219,27 +206,29 @@ 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>,
optimizer: Optimizer, optimizer: Optimizer,
dependency_manager: Option<D>,
include_metadata_hash: bool,
debug_config: DebugConfig, debug_config: DebugConfig,
llvm_arguments: &'ctx [String],
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory, 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, memory_config.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
}); });
@@ -256,12 +245,8 @@ where
functions: HashMap::with_capacity(Self::FUNCTIONS_HASHMAP_INITIAL_CAPACITY), functions: HashMap::with_capacity(Self::FUNCTIONS_HASHMAP_INITIAL_CAPACITY),
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,
memory_config, memory_config,
dependency_manager,
include_metadata_hash,
debug_info, debug_info,
debug_config, debug_config,
@@ -272,12 +257,10 @@ where
/// Builds the LLVM IR module, returning the build artifacts. /// Builds the LLVM IR module, returning the build artifacts.
pub fn build( pub fn build(
mut self, self,
contract_path: &str, contract_path: &str,
metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]>, metadata_hash: Option<revive_common::Keccak256>,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let module_clone = self.module.clone();
self.link_polkavm_exports(contract_path)?; self.link_polkavm_exports(contract_path)?;
self.link_immutable_data(contract_path)?; self.link_immutable_data(contract_path)?;
@@ -326,33 +309,16 @@ where
) )
})?; })?;
let shared_object = revive_linker::link(buffer.as_slice())?; let object = buffer.as_slice().to_vec();
self.debug_config self.debug_config.dump_object(contract_path, &object)?;
.dump_object(contract_path, &shared_object)?;
let polkavm_bytecode = crate::polkavm::build(
revive_linker::polkavm_linker(shared_object, !self.debug_config().emit_debug_info)?; &object,
metadata_hash
let build = match crate::polkavm::build_assembly_text( .as_ref()
contract_path, .map(|hash| hash.as_bytes().try_into().unwrap()),
&polkavm_bytecode, )
metadata_hash,
self.debug_config(),
) {
Ok(build) => build,
Err(_error)
if self.optimizer.settings() != &OptimizerSettings::size()
&& self.optimizer.settings().is_fallback_to_size_enabled() =>
{
self.optimizer = Optimizer::new(OptimizerSettings::size());
self.module = module_clone;
self.build(contract_path, metadata_hash)?
}
Err(error) => Err(error)?,
};
Ok(build)
} }
/// Verifies the current LLVM IR module. /// Verifies the current LLVM IR module.
@@ -429,11 +395,15 @@ where
} }
} }
/// Declare an external global. /// Declare an external global. This is an idempotent method.
pub fn declare_global<T>(&mut self, name: &str, r#type: T, address_space: AddressSpace) pub fn declare_global<T>(&mut self, name: &str, r#type: T, address_space: AddressSpace)
where where
T: BasicType<'ctx> + Clone + Copy, T: BasicType<'ctx> + Clone + Copy,
{ {
if self.globals.contains_key(name) {
return;
}
let global = Global::declare(self, r#type, address_space, name); let global = Global::declare(self, r#type, address_space, name);
self.globals.insert(name.to_owned(), global); self.globals.insert(name.to_owned(), global);
} }
@@ -455,8 +425,16 @@ 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)>,
is_frontend: bool,
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> { ) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
let value = self.module().add_function(name, r#type, linkage); assert!(
self.get_function(name, is_frontend).is_none(),
"ICE: function '{name}' declared subsequentally"
);
let name = self.internal_function_name(name, is_frontend);
let value = self.module().add_function(&name, r#type, linkage);
if self.debug_info().is_some() { if self.debug_info().is_some() {
self.builder().unset_current_debug_location(); self.builder().unset_current_debug_location();
@@ -470,7 +448,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");
@@ -496,7 +475,7 @@ where
}; };
let function = Function::new( let function = Function::new(
name.to_owned(), name.clone(),
FunctionDeclaration::new(r#type, value), FunctionDeclaration::new(r#type, value),
r#return, r#return,
entry_block, entry_block,
@@ -504,7 +483,7 @@ where
); );
Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer); Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer);
let function = Rc::new(RefCell::new(function)); let function = Rc::new(RefCell::new(function));
self.functions.insert(name.to_string(), function.clone()); self.functions.insert(name, function.clone());
self.pop_debug_scope(); self.pop_debug_scope();
@@ -512,8 +491,14 @@ where
} }
/// Returns a shared reference to the specified function. /// Returns a shared reference to the specified function.
pub fn get_function(&self, name: &str) -> Option<Rc<RefCell<Function<'ctx>>>> { pub fn get_function(
self.functions.get(name).cloned() &self,
name: &str,
is_frontend: bool,
) -> Option<Rc<RefCell<Function<'ctx>>>> {
self.functions
.get(&self.internal_function_name(name, is_frontend))
.cloned()
} }
/// Returns a shared reference to the current active function. /// Returns a shared reference to the current active function.
@@ -525,8 +510,13 @@ 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(
let function = self.functions.get(name).cloned().ok_or_else(|| { &mut self,
name: &str,
location: Option<(u32, u32)>,
frontend: bool,
) -> anyhow::Result<()> {
let function = self.get_function(name, frontend).ok_or_else(|| {
anyhow::anyhow!("Failed to activate an undeclared function `{}`", name) anyhow::anyhow!("Failed to activate an undeclared function `{}`", name)
})?; })?;
self.current_function = Some(function); self.current_function = Some(function);
@@ -534,7 +524,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(())
} }
@@ -635,54 +626,6 @@ where
.expect("The current context is not in a loop") .expect("The current context is not in a loop")
} }
/// Compiles a contract dependency, if the dependency manager is set.
pub fn compile_dependency(&mut self, name: &str) -> anyhow::Result<String> {
self.dependency_manager
.to_owned()
.ok_or_else(|| anyhow::anyhow!("The dependency manager is unset"))
.and_then(|manager| {
Dependency::compile(
manager,
name,
self.optimizer.settings().to_owned(),
self.include_metadata_hash,
self.debug_config.clone(),
self.llvm_arguments,
self.memory_config,
)
})
}
/// Gets a full contract_path from the dependency manager.
pub fn resolve_path(&self, identifier: &str) -> anyhow::Result<String> {
self.dependency_manager
.to_owned()
.ok_or_else(|| anyhow::anyhow!("The dependency manager is unset"))
.and_then(|manager| {
let full_path = manager.resolve_path(identifier)?;
Ok(full_path)
})
}
/// Gets a deployed library address from the dependency manager.
pub fn resolve_library(&self, path: &str) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
self.dependency_manager
.to_owned()
.ok_or_else(|| anyhow::anyhow!("The dependency manager is unset"))
.and_then(|manager| {
let address = manager.resolve_library(path)?;
let address = self.word_const_str_hex(address.as_str());
Ok(address)
})
}
/// Extracts the dependency manager.
pub fn take_dependency_manager(&mut self) -> D {
self.dependency_manager
.take()
.expect("The dependency manager is unset")
}
/// Returns the debug info. /// Returns the debug info.
pub fn debug_info(&self) -> Option<&DebugInfo<'ctx>> { pub fn debug_info(&self) -> Option<&DebugInfo<'ctx>> {
self.debug_info.as_ref() self.debug_info.as_ref()
@@ -715,21 +658,27 @@ 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.
/// Use this if [`build_alloca_at_entry`] might change program semantics. /// Use this if [`Self::build_alloca_at_entry`] might change program semantics.
/// Otherwise, alloca should always be built at the function prelude! /// Otherwise, alloca should always be built at the function prelude!
pub fn build_alloca<T: BasicType<'ctx> + Clone + Copy>( pub fn build_alloca<T: BasicType<'ctx> + Clone + Copy>(
&self, &self,
@@ -787,9 +736,9 @@ where
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> { ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
match pointer.address_space { match pointer.address_space {
AddressSpace::Heap => { AddressSpace::Heap => {
let name = <PolkaVMLoadHeapWordFunction as RuntimeFunction<D>>::NAME; let name = <PolkaVMLoadHeapWordFunction as RuntimeFunction>::NAME;
let declaration = let declaration =
<PolkaVMLoadHeapWordFunction as RuntimeFunction<D>>::declaration(self); <PolkaVMLoadHeapWordFunction as RuntimeFunction>::declaration(self);
let arguments = [self let arguments = [self
.builder() .builder()
.build_ptr_to_int(pointer.value, self.xlen_type(), "offset_ptrtoint")? .build_ptr_to_int(pointer.value, self.xlen_type(), "offset_ptrtoint")?
@@ -825,7 +774,7 @@ where
match pointer.address_space { match pointer.address_space {
AddressSpace::Heap => { AddressSpace::Heap => {
let declaration = let declaration =
<PolkaVMStoreHeapWordFunction as RuntimeFunction<D>>::declaration(self); <PolkaVMStoreHeapWordFunction as RuntimeFunction>::declaration(self);
let arguments = [ let arguments = [
pointer.to_int(self).as_basic_value_enum(), pointer.to_int(self).as_basic_value_enum(),
value.as_basic_value_enum(), value.as_basic_value_enum(),
@@ -945,10 +894,7 @@ where
pub fn build_runtime_call_to_getter( pub fn build_runtime_call_to_getter(
&self, &self,
import: &'static str, import: &'static str,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let pointer = self.build_alloca_at_entry(self.word_type(), &format!("{import}_output")); let pointer = self.build_alloca_at_entry(self.word_type(), &format!("{import}_output"));
self.build_runtime_call(import, &[pointer.to_int(self).into()]); self.build_runtime_call(import, &[pointer.to_int(self).into()]);
self.build_load(pointer, import) self.build_load(pointer, import)
@@ -1043,7 +989,7 @@ where
length: inkwell::values::IntValue<'ctx>, length: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
self.build_call( self.build_call(
<Exit as RuntimeFunction<D>>::declaration(self), <Exit as RuntimeFunction>::declaration(self),
&[flags.into(), offset.into(), length.into()], &[flags.into(), offset.into(), length.into()],
"exit", "exit",
); );
@@ -1067,14 +1013,14 @@ where
Ok(self Ok(self
.build_call( .build_call(
<WordToPointer as RuntimeFunction<D>>::declaration(self), <WordToPointer as RuntimeFunction>::declaration(self),
&[value.into()], &[value.into()],
"word_to_pointer", "word_to_pointer",
) )
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!( panic!(
"revive runtime function {} should return a value", "revive runtime function {} should return a value",
<WordToPointer as RuntimeFunction<D>>::NAME, <WordToPointer as RuntimeFunction>::NAME,
) )
}) })
.into_int_value()) .into_int_value())
@@ -1090,7 +1036,7 @@ where
size: inkwell::values::IntValue<'ctx>, size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> { ) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
let call_site_value = self.builder().build_call( let call_site_value = self.builder().build_call(
<PolkaVMSbrkFunction as RuntimeFunction<D>>::declaration(self).function_value(), <PolkaVMSbrkFunction as RuntimeFunction>::declaration(self).function_value(),
&[offset.into(), size.into()], &[offset.into(), size.into()],
"alloc_start", "alloc_start",
)?; )?;
@@ -1112,7 +1058,7 @@ where
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!( panic!(
"revive runtime function {} should return a value", "revive runtime function {} should return a value",
<PolkaVMSbrkFunction as RuntimeFunction<D>>::NAME, <PolkaVMSbrkFunction as RuntimeFunction>::NAME,
) )
}) })
.into_pointer_value()) .into_pointer_value())
@@ -1412,19 +1358,8 @@ where
/// Returns the Yul data reference. /// Returns the Yul data reference.
/// # Panics /// # Panics
/// If the Yul data has not been initialized. /// If the Yul data has not been initialized.
pub fn yul(&self) -> &YulData { pub fn yul(&self) -> Option<&YulData> {
self.yul_data self.yul_data.as_ref()
.as_ref()
.expect("The Yul data must have been initialized")
}
/// Returns the Yul data mutable reference.
/// # Panics
/// If the Yul data has not been initialized.
pub fn yul_mut(&mut self) -> &mut YulData {
self.yul_data
.as_mut()
.expect("The Yul data must have been initialized")
} }
/// Returns the current number of immutables values in the contract. /// Returns the current number of immutables values in the contract.
@@ -1443,7 +1378,20 @@ where
} }
pub fn heap_size(&self) -> inkwell::values::IntValue<'ctx> { pub fn heap_size(&self) -> inkwell::values::IntValue<'ctx> {
self.xlen_type() self.xlen_type().const_int(
.const_int(self.memory_config.heap_size as u64, false) self.memory_config
.heap_size
.unwrap_or(PolkaVMDefaultHeapMemorySize) as u64,
false,
)
}
/// Returns the internal function name.
fn internal_function_name(&self, name: &str, is_frontend: bool) -> String {
if is_frontend {
format!("{name}_{}", self.code_type().unwrap())
} else {
name.to_string()
}
} }
} }
@@ -2,21 +2,20 @@
use inkwell::values::BasicValueEnum; use inkwell::values::BasicValueEnum;
use revive_common::BYTE_LENGTH_BYTE;
use revive_common::BYTE_LENGTH_WORD;
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::WriteLLVM; use crate::polkavm::WriteLLVM;
/// Load a word size value from a heap pointer. /// Load a word size value from a heap pointer.
pub struct LoadWord; pub struct LoadWord;
impl<D> RuntimeFunction<D> for LoadWord impl RuntimeFunction for LoadWord {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_load_heap_word"; const NAME: &'static str = "__revive_load_heap_word";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context context
.word_type() .word_type()
.fn_type(&[context.xlen_type().into()], false) .fn_type(&[context.xlen_type().into()], false)
@@ -24,12 +23,12 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
let offset = Self::paramater(context, 0).into_int_value(); let offset = Self::paramater(context, 0).into_int_value();
let length = context let length = context
.xlen_type() .xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false); .const_int(BYTE_LENGTH_WORD as u64, false);
let pointer = context.build_heap_gep(offset, length)?; let pointer = context.build_heap_gep(offset, length)?;
let value = context let value = context
.builder() .builder()
@@ -38,7 +37,7 @@ where
.basic_block() .basic_block()
.get_last_instruction() .get_last_instruction()
.expect("Always exists") .expect("Always exists")
.set_alignment(revive_common::BYTE_LENGTH_BYTE as u32) .set_alignment(BYTE_LENGTH_BYTE as u32)
.expect("Alignment is valid"); .expect("Alignment is valid");
let swapped_value = context.build_byte_swap(value)?; let swapped_value = context.build_byte_swap(value)?;
@@ -46,29 +45,23 @@ where
} }
} }
impl<D> WriteLLVM<D> for LoadWord impl WriteLLVM for LoadWord {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Store a word size value through a heap pointer. /// Store a word size value through a heap pointer.
pub struct StoreWord; pub struct StoreWord;
impl<D> RuntimeFunction<D> for StoreWord impl RuntimeFunction for StoreWord {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_store_heap_word"; const NAME: &'static str = "__revive_store_heap_word";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type( context.void_type().fn_type(
&[context.xlen_type().into(), context.word_type().into()], &[context.xlen_type().into(), context.word_type().into()],
false, false,
@@ -77,12 +70,12 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
let offset = Self::paramater(context, 0).into_int_value(); let offset = Self::paramater(context, 0).into_int_value();
let length = context let length = context
.xlen_type() .xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false); .const_int(BYTE_LENGTH_WORD as u64, false);
let pointer = context.build_heap_gep(offset, length)?; let pointer = context.build_heap_gep(offset, length)?;
let value = context.build_byte_swap(Self::paramater(context, 1))?; let value = context.build_byte_swap(Self::paramater(context, 1))?;
@@ -90,21 +83,18 @@ where
context context
.builder() .builder()
.build_store(pointer.value, value)? .build_store(pointer.value, value)?
.set_alignment(revive_common::BYTE_LENGTH_BYTE as u32) .set_alignment(BYTE_LENGTH_BYTE as u32)
.expect("Alignment is valid"); .expect("Alignment is valid");
Ok(None) Ok(None)
} }
} }
impl<D> WriteLLVM<D> for StoreWord impl WriteLLVM for StoreWord {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
@@ -5,7 +5,6 @@ use inkwell::types::BasicType;
use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::global::Global; use crate::polkavm::context::global::Global;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
pub mod heap; pub mod heap;
pub mod storage; pub mod storage;
@@ -39,13 +38,10 @@ impl<'ctx> Pointer<'ctx> {
} }
/// Wraps a 256-bit primitive type pointer. /// Wraps a 256-bit primitive type pointer.
pub fn new_stack_field<D>( pub fn new_stack_field(
context: &Context<'ctx, D>, context: &Context<'ctx>,
value: inkwell::values::PointerValue<'ctx>, value: inkwell::values::PointerValue<'ctx>,
) -> Self ) -> Self {
where
D: Dependency + Clone,
{
Self { Self {
r#type: context.word_type().as_basic_type_enum(), r#type: context.word_type().as_basic_type_enum(),
address_space: AddressSpace::Stack, address_space: AddressSpace::Stack,
@@ -54,15 +50,14 @@ impl<'ctx> Pointer<'ctx> {
} }
/// Creates a new pointer with the specified `offset`. /// Creates a new pointer with the specified `offset`.
pub fn new_with_offset<D, T>( pub fn new_with_offset<T>(
context: &Context<'ctx, D>, context: &Context<'ctx>,
address_space: AddressSpace, address_space: AddressSpace,
r#type: T, r#type: T,
offset: inkwell::values::IntValue<'ctx>, offset: inkwell::values::IntValue<'ctx>,
name: &str, name: &str,
) -> Self ) -> Self
where where
D: Dependency + Clone,
T: BasicType<'ctx>, T: BasicType<'ctx>,
{ {
assert_ne!( assert_ne!(
@@ -92,25 +87,19 @@ impl<'ctx> Pointer<'ctx> {
} }
/// Cast this pointer to a register sized integer value. /// Cast this pointer to a register sized integer value.
pub fn to_int<D>(&self, context: &Context<'ctx, D>) -> inkwell::values::IntValue<'ctx> pub fn to_int(&self, context: &Context<'ctx>) -> inkwell::values::IntValue<'ctx> {
where
D: Dependency + Clone,
{
context context
.builder() .builder()
.build_ptr_to_int(self.value, context.xlen_type(), "ptr_to_xlen") .build_ptr_to_int(self.value, context.xlen_type(), "ptr_to_xlen")
.expect("we should be positioned") .expect("we should be positioned")
} }
pub fn address_space_cast<D>( pub fn address_space_cast(
self, self,
context: &Context<'ctx, D>, context: &Context<'ctx>,
address_space: AddressSpace, address_space: AddressSpace,
name: &str, name: &str,
) -> anyhow::Result<Self> ) -> anyhow::Result<Self> {
where
D: Dependency + Clone,
{
let value = context.builder().build_address_space_cast( let value = context.builder().build_address_space_cast(
self.value, self.value,
context.llvm().ptr_type(address_space.into()), context.llvm().ptr_type(address_space.into()),
@@ -4,19 +4,15 @@ use inkwell::values::BasicValueEnum;
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::WriteLLVM; use crate::polkavm::WriteLLVM;
/// Load a word size value from a storage pointer. /// Load a word size value from a storage pointer.
pub struct LoadWord; pub struct LoadWord;
impl<D> RuntimeFunction<D> for LoadWord impl RuntimeFunction for LoadWord {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_load_storage_word"; const NAME: &'static str = "__revive_load_storage_word";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context context
.word_type() .word_type()
.fn_type(&[context.llvm().ptr_type(Default::default()).into()], false) .fn_type(&[context.llvm().ptr_type(Default::default()).into()], false)
@@ -24,7 +20,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
Ok(Some(emit_load( Ok(Some(emit_load(
context, context,
@@ -34,29 +30,23 @@ where
} }
} }
impl<D> WriteLLVM<D> for LoadWord impl WriteLLVM for LoadWord {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Load a word size value from a transient storage pointer. /// Load a word size value from a transient storage pointer.
pub struct LoadTransientWord; pub struct LoadTransientWord;
impl<D> RuntimeFunction<D> for LoadTransientWord impl RuntimeFunction for LoadTransientWord {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_load_transient_storage_word"; const NAME: &'static str = "__revive_load_transient_storage_word";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context context
.word_type() .word_type()
.fn_type(&[context.llvm().ptr_type(Default::default()).into()], false) .fn_type(&[context.llvm().ptr_type(Default::default()).into()], false)
@@ -64,35 +54,29 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
Ok(Some(emit_load(context, Self::paramater(context, 0), true)?)) Ok(Some(emit_load(context, Self::paramater(context, 0), true)?))
} }
} }
impl<D> WriteLLVM<D> for LoadTransientWord impl WriteLLVM for LoadTransientWord {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Store a word size value through a storage pointer. /// Store a word size value through a storage pointer.
pub struct StoreWord; pub struct StoreWord;
impl<D> RuntimeFunction<D> for StoreWord impl RuntimeFunction for StoreWord {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_store_storage_word"; const NAME: &'static str = "__revive_store_storage_word";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type( context.void_type().fn_type(
&[ &[
context.llvm().ptr_type(Default::default()).into(), context.llvm().ptr_type(Default::default()).into(),
@@ -104,7 +88,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
emit_store( emit_store(
context, context,
@@ -117,29 +101,23 @@ where
} }
} }
impl<D> WriteLLVM<D> for StoreWord impl WriteLLVM for StoreWord {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Store a word size value through a transient storage pointer. /// Store a word size value through a transient storage pointer.
pub struct StoreTransientWord; pub struct StoreTransientWord;
impl<D> RuntimeFunction<D> for StoreTransientWord impl RuntimeFunction for StoreTransientWord {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_store_transient_storage_word"; const NAME: &'static str = "__revive_store_transient_storage_word";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type( context.void_type().fn_type(
&[ &[
context.llvm().ptr_type(Default::default()).into(), context.llvm().ptr_type(Default::default()).into(),
@@ -151,7 +129,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
emit_store( emit_store(
context, context,
@@ -164,21 +142,18 @@ where
} }
} }
impl<D> WriteLLVM<D> for StoreTransientWord impl WriteLLVM for StoreTransientWord {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
fn emit_load<'ctx, D: Dependency + Clone>( fn emit_load<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
key: BasicValueEnum<'ctx>, key: BasicValueEnum<'ctx>,
transient: bool, transient: bool,
) -> anyhow::Result<BasicValueEnum<'ctx>> { ) -> anyhow::Result<BasicValueEnum<'ctx>> {
@@ -229,8 +204,8 @@ fn emit_load<'ctx, D: Dependency + Clone>(
}) })
} }
fn emit_store<'ctx, D: Dependency + Clone>( fn emit_store<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
key: BasicValueEnum<'ctx>, key: BasicValueEnum<'ctx>,
value: BasicValueEnum<'ctx>, value: BasicValueEnum<'ctx>,
transient: bool, transient: bool,
@@ -8,14 +8,10 @@ use crate::polkavm::context::function::declaration::Declaration;
use crate::polkavm::context::function::Function; use crate::polkavm::context::function::Function;
use crate::polkavm::context::Attribute; use crate::polkavm::context::Attribute;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// The revive runtime function interface simplifies declaring runtime functions /// The revive runtime function interface simplifies declaring runtime functions
/// and code emitting by providing helpful default implementations. /// and code emitting by providing helpful default implementations.
pub trait RuntimeFunction<D> pub trait RuntimeFunction {
where
D: Dependency + Clone,
{
/// The function name. /// The function name.
const NAME: &'static str; const NAME: &'static str;
@@ -26,15 +22,17 @@ where
]; ];
/// The function type. /// The function type.
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx>; fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx>;
/// Declare the function. /// Declare the function.
fn declare(&self, context: &mut Context<D>) -> anyhow::Result<()> { fn declare(&self, context: &mut Context) -> anyhow::Result<()> {
let function = context.add_function( let function = context.add_function(
Self::NAME, Self::NAME,
Self::r#type(context), Self::r#type(context),
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::LinkOnceODR),
None,
false,
)?; )?;
let mut attributes = Self::ATTRIBUTES.to_vec(); let mut attributes = Self::ATTRIBUTES.to_vec();
@@ -48,22 +46,28 @@ where
&attributes, &attributes,
true, true,
); );
let function = function.borrow().declaration().function_value();
let comdat = context
.module()
.get_or_insert_comdat(&format!("{}_comdat", Self::NAME));
comdat.set_selection_kind(inkwell::comdat::ComdatSelectionKind::NoDuplicates);
function.as_global_value().set_comdat(comdat);
Ok(()) Ok(())
} }
/// Get the function declaration. /// Get the function declaration.
fn declaration<'ctx>(context: &Context<'ctx, D>) -> Declaration<'ctx> { fn declaration<'ctx>(context: &Context<'ctx>) -> Declaration<'ctx> {
context context
.get_function(Self::NAME) .get_function(Self::NAME, false)
.unwrap_or_else(|| panic!("runtime function {} should be declared", Self::NAME)) .unwrap_or_else(|| panic!("runtime function {} should be declared", Self::NAME))
.borrow() .borrow()
.declaration() .declaration()
} }
/// Emit the function. /// Emit the function.
fn emit(&self, context: &mut Context<D>) -> anyhow::Result<()> { fn emit(&self, context: &mut Context) -> anyhow::Result<()> {
context.set_current_function(Self::NAME, None)?; context.set_current_function(Self::NAME, None, false)?;
context.set_basic_block(context.current_function().borrow().entry_block()); context.set_basic_block(context.current_function().borrow().entry_block());
let return_value = self.emit_body(context)?; let return_value = self.emit_body(context)?;
@@ -77,13 +81,13 @@ where
/// Emit the function body. /// Emit the function body.
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>>; ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>>;
/// Emit the function return instructions. /// Emit the function return instructions.
fn emit_epilogue<'ctx>( fn emit_epilogue<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
return_value: Option<inkwell::values::BasicValueEnum<'ctx>>, return_value: Option<inkwell::values::BasicValueEnum<'ctx>>,
) { ) {
let return_block = context.current_function().borrow().return_block(); let return_block = context.current_function().borrow().return_block();
@@ -97,12 +101,12 @@ where
/// Get the nth function paramater. /// Get the nth function paramater.
fn paramater<'ctx>( fn paramater<'ctx>(
context: &Context<'ctx, D>, context: &Context<'ctx>,
index: usize, index: usize,
) -> inkwell::values::BasicValueEnum<'ctx> { ) -> inkwell::values::BasicValueEnum<'ctx> {
let name = Self::NAME; let name = Self::NAME;
context context
.get_function(name) .get_function(name, false)
.unwrap_or_else(|| panic!("runtime function {name} should be declared")) .unwrap_or_else(|| panic!("runtime function {name} should be declared"))
.borrow() .borrow()
.declaration() .declaration()
@@ -2,6 +2,8 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use revive_common::BYTE_LENGTH_WORD;
/// The LLVM IR generator Solidity data. /// The LLVM IR generator Solidity data.
/// Describes some data that is only relevant to Solidity. /// Describes some data that is only relevant to Solidity.
#[derive(Debug, Default)] #[derive(Debug, Default)]
@@ -19,14 +21,14 @@ impl SolidityData {
/// Returns the current size of immutable values in the contract. /// Returns the current size of immutable values in the contract.
pub fn immutables_size(&self) -> usize { pub fn immutables_size(&self) -> usize {
self.immutables.len() * revive_common::BYTE_LENGTH_WORD self.immutables.len() * BYTE_LENGTH_WORD
} }
/// Allocates memory for an immutable value in the auxiliary heap. /// Allocates memory for an immutable value in the auxiliary heap.
/// If the identifier is already known, just returns its offset. /// If the identifier is already known, just returns its offset.
pub fn allocate_immutable(&mut self, identifier: &str) -> usize { pub fn allocate_immutable(&mut self, identifier: &str) -> usize {
let number_of_elements = self.immutables.len(); let number_of_elements = self.immutables.len();
let new_offset = number_of_elements * revive_common::BYTE_LENGTH_WORD; let new_offset = number_of_elements * BYTE_LENGTH_WORD;
*self *self
.immutables .immutables
.entry(identifier.to_owned()) .entry(identifier.to_owned())
@@ -4,24 +4,21 @@ use crate::optimizer::settings::Settings as OptimizerSettings;
use crate::optimizer::Optimizer; use crate::optimizer::Optimizer;
use crate::polkavm::context::attribute::Attribute; use crate::polkavm::context::attribute::Attribute;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::DummyDependency; use crate::PolkaVMTarget;
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<'_> {
crate::initialize_llvm(crate::Target::PVM, "resolc", Default::default()); crate::initialize_llvm(PolkaVMTarget::PVM, "resolc", Default::default());
let module = llvm.create_module("test"); let module = llvm.create_module("test");
let optimizer = Optimizer::new(optimizer_settings); let optimizer = Optimizer::new(optimizer_settings);
Context::<DummyDependency>::new( Context::new(
llvm, llvm,
module, module,
optimizer, optimizer,
None,
true,
Default::default(),
Default::default(), Default::default(),
Default::default(), Default::default(),
) )
@@ -40,6 +37,8 @@ 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,
false,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -63,6 +62,8 @@ 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,
false,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -86,6 +87,8 @@ 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,
false,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(function assert!(function
@@ -109,6 +112,8 @@ 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,
false,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -132,6 +137,8 @@ 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,
false,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(function assert!(function
@@ -2,60 +2,25 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use num::Zero;
/// The LLVM IR generator Yul data. /// The LLVM IR generator Yul data.
/// Describes some data that is only relevant to Yul. ///
/// Contains data that is only relevant to Yul.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct YulData { pub struct YulData {
/// The list of constant arrays in the code section. /// Mapping from Yul object identifiers to full contract paths.
/// It is a temporary storage used until the finalization method is called. identifier_paths: BTreeMap<String, String>,
const_arrays: BTreeMap<u8, Vec<num::BigUint>>,
} }
impl YulData { impl YulData {
/// Declares a temporary constant array representation. /// A shorthand constructor.
pub fn const_array_declare(&mut self, index: u8, size: u16) -> anyhow::Result<()> { pub fn new(identifier_paths: BTreeMap<String, String>) -> Self {
if self.const_arrays.contains_key(&index) { Self { identifier_paths }
anyhow::bail!(
"The constant array with index {} is already declared",
index
);
}
self.const_arrays
.insert(index, vec![num::BigUint::zero(); size as usize]);
Ok(())
} }
/// Sets a value in the constant array representation. /// Resolves the full contract path by the Yul object identifier.
pub fn const_array_set( pub fn resolve_path(&self, identifier: &str) -> Option<&str> {
&mut self, self.identifier_paths
index: u8, .get(identifier)
offset: u16, .map(|path| path.as_str())
value: num::BigUint,
) -> anyhow::Result<()> {
let array = self.const_arrays.get_mut(&index).ok_or_else(|| {
anyhow::anyhow!("The constant array with index {} is not declared", index)
})?;
if offset >= array.len() as u16 {
anyhow::bail!(
"The constant array with index {} has size {} but the offset is {}",
index,
array.len(),
offset,
);
}
array[offset as usize] = value;
Ok(())
}
/// Finalizes the constant array declaration.
pub fn const_array_take(&mut self, index: u8) -> anyhow::Result<Vec<num::BigUint>> {
self.const_arrays.remove(&index).ok_or_else(|| {
anyhow::anyhow!("The constant array with index {} is not declared", index)
})
} }
} }
@@ -4,21 +4,17 @@ use inkwell::values::BasicValue;
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::PolkaVMDivisionFunction; use crate::PolkaVMDivisionFunction;
use crate::PolkaVMRemainderFunction; use crate::PolkaVMRemainderFunction;
use crate::PolkaVMSignedDivisionFunction; use crate::PolkaVMSignedDivisionFunction;
use crate::PolkaVMSignedRemainderFunction; use crate::PolkaVMSignedRemainderFunction;
/// Translates the arithmetic addition. /// Translates the arithmetic addition.
pub fn addition<'ctx, D>( pub fn addition<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.builder() .builder()
.build_int_add(operand_1, operand_2, "addition_result")? .build_int_add(operand_1, operand_2, "addition_result")?
@@ -26,14 +22,11 @@ where
} }
/// Translates the arithmetic subtraction. /// Translates the arithmetic subtraction.
pub fn subtraction<'ctx, D>( pub fn subtraction<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.builder() .builder()
.build_int_sub(operand_1, operand_2, "subtraction_result")? .build_int_sub(operand_1, operand_2, "subtraction_result")?
@@ -41,14 +34,11 @@ where
} }
/// Translates the arithmetic multiplication. /// Translates the arithmetic multiplication.
pub fn multiplication<'ctx, D>( pub fn multiplication<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.builder() .builder()
.build_int_mul(operand_1, operand_2, "multiplication_result")? .build_int_mul(operand_1, operand_2, "multiplication_result")?
@@ -56,32 +46,26 @@ where
} }
/// Translates the arithmetic division. /// Translates the arithmetic division.
pub fn division<'ctx, D>( pub fn division<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where let name = <PolkaVMDivisionFunction as RuntimeFunction>::NAME;
D: Dependency + Clone, let declaration = <PolkaVMDivisionFunction as RuntimeFunction>::declaration(context);
{
let name = <PolkaVMDivisionFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMDivisionFunction as RuntimeFunction<D>>::declaration(context);
Ok(context Ok(context
.build_call(declaration, &[operand_1.into(), operand_2.into()], "div") .build_call(declaration, &[operand_1.into(), operand_2.into()], "div")
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",))) .unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
} }
/// Translates the arithmetic remainder. /// Translates the arithmetic remainder.
pub fn remainder<'ctx, D>( pub fn remainder<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where let name = <PolkaVMRemainderFunction as RuntimeFunction>::NAME;
D: Dependency + Clone, let declaration = <PolkaVMRemainderFunction as RuntimeFunction>::declaration(context);
{
let name = <PolkaVMRemainderFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMRemainderFunction as RuntimeFunction<D>>::declaration(context);
Ok(context Ok(context
.build_call(declaration, &[operand_1.into(), operand_2.into()], "rem") .build_call(declaration, &[operand_1.into(), operand_2.into()], "rem")
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",))) .unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
@@ -91,32 +75,26 @@ where
/// Two differences between the EVM and LLVM IR: /// Two differences between the EVM and LLVM IR:
/// 1. In case of division by zero, 0 is returned. /// 1. In case of division by zero, 0 is returned.
/// 2. In case of overflow, the first argument is returned. /// 2. In case of overflow, the first argument is returned.
pub fn division_signed<'ctx, D>( pub fn division_signed<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where let name = <PolkaVMSignedDivisionFunction as RuntimeFunction>::NAME;
D: Dependency + Clone, let declaration = <PolkaVMSignedDivisionFunction as RuntimeFunction>::declaration(context);
{
let name = <PolkaVMSignedDivisionFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMSignedDivisionFunction as RuntimeFunction<D>>::declaration(context);
Ok(context Ok(context
.build_call(declaration, &[operand_1.into(), operand_2.into()], "sdiv") .build_call(declaration, &[operand_1.into(), operand_2.into()], "sdiv")
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",))) .unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
} }
/// Translates the signed arithmetic remainder. /// Translates the signed arithmetic remainder.
pub fn remainder_signed<'ctx, D>( pub fn remainder_signed<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where let name = <PolkaVMSignedRemainderFunction as RuntimeFunction>::NAME;
D: Dependency + Clone, let declaration = <PolkaVMSignedRemainderFunction as RuntimeFunction>::declaration(context);
{
let name = <PolkaVMSignedRemainderFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMSignedRemainderFunction as RuntimeFunction<D>>::declaration(context);
Ok(context Ok(context
.build_call(declaration, &[operand_1.into(), operand_2.into()], "srem") .build_call(declaration, &[operand_1.into(), operand_2.into()], "srem")
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",))) .unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
+33 -55
View File
@@ -2,18 +2,17 @@
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use revive_common::BIT_LENGTH_BYTE;
use revive_common::BIT_LENGTH_WORD;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the bitwise OR. /// Translates the bitwise OR.
pub fn or<'ctx, D>( pub fn or<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.builder() .builder()
.build_or(operand_1, operand_2, "or_result")? .build_or(operand_1, operand_2, "or_result")?
@@ -21,14 +20,11 @@ where
} }
/// Translates the bitwise XOR. /// Translates the bitwise XOR.
pub fn xor<'ctx, D>( pub fn xor<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.builder() .builder()
.build_xor(operand_1, operand_2, "xor_result")? .build_xor(operand_1, operand_2, "xor_result")?
@@ -36,14 +32,11 @@ where
} }
/// Translates the bitwise AND. /// Translates the bitwise AND.
pub fn and<'ctx, D>( pub fn and<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.builder() .builder()
.build_and(operand_1, operand_2, "and_result")? .build_and(operand_1, operand_2, "and_result")?
@@ -51,14 +44,11 @@ where
} }
/// Translates the bitwise shift left. /// Translates the bitwise shift left.
pub fn shift_left<'ctx, D>( pub fn shift_left<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
shift: inkwell::values::IntValue<'ctx>, shift: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let overflow_block = context.append_basic_block("shift_left_overflow"); let overflow_block = context.append_basic_block("shift_left_overflow");
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");
@@ -66,7 +56,7 @@ where
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,
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), context.word_const((BIT_LENGTH_WORD - 1) as u64),
"shift_left_is_overflow", "shift_left_is_overflow",
)?; )?;
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
@@ -93,14 +83,11 @@ where
} }
/// Translates the bitwise shift right. /// Translates the bitwise shift right.
pub fn shift_right<'ctx, D>( pub fn shift_right<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
shift: inkwell::values::IntValue<'ctx>, shift: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let overflow_block = context.append_basic_block("shift_right_overflow"); let overflow_block = context.append_basic_block("shift_right_overflow");
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");
@@ -108,7 +95,7 @@ where
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,
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), context.word_const((BIT_LENGTH_WORD - 1) as u64),
"shift_right_is_overflow", "shift_right_is_overflow",
)?; )?;
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
@@ -137,14 +124,11 @@ where
} }
/// Translates the arithmetic bitwise shift right. /// Translates the arithmetic bitwise shift right.
pub fn shift_right_arithmetic<'ctx, D>( pub fn shift_right_arithmetic<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
shift: inkwell::values::IntValue<'ctx>, shift: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let overflow_block = context.append_basic_block("shift_right_arithmetic_overflow"); let overflow_block = context.append_basic_block("shift_right_arithmetic_overflow");
let overflow_positive_block = let overflow_positive_block =
context.append_basic_block("shift_right_arithmetic_overflow_positive"); context.append_basic_block("shift_right_arithmetic_overflow_positive");
@@ -156,7 +140,7 @@ where
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,
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), context.word_const((BIT_LENGTH_WORD - 1) as u64),
"shift_right_arithmetic_is_overflow", "shift_right_arithmetic_is_overflow",
)?; )?;
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
@@ -164,7 +148,7 @@ where
context.set_basic_block(overflow_block); context.set_basic_block(overflow_block);
let sign_bit = context.builder().build_right_shift( let sign_bit = context.builder().build_right_shift(
value, value,
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64), context.word_const((BIT_LENGTH_WORD - 1) as u64),
false, false,
"shift_right_arithmetic_sign_bit", "shift_right_arithmetic_sign_bit",
)?; )?;
@@ -204,7 +188,7 @@ where
&context.word_type().const_all_ones(), &context.word_type().const_all_ones(),
overflow_negative_block, overflow_negative_block,
), ),
(&context.word_const(0), overflow_block), (&context.word_const(0), overflow_positive_block),
]); ]);
Ok(result.as_basic_value()) Ok(result.as_basic_value())
} }
@@ -217,14 +201,11 @@ where
/// Because this opcode returns zero on overflows, the index `operand_1` /// Because this opcode returns zero on overflows, the index `operand_1`
/// is checked for overflow. On overflow, the mask will be all zeros, /// is checked for overflow. On overflow, the mask will be all zeros,
/// resulting in a branchless implementation. /// resulting in a branchless implementation.
pub fn byte<'ctx, D>( pub fn byte<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
const MAX_INDEX_BYTES: u64 = 31; const MAX_INDEX_BYTES: u64 = 31;
let is_overflow_bit = context.builder().build_int_compare( let is_overflow_bit = context.builder().build_int_compare(
@@ -254,16 +235,13 @@ where
.build_int_truncate(operand_1, context.byte_type(), "index_truncated")?; .build_int_truncate(operand_1, context.byte_type(), "index_truncated")?;
let index_in_bits = context.builder().build_int_mul( let index_in_bits = context.builder().build_int_mul(
index_truncated, index_truncated,
context context.byte_type().const_int(BIT_LENGTH_BYTE as u64, false),
.byte_type()
.const_int(revive_common::BIT_LENGTH_BYTE as u64, false),
"index_in_bits", "index_in_bits",
)?; )?;
let index_from_most_significant_bit = context.builder().build_int_sub( let index_from_most_significant_bit = context.builder().build_int_sub(
context.byte_type().const_int( context
MAX_INDEX_BYTES * revive_common::BIT_LENGTH_BYTE as u64, .byte_type()
false, .const_int(MAX_INDEX_BYTES * BIT_LENGTH_BYTE as u64, false),
),
index_in_bits, index_in_bits,
"index_from_msb", "index_from_msb",
)?; )?;
+19 -37
View File
@@ -2,18 +2,15 @@
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use crate::polkavm::context::argument::Argument;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
const STATIC_CALL_FLAG: u32 = 0b0001_0000; const STATIC_CALL_FLAG: u32 = 0b0001_0000;
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000; const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300; const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300;
/// Translates a contract call. /// Translates a contract call.
#[allow(clippy::too_many_arguments)] pub fn call<'ctx>(
pub fn call<'ctx, D>( context: &mut Context<'ctx>,
context: &mut Context<'ctx, D>,
gas: inkwell::values::IntValue<'ctx>, gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>, address: inkwell::values::IntValue<'ctx>,
value: Option<inkwell::values::IntValue<'ctx>>, value: Option<inkwell::values::IntValue<'ctx>>,
@@ -23,10 +20,7 @@ pub fn call<'ctx, D>(
output_length: inkwell::values::IntValue<'ctx>, output_length: inkwell::values::IntValue<'ctx>,
_constants: Vec<Option<num::BigUint>>, _constants: Vec<Option<num::BigUint>>,
static_call: bool, static_call: bool,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let address_pointer = context.build_address_argument_store(address)?; let address_pointer = context.build_address_argument_store(address)?;
let value = value.unwrap_or_else(|| context.word_const(0)); let value = value.unwrap_or_else(|| context.word_const(0));
@@ -115,9 +109,8 @@ where
.as_basic_value_enum()) .as_basic_value_enum())
} }
#[allow(clippy::too_many_arguments)] pub fn delegate_call<'ctx>(
pub fn delegate_call<'ctx, D>( context: &mut Context<'ctx>,
context: &mut Context<'ctx, D>,
_gas: inkwell::values::IntValue<'ctx>, _gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>, address: inkwell::values::IntValue<'ctx>,
input_offset: inkwell::values::IntValue<'ctx>, input_offset: inkwell::values::IntValue<'ctx>,
@@ -125,10 +118,7 @@ pub fn delegate_call<'ctx, D>(
output_offset: inkwell::values::IntValue<'ctx>, output_offset: inkwell::values::IntValue<'ctx>,
output_length: inkwell::values::IntValue<'ctx>, output_length: inkwell::values::IntValue<'ctx>,
_constants: Vec<Option<num::BigUint>>, _constants: Vec<Option<num::BigUint>>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let address_pointer = context.build_address_argument_store(address)?; let address_pointer = context.build_address_argument_store(address)?;
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?; let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
@@ -199,21 +189,16 @@ where
} }
/// Translates the Yul `linkersymbol` instruction. /// Translates the Yul `linkersymbol` instruction.
pub fn linker_symbol<'ctx, D>( pub fn linker_symbol<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
mut arguments: [Argument<'ctx>; 1], path: &str,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where context.declare_global(
D: Dependency + Clone, path,
{ context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
let path = arguments[0] Default::default(),
.original );
.take() context.build_load_address(context.get_global(path)?.into())
.ok_or_else(|| anyhow::anyhow!("Linker symbol literal is missing"))?;
Ok(context
.resolve_library(path.as_str())?
.as_basic_value_enum())
} }
/// The Solidity `address.transfer` and `address.send` call detection heuristic. /// The Solidity `address.transfer` and `address.send` call detection heuristic.
@@ -236,18 +221,15 @@ where
/// ///
/// # Returns /// # Returns
/// The call flags xlen `IntValue` and the deposit limit word `IntValue`. /// The call flags xlen `IntValue` and the deposit limit word `IntValue`.
fn call_reentrancy_heuristic<'ctx, D>( fn call_reentrancy_heuristic<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
gas: inkwell::values::IntValue<'ctx>, gas: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>, input_length: inkwell::values::IntValue<'ctx>,
output_length: inkwell::values::IntValue<'ctx>, output_length: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<( ) -> anyhow::Result<(
inkwell::values::IntValue<'ctx>, inkwell::values::IntValue<'ctx>,
inkwell::values::IntValue<'ctx>, inkwell::values::IntValue<'ctx>,
)> )> {
where
D: Dependency + Clone,
{
// Branch-free SSA implementation: First derive the heuristic boolean (int1) value. // Branch-free SSA implementation: First derive the heuristic boolean (int1) value.
let input_length_or_output_length = let input_length_or_output_length =
context context
@@ -1,16 +1,12 @@
//! Translates the calldata instructions. //! Translates the calldata instructions.
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the calldata load. /// Translates the calldata load.
pub fn load<'ctx, D>( pub fn load<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
offset: inkwell::values::IntValue<'ctx>, offset: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let output_pointer = context.build_alloca_at_entry(context.word_type(), "call_data_output"); let output_pointer = context.build_alloca_at_entry(context.word_type(), "call_data_output");
let offset = context.safe_truncate_int_to_xlen(offset)?; let offset = context.safe_truncate_int_to_xlen(offset)?;
@@ -23,12 +19,9 @@ where
} }
/// Translates the calldata size. /// Translates the calldata size.
pub fn size<'ctx, D>( pub fn size<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let value = context.get_global_value(crate::polkavm::GLOBAL_CALLDATA_SIZE)?; let value = context.get_global_value(crate::polkavm::GLOBAL_CALLDATA_SIZE)?;
Ok(context Ok(context
.builder() .builder()
@@ -41,15 +34,12 @@ where
} }
/// Translates the calldata copy. /// Translates the calldata copy.
pub fn copy<'ctx, D>( pub fn copy<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
destination_offset: inkwell::values::IntValue<'ctx>, destination_offset: inkwell::values::IntValue<'ctx>,
source_offset: inkwell::values::IntValue<'ctx>, source_offset: inkwell::values::IntValue<'ctx>,
size: inkwell::values::IntValue<'ctx>, size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()> ) -> anyhow::Result<()> {
where
D: Dependency + Clone,
{
let source_offset = context.safe_truncate_int_to_xlen(source_offset)?; let source_offset = context.safe_truncate_int_to_xlen(source_offset)?;
let size = context.safe_truncate_int_to_xlen(size)?; let size = context.safe_truncate_int_to_xlen(size)?;
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?; let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
@@ -3,19 +3,15 @@
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the comparison operations. /// Translates the comparison operations.
/// There is not difference between the EVM and LLVM IR behaviors. /// There is not difference between the EVM and LLVM IR behaviors.
pub fn compare<'ctx, D>( pub fn compare<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
operation: inkwell::IntPredicate, operation: inkwell::IntPredicate,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let result = context.builder().build_int_compare( let result = context.builder().build_int_compare(
operation, operation,
operand_1, operand_1,
+39 -74
View File
@@ -2,17 +2,15 @@
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use revive_common::BIT_LENGTH_ETH_ADDRESS;
use crate::polkavm::context::pointer::Pointer; use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the `gas_limit` instruction. /// Translates the `gas_limit` instruction.
pub fn gas_limit<'ctx, D>( pub fn gas_limit<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let gas_limit_value = context let gas_limit_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::GAS_LIMIT, &[]) .build_runtime_call(revive_runtime_api::polkavm_imports::GAS_LIMIT, &[])
.expect("the gas_limit syscall method should return a value") .expect("the gas_limit syscall method should return a value")
@@ -25,12 +23,9 @@ where
} }
/// Translates the `gas_price` instruction. /// Translates the `gas_price` instruction.
pub fn gas_price<'ctx, D>( pub fn gas_price<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let gas_price_value = context let gas_price_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::GAS_PRICE, &[]) .build_runtime_call(revive_runtime_api::polkavm_imports::GAS_PRICE, &[])
.expect("the gas_price syscall method should return a value") .expect("the gas_price syscall method should return a value")
@@ -43,13 +38,10 @@ where
} }
/// Translates the `tx.origin` instruction. /// Translates the `tx.origin` instruction.
pub fn origin<'ctx, D>( pub fn origin<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where let address_type = context.integer_type(BIT_LENGTH_ETH_ADDRESS);
D: Dependency + Clone,
{
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer: Pointer<'_> = context let address_pointer: Pointer<'_> = context
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into(); .into();
@@ -62,43 +54,31 @@ where
} }
/// Translates the `chain_id` instruction. /// Translates the `chain_id` instruction.
pub fn chain_id<'ctx, D>( pub fn chain_id<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::CHAIN_ID) context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::CHAIN_ID)
} }
/// Translates the `block_number` instruction. /// Translates the `block_number` instruction.
pub fn block_number<'ctx, D>( pub fn block_number<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BLOCK_NUMBER) context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BLOCK_NUMBER)
} }
/// Translates the `block_timestamp` instruction. /// Translates the `block_timestamp` instruction.
pub fn block_timestamp<'ctx, D>( pub fn block_timestamp<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::NOW) context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::NOW)
} }
/// Translates the `block_hash` instruction. /// Translates the `block_hash` instruction.
pub fn block_hash<'ctx, D>( pub fn block_hash<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
index: inkwell::values::IntValue<'ctx>, index: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
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_pointer = 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_pointer, index)?; context.build_store(index_pointer, index)?;
@@ -114,22 +94,16 @@ where
} }
/// Translates the `difficulty` instruction. /// Translates the `difficulty` instruction.
pub fn difficulty<'ctx, D>( pub fn difficulty<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context.word_const(2500000000000000).as_basic_value_enum()) Ok(context.word_const(2500000000000000).as_basic_value_enum())
} }
/// Translates the `coinbase` instruction. /// Translates the `coinbase` instruction.
pub fn coinbase<'ctx, D>( pub fn coinbase<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let pointer: Pointer<'_> = context let pointer: Pointer<'_> = context
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into(); .into();
@@ -141,22 +115,16 @@ where
} }
/// Translates the `basefee` instruction. /// Translates the `basefee` instruction.
pub fn basefee<'ctx, D>( pub fn basefee<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BASE_FEE) context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BASE_FEE)
} }
/// Translates the `address` instruction. /// Translates the `address` instruction.
pub fn address<'ctx, D>( pub fn address<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let pointer: Pointer<'_> = context let pointer: Pointer<'_> = context
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into(); .into();
@@ -168,12 +136,9 @@ where
} }
/// Translates the `caller` instruction. /// Translates the `caller` instruction.
pub fn caller<'ctx, D>( pub fn caller<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let pointer: Pointer<'_> = context let pointer: Pointer<'_> = context
.get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
.into(); .into();
+58 -54
View File
@@ -3,24 +3,22 @@
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use num::Zero; use num::Zero;
use revive_common::BIT_LENGTH_ETH_ADDRESS;
use crate::polkavm::context::argument::Argument; use crate::polkavm::context::argument::Argument;
use crate::polkavm::context::code_type::CodeType; use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the contract `create` and `create2` instruction. /// Translates the contract `create` and `create2` instruction.
/// ///
/// A `salt` value of `None` is equivalent to `create1`. /// A `salt` value of `None` is equivalent to `create1`.
pub fn create<'ctx, D>( pub fn create<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
input_offset: inkwell::values::IntValue<'ctx>, input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>, input_length: inkwell::values::IntValue<'ctx>,
salt: Option<inkwell::values::IntValue<'ctx>>, salt: Option<inkwell::values::IntValue<'ctx>>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?; let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?; let input_length = context.safe_truncate_int_to_xlen(input_length)?;
@@ -40,7 +38,7 @@ where
}; };
let address_pointer = context.build_alloca_at_entry( let address_pointer = context.build_alloca_at_entry(
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), context.integer_type(BIT_LENGTH_ETH_ADDRESS),
"address_pointer", "address_pointer",
); );
context.build_store(address_pointer, context.word_const(0))?; context.build_store(address_pointer, context.word_const(0))?;
@@ -96,77 +94,83 @@ where
/// Translates the contract hash instruction, which is actually used to set the hash of the contract /// Translates the contract hash instruction, which is actually used to set the hash of the contract
/// being created, or other related auxiliary data. /// being created, or other related auxiliary data.
/// Represents `dataoffset` in Yul and `PUSH [$]` in the EVM legacy assembly. /// Represents `dataoffset` in Yul and `PUSH [$]` in the EVM legacy assembly.
pub fn contract_hash<'ctx, D>( pub fn contract_hash<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
identifier: String, identifier: String,
) -> anyhow::Result<Argument<'ctx>> ) -> anyhow::Result<Argument<'ctx>> {
where
D: Dependency + Clone,
{
let code_type = context let code_type = context
.code_type() .code_type()
.ok_or_else(|| anyhow::anyhow!("The contract code part type is undefined"))?; .ok_or_else(|| anyhow::anyhow!("The contract code part type is undefined"))?;
let parent = context.module().get_name().to_str().expect("Always valid"); let parent = context.module().get_name().to_str().expect("Always valid");
let contract_path = let full_path = match context.yul() {
context Some(yul_data) => yul_data
.resolve_path(identifier.as_str()) .resolve_path(
.map_err(|error| match code_type { identifier
CodeType::Runtime if identifier.ends_with("_deployed") => { .strip_suffix("_deployed")
anyhow::anyhow!("type({}).runtimeCode is not supported", identifier) .unwrap_or(identifier.as_str()),
} )
_ => error, .expect("Always exists")
})?; .to_owned(),
if contract_path.as_str() == parent { None => identifier.clone(),
return Ok(Argument::value(context.word_const(0).as_basic_value_enum()) };
.with_constant(num::BigUint::zero()));
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime { match code_type {
anyhow::bail!("type({}).runtimeCode is not supported", identifier); CodeType::Deploy if full_path == parent => {
return Ok(Argument::value(context.word_const(0).as_basic_value_enum())
.with_constant(num::BigUint::zero()));
}
CodeType::Runtime if context.yul().is_some() && identifier.ends_with("_deployed") => {
anyhow::bail!("type({identifier}).runtimeCode is not supported");
}
_ => {}
} }
let hash_string = context.compile_dependency(identifier.as_str())?; context.declare_global(&full_path, context.word_type(), Default::default());
let hash_value = context context
.word_const_str_hex(hash_string.as_str()) .build_load(context.get_global(&full_path)?.into(), &full_path)
.as_basic_value_enum(); .map(Argument::value)
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
/// the hash of the bytecode of the contract whose instance is being created. /// the hash of the bytecode of the contract whose instance is being created.
/// Represents `datasize` in Yul and `PUSH #[$]` in the EVM legacy assembly. /// Represents `datasize` in Yul and `PUSH #[$]` in the EVM legacy assembly.
pub fn header_size<'ctx, D>( pub fn header_size<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
identifier: String, identifier: String,
) -> anyhow::Result<Argument<'ctx>> ) -> anyhow::Result<Argument<'ctx>> {
where
D: Dependency + Clone,
{
let code_type = context let code_type = context
.code_type() .code_type()
.ok_or_else(|| anyhow::anyhow!("The contract code part type is undefined"))?; .ok_or_else(|| anyhow::anyhow!("The contract code part type is undefined"))?;
let parent = context.module().get_name().to_str().expect("Always valid"); let parent = context.module().get_name().to_str().expect("Always valid");
let contract_path = let full_path = match context.yul() {
context Some(yul_data) => yul_data
.resolve_path(identifier.as_str()) .resolve_path(
.map_err(|error| match code_type { identifier
CodeType::Runtime if identifier.ends_with("_deployed") => { .strip_suffix("_deployed")
anyhow::anyhow!("type({}).runtimeCode is not supported", identifier) .unwrap_or(identifier.as_str()),
} )
_ => error, .unwrap_or_else(|| panic!("ICE: {identifier} not found {yul_data:?}")),
})?; None => identifier.as_str(),
if contract_path.as_str() == parent { };
return Ok(Argument::value(context.word_const(0).as_basic_value_enum())
.with_constant(num::BigUint::zero())); match code_type {
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime { CodeType::Deploy if full_path == parent => {
anyhow::bail!("type({}).runtimeCode is not supported", identifier); return Ok(Argument::value(context.word_const(0).as_basic_value_enum())
.with_constant(num::BigUint::zero()));
}
CodeType::Runtime if context.yul().is_some() && identifier.ends_with("_deployed") => {
anyhow::bail!("type({identifier}).runtimeCode is not supported");
}
_ => {}
} }
let size_bigint = num::BigUint::from(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE);
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();
let size_bigint = num::BigUint::from(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE);
Ok(Argument::value(size_value).with_constant(size_bigint)) Ok(Argument::value(size_value).with_constant(size_bigint))
} }
@@ -1,17 +1,13 @@
//! Translates the cryptographic operations. //! Translates the cryptographic operations.
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the `sha3` instruction. /// Translates the `sha3` instruction.
pub fn sha3<'ctx, D>( pub fn sha3<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
offset: inkwell::values::IntValue<'ctx>, offset: inkwell::values::IntValue<'ctx>,
length: inkwell::values::IntValue<'ctx>, length: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let offset_casted = context.safe_truncate_int_to_xlen(offset)?; let offset_casted = context.safe_truncate_int_to_xlen(offset)?;
let length_casted = context.safe_truncate_int_to_xlen(length)?; let length_casted = context.safe_truncate_int_to_xlen(length)?;
let input_pointer = context.build_heap_gep(offset_casted, length_casted)?; let input_pointer = context.build_heap_gep(offset_casted, length_casted)?;
@@ -1,15 +1,11 @@
//! Translates the value and balance operations. //! Translates the value and balance operations.
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the `gas` instruction. /// Translates the `gas` instruction.
pub fn gas<'ctx, D>( pub fn gas<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let ref_time_left_value = context let ref_time_left_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::REF_TIME_LEFT, &[]) .build_runtime_call(revive_runtime_api::polkavm_imports::REF_TIME_LEFT, &[])
.expect("the ref_time_left syscall method should return a value") .expect("the ref_time_left syscall method should return a value")
@@ -22,12 +18,9 @@ where
} }
/// Translates the `value` instruction. /// Translates the `value` instruction.
pub fn value<'ctx, D>( pub fn value<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let output_pointer = context.build_alloca_at_entry(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(
@@ -38,13 +31,10 @@ where
} }
/// Translates the `balance` instructions. /// Translates the `balance` instructions.
pub fn balance<'ctx, D>( pub fn balance<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
address: inkwell::values::IntValue<'ctx>, address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
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_at_entry(context.word_type(), "balance_pointer");
let balance = context.builder().build_ptr_to_int( let balance = context.builder().build_ptr_to_int(
@@ -62,12 +52,9 @@ where
} }
/// Translates the `selfbalance` instructions. /// Translates the `selfbalance` instructions.
pub fn self_balance<'ctx, D>( pub fn self_balance<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let balance_pointer = context.build_alloca_at_entry(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,
+35 -56
View File
@@ -1,19 +1,16 @@
//! Translates a log or event call. //! Translates a log or event call.
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use revive_common::BYTE_LENGTH_WORD;
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::WriteLLVM; use crate::polkavm::WriteLLVM;
/// A function for emitting EVM event logs from contract code. /// A function for emitting EVM event logs from contract code.
pub struct EventLog<const N: usize>; pub struct EventLog<const N: usize>;
impl<D, const N: usize> RuntimeFunction<D> for EventLog<N> impl<const N: usize> RuntimeFunction for EventLog<N> {
where
D: Dependency + Clone,
{
const NAME: &'static str = match N { const NAME: &'static str = match N {
0 => "__revive_log_0", 0 => "__revive_log_0",
1 => "__revive_log_1", 1 => "__revive_log_1",
@@ -23,7 +20,7 @@ where
_ => unreachable!(), _ => unreachable!(),
}; };
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
let mut parameter_types = vec![context.xlen_type().into(), context.xlen_type().into()]; let mut parameter_types = vec![context.xlen_type().into(), context.xlen_type().into()];
parameter_types.extend_from_slice(&[context.word_type().into(); N]); parameter_types.extend_from_slice(&[context.word_type().into(); N]);
context.void_type().fn_type(&parameter_types, false) context.void_type().fn_type(&parameter_types, false)
@@ -31,7 +28,7 @@ where
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let input_offset = Self::paramater(context, 0).into_int_value(); let input_offset = Self::paramater(context, 0).into_int_value();
let input_length = Self::paramater(context, 1).into_int_value(); let input_length = Self::paramater(context, 1).into_int_value();
@@ -49,7 +46,7 @@ where
input_length.as_basic_value_enum(), input_length.as_basic_value_enum(),
] ]
} else { } else {
let topics_buffer_size = N * revive_common::BYTE_LENGTH_WORD; let topics_buffer_size = N * BYTE_LENGTH_WORD;
let topics_buffer_pointer = context.build_alloca_at_entry( let topics_buffer_pointer = context.build_alloca_at_entry(
context.byte_type().array_type(topics_buffer_size as u32), context.byte_type().array_type(topics_buffer_size as u32),
"topics_buffer", "topics_buffer",
@@ -59,7 +56,7 @@ where
let topic = Self::paramater(context, n + 2); let topic = Self::paramater(context, n + 2);
let topic_buffer_offset = context let topic_buffer_offset = context
.xlen_type() .xlen_type()
.const_int((n * revive_common::BYTE_LENGTH_WORD) as u64, false); .const_int((n * BYTE_LENGTH_WORD) as u64, false);
context.build_store( context.build_store(
context.build_gep( context.build_gep(
topics_buffer_pointer, topics_buffer_pointer,
@@ -98,82 +95,64 @@ where
} }
} }
impl<D> WriteLLVM<D> for EventLog<0> impl WriteLLVM for EventLog<0> {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
impl<D> WriteLLVM<D> for EventLog<1> impl WriteLLVM for EventLog<1> {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
impl<D> WriteLLVM<D> for EventLog<2> impl WriteLLVM for EventLog<2> {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
impl<D> WriteLLVM<D> for EventLog<3> impl WriteLLVM for EventLog<3> {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
impl<D> WriteLLVM<D> for EventLog<4> impl WriteLLVM for EventLog<4> {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Translates a log or event call. /// Translates a log or event call.
pub fn log<'ctx, D, const N: usize>( pub fn log<'ctx, const N: usize>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
input_offset: inkwell::values::IntValue<'ctx>, input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>, input_length: inkwell::values::IntValue<'ctx>,
topics: [inkwell::values::BasicValueEnum<'ctx>; N], topics: [inkwell::values::BasicValueEnum<'ctx>; N],
) -> anyhow::Result<()> ) -> anyhow::Result<()> {
where let declaration = <EventLog<N> as RuntimeFunction>::declaration(context);
D: Dependency + Clone,
{
let declaration = <EventLog<N> as RuntimeFunction<D>>::declaration(context);
let mut arguments = vec![ let mut arguments = vec![
context.safe_truncate_int_to_xlen(input_offset)?.into(), context.safe_truncate_int_to_xlen(input_offset)?.into(),
context.safe_truncate_int_to_xlen(input_length)?.into(), context.safe_truncate_int_to_xlen(input_length)?.into(),
@@ -1,17 +1,15 @@
//! Translates the external code operations. //! Translates the external code operations.
use revive_common::BIT_LENGTH_ETH_ADDRESS;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the `extcodesize` instruction if `address` is `Some`. /// Translates the `extcodesize` instruction if `address` is `Some`.
/// Otherwise, translates the `codesize` instruction. /// Otherwise, translates the `codesize` instruction.
pub fn size<'ctx, D>( pub fn size<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
address: Option<inkwell::values::IntValue<'ctx>>, address: Option<inkwell::values::IntValue<'ctx>>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let address = match address { let address = match address {
Some(address) => address, Some(address) => address,
None => super::context::address(context)?.into_int_value(), None => super::context::address(context)?.into_int_value(),
@@ -33,14 +31,11 @@ where
} }
/// Translates the `extcodehash` instruction. /// Translates the `extcodehash` instruction.
pub fn hash<'ctx, D>( pub fn hash<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
address: inkwell::values::IntValue<'ctx>, address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where let address_type = context.integer_type(BIT_LENGTH_ETH_ADDRESS);
D: Dependency + Clone,
{
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer = context.build_alloca_at_entry(address_type, "address_pointer"); let address_pointer = context.build_alloca_at_entry(address_type, "address_pointer");
let address_truncated = let address_truncated =
context context
@@ -7,7 +7,6 @@ use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::pointer::Pointer; use crate::polkavm::context::pointer::Pointer;
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::WriteLLVM; use crate::polkavm::WriteLLVM;
/// A function for requesting the immutable data from the runtime. /// A function for requesting the immutable data from the runtime.
@@ -20,19 +19,16 @@ use crate::polkavm::WriteLLVM;
/// However, this is a one time assertion, hence worth it. /// However, this is a one time assertion, hence worth it.
pub struct Load; pub struct Load;
impl<D> RuntimeFunction<D> for Load impl RuntimeFunction for Load {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_load_immutable_data"; const NAME: &'static str = "__revive_load_immutable_data";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type(Default::default(), false) context.void_type().fn_type(Default::default(), false)
} }
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let immutable_data_size_pointer = context let immutable_data_size_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_SIZE)? .get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_SIZE)?
@@ -109,35 +105,29 @@ where
} }
} }
impl<D> WriteLLVM<D> for Load impl WriteLLVM for Load {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
/// Store the immutable data from the constructor code. /// Store the immutable data from the constructor code.
pub struct Store; pub struct Store;
impl<D> RuntimeFunction<D> for Store impl RuntimeFunction for Store {
where
D: Dependency + Clone,
{
const NAME: &'static str = "__revive_store_immutable_data"; const NAME: &'static str = "__revive_store_immutable_data";
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
context.void_type().fn_type(Default::default(), false) context.void_type().fn_type(Default::default(), false)
} }
fn emit_body<'ctx>( fn emit_body<'ctx>(
&self, &self,
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> { ) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
let immutable_data_size_pointer = context let immutable_data_size_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_SIZE)? .get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_SIZE)?
@@ -192,16 +182,13 @@ where
} }
} }
impl<D> WriteLLVM<D> for Store impl WriteLLVM for Store {
where fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
D: Dependency + Clone, <Self as RuntimeFunction>::declare(self, context)
{
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<()> { fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
<Self as RuntimeFunction<_>>::emit(&self, context) <Self as RuntimeFunction>::emit(&self, context)
} }
} }
@@ -210,23 +197,20 @@ where
/// In deploy code the values are read from the stack. /// In deploy code the values are read from the stack.
/// ///
/// In runtime code they are loaded lazily with the `get_immutable_data` syscall. /// In runtime code they are loaded lazily with the `get_immutable_data` syscall.
pub fn load<'ctx, D>( pub fn load<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
index: inkwell::values::IntValue<'ctx>, index: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
match context.code_type() { match context.code_type() {
None => { None => {
anyhow::bail!("Immutables are not available if the contract part is undefined"); anyhow::bail!("Immutables are not available if the contract part is undefined");
} }
Some(CodeType::Deploy) => load_from_memory(context, index), Some(CodeType::Deploy) => load_from_memory(context, index),
Some(CodeType::Runtime) => { Some(CodeType::Runtime) => {
let name = <Load as RuntimeFunction<D>>::NAME; let name = <Load as RuntimeFunction>::NAME;
context.build_call( context.build_call(
context context
.get_function(name) .get_function(name, false)
.expect("is always declared for runtime code") .expect("is always declared for runtime code")
.borrow() .borrow()
.declaration(), .declaration(),
@@ -244,14 +228,11 @@ where
/// being prepared for storing them using the `set_immutable_data` syscall. /// being prepared for storing them using the `set_immutable_data` syscall.
/// ///
/// Ignored in the runtime code. /// Ignored in the runtime code.
pub fn store<'ctx, D>( pub fn store<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
index: inkwell::values::IntValue<'ctx>, index: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()> ) -> anyhow::Result<()> {
where
D: Dependency + Clone,
{
match context.code_type() { match context.code_type() {
None => { None => {
anyhow::bail!("Immutables are not available if the contract part is undefined"); anyhow::bail!("Immutables are not available if the contract part is undefined");
@@ -279,13 +260,10 @@ where
} }
} }
pub fn load_from_memory<'ctx, D>( pub fn load_from_memory<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
index: inkwell::values::IntValue<'ctx>, index: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let immutable_data_pointer = context let immutable_data_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER)? .get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER)?
.value .value
+12 -25
View File
@@ -3,18 +3,14 @@
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the `addmod` instruction. /// Translates the `addmod` instruction.
pub fn add_mod<'ctx, D>( pub fn add_mod<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
modulo: inkwell::values::IntValue<'ctx>, modulo: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.build_call( .build_call(
context.llvm_runtime().add_mod, context.llvm_runtime().add_mod,
@@ -29,15 +25,12 @@ where
} }
/// Translates the `mulmod` instruction. /// Translates the `mulmod` instruction.
pub fn mul_mod<'ctx, D>( pub fn mul_mod<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
operand_1: inkwell::values::IntValue<'ctx>, operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>, operand_2: inkwell::values::IntValue<'ctx>,
modulo: inkwell::values::IntValue<'ctx>, modulo: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.build_call( .build_call(
context.llvm_runtime().mul_mod, context.llvm_runtime().mul_mod,
@@ -52,14 +45,11 @@ where
} }
/// Translates the `exp` instruction. /// Translates the `exp` instruction.
pub fn exponent<'ctx, D>( pub fn exponent<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
exponent: inkwell::values::IntValue<'ctx>, exponent: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.build_call( .build_call(
context.llvm_runtime().exp, context.llvm_runtime().exp,
@@ -70,14 +60,11 @@ where
} }
/// Translates the `signextend` instruction. /// Translates the `signextend` instruction.
pub fn sign_extend<'ctx, D>( pub fn sign_extend<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
bytes: inkwell::values::IntValue<'ctx>, bytes: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.build_call( .build_call(
context.llvm_runtime().sign_extend, context.llvm_runtime().sign_extend,
+14 -26
View File
@@ -1,19 +1,16 @@
//! Translates the heap memory operations. //! Translates the heap memory operations.
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use revive_common::BYTE_LENGTH_BYTE;
use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::pointer::Pointer; use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the `msize` instruction. /// Translates the `msize` instruction.
pub fn msize<'ctx, D>( pub fn msize<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
Ok(context Ok(context
.builder() .builder()
.build_int_z_extend( .build_int_z_extend(
@@ -26,13 +23,10 @@ where
/// Translates the `mload` instruction. /// Translates the `mload` instruction.
/// Uses the main heap. /// Uses the main heap.
pub fn load<'ctx, D>( pub fn load<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
offset: inkwell::values::IntValue<'ctx>, offset: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
where
D: Dependency + Clone,
{
let pointer = Pointer::new_with_offset( let pointer = Pointer::new_with_offset(
context, context,
AddressSpace::Heap, AddressSpace::Heap,
@@ -45,14 +39,11 @@ where
/// Translates the `mstore` instruction. /// Translates the `mstore` instruction.
/// Uses the main heap. /// Uses the main heap.
pub fn store<'ctx, D>( pub fn store<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
offset: inkwell::values::IntValue<'ctx>, offset: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()> ) -> anyhow::Result<()> {
where
D: Dependency + Clone,
{
let pointer = Pointer::new_with_offset( let pointer = Pointer::new_with_offset(
context, context,
AddressSpace::Heap, AddressSpace::Heap,
@@ -66,14 +57,11 @@ where
/// Translates the `mstore8` instruction. /// Translates the `mstore8` instruction.
/// Uses the main heap. /// Uses the main heap.
pub fn store_byte<'ctx, D>( pub fn store_byte<'ctx>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx>,
offset: inkwell::values::IntValue<'ctx>, offset: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()> ) -> anyhow::Result<()> {
where
D: Dependency + Clone,
{
let byte_type = context.byte_type(); let byte_type = context.byte_type();
let value = context let value = context
.builder() .builder()
@@ -92,7 +80,7 @@ where
context context
.builder() .builder()
.build_store(pointer, value)? .build_store(pointer, value)?
.set_alignment(revive_common::BYTE_LENGTH_BYTE as u32) .set_alignment(BYTE_LENGTH_BYTE as u32)
.expect("Alignment is valid"); .expect("Alignment is valid");
Ok(()) Ok(())
} }

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