mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-22 02:07:55 +00:00
@@ -1,2 +1,3 @@
|
||||
/target
|
||||
*.dot
|
||||
.vscode/
|
||||
|
||||
Generated
+327
-72
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
[package]
|
||||
name = "compiler-builtins"
|
||||
name = "revive-builtins"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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" }
|
||||
@@ -0,0 +1,2 @@
|
||||
mod module;
|
||||
pub mod program;
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod environment;
|
||||
pub mod target;
|
||||
@@ -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 +0,0 @@
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
use indexmap::IndexMap;
|
||||
use petgraph::prelude::*;
|
||||
|
||||
use crate::{
|
||||
cfg::{Branch, Program},
|
||||
instruction::Instruction,
|
||||
symbol::Kind,
|
||||
};
|
||||
|
||||
use super::BlockAnalysis;
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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,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(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Vendored
@@ -0,0 +1 @@
|
||||
6080604052348015600f57600080fd5b50606180601d6000396000f3fe608060405260043610601c5760003560e01c8063c2985578146021575b600080fd5b60276029565b005b56fea2646970667358221220493add52a9d6686c38a8a20786dbed15474f43c23a0d7c3ab1f57988f13aa10664736f6c63430008170033
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
600035600160009160025b818111601c576001019180930191600a565b505060005260206000f350
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
60a0604052348015600f57600080fd5b506040516100ef3803806100ef8339818101604052810190602f91906072565b806080818152505050609a565b600080fd5b6000819050919050565b6052816041565b8114605c57600080fd5b50565b600081519050606c81604b565b92915050565b6000602082840312156085576084603c565b5b6000609184828501605f565b91505092915050565b608051603f6100b060003960005050603f6000f3fe6080604052600080fdfea26469706673582212207fa219f468d46a3e6f8235806d69ac4511b9f86f1459e1e01d108b9e701b902764736f6c63430008170033
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user