custom ir

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
Cyrill Leutwiler
2024-02-02 09:10:03 +01:00
parent 7a094f17c0
commit d238d8f39e
48 changed files with 4399 additions and 603 deletions
+1
View File
@@ -1,2 +1,3 @@
/target
*.dot
.vscode/
Generated
+327 -72
View File
@@ -3,14 +3,22 @@
version = 3
[[package]]
name = "alloy-rlp"
version = "0.3.3"
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "alloy-rlp"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac"
dependencies = [
"arrayvec",
"bytes",
"smol_str",
]
[[package]]
@@ -30,9 +38,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.75"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "arc-swap"
@@ -273,7 +281,7 @@ dependencies = [
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets",
"windows-targets 0.48.5",
]
[[package]]
@@ -300,10 +308,6 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "compiler-builtins"
version = "0.1.0"
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
@@ -379,6 +383,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "evmil"
version = "0.4.5"
@@ -392,6 +406,18 @@ dependencies = [
"ruint",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fastrand"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fastrlp"
version = "0.3.1"
@@ -445,15 +471,25 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
dependencies = [
"fallible-iterator",
"stable_deref_trait",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -489,9 +525,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iana-time-zone"
version = "0.1.58"
version = "0.1.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -550,6 +586,31 @@ dependencies = [
"hashbrown 0.14.3",
]
[[package]]
name = "inkwell"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f4fcb4a4fa0b8f7b4178e24e6317d6f8b95ab500d8e6e1bd4283b6860e369c1"
dependencies = [
"either",
"inkwell_internals",
"libc",
"llvm-sys",
"once_cell",
"parking_lot",
]
[[package]]
name = "inkwell_internals"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b185e7d068d6820411502efa14d8fbf010750485399402156b72dd2a548ef8e9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "itertools"
version = "0.10.5"
@@ -582,9 +643,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libm"
@@ -598,6 +659,12 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "lld-sys"
version = "0.1.0"
@@ -606,6 +673,19 @@ dependencies = [
"libc",
]
[[package]]
name = "llvm-sys"
version = "160.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f9888529887dfda6c59e20f0e0727b17e826cd54ae1ddf0d4c83850fa23b69"
dependencies = [
"cc",
"lazy_static",
"libc",
"regex",
"semver 1.0.21",
]
[[package]]
name = "lock_api"
version = "0.4.11"
@@ -659,9 +739,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.6.4"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "num-bigint"
@@ -694,6 +774,15 @@ dependencies = [
"libm",
]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
@@ -761,7 +850,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-targets 0.48.5",
]
[[package]]
@@ -772,9 +861,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "pest"
version = "2.7.5"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5"
checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06"
dependencies = [
"memchr",
"thiserror",
@@ -791,6 +880,24 @@ dependencies = [
"indexmap 2.1.0",
]
[[package]]
name = "polkavm-common"
version = "0.3.0"
source = "git+https://github.com/koute/polkavm.git?rev=3552524a248a025de8e608394fcf9eb7c528eb11#3552524a248a025de8e608394fcf9eb7c528eb11"
[[package]]
name = "polkavm-linker"
version = "0.3.0"
source = "git+https://github.com/koute/polkavm.git?rev=3552524a248a025de8e608394fcf9eb7c528eb11#3552524a248a025de8e608394fcf9eb7c528eb11"
dependencies = [
"gimli",
"hashbrown 0.14.3",
"log",
"object",
"polkavm-common",
"rustc-demangle",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -844,9 +951,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.70"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
@@ -869,9 +976,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.33"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -930,24 +1037,65 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "revive-builtins"
version = "0.1.0"
[[package]]
name = "revive-cli"
version = "0.1.0"
dependencies = [
"evmil",
"hex",
"revive-codegen",
"revive-ir",
"revive-target-polkavm",
]
[[package]]
name = "revive-codegen"
version = "0.1.0"
dependencies = [
"inkwell",
"revive-compilation-target",
"revive-ir",
]
[[package]]
name = "revive-environment"
name = "revive-compilation-target"
version = "0.1.0"
dependencies = [
"inkwell",
]
[[package]]
name = "revive-ir"
@@ -957,11 +1105,23 @@ dependencies = [
"indexmap 2.1.0",
"petgraph",
"primitive-types",
"revive-compilation-target",
]
[[package]]
name = "revive-polkavm"
name = "revive-target-polkavm"
version = "0.1.0"
dependencies = [
"inkwell",
"libc",
"lld-sys",
"polkavm-common",
"polkavm-linker",
"revive-builtins",
"revive-codegen",
"revive-compilation-target",
"tempfile",
]
[[package]]
name = "rlp"
@@ -1003,6 +1163,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09"
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hex"
version = "2.1.0"
@@ -1024,14 +1190,27 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.20",
"semver 1.0.21",
]
[[package]]
name = "rustix"
version = "0.38.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
dependencies = [
"bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "scopeguard"
@@ -1050,9 +1229,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.20"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "semver-parser"
@@ -1065,9 +1244,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.193"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
@@ -1084,20 +1263,20 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.193"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.48",
]
[[package]]
name = "serde_json"
version = "1.0.108"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"itoa",
"ryu",
@@ -1123,13 +1302,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "smol_str"
version = "0.2.0"
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c"
dependencies = [
"serde",
]
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
@@ -1156,9 +1332,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
@@ -1172,10 +1348,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "termcolor"
version = "1.4.0"
name = "tempfile"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
"windows-sys",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
@@ -1188,22 +1377,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
version = "1.0.50"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.48",
]
[[package]]
@@ -1326,7 +1515,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.48",
"wasm-bindgen-shared",
]
@@ -1348,7 +1537,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.48",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -1392,11 +1581,20 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.51.1"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
"windows-targets 0.52.0",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
@@ -1405,13 +1603,28 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
@@ -1420,36 +1633,72 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
@@ -1457,10 +1706,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.5.26"
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa"
dependencies = [
"memchr",
]
@@ -1500,5 +1755,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.48",
]
+3
View File
@@ -11,3 +11,6 @@ indexmap = "2.1.0"
inkwell = { version = "0.2.0", features = ["target-riscv", "no-libffi-linking", "llvm16-0"] }
cc = "1.0"
libc = "0.2"
tempfile = "3.8"
polkavm-common = { git = "https://github.com/koute/polkavm.git", rev = "3552524a248a025de8e608394fcf9eb7c528eb11" }
polkavm-linker = { git = "https://github.com/koute/polkavm.git", rev = "3552524a248a025de8e608394fcf9eb7c528eb11" }
+1 -1
View File
@@ -1,5 +1,5 @@
[package]
name = "compiler-builtins"
name = "revive-builtins"
version = "0.1.0"
edition = "2021"
build = "build.rs"
+1 -1
View File
@@ -16,7 +16,7 @@ fn main() {
let lib_path = std::path::PathBuf::from(llvm_lib_dir.trim())
.join("linux")
.join(lib);
let archive = fs::read(lib_path).expect("clang builtins for riscv32 not fonud");
let archive = fs::read(lib_path).expect("clang builtins for riscv32 not found");
let out_dir = env::var_os("OUT_DIR").expect("has OUT_DIR");
let archive_path = Path::new(&out_dir).join(lib);
+4 -1
View File
@@ -8,4 +8,7 @@ edition = "2021"
[dependencies]
hex = { workspace = true }
evmil = { workspace = true }
revive-ir = { path = "../ir" }
revive-ir = { path = "../ir" }
revive-codegen = { path = "../codegen" }
revive-target-polkavm = { path = "../target-polkavm" }
+13 -2
View File
@@ -1,10 +1,21 @@
use evmil::bytecode::Disassemble;
use revive_ir::cfg::{BasicBlockFormatOption, Program};
use revive_ir::cfg::BasicBlockFormatOption;
use revive_target_polkavm::PolkaVm;
fn main() {
let hexcode = std::fs::read_to_string(std::env::args().nth(1).unwrap()).unwrap();
let bytecode = hex::decode(hexcode.trim()).unwrap();
let instructions = bytecode.disassemble();
Program::new(instructions).dot(BasicBlockFormatOption::ByteCode);
let mut ir = revive_ir::cfg::Program::new(&instructions);
ir.optimize();
ir.dot(BasicBlockFormatOption::Ir);
let target = PolkaVm::default();
let program = revive_codegen::program::Program::new(&target).unwrap();
program.emit(ir);
let artifact = program.compile_and_link();
std::fs::write("/tmp/out.pvm", artifact).unwrap();
}
@@ -1,8 +1,12 @@
[package]
name = "revive-polkavm"
name = "revive-codegen"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
inkwell = { workspace = true }
revive-compilation-target = { path = "../compilation-target" }
revive-ir = { path = "../ir" }
+2
View File
@@ -0,0 +1,2 @@
mod module;
pub mod program;
+37
View File
@@ -0,0 +1,37 @@
use inkwell::{
module::Module,
support::LLVMString,
targets::{RelocMode, TargetTriple},
};
use revive_compilation_target::target::Target;
pub(crate) fn create<'ctx, T>(target: &'ctx T) -> Result<Module<'ctx>, LLVMString>
where
T: Target<'ctx>,
{
let module = target.context().create_module("contract");
module.set_triple(&TargetTriple::create(<T as Target>::TARGET_TRIPLE));
module.set_source_file_name("contract.bin");
set_flags(target, &module);
for lib in target.libraries() {
module.link_in_module(lib)?;
}
Ok(module)
}
fn set_flags<'ctx, T>(target: &'ctx T, module: &Module<'ctx>)
where
T: Target<'ctx>,
{
if let RelocMode::PIC = <T as Target>::RELOC_MODE {
module.add_basic_value_flag(
"PIE Level",
inkwell::module::FlagBehavior::Override,
target.context().i32_type().const_int(2, false),
);
}
}
+93
View File
@@ -0,0 +1,93 @@
use inkwell::{
builder::Builder,
module::{Linkage, Module},
support::LLVMString,
targets::{FileType, TargetTriple},
values::{FunctionValue, GlobalValue},
AddressSpace,
};
use revive_compilation_target::environment::Environment;
use revive_compilation_target::target::Target;
use crate::module;
pub struct Program<'ctx, T> {
pub module: Module<'ctx>,
pub builder: Builder<'ctx>,
pub calldata: GlobalValue<'ctx>,
pub returndata: GlobalValue<'ctx>,
pub target: &'ctx T,
pub start: FunctionValue<'ctx>,
}
impl<'ctx, T> Program<'ctx, T>
where
T: Target<'ctx> + Environment<'ctx>,
{
pub fn new(target: &'ctx T) -> Result<Self, LLVMString> {
T::initialize_llvm();
let context = target.context();
let module = module::create(target)?;
let builder = context.create_builder();
let address_space = Some(AddressSpace::default());
let calldata_type = context.i8_type().array_type(T::CALLDATA_SIZE);
let calldata = module.add_global(calldata_type, address_space, "calldata");
let returndata_type = context.i8_type().array_type(T::RETURNDATA_SIZE);
let returndata = module.add_global(returndata_type, address_space, "returndata");
let start_fn_type = target.context().void_type().fn_type(&[], false);
let start = module.add_function("start", start_fn_type, Some(Linkage::Internal));
Ok(Self {
module,
builder,
calldata,
returndata,
target,
start,
})
}
pub fn emit(&self, program: revive_ir::cfg::Program) {
self.emit_start();
}
pub fn compile_and_link(&self) -> Vec<u8> {
inkwell::targets::Target::from_name(T::TARGET_NAME)
.expect("target name should be valid")
.create_target_machine(
&TargetTriple::create(T::TARGET_TRIPLE),
T::CPU,
T::TARGET_FEATURES,
self.target.optimization_level(),
T::RELOC_MODE,
T::CODE_MODEL,
)
.expect("target configuration should be valid")
.write_to_memory_buffer(&self.module, FileType::Object)
.map(|out| self.target.link(out.as_slice()))
.expect("linker should succeed")
.to_vec()
}
fn emit_start(&self) {
let start = self.start;
let block = self
.start
.get_last_basic_block()
.unwrap_or_else(|| self.target.context().append_basic_block(start, "entry"));
self.builder.position_at_end(block);
self.builder.build_return(None);
let env_start = self.target.call_start(&self.builder, self.start);
self.module
.link_in_module(env_start)
.expect("entrypoint module should be linkable");
}
}
@@ -1,8 +1,9 @@
[package]
name = "revive-environment"
name = "revive-compilation-target"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
inkwell = { workspace = true }
@@ -0,0 +1,15 @@
use inkwell::{builder::Builder, module::Module, values::FunctionValue};
/// [Environment] describes EVM runtime functionality.
pub trait Environment<'ctx> {
const STACK_SIZE: u32 = 1024 * 32;
const CALLDATA_SIZE: u32 = 0x10000;
const RETURNDATA_SIZE: u32 = 0x10000;
const MEMORY_SIZE: u32 = 0x100000;
/// Build a module containing all required runtime exports and imports.
///
/// The `start` function is the entrypoint to the contract logic.
/// The returned `Module` is expected to call `start` somewhere.
fn call_start(&'ctx self, builder: &Builder<'ctx>, start: FunctionValue<'ctx>) -> Module<'ctx>;
}
+2
View File
@@ -0,0 +1,2 @@
pub mod environment;
pub mod target;
+27
View File
@@ -0,0 +1,27 @@
use inkwell::{
context::Context,
module::Module,
targets::{CodeModel, RelocMode},
OptimizationLevel,
};
pub trait Target<'ctx> {
const TARGET_NAME: &'ctx str;
const TARGET_TRIPLE: &'ctx str;
const TARGET_FEATURES: &'ctx str;
const CPU: &'ctx str;
const RELOC_MODE: RelocMode = RelocMode::Default;
const CODE_MODEL: CodeModel = CodeModel::Default;
fn initialize_llvm() {
inkwell::targets::Target::initialize_riscv(&Default::default());
}
fn context(&self) -> &Context;
fn libraries(&'ctx self) -> Vec<Module<'ctx>>;
fn link(&self, blob: &[u8]) -> Vec<u8>;
fn optimization_level(&self) -> OptimizationLevel;
}
-1
View File
@@ -1 +0,0 @@
+3 -1
View File
@@ -9,4 +9,6 @@ edition = "2021"
evmil = { workspace = true }
petgraph = { workspace = true }
primitive-types = { workspace = true }
indexmap = { workspace = true }
indexmap = { workspace = true }
revive-compilation-target = { path = "../compilation-target" }
+3 -3
View File
@@ -30,13 +30,13 @@ impl Address {
#[derive(Clone, Copy)]
pub enum Type {
Int { size: u8 },
Int { size: u16 },
Bytes { size: u8 },
Bool,
}
impl Type {
pub fn int(size: u8) -> Self {
pub fn int(size: u16) -> Self {
Self::Int { size }
}
@@ -47,7 +47,7 @@ impl Type {
impl Default for Type {
fn default() -> Self {
Type::Int { size: 32 }
Type::Bytes { size: 32 }
}
}
+60
View File
@@ -0,0 +1,60 @@
use indexmap::{IndexMap, IndexSet};
use petgraph::prelude::*;
use crate::{
analysis::BlockAnalysis,
cfg::{Branch, Program},
instruction::Instruction,
symbol::Kind,
};
/// Remove basic blocks not reachable from the start node.
#[derive(Default)]
pub struct ReachableCode(pub IndexSet<NodeIndex>);
impl BlockAnalysis for ReachableCode {
fn analyze_block(&mut self, node: NodeIndex, _program: &mut Program) {
self.0.insert(node);
}
fn apply_results(&mut self, program: &mut Program) {
program.cfg.graph.retain_nodes(|_, i| self.0.contains(&i));
}
}
/// Remove edges to the jump table if the jump target is statically known.
#[derive(Default)]
pub struct StaticJumps(IndexMap<EdgeIndex, (NodeIndex, NodeIndex)>);
impl BlockAnalysis for StaticJumps {
fn analyze_block(&mut self, node: NodeIndex, program: &mut Program) {
for edge in program.cfg.graph.edges(node) {
if *edge.weight() == Branch::Static {
continue;
}
if let Some(Instruction::ConditionalBranch { target, .. })
| Some(Instruction::UncoditionalBranch { target }) =
program.cfg.graph[node].instructions.last()
{
let Kind::Constant(bytecode_offset) = target.symbol().kind else {
continue;
};
let destination = program
.jump_targets
.get(&bytecode_offset.as_usize())
.unwrap_or(&program.cfg.invalid_jump);
self.0.insert(edge.id(), (node, *destination));
}
}
}
fn apply_results(&mut self, program: &mut Program) {
for (edge, (a, b)) in &self.0 {
program.cfg.graph.remove_edge(*edge);
program.cfg.graph.add_edge(*a, *b, Branch::Static);
}
}
}
+19
View File
@@ -0,0 +1,19 @@
use crate::{cfg::Program, instruction::Instruction, symbol::Type, POINTER_SIZE};
use petgraph::prelude::*;
use super::BlockAnalysis;
#[derive(Default)]
pub struct Unstack;
impl BlockAnalysis for Unstack {
fn analyze_block(&mut self, node: NodeIndex, program: &mut Program) {
for instruction in &program.cfg.graph[node].instructions {
match instruction {
_ => {}
}
}
}
fn apply_results(&mut self, _program: &mut Program) {}
}
+373
View File
@@ -0,0 +1,373 @@
use indexmap::{IndexMap, IndexSet};
use petgraph::prelude::*;
use crate::{
analysis::BlockAnalysis,
cfg::{Program, StackInfo},
instruction::{Instruction, Operator},
symbol::{Global, Kind, Symbol, SymbolBuilder, SymbolRef, SymbolTable},
};
#[derive(Default)]
pub struct IrBuilder;
impl BlockAnalysis for IrBuilder {
fn analyze_block(&mut self, node: NodeIndex, program: &mut Program) {
let mut builder = BlockBuilder::new(node, &mut program.symbol_table);
for opcode in &program.evm_instructions[program.cfg.graph[node].opcodes.to_owned()] {
builder.translate(&opcode.instruction);
}
let (instructions, stack_info) = builder.done();
program.cfg.graph[node].instructions = instructions;
program.cfg.graph[node].stack_info = stack_info;
}
fn apply_results(&mut self, _program: &mut Program) {}
}
pub struct BlockBuilder<'tbl> {
state: State<'tbl>,
instructions: Vec<Instruction>,
}
impl<'tbl> BlockBuilder<'tbl> {
fn new(node: NodeIndex, symbol_table: &'tbl mut SymbolTable) -> Self {
Self {
state: State::new(node, symbol_table),
instructions: Default::default(),
}
}
fn done(self) -> (Vec<Instruction>, StackInfo) {
let stack_info = StackInfo {
arguments: self.state.borrows,
generates: self.state.stack,
height: self.state.height,
};
assert_eq!(
stack_info.arguments as i32 + stack_info.height,
stack_info.generates.len() as i32,
"local stack elements must equal stack arguments taken + local height"
);
(self.instructions, stack_info)
}
fn translate(&mut self, opcode: &evmil::bytecode::Instruction) {
use evmil::bytecode::Instruction::*;
self.instructions.extend(match opcode {
JUMPDEST => Vec::new(),
PUSH(bytes) => {
self.state.push(Symbol::builder().constant(bytes));
Vec::new()
}
POP => {
self.state.pop();
Vec::new()
}
SWAP(n) => self.state.swap(*n as usize),
DUP(n) => vec![Instruction::Copy {
y: self.state.nth(*n as usize),
x: self.state.push(Symbol::builder().variable()),
}],
ADD => vec![Instruction::BinaryAssign {
y: self.state.pop(),
z: self.state.pop(),
x: self.state.push(Symbol::builder().variable()),
operator: Operator::Add,
}],
SUB => vec![Instruction::BinaryAssign {
y: self.state.pop(),
z: self.state.pop(),
x: self.state.push(Symbol::builder().variable()),
operator: Operator::Sub,
}],
MSTORE => vec![Instruction::IndexedAssign {
x: self.state.symbol_table.global(Global::Memory),
index: self.state.pop(),
y: self.state.pop(),
}],
MLOAD => vec![Instruction::IndexedCopy {
index: self.state.pop(),
x: self.state.push(Symbol::builder().variable()),
y: self.state.symbol_table.global(Global::Memory),
}],
JUMP => vec![Instruction::UncoditionalBranch {
target: self.state.pop(),
}],
JUMPI => vec![Instruction::ConditionalBranch {
target: self.state.pop(),
condition: self.state.pop(),
}],
CALLDATACOPY => vec![Instruction::Procedure {
symbol: Global::CallDataCopy,
parameters: vec![self.state.pop(), self.state.pop(), self.state.pop()],
}],
CALLDATALOAD => vec![Instruction::IndexedCopy {
index: self.state.pop(),
x: self.state.push(Symbol::builder().variable()),
y: self.state.symbol_table.global(Global::CallData),
}],
RETURN => vec![Instruction::Procedure {
symbol: Global::Return,
parameters: vec![self.state.pop(), self.state.pop()],
}],
GT => vec![Instruction::BinaryAssign {
y: self.state.pop(),
z: self.state.pop(),
x: self.state.push(Symbol::builder().variable()),
operator: Operator::GreaterThan,
}],
LT => vec![Instruction::BinaryAssign {
y: self.state.pop(),
z: self.state.pop(),
x: self.state.push(Symbol::builder().variable()),
operator: Operator::LessThan,
}],
EQ => vec![Instruction::BinaryAssign {
y: self.state.pop(),
z: self.state.pop(),
x: self.state.push(Symbol::builder().variable()),
operator: Operator::Equal,
}],
ISZERO => vec![Instruction::UnaryAssign {
y: self.state.pop(),
x: self.state.push(Symbol::builder().variable()),
operator: Operator::IsZero,
}],
_ => {
eprintln!("unimplement instruction: {opcode}");
Vec::new()
}
})
}
}
struct State<'tbl> {
node: NodeIndex,
symbol_table: &'tbl mut SymbolTable,
stack: Vec<SymbolRef>,
/// Every pop on an empty stack was counts as an additional argument.
borrows: usize,
/// Caches the arguments the block borrows from the stack.
arguments: IndexMap<usize, SymbolRef>,
/// Tracks the relative stack height:
/// - Pushes increase the height by one
/// - Pops decrease the height by one
height: i32,
}
impl<'tbl> State<'tbl> {
fn new(node: NodeIndex, symbol_table: &'tbl mut SymbolTable) -> Self {
Self {
node,
symbol_table,
stack: Default::default(),
borrows: Default::default(),
arguments: Default::default(),
height: Default::default(),
}
}
fn pop(&mut self) -> SymbolRef {
self.height -= 1;
self.stack.pop().unwrap_or_else(|| {
self.borrows += 1;
self.nth(0)
})
}
fn push(&mut self, builder: SymbolBuilder<(), Kind>) -> SymbolRef {
let symbol = builder.temporary().done();
let symbol = self.symbol_table.insert(self.node, symbol);
self.stack.push(symbol.clone());
self.height += 1;
symbol
}
fn swap(&mut self, n: usize) -> Vec<Instruction> {
// For free if both elements are local to the basic block
let top = self.stack.len().saturating_sub(1);
if n <= top {
self.stack.swap(top - n, top);
return Vec::new();
}
let tmp = self.symbol_table.temporary(self.node);
let a = self.nth(0);
let b = self.nth(n);
vec![
Instruction::Copy {
x: tmp.clone(),
y: a.clone(),
},
Instruction::Copy { x: a, y: b.clone() },
Instruction::Copy { x: b, y: tmp },
]
}
fn nth(&mut self, n: usize) -> SymbolRef {
self.stack
.iter()
.rev()
.nth(n)
.or_else(|| self.arguments.get(&(self.slot(n) as usize)))
.cloned()
.unwrap_or_else(|| {
let builder = Symbol::builder().stack(self.slot(n)).variable();
let symbol = self.symbol_table.insert(self.node, builder.done());
self.arguments.insert(self.slot(n) as usize, symbol.clone());
symbol
})
}
fn slot(&self, n: usize) -> i32 {
n as i32 - (self.stack.len() as i32 - self.borrows as i32)
}
}
#[cfg(test)]
mod tests {
use super::{BlockBuilder, State};
use crate::{
cfg::StackInfo,
instruction::Instruction,
symbol::{Symbol, SymbolTable},
};
use evmil::bytecode::Instruction::*;
fn translate<'tbl>(code: &[evmil::bytecode::Instruction]) -> (Vec<Instruction>, StackInfo) {
code.iter()
.fold(
BlockBuilder::new(Default::default(), &mut SymbolTable::default()),
|mut builder, instruction| {
builder.translate(instruction);
builder
},
)
.done()
}
#[test]
fn stack_slot_works() {
let mut symbol_table = SymbolTable::default();
let mut state = State::new(Default::default(), &mut symbol_table);
state.push(Symbol::builder().variable());
assert_eq!(state.slot(0), -1);
assert_eq!(state.slot(1), 0);
assert_eq!(state.slot(2), 1);
state.pop();
state.pop();
assert_eq!(state.slot(0), 1);
assert_eq!(state.slot(1), 2);
assert_eq!(state.slot(2), 3);
state.push(Symbol::builder().variable());
state.push(Symbol::builder().variable());
assert_eq!(state.slot(0), -1);
assert_eq!(state.slot(1), 0);
assert_eq!(state.slot(2), 1);
}
#[test]
fn push_works() {
let state = translate(&[PUSH(vec![1])]).1;
assert_eq!(state.height, 1);
assert_eq!(state.arguments, 0);
assert_eq!(state.generates.len(), 1);
}
#[test]
fn add_works() {
let state = translate(&[ADD]).1;
assert_eq!(state.height, -1);
assert_eq!(state.arguments, 2);
assert_eq!(state.generates.len(), 1);
}
#[test]
fn dup_works() {
let state = translate(&[DUP(4)]).1;
assert_eq!(state.height, 1);
assert_eq!(state.arguments, 0);
assert_eq!(state.generates.len(), 1);
}
#[test]
fn swap_works() {
let state = translate(&[SWAP(4)]).1;
assert_eq!(state.height, 0);
assert_eq!(state.arguments, 0);
assert_eq!(state.generates.len(), 0);
}
#[test]
fn jump() {
let state = translate(&[JUMP]).1;
assert_eq!(state.height, -1);
assert_eq!(state.arguments, 1);
assert_eq!(state.generates.len(), 0);
}
#[test]
fn pop5_push2() {
let state = translate(&[POP, POP, POP, POP, POP, PUSH(vec![1]), PUSH(vec![1])]).1;
assert_eq!(state.height, -3);
assert_eq!(state.arguments, 5);
assert_eq!(state.generates.len(), 2);
}
#[test]
fn fibonacci_loop_body() {
let state = translate(&[
PUSH(vec![1]),
ADD,
SWAP(2),
DUP(1),
SWAP(4),
ADD,
SWAP(2),
PUSH(vec![10]),
JUMP,
])
.1;
assert_eq!(state.height, 0);
assert_eq!(state.arguments, 1);
assert_eq!(state.generates.len(), 1);
}
}
+31
View File
@@ -0,0 +1,31 @@
use petgraph::prelude::*;
use crate::cfg::Program;
pub mod control_flow;
pub mod dominance;
pub mod evm_bytecode;
pub mod types;
/// The analyzer visits each basic block using DFS.
pub trait BlockAnalysis: Default {
fn analyze_block(&mut self, node: NodeIndex, program: &mut Program);
fn apply_results(&mut self, program: &mut Program);
}
pub fn analyze<Pass>(program: &mut Program) -> Pass
where
Pass: BlockAnalysis,
{
let mut dfs = Dfs::new(&program.cfg.graph, program.cfg.start);
let mut pass = Pass::default();
while let Some(node) = dfs.next(&program.cfg.graph) {
pass.analyze_block(node, program);
}
pass.apply_results(program);
pass
}
+10
View File
@@ -0,0 +1,10 @@
use indexmap::IndexMap;
use petgraph::prelude::*;
use crate::{
cfg::{Branch, Program},
instruction::Instruction,
symbol::Kind,
};
use super::BlockAnalysis;
+42
View File
@@ -0,0 +1,42 @@
use crate::{cfg::Program, instruction::Instruction, symbol::Type, POINTER_SIZE};
use petgraph::prelude::*;
use super::BlockAnalysis;
#[derive(Default)]
pub struct TypePropagation;
impl BlockAnalysis for TypePropagation {
fn analyze_block(&mut self, node: NodeIndex, program: &mut Program) {
for instruction in &program.cfg.graph[node].instructions {
match instruction {
Instruction::ConditionalBranch { condition, target } => {
condition.replace_type(Type::Bool);
target.replace_type(Type::Int(POINTER_SIZE));
}
Instruction::UncoditionalBranch { target } => {
target.replace_type(Type::Int(POINTER_SIZE));
}
Instruction::BinaryAssign { x, y, z, .. } => {
y.replace_type(x.symbol().type_hint);
z.replace_type(x.symbol().type_hint);
}
Instruction::Copy { x, y } | Instruction::UnaryAssign { x, y, .. } => {
x.replace_type(y.symbol().type_hint);
}
Instruction::IndexedCopy { index, .. }
| Instruction::IndexedAssign { index, .. } => {
index.replace_type(Type::Int(POINTER_SIZE))
}
_ => {}
}
}
}
fn apply_results(&mut self, _program: &mut Program) {}
}
+154 -137
View File
@@ -1,48 +1,74 @@
use std::fmt::Write;
use std::ops::Range;
use evmil::bytecode;
use petgraph::{
dot::{Config, Dot},
graph::DiGraph,
stable_graph::NodeIndex,
};
use indexmap::IndexMap;
use petgraph::dot::{Config, Dot};
use petgraph::prelude::*;
use crate::{
instruction::{self, Instruction},
symbol::SymbolTable,
};
use crate::pass::dead_code::DeadCodeElimination;
use crate::pass::lift::BytecodeLifter;
use crate::pass::Pass;
use crate::symbol::SymbolRef;
use crate::{instruction::Instruction, symbol::SymbolTable};
pub struct Cfg {
pub graph: StableDiGraph<BasicBlock, Branch>,
pub start: NodeIndex,
pub jump_table: NodeIndex,
pub terminator: NodeIndex,
pub invalid_jump: NodeIndex,
}
#[derive(Debug, PartialEq)]
pub enum Branch {
Static,
Dynamic,
}
impl Default for Cfg {
fn default() -> Self {
let mut graph = StableDiGraph::new();
Self {
start: graph.add_node(Default::default()),
jump_table: graph.add_node(Default::default()),
terminator: graph.add_node(Default::default()),
invalid_jump: graph.add_node(Default::default()),
graph,
}
}
}
#[derive(Clone, Debug)]
pub struct EvmInstruction {
pub bytecode_offset: usize,
pub instruction: bytecode::Instruction,
pub instruction: evmil::bytecode::Instruction,
}
#[derive(Debug, Default)]
pub struct BasicBlock {
pub entry: Option<Entry>,
pub opcodes: Range<usize>,
pub instructions: Vec<Instruction>,
pub stack_info: StackInfo,
}
#[derive(Clone, Copy, Default)]
pub enum BasicBlockFormatOption {
ByteCode,
Ir,
#[default]
None,
#[derive(Debug, Default)]
pub struct StackInfo {
pub arguments: usize,
pub generates: Vec<SymbolRef>,
pub height: i32,
}
impl BasicBlock {
fn linear_at(start: usize) -> Self {
Self {
opcodes: start..start + 1,
..Default::default()
}
}
fn format(&self, evm_bytecode: &[EvmInstruction], options: BasicBlockFormatOption) -> String {
let offset = evm_bytecode[self.opcodes.start].bytecode_offset;
let start = if let Some(Entry::Start) = self.entry {
"Start\n".to_string()
} else {
String::new()
};
let instructions = match options {
match options {
BasicBlockFormatOption::ByteCode => evm_bytecode[self.opcodes.start..self.opcodes.end]
.iter()
.fold(String::new(), |mut acc, opcode| {
@@ -58,164 +84,155 @@ impl BasicBlock {
})
}
_ => String::new(),
};
format!("{start}Offset: 0x{offset:02x}\n---\n{instructions}")
}
}
}
#[derive(Clone, Debug)]
pub enum Entry {
Start,
Jumpdest(NodeIndex),
Else(NodeIndex),
}
#[derive(Debug)]
pub enum Jump {
Direct,
Indirect,
#[derive(Clone, Copy, Default)]
pub enum BasicBlockFormatOption {
ByteCode,
Ir,
#[default]
None,
}
pub struct Program {
pub evm_instructions: Vec<EvmInstruction>,
pub cfg: DiGraph<BasicBlock, Jump>,
pub cfg: Cfg,
pub symbol_table: SymbolTable,
pub jump_targets: IndexMap<usize, NodeIndex>,
}
impl Program {
pub fn new(bytecode: Vec<bytecode::Instruction>) -> Self {
let mut cfg = DiGraph::new();
let mut symbol_table = SymbolTable::default();
/// Create a new [Program] from EVM bytecode.
///
/// - Dynamic jumps reach the dynamic jump table
/// - `JUMPDEST` and `JUMPI` split up the node
/// - Instructions not returning reach the terminator node
pub fn new(bytecode: &[evmil::bytecode::Instruction]) -> Self {
let mut evm_instructions = Vec::with_capacity(bytecode.len());
let mut current_block = Some(BasicBlock {
entry: Some(Entry::Start),
..Default::default()
});
let mut cfg = Cfg::default();
let mut jump_targets = IndexMap::default();
let mut bytecode_offset = 0;
let mut node = cfg.graph.add_node(Default::default());
cfg.graph.add_edge(cfg.start, node, Branch::Static);
cfg.graph
.add_edge(cfg.invalid_jump, cfg.terminator, Branch::Static);
cfg.graph
.add_edge(cfg.jump_table, cfg.invalid_jump, Branch::Dynamic);
for (index, opcode) in bytecode.iter().enumerate() {
evm_instructions.push(EvmInstruction {
bytecode_offset,
instruction: opcode.clone(),
});
bytecode_offset += opcode.length();
cfg.graph[node].opcodes.end = index + 1;
let instructions = instruction::translate(opcode, &mut symbol_table);
use bytecode::Instruction::*;
use evmil::bytecode::Instruction::*;
match opcode {
JUMPDEST => {
// If we are already in a bb, conclude it
let entry = current_block.take().map(|mut node| {
node.opcodes.end = index + 1;
let entry = node.entry.clone();
let node_index = cfg.add_node(node);
// The preceding instruction did already split up control flow
JUMPDEST
if matches!(
evm_instructions[index.saturating_sub(1)].instruction,
JUMP | JUMPI | RETURN | REVERT | INVALID | STOP | SELFDESTRUCT
) =>
{
cfg.graph.add_edge(cfg.jump_table, node, Branch::Dynamic);
// If the block had an entry, add an edge from the previous block to it
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry
{
cfg.add_edge(incoming, node_index, Jump::Direct);
}
node_index
});
// JUMPDEST implicitly starts a new bb
current_block = Some(BasicBlock {
entry: entry.map(Entry::Jumpdest),
opcodes: Range {
start: index + 1,
end: index + 1,
},
..Default::default()
});
jump_targets.insert(bytecode_offset, node);
}
JUMP | STOP | RETURN | REVERT | INVALID => {
// Conclude this bb; if we are not already in a bb we must create a new one
let mut node = current_block.take().unwrap_or_else(|| BasicBlock {
opcodes: Range {
start: index,
end: index + 1,
},
..Default::default()
});
node.instructions.extend(instructions);
node.opcodes.end = index + 1;
JUMPDEST => {
cfg.graph[node].opcodes.end = index;
let previous_node = node;
node = cfg.graph.add_node(BasicBlock::linear_at(index));
let entry = node.entry.clone();
let node_index = cfg.add_node(node);
cfg.graph.add_edge(cfg.jump_table, node, Branch::Dynamic);
cfg.graph.add_edge(previous_node, node, Branch::Static);
// If the block had an entry, add an edge from the previous block to it
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry {
cfg.add_edge(incoming, node_index, Jump::Direct);
}
jump_targets.insert(bytecode_offset, node);
}
JUMP => {
cfg.graph.add_edge(node, cfg.jump_table, Branch::Dynamic);
node = cfg.graph.add_node(BasicBlock::linear_at(index + 1));
}
JUMPI => {
// Conclude this bb; if we are not already in a bb we must create a new one
let mut node = current_block.take().unwrap_or_else(|| BasicBlock {
opcodes: Range {
start: index,
end: index + 1,
},
..Default::default()
});
node.instructions.extend(instructions);
node.opcodes.end = index + 1;
cfg.graph.add_edge(node, cfg.jump_table, Branch::Dynamic);
let entry = node.entry.clone();
let node_index = cfg.add_node(node);
// If the block had an entry, add an edge from the previous block to it
if let Some(Entry::Else(incoming)) | Some(Entry::Jumpdest(incoming)) = entry {
cfg.add_edge(incoming, node_index, Jump::Direct);
}
// JUMPI implicitly starts a new bb for the else branch
current_block = Some(BasicBlock {
entry: Some(Entry::Else(node_index)),
opcodes: Range {
start: index + 1,
end: index + 1,
},
..Default::default()
});
let previous_node = node;
node = cfg.graph.add_node(BasicBlock::linear_at(index + 1));
cfg.graph.add_edge(previous_node, node, Branch::Static);
}
_ => current_block
.get_or_insert(BasicBlock {
opcodes: Range {
start: index,
end: index + 1,
},
..Default::default()
})
.instructions
.extend(instructions),
STOP | RETURN | REVERT | INVALID | SELFDESTRUCT => {
cfg.graph.add_edge(node, cfg.terminator, Branch::Static);
node = cfg.graph.add_node(BasicBlock::linear_at(index + 1));
}
_ => {}
}
bytecode_offset += opcode.length();
}
Self {
evm_instructions,
cfg,
symbol_table,
symbol_table: Default::default(),
jump_targets,
}
}
pub fn optimize(&mut self) {
DeadCodeElimination::run(&mut Default::default(), self);
BytecodeLifter::run(&mut Default::default(), self);
DeadCodeElimination::run(&mut Default::default(), self)
}
pub fn dot(&self, format_options: BasicBlockFormatOption) {
let get_node_attrs = move |_, (_, node): (_, &BasicBlock)| {
format!(
"label = \"{}\"",
node.format(&self.evm_instructions, format_options)
)
let get_node_attrs = move |_, (index, node): (_, &BasicBlock)| {
let (color, shape, label) = if index == self.cfg.terminator {
("red", "oval", "Terminator".to_string())
} else if index == self.cfg.start {
("red", "oval", "Start".to_string())
} else if index == self.cfg.invalid_jump {
("blue", "hexagon", "Invalid jump target".to_string())
} else if index == self.cfg.jump_table {
("blue", "diamond", "Dynamic jump table".to_string())
} else {
let instructions = node.format(&self.evm_instructions, format_options);
let start = &self.evm_instructions[node.opcodes.start].bytecode_offset;
let end = &self
.evm_instructions
.get(node.opcodes.end)
.unwrap_or_else(|| &self.evm_instructions[node.opcodes.end - 1])
.bytecode_offset;
(
"black",
"rectangle",
format!("Bytecode (0x{start:02x}, 0x{end:02x}]\n---\n{instructions}",),
)
};
format!("color={color} shape={shape} label=\"{label}\"",)
};
let get_edge_attrs = |_, edge: petgraph::stable_graph::EdgeReference<'_, Branch>| {
let style = match edge.weight() {
Branch::Static => "solid",
Branch::Dynamic => "dashed",
};
format!("style={style}")
};
let dot = Dot::with_attr_getters(
&self.cfg,
&self.cfg.graph,
&[Config::EdgeNoLabel, Config::NodeNoLabel],
&|_, edge| format!("label = \"{:?}\"", edge.weight()),
&get_edge_attrs,
&get_node_attrs,
);
+36 -303
View File
@@ -1,67 +1,68 @@
use evmil::bytecode::Instruction as EvmInstruction;
use primitive_types::U256;
use crate::symbol::{Global, SymbolRef};
use std::fmt::Write;
use crate::{
symbol::{Global, Symbol, SymbolTable, Type},
POINTER_SIZE,
};
#[derive(PartialEq, Debug)]
pub enum Instruction {
Nop,
/// `x = y op z`
BinaryAssign {
x: Symbol,
y: Symbol,
x: SymbolRef,
y: SymbolRef,
operator: Operator,
z: Symbol,
z: SymbolRef,
},
/// `x = op y`
UnaryAssign {
x: Symbol,
x: SymbolRef,
operator: Operator,
y: Symbol,
y: SymbolRef,
},
/// `branch target`
UncoditionalBranch { target: Symbol },
UncoditionalBranch {
target: SymbolRef,
},
/// `branch target if condition`
ConditionalBranch { condition: Symbol, target: Symbol },
ConditionalBranch {
condition: SymbolRef,
target: SymbolRef,
},
/// `call(label, n)`
Procedure {
symbol: Global,
parameters: Vec<Symbol>,
parameters: Vec<SymbolRef>,
},
/// `x = call(label, n)`
Function {
symbol: Global,
x: Symbol,
parameters: Vec<Symbol>,
x: SymbolRef,
parameters: Vec<SymbolRef>,
},
/// `x = y`
Copy { x: Symbol, y: Symbol },
Copy {
x: SymbolRef,
y: SymbolRef,
},
/// `x[index] = y`
IndexedAssign { x: Symbol, index: Symbol, y: Symbol },
IndexedAssign {
x: SymbolRef,
index: SymbolRef,
y: SymbolRef,
},
/// `x = y[index]`
IndexedCopy { x: Symbol, y: Symbol, index: Symbol },
}
impl Instruction {
fn target_address(&self) -> Symbol {
match self {
Instruction::Copy { x, .. } => *x,
Instruction::IndexedAssign { x, .. } => *x,
Instruction::IndexedCopy { x, .. } => *x,
_ => unreachable!(),
}
}
IndexedCopy {
x: SymbolRef,
y: SymbolRef,
index: SymbolRef,
},
}
impl std::fmt::Display for Instruction {
@@ -104,6 +105,8 @@ impl std::fmt::Display for Instruction {
Self::IndexedAssign { x, index, y } => write!(f, "{x}[{index}] = {y}"),
Self::IndexedCopy { x, y, index } => write!(f, "{x} = {y}[{index}]"),
Self::Nop => write!(f, "no-op"),
}
}
}
@@ -122,11 +125,11 @@ pub enum Operator {
Exp,
SignExtend,
LessThat,
LessThan,
GreaterThan,
SignedLessThan,
SignedGreaterThan,
Eq,
Equal,
IsZero,
And,
@@ -138,273 +141,3 @@ pub enum Operator {
ShiftRight,
ShiftArithmeticRight,
}
struct StackPop {
decrement: Instruction,
load: Instruction,
}
/// Pop a value from the stack.
///
/// Returns 2 `Instruction`: Decrementing the stack pointer and the value copy.
fn stack_pop(symbol_table: &mut SymbolTable) -> StackPop {
let decrement = decrement_stack_height(symbol_table);
let load = Instruction::IndexedCopy {
x: symbol_table.temporary(None),
y: symbol_table.global(Global::Stack),
index: symbol_table.global(Global::StackHeight),
};
StackPop { decrement, load }
}
/// Decrease the stack height by one.
fn decrement_stack_height(symbol_table: &mut SymbolTable) -> Instruction {
Instruction::BinaryAssign {
x: symbol_table.global(Global::StackHeight),
y: symbol_table.global(Global::StackHeight),
operator: Operator::Sub,
z: symbol_table.constant(U256::one(), Some(Global::StackHeight.typ())),
}
}
struct StackPush {
assign: Instruction,
increment: Instruction,
}
/// Push a `value` to the stack.
///
/// Returns 2 `Instruction`: the value assign and the stack height increase.
fn stack_push(symbol_table: &mut SymbolTable, value: Symbol) -> StackPush {
let assign = Instruction::IndexedAssign {
x: symbol_table.global(Global::Stack),
index: symbol_table.global(Global::StackHeight),
y: value,
};
let increment = increment_stack_height(symbol_table);
StackPush { assign, increment }
}
/// Increment the stack height by one.
fn increment_stack_height(symbol_table: &mut SymbolTable) -> Instruction {
Instruction::BinaryAssign {
x: symbol_table.global(Global::StackHeight),
y: symbol_table.global(Global::StackHeight),
operator: Operator::Add,
z: symbol_table.constant(U256::one(), Some(Global::StackHeight.typ())),
}
}
/// Lower an EVM instruction into corresponding 3AC instructions.
pub fn translate(opcode: &EvmInstruction, symbol_table: &mut SymbolTable) -> Vec<Instruction> {
use EvmInstruction::*;
match opcode {
JUMPDEST => Vec::new(),
PUSH(bytes) => {
let type_hint = Some(Type::Bytes(bytes.len()));
let value = symbol_table.constant(U256::from_big_endian(bytes), type_hint);
let push = stack_push(symbol_table, value);
vec![push.assign, push.increment]
}
POP => vec![decrement_stack_height(symbol_table)],
MSTORE => {
let offset = stack_pop(symbol_table);
let value = stack_pop(symbol_table);
let store = Instruction::IndexedAssign {
x: symbol_table.global(Global::Memory),
index: offset.load.target_address(),
y: value.load.target_address(),
};
vec![
offset.decrement,
offset.load,
value.decrement,
value.load,
store,
]
}
JUMP => {
let target = stack_pop(symbol_table);
let jump = Instruction::UncoditionalBranch {
target: target.load.target_address(),
};
vec![target.decrement, target.load, jump]
}
RETURN => {
let offset = stack_pop(symbol_table);
let size = stack_pop(symbol_table);
let procedure = Instruction::Procedure {
symbol: Global::Return,
parameters: vec![offset.load.target_address(), size.load.target_address()],
};
vec![
offset.decrement,
offset.load,
size.decrement,
size.load,
procedure,
]
}
CALLDATACOPY => {
let destination_offset = stack_pop(symbol_table);
let offset = stack_pop(symbol_table);
let size = stack_pop(symbol_table);
let parameters = vec![
destination_offset.load.target_address(),
offset.load.target_address(),
size.load.target_address(),
];
let procedure = Instruction::Procedure {
symbol: Global::MemoryCopy,
parameters,
};
vec![
destination_offset.decrement,
destination_offset.load,
offset.decrement,
offset.load,
size.decrement,
size.load,
procedure,
]
}
CALLDATALOAD => {
let index = stack_pop(symbol_table);
let value = Instruction::IndexedCopy {
x: symbol_table.temporary(None),
y: symbol_table.global(Global::CallData),
index: index.load.target_address(),
};
let push = stack_push(symbol_table, value.target_address());
vec![
index.decrement,
index.load,
value,
push.assign,
push.increment,
]
}
STOP => {
vec![Instruction::Procedure {
symbol: Global::Stop,
parameters: Default::default(),
}]
}
INVALID => {
let offset = symbol_table.constant(U256::zero(), Some(Type::Int(POINTER_SIZE)));
let size = symbol_table.constant(U256::zero(), Some(Type::Int(POINTER_SIZE)));
vec![Instruction::Procedure {
symbol: Global::Revert,
parameters: vec![offset, size],
}]
}
REVERT => {
let offset = stack_pop(symbol_table);
let size = stack_pop(symbol_table);
let procedure = Instruction::Procedure {
symbol: Global::Revert,
parameters: vec![offset.load.target_address(), size.load.target_address()],
};
vec![
offset.decrement,
offset.load,
size.decrement,
size.load,
procedure,
]
}
//_ => todo!("{opcode}"),
_ => Vec::new(),
}
}
#[cfg(test)]
mod tests {
use evmil::bytecode;
use primitive_types::U256;
use crate::{
instruction::Operator,
symbol::{Address, Global, Kind, Symbol, Type},
};
use super::Instruction;
#[test]
fn lower_push_works() {
let mut symbol_table = Default::default();
let opcode = bytecode::Instruction::PUSH(vec![0x01]);
let result = super::translate(&opcode, &mut symbol_table);
let expected = vec![
Instruction::IndexedAssign {
x: Symbol {
address: Address::Label(Global::Stack),
type_hint: Global::Stack.typ(),
kind: Global::Stack.kind(),
},
index: Symbol {
address: Address::Label(Global::StackHeight),
type_hint: Global::StackHeight.typ(),
kind: Global::StackHeight.kind(),
},
y: Symbol {
address: Address::Constant(U256::one()),
type_hint: Type::Bytes(1),
kind: Kind::Value,
},
},
Instruction::BinaryAssign {
x: Symbol {
address: Address::Label(Global::StackHeight),
type_hint: Global::StackHeight.typ(),
kind: Global::StackHeight.kind(),
},
y: Symbol {
address: Address::Label(Global::StackHeight),
type_hint: Global::StackHeight.typ(),
kind: Global::StackHeight.kind(),
},
operator: Operator::Add,
z: Symbol {
address: Address::Constant(U256::one()),
type_hint: Global::StackHeight.typ(),
kind: Kind::Value,
},
},
];
assert_eq!(expected, result);
}
}
+2
View File
@@ -1,5 +1,7 @@
pub mod analysis;
pub mod cfg;
pub mod instruction;
pub mod pass;
pub mod symbol;
pub static POINTER_SIZE: usize = 32;
+15
View File
@@ -0,0 +1,15 @@
use crate::{
analysis::{analyze, control_flow::ReachableCode},
cfg::Program,
};
use super::Pass;
#[derive(Default)]
pub struct DeadCodeElimination;
impl Pass for DeadCodeElimination {
fn run(&mut self, program: &mut Program) {
analyze::<ReachableCode>(program);
}
}
+19
View File
@@ -0,0 +1,19 @@
use crate::{
analysis::{
analyze, control_flow::StaticJumps, evm_bytecode::IrBuilder, types::TypePropagation,
},
cfg::Program,
};
use super::Pass;
#[derive(Default)]
pub struct BytecodeLifter;
impl Pass for BytecodeLifter {
fn run(&mut self, program: &mut Program) {
analyze::<IrBuilder>(program);
analyze::<StaticJumps>(program);
analyze::<TypePropagation>(program);
}
}
+8
View File
@@ -0,0 +1,8 @@
use crate::cfg::Program;
pub mod dead_code;
pub mod lift;
pub trait Pass: Default {
fn run(&mut self, program: &mut Program);
}
+169 -48
View File
@@ -1,53 +1,136 @@
use indexmap::IndexSet;
use indexmap::IndexMap;
use petgraph::prelude::NodeIndex;
use primitive_types::U256;
use std::{cell::RefCell, rc::Rc};
use crate::POINTER_SIZE;
#[derive(Debug, Default)]
pub struct SymbolTable {
symbols: IndexSet<Symbol>,
nonce: usize,
table: IndexMap<NodeIndex, IndexMap<usize, Rc<RefCell<Symbol>>>>,
symbols: IndexMap<usize, Rc<RefCell<Symbol>>>,
global_scope: NodeIndex,
id_nonce: usize,
}
impl SymbolTable {
pub fn merge_scopes(&mut self, node: NodeIndex, target: NodeIndex) {
let sym = self.symbols.remove(&0).unwrap();
let new = self
.table
.get(&NodeIndex::default())
.unwrap()
.get(&0)
.unwrap();
//RefCell::replace(&sym, Rc::clone(new));
}
pub fn get_symbol(&self, id: usize) -> SymbolRef {
SymbolRef {
inner: self.symbols.get(&id).unwrap().clone(),
id,
}
}
pub fn insert(&mut self, scope: NodeIndex, symbol: Symbol) -> SymbolRef {
let id = self.next();
let inner = Rc::new(RefCell::new(symbol));
self.table
.entry(scope)
.or_default()
.insert(id, Rc::clone(&inner));
self.symbols.insert(id, inner.clone());
SymbolRef { inner, id }
}
pub fn global(&mut self, label: Global) -> SymbolRef {
self.table
.entry(self.global_scope)
.or_default()
.iter()
.find(|(_, symbol)| symbol.borrow().address == Address::Label(label))
.map(|(id, _)| *id)
.map(|id| self.get_symbol(id))
.unwrap_or_else(|| self.insert(self.global_scope, Symbol::builder().global(label)))
}
pub fn temporary(&mut self, node: NodeIndex) -> SymbolRef {
self.insert(node, Symbol::builder().temporary().variable().done())
}
fn next(&mut self) -> usize {
let current = self.nonce;
self.nonce += 1;
let current = self.id_nonce;
self.id_nonce += 1;
current
}
}
pub fn temporary(&mut self, type_hint: Option<Type>) -> Symbol {
let id = self.next();
let symbol = Symbol {
address: Address::Temporary(id),
type_hint: type_hint.unwrap_or_default(),
kind: Kind::Value,
};
assert!(self.symbols.insert(symbol));
#[derive(Default)]
pub struct SymbolBuilder<A = (), K = ()> {
address: A,
type_hint: Type,
kind: K,
}
symbol
impl<K> SymbolBuilder<(), K> {
pub fn temporary(self) -> SymbolBuilder<Address, K> {
SymbolBuilder {
address: Address::Temporary,
type_hint: self.type_hint,
kind: self.kind,
}
}
pub fn constant(&mut self, value: U256, type_hint: Option<Type>) -> Symbol {
let symbol = Symbol {
address: Address::Constant(value),
type_hint: type_hint.unwrap_or_default(),
kind: Kind::Value,
};
self.symbols.insert(symbol);
symbol
pub fn stack(self, slot: i32) -> SymbolBuilder<Address, K> {
SymbolBuilder {
address: Address::Stack(slot),
type_hint: self.type_hint,
kind: self.kind,
}
}
pub fn global(&mut self, label: Global) -> Symbol {
let symbol = Symbol {
pub fn global(self, label: Global) -> Symbol {
Symbol {
address: Address::Label(label),
type_hint: label.typ(),
kind: label.kind(),
};
self.symbols.insert(symbol);
}
}
}
symbol
impl<A> SymbolBuilder<A, ()> {
pub fn constant(self, bytes: &[u8]) -> SymbolBuilder<A, Kind> {
SymbolBuilder {
address: self.address,
type_hint: Type::Bytes(bytes.len()),
kind: Kind::Constant(U256::from_big_endian(bytes)),
}
}
pub fn variable(self) -> SymbolBuilder<A, Kind> {
SymbolBuilder {
address: self.address,
type_hint: self.type_hint,
kind: Kind::Variable,
}
}
}
impl<A, K> SymbolBuilder<A, K> {
pub fn of(self, type_hint: Type) -> Self {
Self { type_hint, ..self }
}
}
impl SymbolBuilder<Address, Kind> {
pub fn done(self) -> Symbol {
Symbol {
address: self.address,
type_hint: self.type_hint,
kind: self.kind,
}
}
}
@@ -58,40 +141,76 @@ pub struct Symbol {
pub kind: Kind,
}
impl std::fmt::Display for Symbol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({} {})", self.type_hint, self.address)?;
impl Symbol {
pub fn builder() -> SymbolBuilder {
Default::default()
}
}
match self.kind {
Kind::Pointer => write!(f, "*"),
_ => Ok(()),
#[derive(Clone, Debug)]
pub struct SymbolRef {
inner: Rc<RefCell<Symbol>>,
id: usize,
}
impl std::fmt::Display for SymbolRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let symbol = self.symbol();
let address = format!("${}_{}", self.id, symbol.address);
match symbol.kind {
Kind::Pointer => write!(f, "*{address}"),
Kind::Constant(value) => {
write!(f, "{} {address} := {value}", symbol.type_hint)
}
_ => write!(f, "{} {address} ", symbol.type_hint),
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
impl SymbolRef {
pub fn replace_type(&self, type_hint: Type) {
self.inner.replace_with(|old| Symbol {
address: old.address,
kind: old.kind,
type_hint,
});
}
pub fn symbol(&self) -> Symbol {
*self.inner.borrow()
}
pub fn id(&self) -> usize {
self.id
}
}
impl PartialEq for SymbolRef {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Address {
Constant(U256),
Temporary(usize),
#[default]
Temporary,
Stack(i32),
Label(Global),
}
impl std::fmt::Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Constant(value) => write!(f, "0x{value:02x}"),
Self::Temporary(n) => write!(f, "tmp_{n}"),
Self::Stack(slot) => write!(f, "stack[{slot}]"),
Self::Temporary => write!(f, "tmp"),
Self::Label(label) => write!(f, "{label:?}"),
}
}
}
impl Address {
pub fn from_be_bytes(bytes: &[u8]) -> Self {
Self::Constant(U256::from_big_endian(bytes))
}
}
#[derive(Debug, PartialEq, Eq, Hash, Default, Clone, Copy)]
pub enum Type {
#[default]
@@ -120,10 +239,12 @@ impl Type {
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Kind {
Pointer,
Value,
#[default]
Variable,
Constant(U256),
Function,
}
@@ -189,7 +310,7 @@ impl Global {
pub fn kind(&self) -> Kind {
match self {
Self::Stack | Self::CallData | Self::Memory | Self::ReturnData => Kind::Pointer,
Self::StackHeight => Kind::Value,
Self::StackHeight => Kind::Variable,
_ => Kind::Function,
}
}
+15 -13
View File
@@ -1,25 +1,27 @@
// SPDX-License-Identifier: Apache-2.0
fn llvm_config(arg: &str) -> String {
let output = std::process::Command::new("llvm-config")
.args([arg])
.output()
.unwrap_or_else(|_| panic!("`llvm-config {arg}` failed"));
use std::{io::Read, process::Command};
String::from_utf8(output.stdout)
.unwrap_or_else(|_| panic!("output of `llvm-config {arg}` should be utf8"))
}
fn main() {
let mut flags = String::new();
Command::new("llvm-config")
.args(["--cxxflags"])
.output()
.expect("llvm-config should be able to provide CXX flags")
.stdout
.as_slice()
.read_to_string(&mut flags)
.expect("llvm-config output should be utf8");
let mut builder = cc::Build::new();
flags
llvm_config("--cxxflags")
.split_whitespace()
.fold(&mut builder, |builder, flag| builder.flag(flag))
.cpp(true)
.file("src/linker.cpp")
.compile("liblinker.a");
println!("cargo:rustc-link-search=native={}", llvm_config("--libdir"));
for lib in ["lldELF", "lldCommon", "lldMachO"] {
println!("cargo:rustc-link-lib=static={lib}");
}
println!("cargo:rerun-if-changed=build.rs");
}
+1 -3
View File
@@ -1,5 +1,3 @@
// SPDX-License-Identifier: Apache-2.0
#include "lld/Common/Driver.h"
#include "lld/Common/CommonLinkerContext.h"
#include "llvm/Support/CrashRecoveryContext.h"
@@ -19,4 +17,4 @@ extern "C" bool LLDELFLink(const char *argv[], size_t length)
llvm::CrashRecoveryContext crc;
return canRunAgain && crc.RunSafely([&]()
{ lld::CommonLinkerContext::destroy(); });
}
}
-14
View File
@@ -1,14 +0,0 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "revive-target-polkavm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
inkwell = { workspace = true }
tempfile = { workspace = true }
polkavm-linker = { workspace = true }
polkavm-common = { workspace = true }
libc = { workspace = true }
revive-codegen = { path = "../codegen" }
revive-compilation-target = { path = "../compilation-target" }
revive-builtins = { path = "../builtins" }
lld-sys = { path = "../lld-sys" }
Binary file not shown.
+77
View File
@@ -0,0 +1,77 @@
use inkwell::{builder::Builder, context::Context, module::Module, values::FunctionValue};
use polkavm_common::elf::FnMetadata;
use revive_compilation_target::environment::Environment;
use revive_compilation_target::target::Target;
use crate::PolkaVm;
impl<'ctx> Environment<'ctx> for PolkaVm {
fn call_start(&'ctx self, builder: &Builder<'ctx>, start: FunctionValue<'ctx>) -> Module<'ctx> {
let module = self.context().create_module("entrypoint");
let (call, deploy) = pvm_exports(&self.0);
module.link_in_module(call).unwrap();
module.link_in_module(deploy).unwrap();
let function_type = self.context().void_type().fn_type(&[], false);
let call = module.add_function("call", function_type, None);
call.set_section(Some(".text.polkavm_export"));
builder.position_at_end(self.context().append_basic_block(call, "entry"));
builder.build_call(start, &[], "call_start");
builder.build_return(None);
let deploy = module.add_function("deploy", function_type, None);
deploy.set_section(Some(".text.polkavm_export"));
builder.position_at_end(self.context().append_basic_block(deploy, "entry"));
builder.build_unreachable();
builder.build_return(None);
module
}
}
pub(super) fn pvm_exports(context: &Context) -> (Module, Module) {
let call_m = context.create_module("pvm_call");
let deploy_m = context.create_module("pvm_deploy");
call_m.set_inline_assembly(&generate_export_assembly("call"));
deploy_m.set_inline_assembly(&generate_export_assembly("deploy"));
(call_m, deploy_m)
}
fn generate_export_assembly(symbol: &str) -> String {
let mut assembly = String::new();
assembly.push_str(".pushsection .polkavm_exports,\"\",@progbits\n");
assembly.push_str(".byte 1\n"); // Version.
assembly.push_str(&format!(".4byte {symbol}\n")); // Address
// Metadata
let mut metadata = Vec::new();
FnMetadata {
name: symbol.to_string(),
args: Default::default(),
return_ty: Default::default(),
}
.serialize(|slice| metadata.extend_from_slice(slice));
assembly.push_str(&bytes_to_asm(&metadata));
assembly.push_str(".popsection\n");
assembly
}
pub fn bytes_to_asm(bytes: &[u8]) -> String {
use std::fmt::Write;
let mut out = String::with_capacity(bytes.len() * 11);
for &byte in bytes {
writeln!(&mut out, ".byte 0x{:02x}", byte).unwrap();
}
out
}
+13
View File
@@ -0,0 +1,13 @@
use inkwell::context::Context;
pub mod environment;
pub mod linker;
pub mod target;
pub struct PolkaVm(Context);
impl Default for PolkaVm {
fn default() -> Self {
Self(Context::create())
}
}
+86
View File
@@ -0,0 +1,86 @@
use std::{ffi::CString, fs};
use lld_sys::LLDELFLink;
use revive_builtins::COMPILER_RT;
const LINKER_SCRIPT: &str = r#"
SECTIONS {
. = 0x10000;
.rodata : { *(.rodata) *(.rodata.*) }
.data.rel.ro : { *(.data.rel.ro) *(.data.rel.ro.*) }
.got : { *(.got) *(.got.*) }
. = ALIGN(0x4000);
.data : { *(.sdata) *(.data) }
.bss : { *(.sbss) *(.bss) *(.bss.*) }
. = 0xf0000000;
.text : { KEEP(*(.text.polkavm_export)) *(.text .text.*) }
/DISCARD/ : { *(.eh_frame) }
. = ALIGN(4);
}"#;
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 }
}
fn polkavm_linker(code: &[u8]) -> Vec<u8> {
let mut config = polkavm_linker::Config::default();
config.set_strip(true);
match polkavm_linker::program_from_elf(config, code) {
Ok(blob) => blob.as_bytes().to_vec(),
Err(reason) => panic!("polkavm linker failed: {}", reason),
}
}
pub(crate) fn link(input: &[u8]) -> 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("libclang_rt.builtins-riscv32.a");
fs::write(&object_path, input).unwrap_or_else(|msg| panic!("{msg} {object_path:?}"));
fs::write(&linker_script_path, LINKER_SCRIPT)
.unwrap_or_else(|msg| panic!("{msg} {linker_script_path:?}"));
fs::write(&compiler_rt_path, COMPILER_RT)
.unwrap_or_else(|msg| panic!("{msg} {compiler_rt_path:?}"));
let ld_args = [
"ld.lld",
"--error-limit=0",
"--relocatable",
"--emit-relocs",
"--no-relax",
"--gc-sections",
"--library-path",
dir.path().to_str().expect("should be utf8"),
"--library",
"clang_rt.builtins-riscv32",
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"),
];
assert!(!invoke_lld(&ld_args), "ld.lld failed");
fs::copy(&object_path, "/tmp/out.o").unwrap();
fs::copy(&output_path, "/tmp/out.so").unwrap();
fs::copy(&linker_script_path, "/tmp/linkder.ld").unwrap();
let blob = fs::read(&output_path).expect("ld.lld should produce output");
polkavm_linker(&blob)
}
+34
View File
@@ -0,0 +1,34 @@
use inkwell::{
context::Context, memory_buffer::MemoryBuffer, module::Module, targets::RelocMode,
OptimizationLevel,
};
use revive_compilation_target::target::Target;
use crate::PolkaVm;
impl<'ctx> Target<'ctx> for PolkaVm {
const TARGET_NAME: &'static str = "riscv32";
const TARGET_TRIPLE: &'static str = "riscv32-unknown-unknown-elf";
const TARGET_FEATURES: &'static str = "+e,+m";
const CPU: &'static str = "generic-rv32";
const RELOC_MODE: RelocMode = RelocMode::PIC;
fn libraries(&'ctx self) -> Vec<Module<'ctx>> {
let guest_bitcode = include_bytes!("../polkavm_guest.bc");
let imports = MemoryBuffer::create_from_memory_range(guest_bitcode, "guest_bc");
vec![Module::parse_bitcode_from_buffer(&imports, &self.0).unwrap()]
}
fn context(&self) -> &Context {
&self.0
}
fn link(&self, blob: &[u8]) -> Vec<u8> {
crate::linker::link(blob)
}
fn optimization_level(&self) -> OptimizationLevel {
OptimizationLevel::Aggressive
}
}
+2672
View File
File diff suppressed because it is too large Load Diff
View File
+1
View File
@@ -0,0 +1 @@
6080604052348015600f57600080fd5b50606180601d6000396000f3fe608060405260043610601c5760003560e01c8063c2985578146021575b600080fd5b60276029565b005b56fea2646970667358221220493add52a9d6686c38a8a20786dbed15474f43c23a0d7c3ab1f57988f13aa10664736f6c63430008170033
-1
View File
@@ -1 +0,0 @@
600035600160009160025b818111601c576001019180930191600a565b505060005260206000f350
+1
View File
@@ -0,0 +1 @@
60a0604052348015600f57600080fd5b506040516100ef3803806100ef8339818101604052810190602f91906072565b806080818152505050609a565b600080fd5b6000819050919050565b6052816041565b8114605c57600080fd5b50565b600081519050606c81604b565b92915050565b6000602082840312156085576084603c565b5b6000609184828501605f565b91505092915050565b608051603f6100b060003960005050603f6000f3fe6080604052600080fdfea26469706673582212207fa219f468d46a3e6f8235806d69ac4511b9f86f1459e1e01d108b9e701b902764736f6c63430008170033
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
ADD -1 [stack(-1)] [stack(-1) = stack(-1) + (stack(-2)]
ADD -2 [stack(-2)] [stack(-2) = stack(-1) + (stack(-1)]