Compare commits

...

37 Commits

Author SHA1 Message Date
xermicus ed4508fa9a events fn
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-14 11:00:05 +02:00
xermicus 3eadcb1c40 truncate to xlen fn
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-14 10:59:39 +02:00
xermicus 83bf9d6041 events
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-14 09:46:18 +02:00
xermicus 2194aeebd0 constant values for basefee and difficulty
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-13 18:47:36 +02:00
xermicus 02055c73bb extend mock runtime to allow executing constructors and cross contract calls
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-13 13:50:35 +02:00
xermicus 0e90317488 add contract name to integration to integration test cases
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-10 13:47:47 +02:00
xermicus 03a1918993 parallelize tests
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-10 10:54:01 +02:00
xermicus 5f5ec1a539 simplify wrapped divisions and sdiv cfg
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-10 10:16:40 +02:00
xermicus 6af889c1ff implement division and remainder operations
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-08 23:21:16 +02:00
Cyrill Leutwiler 864e40901f implement address and msg.sender
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-05-08 16:10:48 +02:00
Cyrill Leutwiler ea63991617 implement address and msg.sender
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-05-08 16:09:23 +02:00
Cyrill Leutwiler f80a96059d allow register sized int type for memory offsets
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-05-08 15:55:24 +02:00
xermicus 6f080bb9f4 rename binary to resolc
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-08 11:16:24 +02:00
xermicus e958da5cd1 rename file extensions
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-08 11:02:05 +02:00
xermicus b55669f5c5 remove the zkasm format
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-08 10:48:01 +02:00
Chris 169740eb5e fix: addressed assembly text build mechanism (#9)
Use `build.assembly_text` for `--asm` output
2024-05-08 09:36:17 +02:00
xermicus 95ff85c6d1 implement block.number and block.timestamp
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-07 18:03:17 +02:00
xermicus a7318f2ef6 implement helper for easy allocation of a word on the stack
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-07 13:40:54 +02:00
xermicus c0dd845b39 s/field/word
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-07 13:37:17 +02:00
xermicus 9f8a8a782d 1.78 clippies
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-06 14:34:51 +02:00
xermicus b1a3a452ac internalize register size
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-06 14:30:32 +02:00
xermicus 518c03d045 internalize runtime API function symbols
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-06 13:45:58 +02:00
Chris a75fc55133 feat: use PolkaVM disassembler (#6)
Integrate the PolkaVM disassembler to fix `--asm` output

Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-05-03 12:31:24 +02:00
Chris c547a9ef78 chore: added code formating target (#8)
Makefile: added code formating target

Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-05-03 11:36:07 +02:00
xermicus 336fc63f1d rename target to polkavm
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-02 08:47:44 +02:00
xermicus 9fc24af355 remove usage of llvm memory attributes
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-02 08:34:45 +02:00
Cyrill Leutwiler 85bd888f48 update benchmark results
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-05-01 22:41:28 +02:00
xermicus 426f673b0a use normal style for comments
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-01 16:12:32 +02:00
xermicus 72515254fe rename llvm-context crate
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-01 15:45:09 +02:00
xermicus 9b341853b4 update to polkavm with lazy execution
Signed-off-by: xermicus <cyrill@parity.io>
2024-04-30 15:23:44 +02:00
xermicus 018d9f39fc measure erc20 code size
Signed-off-by: xermicus <cyrill@parity.io>
2024-04-26 15:51:14 +02:00
xermicus 3d44685168 measure flipper code size
Signed-off-by: xermicus <cyrill@parity.io>
2024-04-26 10:51:41 +02:00
xermicus 44ae9e77d5 clippies
Signed-off-by: xermicus <cyrill@parity.io>
2024-04-26 10:39:56 +02:00
xermicus a85d1ef333 prevent confusing AlwaysInline and NoInline on O0
Signed-off-by: xermicus <cyrill@parity.io>
2024-04-26 10:36:28 +02:00
Cyrill Leutwiler 45d53f2ee4 test for codesizes
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-04-25 17:39:49 +02:00
Cyrill Leutwiler 34c233207e add clippy to Makefile
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-04-25 15:10:02 +02:00
Cyrill Leutwiler ec46b211ab extensive benchmark results
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-04-24 23:19:41 +02:00
232 changed files with 3883 additions and 4507 deletions
Generated
+253 -161
View File
@@ -68,7 +68,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
"syn-solidity",
"tiny-keccak",
]
@@ -92,16 +92,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
version = "1.0.6"
name = "anstream"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
]
[[package]]
name = "anyhow"
version = "1.0.82"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
[[package]]
name = "ark-ff"
@@ -241,14 +284,14 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
]
[[package]]
name = "autocfg"
version = "1.2.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "base16ct"
@@ -351,9 +394,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.95"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b"
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
[[package]]
name = "cfg-if"
@@ -406,6 +449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
@@ -414,8 +458,22 @@ version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.63",
]
[[package]]
@@ -424,6 +482,12 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "colored"
version = "2.1.0"
@@ -672,35 +736,11 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "era-compiler-llvm-context"
version = "1.4.1"
dependencies = [
"anyhow",
"hex",
"inkwell",
"itertools 0.12.1",
"md5",
"num",
"once_cell",
"pallet-contracts-pvm-llapi",
"regex",
"revive-builtins",
"revive-common",
"revive-linker",
"revive-stdlib",
"semver 1.0.22",
"serde",
"sha2",
"sha3",
"zkevm_opcode_defs",
]
[[package]]
name = "errno"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -755,9 +795,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fastrand"
version = "2.0.2"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "fastrlp"
@@ -817,9 +857,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
@@ -868,9 +908,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.3"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "heck"
@@ -887,6 +927,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
@@ -923,6 +969,15 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "iced-x86"
version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b"
dependencies = [
"lazy_static",
]
[[package]]
name = "impl-codec"
version = "0.6.0"
@@ -968,13 +1023,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
"hashbrown 0.14.5",
]
[[package]]
name = "inkwell"
version = "0.4.0"
source = "git+https://github.com/TheDan64/inkwell.git#5d5a531c765a6ad37aa6591c0287d0f9109fff62"
source = "git+https://github.com/TheDan64/inkwell.git#6c0fb56b3554e939f9ca61b465043d6a84fb7b95"
dependencies = [
"either",
"inkwell_internals",
@@ -988,11 +1043,11 @@ dependencies = [
[[package]]
name = "inkwell_internals"
version = "0.9.0"
source = "git+https://github.com/TheDan64/inkwell.git#5d5a531c765a6ad37aa6591c0287d0f9109fff62"
source = "git+https://github.com/TheDan64/inkwell.git#6c0fb56b3554e939f9ca61b465043d6a84fb7b95"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
]
[[package]]
@@ -1006,6 +1061,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itertools"
version = "0.10.5"
@@ -1064,9 +1125,9 @@ dependencies = [
[[package]]
name = "keccak-asm"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444"
checksum = "47a3633291834c4fbebf8673acbc1b04ec9d151418ff9b8e26dcd79129928758"
dependencies = [
"digest 0.10.7",
"sha3-asm",
@@ -1080,9 +1141,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.153"
version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]]
name = "libm"
@@ -1125,7 +1186,7 @@ dependencies = [
"lazy_static",
"libc",
"regex-lite",
"semver 1.0.22",
"semver 1.0.23",
]
[[package]]
@@ -1157,9 +1218,9 @@ dependencies = [
[[package]]
name = "num"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
@@ -1171,20 +1232,19 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
@@ -1200,9 +1260,9 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.44"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
@@ -1211,11 +1271,10 @@ dependencies = [
[[package]]
name = "num-rational"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
@@ -1223,9 +1282,9 @@ dependencies = [
[[package]]
name = "num-traits"
version = "0.2.18"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
@@ -1261,9 +1320,9 @@ dependencies = [
[[package]]
name = "parity-scale-codec"
version = "3.6.9"
version = "3.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe"
checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee"
dependencies = [
"arrayvec",
"bitvec",
@@ -1275,11 +1334,11 @@ dependencies = [
[[package]]
name = "parity-scale-codec-derive"
version = "3.6.9"
version = "3.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b"
checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c"
dependencies = [
"proc-macro-crate 2.0.2",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 1.0.109",
@@ -1287,9 +1346,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.14"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "path-slash"
@@ -1299,9 +1358,9 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
[[package]]
name = "pest"
version = "2.7.9"
version = "2.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95"
checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8"
dependencies = [
"memchr",
"thiserror",
@@ -1348,8 +1407,8 @@ dependencies = [
[[package]]
name = "polkavm"
version = "0.9.3"
source = "git+https://github.com/koute/polkavm.git#4ca06cc1b7cb435b2b92e81e30c2eb0e988f563e"
version = "0.10.0"
source = "git+https://github.com/koute/polkavm.git?rev=266658b#266658b328975ec07ec389f8d966a894f37a8916"
dependencies = [
"libc",
"log",
@@ -1360,27 +1419,39 @@ dependencies = [
[[package]]
name = "polkavm-assembler"
version = "0.9.0"
source = "git+https://github.com/koute/polkavm.git#4ca06cc1b7cb435b2b92e81e30c2eb0e988f563e"
version = "0.10.0"
source = "git+https://github.com/koute/polkavm.git?rev=266658b#266658b328975ec07ec389f8d966a894f37a8916"
dependencies = [
"log",
]
[[package]]
name = "polkavm-common"
version = "0.9.0"
source = "git+https://github.com/koute/polkavm.git#4ca06cc1b7cb435b2b92e81e30c2eb0e988f563e"
version = "0.10.0"
source = "git+https://github.com/koute/polkavm.git?rev=266658b#266658b328975ec07ec389f8d966a894f37a8916"
dependencies = [
"log",
]
[[package]]
name = "polkavm-disassembler"
version = "0.10.0"
source = "git+https://github.com/koute/polkavm.git?rev=266658b#266658b328975ec07ec389f8d966a894f37a8916"
dependencies = [
"clap 4.5.4",
"iced-x86",
"polkavm",
"polkavm-common",
"polkavm-linker",
]
[[package]]
name = "polkavm-linker"
version = "0.9.2"
source = "git+https://github.com/koute/polkavm.git#4ca06cc1b7cb435b2b92e81e30c2eb0e988f563e"
version = "0.10.0"
source = "git+https://github.com/koute/polkavm.git?rev=266658b#266658b328975ec07ec389f8d966a894f37a8916"
dependencies = [
"gimli",
"hashbrown 0.14.3",
"hashbrown 0.14.5",
"log",
"object",
"polkavm-common",
@@ -1390,8 +1461,8 @@ dependencies = [
[[package]]
name = "polkavm-linux-raw"
version = "0.9.0"
source = "git+https://github.com/koute/polkavm.git#4ca06cc1b7cb435b2b92e81e30c2eb0e988f563e"
version = "0.10.0"
source = "git+https://github.com/koute/polkavm.git?rev=266658b#266658b328975ec07ec389f8d966a894f37a8916"
[[package]]
name = "ppv-lite86"
@@ -1414,22 +1485,11 @@ dependencies = [
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
]
[[package]]
name = "proc-macro-crate"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24"
dependencies = [
"toml_datetime",
"toml_edit 0.20.2",
"toml_edit",
]
[[package]]
@@ -1458,9 +1518,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.81"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
@@ -1652,6 +1712,7 @@ dependencies = [
name = "revive-differential"
version = "0.1.0"
dependencies = [
"alloy-primitives",
"evm-interpreter",
"primitive-types",
]
@@ -1670,11 +1731,15 @@ dependencies = [
"alloy-primitives",
"alloy-sol-types",
"env_logger",
"era-compiler-llvm-context",
"hex",
"polkavm",
"rayon",
"revive-common",
"revive-differential",
"revive-llvm-context",
"revive-solidity",
"serde",
"serde_json",
"sha1",
]
@@ -1692,13 +1757,38 @@ dependencies = [
"tempfile",
]
[[package]]
name = "revive-llvm-context"
version = "1.4.1"
dependencies = [
"anyhow",
"hex",
"inkwell",
"itertools 0.12.1",
"md5",
"num",
"once_cell",
"pallet-contracts-pvm-llapi",
"polkavm-common",
"polkavm-disassembler",
"regex",
"revive-builtins",
"revive-common",
"revive-linker",
"revive-stdlib",
"semver 1.0.23",
"serde",
"sha2",
"sha3",
"zkevm_opcode_defs",
]
[[package]]
name = "revive-solidity"
version = "1.4.0"
dependencies = [
"anyhow",
"colored",
"era-compiler-llvm-context",
"hex",
"inkwell",
"md5",
@@ -1710,7 +1800,8 @@ dependencies = [
"rayon",
"regex",
"revive-common",
"semver 1.0.22",
"revive-llvm-context",
"semver 1.0.23",
"serde",
"serde_json",
"sha3",
@@ -1778,9 +1869,9 @@ checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343"
[[package]]
name = "rustc-demangle"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
@@ -1809,7 +1900,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.22",
"semver 1.0.23",
]
[[package]]
@@ -1839,9 +1930,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "same-file"
@@ -1854,9 +1945,9 @@ dependencies = [
[[package]]
name = "scale-info"
version = "2.11.2"
version = "2.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0"
checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024"
dependencies = [
"bitvec",
"cfg-if",
@@ -1867,11 +1958,11 @@ dependencies = [
[[package]]
name = "scale-info-derive"
version = "2.11.2"
version = "2.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7"
checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 1.0.109",
@@ -1902,9 +1993,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.22"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
dependencies = [
"serde",
]
@@ -1920,29 +2011,29 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.198"
version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.198"
version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
]
[[package]]
name = "serde_json"
version = "1.0.116"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
@@ -1993,9 +2084,9 @@ dependencies = [
[[package]]
name = "sha3-asm"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e"
checksum = "a9b57fd861253bff08bb1919e995f90ba8f4889de2726091c8876f3a4e823b40"
dependencies = [
"cc",
"cfg-if",
@@ -2058,6 +2149,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "structopt"
version = "0.3.26"
@@ -2101,9 +2198,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.60"
version = "2.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
dependencies = [
"proc-macro2",
"quote",
@@ -2119,7 +2216,7 @@ dependencies = [
"paste",
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
]
[[package]]
@@ -2151,22 +2248,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.59"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.59"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
]
[[package]]
@@ -2190,26 +2287,15 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.3"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
[[package]]
name = "toml_edit"
version = "0.19.15"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "toml_edit"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [
"indexmap",
"toml_datetime",
@@ -2260,9 +2346,15 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.11"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "valuable"
@@ -2322,7 +2414,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
"wasm-bindgen-shared",
]
@@ -2344,7 +2436,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2396,9 +2488,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
dependencies = [
"windows-sys 0.52.0",
]
@@ -2568,22 +2660,22 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.7.32"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
]
[[package]]
@@ -2603,7 +2695,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 2.0.63",
]
[[package]]
+5 -3
View File
@@ -27,9 +27,10 @@ path-slash = "0.2"
rayon = "1.8"
structopt = { version = "0.3", default-features = false }
rand = "0.8"
polkavm-common = { git = "https://github.com/koute/polkavm.git" }
polkavm-linker = { git = "https://github.com/koute/polkavm.git" }
polkavm = { git = "https://github.com/koute/polkavm.git" }
polkavm-common = { git = "https://github.com/koute/polkavm.git", rev = "266658b" }
polkavm-linker = { git = "https://github.com/koute/polkavm.git", rev = "266658b" }
polkavm-disassembler = { git = "https://github.com/koute/polkavm.git", rev = "266658b" }
polkavm = { git = "https://github.com/koute/polkavm.git", rev = "266658b" }
alloy-primitives = "0.6"
alloy-sol-types = "0.6"
env_logger = { version = "0.10.0", default-features = false }
@@ -49,3 +50,4 @@ features = ["serde", "llvm18-0", "no-libffi-linking", "target-riscv"]
[profile.benchmark]
inherits = "release"
lto = true
codegen-units = 1
+11 -2
View File
@@ -1,4 +1,4 @@
.PHONY: install test test-solidity test-cli test-integration clean
.PHONY: install format test test-solidity test-cli test-integration test-workspace clean
install: install-bin install-npm
@@ -8,7 +8,11 @@ install-bin:
install-npm:
npm install && npm fund
test: install test-integration test-cli test-solidity
format:
cargo fmt --all --check
test: format install test-cli test-workspace
cargo test --workspace
test-integration: install-bin
cargo test --package revive-integration
@@ -16,6 +20,9 @@ test-integration: install-bin
test-solidity: install
cargo test --package revive-solidity
test-workspace: install
cargo test --workspace
test-cli: install
npm run test:cli
@@ -38,6 +45,8 @@ bench: install-bin
cargo criterion --all --features bench-evm,bench-pvm --message-format=json \
| criterion-table > crates/benchmarks/BENCHMARKS.md
clippy:
cargo clippy --all-features --workspace --tests --benches
clean:
cargo clean ; \
+1 -1
View File
@@ -35,4 +35,4 @@ The project is in a very early PoC phase. Don't yet expect the produced code to
- [ ] Minimize scope of "stdlib"
- [ ] Document differences from EVM
- [ ] Audit for bugs and correctness
- [ ] Rebranding
- [x] Rebranding
+98
View File
@@ -0,0 +1,98 @@
# Benchmarks
## Table of Contents
- [Benchmark Results](#benchmark-results)
- [Baseline](#baseline)
- [OddPorduct](#oddporduct)
- [TriangleNumber](#trianglenumber)
- [FibonacciRecursive](#fibonaccirecursive)
- [FibonacciIterative](#fibonacciiterative)
- [PrepareBaseline](#preparebaseline)
- [PrepareOddProduct](#prepareoddproduct)
- [PrepareTriangleNumber](#preparetrianglenumber)
- [PrepareFibonacciRecursive](#preparefibonaccirecursive)
- [PrepareFibonacciIterative](#preparefibonacciiterative)
## Benchmark Results
### Baseline
| | `EVM` | `PVMInterpreter` | `PVM` |
|:--------|:--------------------------|:---------------------------------|:--------------------------------- |
| **`0`** | `900.78 ns` (✅ **1.00x**) | `715.00 ns` (✅ **1.26x faster**) | `26.22 us` (❌ *29.11x slower*) |
### OddPorduct
| | `EVM` | `PVMInterpreter` | `PVM` |
|:----------------|:--------------------------|:---------------------------------|:--------------------------------- |
| **`300000`** | `223.94 ms` (✅ **1.00x**) | `125.43 ms` (✅ **1.79x faster**) | `2.56 ms` (🚀 **87.60x faster**) |
| **`1200000`** | `907.18 ms` (✅ **1.00x**) | `486.46 ms` (🚀 **1.86x faster**) | `9.96 ms` (🚀 **91.10x faster**) |
| **`12000000`** | `9.41 s` (✅ **1.00x**) | `4.96 s` (🚀 **1.90x faster**) | `98.26 ms` (🚀 **95.75x faster**) |
| **`180000000`** | `133.65 s` (✅ **1.00x**) | `73.98 s` (🚀 **1.81x faster**) | `1.48 s` (🚀 **90.04x faster**) |
| **`720000000`** | `543.61 s` (✅ **1.00x**) | `295.27 s` (🚀 **1.84x faster**) | `6.14 s` (🚀 **88.55x faster**) |
### TriangleNumber
| | `EVM` | `PVMInterpreter` | `PVM` |
|:----------------|:--------------------------|:---------------------------------|:--------------------------------- |
| **`360000`** | `174.29 ms` (✅ **1.00x**) | `134.93 ms` (✅ **1.29x faster**) | `2.58 ms` (🚀 **67.43x faster**) |
| **`1440000`** | `723.79 ms` (✅ **1.00x**) | `518.44 ms` (✅ **1.40x faster**) | `9.93 ms` (🚀 **72.92x faster**) |
| **`14400000`** | `7.03 s` (✅ **1.00x**) | `5.40 s` (✅ **1.30x faster**) | `99.93 ms` (🚀 **70.40x faster**) |
| **`216000000`** | `108.98 s` (✅ **1.00x**) | `77.85 s` (✅ **1.40x faster**) | `1.44 s` (🚀 **75.78x faster**) |
| **`864000000`** | `423.03 s` (✅ **1.00x**) | `323.22 s` (✅ **1.31x faster**) | `5.99 s` (🚀 **70.61x faster**) |
### FibonacciRecursive
| | `EVM` | `PVMInterpreter` | `PVM` |
|:---------|:--------------------------|:---------------------------------|:---------------------------------- |
| **`24`** | `80.63 ms` (✅ **1.00x**) | `159.96 ms` (❌ *1.98x slower*) | `2.73 ms` (🚀 **29.52x faster**) |
| **`27`** | `331.93 ms` (✅ **1.00x**) | `662.76 ms` (❌ *2.00x slower*) | `10.78 ms` (🚀 **30.79x faster**) |
| **`31`** | `2.35 s` (✅ **1.00x**) | `4.44 s` (❌ *1.88x slower*) | `76.69 ms` (🚀 **30.69x faster**) |
| **`36`** | `26.17 s` (✅ **1.00x**) | `51.08 s` (❌ *1.95x slower*) | `819.30 ms` (🚀 **31.94x faster**) |
| **`39`** | `110.50 s` (✅ **1.00x**) | `220.00 s` (❌ *1.99x slower*) | `3.46 s` (🚀 **31.90x faster**) |
### FibonacciIterative
| | `EVM` | `PVMInterpreter` | `PVM` |
|:----------------|:--------------------------|:---------------------------------|:---------------------------------- |
| **`256`** | `84.27 us` (✅ **1.00x**) | `291.83 us` (❌ *3.46x slower*) | `42.87 us` (🚀 **1.97x faster**) |
| **`162500`** | `53.32 ms` (✅ **1.00x**) | `174.85 ms` (❌ *3.28x slower*) | `2.57 ms` (🚀 **20.78x faster**) |
| **`650000`** | `217.77 ms` (✅ **1.00x**) | `699.77 ms` (❌ *3.21x slower*) | `9.91 ms` (🚀 **21.96x faster**) |
| **`6500000`** | `2.14 s` (✅ **1.00x**) | `6.89 s` (❌ *3.22x slower*) | `100.67 ms` (🚀 **21.22x faster**) |
| **`100000000`** | `31.96 s` (✅ **1.00x**) | `106.46 s` (❌ *3.33x slower*) | `1.50 s` (🚀 **21.28x faster**) |
| **`400000000`** | `128.68 s` (✅ **1.00x**) | `447.34 s` (❌ *3.48x slower*) | `6.19 s` (🚀 **20.77x faster**) |
### PrepareBaseline
| | `Evm` | `PVMInterpreterCompile` | `PVMInterpreterInstantiate` | `PVMCompile` | `PVMInstantiate` |
|:--------|:--------------------------|:---------------------------------|:-------------------------------------|:----------------------------------|:---------------------------------- |
| **`0`** | `179.68 ns` (✅ **1.00x**) | `506.01 ns` (❌ *2.82x slower*) | `1.70 us` (❌ *9.45x slower*) | `29.44 us` (❌ *163.87x slower*) | `69.01 us` (❌ *384.08x slower*) |
### PrepareOddProduct
| | `Evm` | `PVMInterpreterCompile` | `PVMInterpreterInstantiate` | `PVMCompile` | `PVMInstantiate` |
|:--------|:--------------------------|:---------------------------------|:-------------------------------------|:---------------------------------|:---------------------------------- |
| **`0`** | `509.96 ns` (✅ **1.00x**) | `485.20 ns` (✅ **1.05x faster**) | `1.69 us` (❌ *3.32x slower*) | `29.88 us` (❌ *58.59x slower*) | `70.20 us` (❌ *137.66x slower*) |
### PrepareTriangleNumber
| | `Evm` | `PVMInterpreterCompile` | `PVMInterpreterInstantiate` | `PVMCompile` | `PVMInstantiate` |
|:--------|:--------------------------|:---------------------------------|:-------------------------------------|:---------------------------------|:---------------------------------- |
| **`0`** | `508.44 ns` (✅ **1.00x**) | `528.74 ns` (✅ **1.04x slower**) | `1.83 us` (❌ *3.60x slower*) | `50.81 us` (❌ *99.94x slower*) | `68.37 us` (❌ *134.48x slower*) |
### PrepareFibonacciRecursive
| | `Evm` | `PVMInterpreterCompile` | `PVMInterpreterInstantiate` | `PVMCompile` | `PVMInstantiate` |
|:--------|:--------------------------|:---------------------------------|:-------------------------------------|:----------------------------------|:---------------------------------- |
| **`0`** | `409.24 ns` (✅ **1.00x**) | `507.67 ns` (❌ *1.24x slower*) | `1.80 us` (❌ *4.40x slower*) | `46.24 us` (❌ *112.99x slower*) | `69.06 us` (❌ *168.76x slower*) |
### PrepareFibonacciIterative
| | `Evm` | `PVMInterpreterCompile` | `PVMInterpreterInstantiate` | `PVMCompile` | `PVMInstantiate` |
|:--------|:--------------------------|:---------------------------------|:-------------------------------------|:----------------------------------|:---------------------------------- |
| **`0`** | `304.00 ns` (✅ **1.00x**) | `524.75 ns` (❌ *1.73x slower*) | `1.88 us` (❌ *6.17x slower*) | `43.50 us` (❌ *143.11x slower*) | `66.82 us` (❌ *219.80x slower*) |
---
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
+34 -64
View File
@@ -5,10 +5,6 @@ use criterion::{
criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, BenchmarkId,
Criterion,
};
#[cfg(any(feature = "bench-pvm-interpreter", feature = "bench-pvm"))]
use polkavm::BackendKind;
use revive_benchmarks::prepare_pvm;
use revive_integration::cases::Contract;
fn bench<P, L, I, M>(mut group: BenchmarkGroup<'_, M>, parameters: &[P], labels: &[L], contract: I)
@@ -35,14 +31,14 @@ where
#[cfg(feature = "bench-pvm-interpreter")]
{
let contract = contract(p.clone());
let (state, mut instance, export) = prepare_pvm(
let (transaction, mut instance, export) = revive_benchmarks::prepare_pvm(
&contract.pvm_runtime,
&contract.calldata,
BackendKind::Interpreter,
contract.calldata,
polkavm::BackendKind::Interpreter,
);
group.bench_with_input(BenchmarkId::new("PVMInterpreter", l), p, |b, _| {
b.iter(|| {
revive_integration::mock_runtime::call(state.clone(), &mut instance, export);
transaction.clone().call_on(&mut instance, export);
});
});
}
@@ -50,14 +46,14 @@ where
#[cfg(feature = "bench-pvm")]
{
let contract = contract(p.clone());
let (state, mut instance, export) = prepare_pvm(
let (transaction, mut instance, export) = revive_benchmarks::prepare_pvm(
&contract.pvm_runtime,
&contract.calldata,
BackendKind::Compiler,
contract.calldata,
polkavm::BackendKind::Compiler,
);
group.bench_with_input(BenchmarkId::new("PVM", l), p, |b, _| {
b.iter(|| {
revive_integration::mock_runtime::call(state.clone(), &mut instance, export);
transaction.clone().call_on(&mut instance, export);
});
});
}
@@ -66,40 +62,34 @@ where
group.finish();
}
#[cfg(feature = "bench-extensive")]
fn group_extensive<'error, M>(
c: &'error mut Criterion<M>,
group_name: &str,
) -> BenchmarkGroup<'error, M>
fn group<'error, M>(c: &'error mut Criterion<M>, group_name: &str) -> BenchmarkGroup<'error, M>
where
M: Measurement,
{
let mut group = c.benchmark_group(group_name);
group
.sample_size(10)
.measurement_time(Duration::from_secs(60));
group
#[cfg(feature = "bench-extensive")]
{
let mut group = c.benchmark_group(group_name);
group
.sample_size(10)
.measurement_time(Duration::from_secs(60));
group
}
#[cfg(not(feature = "bench-extensive"))]
return c.benchmark_group(group_name);
}
fn bench_baseline(c: &mut Criterion) {
let group = group(c, "Baseline");
let parameters = &[0u8];
bench(
c.benchmark_group("Baseline"),
parameters,
parameters,
|_| Contract::baseline(),
);
bench(group, parameters, parameters, |_| Contract::baseline());
}
fn bench_odd_product(c: &mut Criterion) {
let group = group(c, "OddPorduct");
#[cfg(feature = "bench-extensive")]
let group = group_extensive(c, "OddProduct");
#[cfg(not(feature = "bench-extensive"))]
let group = c.benchmark_group("OddProduct");
#[cfg(feature = "bench-extensive")]
let parameters = &[2_000_000i32, 4_000_000, 8_000_000, 120_000_000];
let parameters = &[300000, 1200000, 12000000, 180000000, 720000000];
#[cfg(not(feature = "bench-extensive"))]
let parameters = &[10_000, 100_000];
@@ -107,13 +97,9 @@ fn bench_odd_product(c: &mut Criterion) {
}
fn bench_triangle_number(c: &mut Criterion) {
let group = group(c, "TriangleNumber");
#[cfg(feature = "bench-extensive")]
let group = group_extensive(c, "TriangleNumber");
#[cfg(not(feature = "bench-extensive"))]
let group = c.benchmark_group("TriangleNumber");
#[cfg(feature = "bench-extensive")]
let parameters = &[3_000_000i64, 6_000_000, 12_000_000, 180_000_000];
let parameters = &[360000, 1440000, 14400000, 216000000, 864000000];
#[cfg(not(feature = "bench-extensive"))]
let parameters = &[10_000, 100_000];
@@ -121,13 +107,9 @@ fn bench_triangle_number(c: &mut Criterion) {
}
fn bench_fibonacci_recurisve(c: &mut Criterion) {
#[cfg(not(feature = "bench-extensive"))]
let group = c.benchmark_group("FibonacciRecursive");
let group = group(c, "FibonacciRecursive");
#[cfg(feature = "bench-extensive")]
let group = group_extensive(c, "FibonacciRecursive");
#[cfg(feature = "bench-extensive")]
let parameters = &[26, 30, 34, 38];
let parameters = &[24, 27, 31, 36, 39];
#[cfg(not(feature = "bench-extensive"))]
let parameters = &[12, 16, 20];
@@ -135,13 +117,9 @@ fn bench_fibonacci_recurisve(c: &mut Criterion) {
}
fn bench_fibonacci_iterative(c: &mut Criterion) {
#[cfg(not(feature = "bench-extensive"))]
let group = c.benchmark_group("FibonacciIterative");
let group = group(c, "FibonacciIterative");
#[cfg(feature = "bench-extensive")]
let group = group_extensive(c, "FibonacciIterative");
#[cfg(feature = "bench-extensive")]
let parameters = &[256, 100000, 1000000, 100000000];
let parameters = &[256, 162500, 650000, 6500000, 100000000, 400000000];
#[cfg(not(feature = "bench-extensive"))]
let parameters = &[64, 128, 256];
@@ -149,26 +127,18 @@ fn bench_fibonacci_iterative(c: &mut Criterion) {
}
fn bench_fibonacci_binet(c: &mut Criterion) {
let group = group(c, "FibonacciBinet");
let parameters = &[64, 128, 256];
bench(
c.benchmark_group("FibonacciBinet"),
parameters,
parameters,
Contract::fib_binet,
);
bench(group, parameters, parameters, Contract::fib_binet);
}
fn bench_sha1(c: &mut Criterion) {
let group = group(c, "SHA1");
let parameters = &[vec![0xff], vec![0xff; 64], vec![0xff; 512]];
let labels = parameters.iter().map(|p| p.len()).collect::<Vec<_>>();
bench(
c.benchmark_group("SHA1"),
parameters,
&labels,
Contract::sha1,
);
bench(group, parameters, &labels, Contract::sha1);
}
criterion_group!(
+4 -6
View File
@@ -1,7 +1,5 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use polkavm::BackendKind;
use revive_benchmarks::instantiate_engine;
use revive_integration::cases::Contract;
fn bench(
@@ -22,7 +20,7 @@ fn bench(
#[cfg(feature = "bench-pvm-interpreter")]
{
let engine = instantiate_engine(BackendKind::Interpreter);
let engine = revive_benchmarks::instantiate_engine(polkavm::BackendKind::Interpreter);
group.bench_with_input(
BenchmarkId::new("PVMInterpreterCompile", code_size),
&(&pvm_runtime, engine),
@@ -36,7 +34,7 @@ fn bench(
#[cfg(feature = "bench-pvm-interpreter")]
{
let engine = instantiate_engine(BackendKind::Interpreter);
let engine = revive_benchmarks::instantiate_engine(polkavm::BackendKind::Interpreter);
let module = revive_integration::mock_runtime::recompile_code(&pvm_runtime, &engine);
group.bench_with_input(
BenchmarkId::new("PVMInterpreterInstantiate", code_size),
@@ -51,7 +49,7 @@ fn bench(
#[cfg(feature = "bench-pvm")]
{
let engine = instantiate_engine(BackendKind::Compiler);
let engine = revive_benchmarks::instantiate_engine(polkavm::BackendKind::Compiler);
group.bench_with_input(
BenchmarkId::new("PVMCompile", code_size),
&(&pvm_runtime, engine),
@@ -65,7 +63,7 @@ fn bench(
#[cfg(feature = "bench-pvm")]
{
let engine = instantiate_engine(BackendKind::Compiler);
let engine = revive_benchmarks::instantiate_engine(polkavm::BackendKind::Compiler);
let module = revive_integration::mock_runtime::recompile_code(&pvm_runtime, &engine);
group.bench_with_input(
BenchmarkId::new("PVMInstantiate", code_size),
+6 -5
View File
@@ -1,19 +1,20 @@
use polkavm::{BackendKind, Config, Engine, ExportIndex, Instance, SandboxKind};
use revive_integration::mock_runtime;
use revive_integration::mock_runtime::State;
use revive_integration::mock_runtime::{self, TransactionBuilder};
use revive_integration::mock_runtime::{State, Transaction};
pub fn prepare_pvm(
code: &[u8],
input: &[u8],
input: Vec<u8>,
backend: BackendKind,
) -> (State, Instance<State>, ExportIndex) {
) -> (TransactionBuilder, Instance<Transaction>, ExportIndex) {
let mut config = Config::new();
config.set_backend(Some(backend));
config.set_sandbox(Some(SandboxKind::Linux));
let (instance, export_index) = mock_runtime::prepare(code, Some(config));
let transaction = State::default().transaction().calldata(input);
(State::new(input.to_vec()), instance, export_index)
(transaction, instance, export_index)
}
pub fn instantiate_engine(backend: BackendKind) -> Engine {
+1 -26
View File
@@ -1,13 +1,4 @@
# zkSync Era: Compiler Common
[![Logo](eraLogo.svg)](https://zksync.io/)
zkSync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security
or decentralization. As it's EVM-compatible (with Solidity/Vyper), 99% of Ethereum projects can redeploy without
needing to refactor or re-audit any code. zkSync Era also uses an LLVM-based compiler that will eventually enable
developers to write smart contracts in popular languages such as C++ and Rust.
This repository contains the common compiler constants.
# revive: Compiler Common
## License
@@ -17,19 +8,3 @@ This library is distributed under the terms of either
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.
## Official Links
- [Website](https://zksync.io/)
- [GitHub](https://github.com/matter-labs)
- [Twitter](https://twitter.com/zksync)
- [Twitter for Devs](https://twitter.com/zkSyncDevs)
- [Discord](https://join.zksync.dev/)
## Disclaimer
zkSync Era has been through extensive testing and audits, and although it is live, it is still in alpha state and
will undergo further audits and bug bounty programs. We would love to hear our community's thoughts and suggestions
about it!
It's important to note that forking it now could potentially lead to missing important
security updates, critical features, and performance improvements.
-2
View File
@@ -1,6 +1,4 @@
//!
//! The number base constants.
//!
/// The binary number base.
pub const BASE_BINARY: u32 = 2;
+10 -4
View File
@@ -1,6 +1,4 @@
//!
//! The common sizes in bits.
//!
/// The `bool` type bit-length.
pub const BIT_LENGTH_BOOLEAN: usize = 1;
@@ -18,8 +16,16 @@ pub const BIT_LENGTH_X64: usize = crate::byte_length::BYTE_LENGTH_X64 * BIT_LENG
pub const BIT_LENGTH_ETH_ADDRESS: usize =
crate::byte_length::BYTE_LENGTH_ETH_ADDRESS * BIT_LENGTH_BYTE;
/// The field (usually `u256` or `i256`) bit-length.
pub const BIT_LENGTH_FIELD: usize = crate::byte_length::BYTE_LENGTH_FIELD * BIT_LENGTH_BYTE;
/// The VM word (usually `u256` or `i256`) bit-length.
pub const BIT_LENGTH_WORD: usize = crate::byte_length::BYTE_LENGTH_WORD * BIT_LENGTH_BYTE;
/// Bit length of the runtime value type.
pub const BIT_LENGTH_VALUE: usize = crate::byte_length::BYTE_LENGTH_VALUE * BIT_LENGTH_BYTE;
/// Bit length of thre runimte block number type.
pub const BIT_LENGTH_BLOCK_NUMBER: usize =
crate::byte_length::BYTE_LENGTH_BLOCK_NUMBER * BIT_LENGTH_BYTE;
/// Bit length of thre runimte block timestamp type.
pub const BIT_LENGTH_BLOCK_TIMESTAMP: usize =
crate::byte_length::BYTE_LENGTH_BLOCK_TIMESTAMP * BIT_LENGTH_BYTE;
+9 -5
View File
@@ -1,6 +1,4 @@
//!
//! The common sizes in bytes.
//!
/// The byte-length.
pub const BYTE_LENGTH_BYTE: usize = 1;
@@ -17,8 +15,14 @@ pub const BYTE_LENGTH_X64: usize = 8;
/// The ETH address byte-length.
pub const BYTE_LENGTH_ETH_ADDRESS: usize = 20;
/// The field byte-length.
pub const BYTE_LENGTH_FIELD: usize = 32;
/// The word byte-length.
pub const BYTE_LENGTH_WORD: usize = 32;
/// Byte length of the runtime value type.
pub const BYTE_LENGTH_VALUE: usize = 16;
pub const BYTE_LENGTH_VALUE: usize = 32;
/// Byte length of the runtime block number type.
pub const BYTE_LENGTH_BLOCK_NUMBER: usize = 8;
/// Byte length of the runtime block timestamp type.
pub const BYTE_LENGTH_BLOCK_TIMESTAMP: usize = 8;
-117
View File
@@ -1,117 +0,0 @@
//!
//! The EraVM address constants.
//!
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_TO_L1: u16 = 0xFFFF;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_PRECOMPILE: u16 = 0xFFFD;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_META: u16 = 0xFFFC;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_MIMIC_CALL: u16 = 0xFFFB;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_SYSTEM_MIMIC_CALL: u16 = 0xFFFA;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_MIMIC_CALL_BYREF: u16 = 0xFFF9;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_SYSTEM_MIMIC_CALL_BYREF: u16 = 0xFFF8;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_RAW_FAR_CALL: u16 = 0xFFF7;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_RAW_FAR_CALL_BYREF: u16 = 0xFFF6;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_SYSTEM_CALL: u16 = 0xFFF5;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_SYSTEM_CALL_BYREF: u16 = 0xFFF4;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_SET_CONTEXT_VALUE_CALL: u16 = 0xFFF3;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_SET_PUBDATA_PRICE: u16 = 0xFFF2;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_INCREMENT_TX_COUNTER: u16 = 0xFFF1;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_GET_GLOBAL_PTR_CALLDATA: u16 = 0xFFF0;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_GET_GLOBAL_CALL_FLAGS: u16 = 0xFFEF;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_GET_GLOBAL_PTR_RETURN_DATA: u16 = 0xFFEE;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_EVENT_INITIALIZE: u16 = 0xFFED;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_EVENT_WRITE: u16 = 0xFFEC;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_LOAD_CALLDATA: u16 = 0xFFEB;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_LOAD_RETURN_DATA: u16 = 0xFFEA;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_ADD: u16 = 0xFFE9;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_SHRINK: u16 = 0xFFE8;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_PACK: u16 = 0xFFE7;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_MULTIPLICATION_HIGH_REGISTER: u16 = 0xFFE6;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_GET_GLOBAL_EXTRA_ABI_DATA: u16 = 0xFFE5;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_DATA_LOAD: u16 = 0xFFE4;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_DATA_COPY: u16 = 0xFFE3;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_DATA_SIZE: u16 = 0xFFE2;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_CONST_ARRAY_DECLARE: u16 = 0xFFE1;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_CONST_ARRAY_SET: u16 = 0xFFE0;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_CONST_ARRAY_FINALIZE: u16 = 0xFFDF;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_CONST_ARRAY_GET: u16 = 0xFFDE;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_DECOMMIT: u16 = 0xFFDD;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_LOAD_DECOMMIT: u16 = 0xFFDC;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_RETURN_FORWARD: u16 = 0xFFDB;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_REVERT_FORWARD: u16 = 0xFFDA;
/// The corresponding simulation predefined address.
pub const ERAVM_ADDRESS_ACTIVE_PTR_SWAP: u16 = 0xFFD9;
-5
View File
@@ -1,5 +0,0 @@
//!
//! The EraVM constants.
//!
pub mod address;
-4
View File
@@ -1,13 +1,9 @@
//!
//! The EVM version.
//!
use serde::Deserialize;
use serde::Serialize;
///
/// The EVM version.
///
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "camelCase")]
pub enum EVMVersion {
+4 -6
View File
@@ -1,6 +1,4 @@
//!
//! The file extensions.
//!
/// The manifest file extension.
pub static EXTENSION_MANIFEST: &str = "toml";
@@ -41,8 +39,8 @@ pub static EXTENSION_LLVM_SOURCE: &str = "ll";
/// The LLVM bitcode file extension.
pub static EXTENSION_LLVM_BINARY: &str = "bc";
/// The EraVM assembly file extension.
pub static EXTENSION_ERAVM_ASSEMBLY: &str = "zasm";
/// The PolkaVM assembly file extension.
pub static EXTENSION_POLKAVM_ASSEMBLY: &str = "pvmasm";
/// The EraVM bytecode file extension.
pub static EXTENSION_ERAVM_BINARY: &str = "zbin";
/// The PolkaVM bytecode file extension.
pub static EXTENSION_POLKAVM_BINARY: &str = "pvm";
+2 -4
View File
@@ -1,21 +1,19 @@
//!
//! The compiler common library.
//!
pub(crate) mod base;
pub(crate) mod bit_length;
pub(crate) mod byte_length;
pub(crate) mod eravm;
pub(crate) mod evm_version;
pub(crate) mod exit_code;
pub(crate) mod extension;
pub(crate) mod polkavm;
pub(crate) mod utils;
pub use self::base::*;
pub use self::bit_length::*;
pub use self::byte_length::*;
pub use self::eravm::address::*;
pub use self::evm_version::EVMVersion;
pub use self::exit_code::*;
pub use self::extension::*;
pub use self::polkavm::address::*;
pub use self::utils::*;
+115
View File
@@ -0,0 +1,115 @@
//! The PolkaVM address constants.
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_TO_L1: u16 = 0xFFFF;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_PRECOMPILE: u16 = 0xFFFD;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_META: u16 = 0xFFFC;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_MIMIC_CALL: u16 = 0xFFFB;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_SYSTEM_MIMIC_CALL: u16 = 0xFFFA;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_MIMIC_CALL_BYREF: u16 = 0xFFF9;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_SYSTEM_MIMIC_CALL_BYREF: u16 = 0xFFF8;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_RAW_FAR_CALL: u16 = 0xFFF7;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_RAW_FAR_CALL_BYREF: u16 = 0xFFF6;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_SYSTEM_CALL: u16 = 0xFFF5;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_SYSTEM_CALL_BYREF: u16 = 0xFFF4;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_SET_CONTEXT_VALUE_CALL: u16 = 0xFFF3;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_SET_PUBDATA_PRICE: u16 = 0xFFF2;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_INCREMENT_TX_COUNTER: u16 = 0xFFF1;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_GET_GLOBAL_PTR_CALLDATA: u16 = 0xFFF0;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_GET_GLOBAL_CALL_FLAGS: u16 = 0xFFEF;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_GET_GLOBAL_PTR_RETURN_DATA: u16 = 0xFFEE;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_EVENT_INITIALIZE: u16 = 0xFFED;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_EVENT_WRITE: u16 = 0xFFEC;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_LOAD_CALLDATA: u16 = 0xFFEB;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_LOAD_RETURN_DATA: u16 = 0xFFEA;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_ADD: u16 = 0xFFE9;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_SHRINK: u16 = 0xFFE8;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_PACK: u16 = 0xFFE7;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_MULTIPLICATION_HIGH_REGISTER: u16 = 0xFFE6;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_GET_GLOBAL_EXTRA_ABI_DATA: u16 = 0xFFE5;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_DATA_LOAD: u16 = 0xFFE4;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_DATA_COPY: u16 = 0xFFE3;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_DATA_SIZE: u16 = 0xFFE2;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_CONST_ARRAY_DECLARE: u16 = 0xFFE1;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_CONST_ARRAY_SET: u16 = 0xFFE0;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_CONST_ARRAY_FINALIZE: u16 = 0xFFDF;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_CONST_ARRAY_GET: u16 = 0xFFDE;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_DECOMMIT: u16 = 0xFFDD;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_LOAD_DECOMMIT: u16 = 0xFFDC;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_RETURN_FORWARD: u16 = 0xFFDB;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_REVERT_FORWARD: u16 = 0xFFDA;
/// The corresponding simulation predefined address.
pub const POLKAVM_ADDRESS_ACTIVE_PTR_SWAP: u16 = 0xFFD9;
+5
View File
@@ -0,0 +1,5 @@
//!
//! The PolkaVM constants.
//!
pub mod address;
-8
View File
@@ -1,12 +1,7 @@
//!
//! The compiler common utils.
//!
///
/// Deserializes a `serde_json` object from slice with the recursion limit disabled.
///
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
///
pub fn deserialize_from_slice<O>(input: &[u8]) -> anyhow::Result<O>
where
O: serde::de::DeserializeOwned,
@@ -18,11 +13,8 @@ where
Ok(result)
}
///
/// Deserializes a `serde_json` object from string with the recursion limit disabled.
///
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
///
pub fn deserialize_from_str<O>(input: &str) -> anyhow::Result<O>
where
O: serde::de::DeserializeOwned,
+1
View File
@@ -8,3 +8,4 @@ edition = "2021"
[dependencies]
evm-interpreter = { workspace = true }
primitive-types = { workspace = true }
alloy-primitives = { workspace = true }
+19 -14
View File
@@ -1,3 +1,4 @@
use alloy_primitives::{keccak256, Address, B256};
use evm_interpreter::{
interpreter::{EtableInterpreter, RunInterpreter},
trap::CallCreateTrap,
@@ -9,20 +10,23 @@ use primitive_types::{H160, H256, U256};
static RUNTIME_ETABLE: Etable<RuntimeState, UnimplementedHandler, CallCreateTrap> =
Etable::runtime();
pub struct UnimplementedHandler;
#[derive(Default)]
pub struct UnimplementedHandler {
logs: Vec<Log>,
}
impl RuntimeEnvironment for UnimplementedHandler {
fn block_hash(&self, _number: U256) -> H256 {
unimplemented!()
}
fn block_number(&self) -> U256 {
unimplemented!()
U256::from(123)
}
fn block_coinbase(&self) -> H160 {
unimplemented!()
}
fn block_timestamp(&self) -> U256 {
unimplemented!()
U256::from(456)
}
fn block_difficulty(&self) -> U256 {
unimplemented!()
@@ -86,8 +90,9 @@ impl RuntimeBackend for UnimplementedHandler {
fn set_storage(&mut self, _address: H160, _index: H256, _value: H256) -> Result<(), ExitError> {
unimplemented!()
}
fn log(&mut self, _log: Log) -> Result<(), ExitError> {
unimplemented!()
fn log(&mut self, log: Log) -> Result<(), ExitError> {
self.logs.push(log);
Ok(())
}
fn mark_delete(&mut self, _address: H160) {
unimplemented!()
@@ -123,10 +128,13 @@ pub struct PreparedEvm {
}
pub fn prepare(code: Vec<u8>, data: Vec<u8>) -> PreparedEvm {
let address = Address::default().create2(B256::default(), keccak256([]).0);
let caller = Address::default().create2(B256::default(), keccak256([]).0);
let state = RuntimeState {
context: Context {
address: H160::default(),
caller: H160::default(),
address: H160::from(address.0 .0),
caller: H160::from(caller.0 .0),
apparent_value: U256::default(),
},
transaction_context: TransactionContext {
@@ -143,12 +151,9 @@ pub fn prepare(code: Vec<u8>, data: Vec<u8>) -> PreparedEvm {
}
}
pub fn execute(pre: PreparedEvm) -> Vec<u8> {
pub fn execute(pre: PreparedEvm) -> (Vec<u8>, Vec<Log>) {
let mut vm = EtableInterpreter::new_valid(pre.vm, &RUNTIME_ETABLE, pre.valids);
vm.run(&mut UnimplementedHandler {})
.exit()
.unwrap()
.unwrap();
vm.retval.clone()
let mut handler = UnimplementedHandler::default();
vm.run(&mut handler).exit().unwrap().unwrap();
(vm.retval.clone(), handler.logs)
}
-1
View File
@@ -9,7 +9,6 @@ include!(concat!(env!("OUT_DIR"), "/bswap.rs"));
/// - Takes a `i256` value argument
/// - Byte swaps it using `rev8` from the `zbb` extension
/// - Returns the `i256` value
///
/// Returns `Error` if the module fails to validate, which should never happen.
pub fn module<'context>(
context: &'context Context,
+5 -1
View File
@@ -14,7 +14,11 @@ env_logger = { workspace = true }
revive-solidity = { path = "../solidity" }
revive-differential = { path = "../differential" }
era-compiler-llvm-context = { path = "../llvm-context" }
revive-llvm-context = { path = "../llvm-context" }
revive-common = { path = "../common" }
[dev-dependencies]
sha1 = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
rayon = { workspace = true }
+10
View File
@@ -0,0 +1,10 @@
{
"Baseline": 3944,
"Computation": 7444,
"DivisionArithmetics": 42784,
"ERC20": 56199,
"Events": 4784,
"FibonacciIterative": 6019,
"Flipper": 4392,
"SHA1": 36107
}
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity ^0.8;
contract Baseline {
function baseline() public payable {}
+13
View File
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract Block {
function timestamp() public view returns (uint ret) {
ret = block.timestamp;
}
function number() public view returns (uint ret) {
ret = block.number;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity ^0.8;
contract Computation {
function triangle_number(int64 n) public pure returns (int64 sum) {
+13
View File
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract Context {
function address_this() public view returns (address ret) {
ret = address(this);
}
function caller() public view returns (address ret) {
ret = msg.sender;
}
}
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract DivisionArithmetics {
function div(uint n, uint d) public pure returns (uint q) {
assembly {
q := div(n, d)
}
}
function sdiv(int n, int d) public pure returns (int q) {
assembly {
q := sdiv(n, d)
}
}
function mod(uint n, uint d) public pure returns (uint r) {
assembly {
r := mod(n, d)
}
}
function smod(int n, int d) public pure returns (int r) {
assembly {
r := smod(n, d)
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
pragma solidity ^0.8;
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IERC20 {
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract Events {
event A() anonymous;
event E(uint, uint indexed, uint indexed, uint indexed);
function emitEvent(uint topics) public {
if (topics == 0) {
emit A();
} else {
emit E(topics, 1, 2, 3);
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
pragma solidity ^0.8;
// https://medium.com/coinmonks/fibonacci-in-solidity-8477d907e22a
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity ^0.8;
contract MSize {
uint[] public data;
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity ^0.8;
contract Value {
function value() public payable returns (uint ret) {
+1 -1
View File
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity ^0.8;
contract Flipper {
bool coin;
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity ^0.8;
contract MStore8 {
function mStore8(uint value) public pure returns (uint256 word) {
+305 -1
View File
@@ -1,8 +1,11 @@
use alloy_primitives::U256;
use alloy_primitives::{I256, U256};
use alloy_sol_types::{sol, SolCall};
use crate::mock_runtime::{CallOutput, State};
#[derive(Clone)]
pub struct Contract {
pub name: &'static str,
pub evm_runtime: Vec<u8>,
pub pvm_runtime: Vec<u8>,
pub calldata: Vec<u8>,
@@ -10,6 +13,8 @@ pub struct Contract {
sol!(contract Baseline { function baseline() public payable; });
sol!(contract Flipper { function flip() public; });
sol!(contract Computation {
function odd_product(int32 n) public pure returns (int64);
function triangle_number(int64 n) public pure returns (int64 sum);
@@ -39,12 +44,100 @@ sol!(
}
);
sol!(
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(
address owner,
address spender
) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
);
sol!(
contract Block {
function timestamp() public view returns (uint ret);
function number() public view returns (uint ret);
}
);
sol!(
contract Context {
function address_this() public view returns (address);
function caller() public pure returns (address);
}
);
sol!(
contract DivisionArithmetics {
function div(uint n, uint d) public pure returns (uint q);
function sdiv(int n, int d) public pure returns (int q);
function mod(uint n, uint d) public pure returns (uint r);
function smod(int n, int d) public pure returns (int r);
}
);
sol!(
contract MStore8 {
function mStore8(uint value) public pure returns (uint256 word);
}
);
sol!(
contract Events {
event A(uint) anonymous;
event E(uint indexed, uint indexed, uint indexed);
function emitEvent(uint topics) public;
}
);
impl Contract {
/// Execute the contract.
///
/// Useful helper if the contract state can be ignored,
/// as it spares the deploy transaciton.
///
/// - Inserts an account with given `code` into a new state.
/// - Callee and caller account will be `Transaction::default_address()`.
/// - Sets the calldata.
/// - Doesn't execute the constructor or deploy code.
/// - Calls the "call" export on a default backend config.
pub fn execute(&self) -> (State, CallOutput) {
State::default()
.transaction()
.with_default_account(&self.pvm_runtime)
.calldata(self.calldata.clone())
.call()
}
pub fn baseline() -> Self {
let code = include_str!("../contracts/Baseline.sol");
let name = "Baseline";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Baseline::baselineCall::new(()).abi_encode(),
@@ -56,6 +149,7 @@ impl Contract {
let name = "Computation";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Computation::odd_productCall::new((n,)).abi_encode(),
@@ -67,6 +161,7 @@ impl Contract {
let name = "Computation";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Computation::triangle_numberCall::new((n,)).abi_encode(),
@@ -78,6 +173,7 @@ impl Contract {
let name = "FibonacciRecursive";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: FibonacciRecursive::fib3Call::new((U256::from(n),)).abi_encode(),
@@ -89,6 +185,7 @@ impl Contract {
let name = "FibonacciIterative";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: FibonacciIterative::fib3Call::new((U256::from(n),)).abi_encode(),
@@ -100,6 +197,7 @@ impl Contract {
let name = "FibonacciBinet";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: FibonacciBinet::fib3Call::new((U256::from(n),)).abi_encode(),
@@ -111,9 +209,215 @@ impl Contract {
let name = "SHA1";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: SHA1::sha1Call::new((pre,)).abi_encode(),
}
}
pub fn flipper() -> Self {
let code = include_str!("../contracts/flipper.sol");
let name = "Flipper";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Flipper::flipCall::new(()).abi_encode(),
}
}
pub fn erc20() -> Self {
let code = include_str!("../contracts/ERC20.sol");
let name = "ERC20";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: IERC20::totalSupplyCall::new(()).abi_encode(),
}
}
pub fn block_number() -> Self {
let code = include_str!("../contracts/Block.sol");
let name = "Block";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Block::numberCall::new(()).abi_encode(),
}
}
pub fn block_timestamp() -> Self {
let code = include_str!("../contracts/Block.sol");
let name = "Block";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Block::timestampCall::new(()).abi_encode(),
}
}
pub fn context_address() -> Self {
let code = include_str!("../contracts/Context.sol");
let name = "Context";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Context::address_thisCall::new(()).abi_encode(),
}
}
pub fn context_caller() -> Self {
let code = include_str!("../contracts/Context.sol");
let name = "Context";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Context::callerCall::new(()).abi_encode(),
}
}
pub fn division_arithmetics_div(n: U256, d: U256) -> Self {
let code = include_str!("../contracts/DivisionArithmetics.sol");
let name = "DivisionArithmetics";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: DivisionArithmetics::divCall::new((n, d)).abi_encode(),
}
}
pub fn division_arithmetics_sdiv(n: I256, d: I256) -> Self {
let code = include_str!("../contracts/DivisionArithmetics.sol");
let name = "DivisionArithmetics";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: DivisionArithmetics::sdivCall::new((n, d)).abi_encode(),
}
}
pub fn division_arithmetics_mod(n: U256, d: U256) -> Self {
let code = include_str!("../contracts/DivisionArithmetics.sol");
let name = "DivisionArithmetics";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: DivisionArithmetics::modCall::new((n, d)).abi_encode(),
}
}
pub fn division_arithmetics_smod(n: I256, d: I256) -> Self {
let code = include_str!("../contracts/DivisionArithmetics.sol");
let name = "DivisionArithmetics";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: DivisionArithmetics::smodCall::new((n, d)).abi_encode(),
}
}
pub fn mstore8(value: U256) -> Self {
let code = include_str!("../contracts/mStore8.sol");
let name = "MStore8";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: MStore8::mStore8Call::new((value,)).abi_encode(),
}
}
pub fn event(topics: U256) -> Self {
let code = include_str!("../contracts/Events.sol");
let name = "Events";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Events::emitEventCall::new((topics,)).abi_encode(),
}
}
}
#[cfg(test)]
mod tests {
use alloy_primitives::U256;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use serde::{de::Deserialize, Serialize};
use std::{collections::BTreeMap, fs::File};
use super::Contract;
#[test]
fn codesize() {
let path = "codesize.json";
let existing = File::open(path)
.map(|file| {
BTreeMap::<String, usize>::deserialize(&mut serde_json::Deserializer::from_reader(
file,
))
.expect("should be able to deserialze codesize data")
})
.ok();
let extract_code_size = |compile: fn() -> Contract| {
let contract = compile();
let contract_length = contract.pvm_runtime.len();
let size_change = existing
.as_ref()
.and_then(|map| map.get(contract.name))
.filter(|old| **old != 0)
.map(|old| {
let old = *old as f32;
let p = (contract_length as f32 - old) / old * 100.0;
format!("({p}% change from {old} bytes)",)
})
.unwrap_or_default();
println!("{}: {contract_length} bytes {size_change}", contract.name);
(contract.name, contract_length)
};
[
Contract::baseline as fn() -> Contract,
Contract::flipper as fn() -> Contract,
(|| Contract::odd_product(0)) as fn() -> Contract,
(|| Contract::fib_iterative(0)) as fn() -> Contract,
Contract::erc20 as fn() -> Contract,
(|| Contract::sha1(Vec::new())) as fn() -> Contract,
(|| Contract::division_arithmetics_div(U256::ZERO, U256::ZERO)) as fn() -> Contract,
(|| Contract::event(U256::ZERO)) as fn() -> Contract,
]
.into_par_iter()
.map(extract_code_size)
.collect::<BTreeMap<_, _>>()
.serialize(&mut serde_json::Serializer::pretty(
File::create(path).unwrap(),
))
.unwrap_or_else(|err| panic!("can not write codesize data to '{path}': {err}"));
}
}
+34 -11
View File
@@ -1,5 +1,8 @@
use alloy_primitives::{Address, U256};
use cases::Contract;
use mock_runtime::State;
use mock_runtime::{CallOutput, State};
use crate::mock_runtime::{Event, ReturnFlags};
pub mod cases;
pub mod mock_runtime;
@@ -53,7 +56,7 @@ pub fn compile_blob_with_options(
Default::default(),
None,
pipeline,
era_compiler_llvm_context::OptimizerSettings::cycles(),
revive_llvm_context::OptimizerSettings::cycles(),
solc_optimizer_enabled,
)
.expect("source should compile")
@@ -64,22 +67,42 @@ pub fn compile_blob_with_options(
.evm
.as_ref()
.expect("source should produce EVM output")
.assembly_text
.bytecode
.as_ref()
.expect("source should produce assembly text");
.expect("source should produce assembly text")
.object
.as_str();
hex::decode(bytecode).expect("hex encoding should always be valid")
}
pub fn assert_success(contract: Contract, differential: bool) -> State {
let (mut instance, export) = mock_runtime::prepare(&contract.pvm_runtime, None);
let state = mock_runtime::call(State::new(contract.calldata.clone()), &mut instance, export);
assert_eq!(state.output.flags, 0);
pub fn assert_success(contract: &Contract, differential: bool) -> (State, CallOutput) {
let (state, output) = contract.execute();
assert_eq!(output.flags, ReturnFlags::Success);
if differential {
let evm = revive_differential::prepare(contract.evm_runtime, contract.calldata);
assert_eq!(state.output.data.clone(), revive_differential::execute(evm));
let evm =
revive_differential::prepare(contract.evm_runtime.clone(), contract.calldata.clone());
let (evm_output, evm_log) = revive_differential::execute(evm);
assert_eq!(output.data.clone(), evm_output);
assert_eq!(output.events.len(), evm_log.len());
assert_eq!(
output.events,
evm_log
.iter()
.map(|log| Event {
address: Address::from_slice(log.address.as_bytes()),
data: log.data.clone(),
topics: log
.topics
.iter()
.map(|topic| U256::from_be_bytes(topic.0))
.collect(),
})
.collect::<Vec<_>>()
);
}
state
(state, output)
}
+482 -76
View File
@@ -1,66 +1,339 @@
//! Mock environment used for integration tests.
//! TODO: Switch to drink! once RISCV is ready in polkadot-sdk
use std::collections::HashMap;
use alloy_primitives::{Keccak256, U256};
use alloy_primitives::{keccak256, Address, Keccak256, B256, U256};
use polkavm::{
Caller, Config, Engine, ExportIndex, GasMeteringKind, Instance, Linker, Module, ModuleConfig,
ProgramBlob, Trap,
};
use revive_llvm_context::polkavm_const::runtime_api;
#[derive(Default, Clone, Debug)]
pub struct State {
pub input: Vec<u8>,
pub output: CallOutput,
pub value: u128,
pub storage: HashMap<U256, U256>,
/// The mocked blockchain account.
#[derive(Debug, Default, Clone)]
struct Account {
value: U256,
contract: Option<U256>,
storage: HashMap<U256, U256>,
}
#[derive(Clone, Debug)]
pub struct CallOutput {
pub flags: u32,
/// Emitted event data.
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Event {
pub address: Address,
pub data: Vec<u8>,
pub topics: Vec<U256>,
}
impl Default for CallOutput {
fn default() -> Self {
Self {
flags: u32::MAX,
data: Vec::new(),
/// The result of the contract call.
#[derive(Debug, Default, Clone)]
pub struct CallOutput {
/// The return flags.
pub flags: ReturnFlags,
/// The contract call output.
pub data: Vec<u8>,
/// The emitted events.
pub events: Vec<Event>,
}
/// The contract blob export to be called.
#[derive(Clone, Debug, Default)]
enum Export {
#[default]
Call,
Deploy(U256),
}
/// Possible contract call return flags.
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(u32)]
pub enum ReturnFlags {
/// The contract execution returned normally.
Success = 0,
/// The contract execution returned normally but state changes should be reverted.
Revert = 1,
/// The contract trapped unexpectedly during execution.
#[default]
Trap = u32::MAX,
}
impl From<u32> for ReturnFlags {
fn from(value: u32) -> Self {
match value {
0 => Self::Success,
1 => Self::Revert,
u32::MAX => Self::Trap,
_ => panic!("invalid return flag: {value}"),
}
}
}
/// The local context inside the call stack.
#[derive(Debug, Clone)]
struct Frame {
/// The account that is being executed.
callee: Address,
/// The caller account.
caller: Address,
/// The value transferred with this transaction.
callvalue: U256,
/// The calldata for the contract execution.
input: Vec<u8>,
// The contract call output.
output: CallOutput,
/// The export to call.
export: Export,
}
impl Default for Frame {
fn default() -> Self {
Self {
callee: Transaction::default_address(),
caller: Transaction::default_address(),
callvalue: Default::default(),
input: Default::default(),
output: Default::default(),
export: Default::default(),
}
}
}
/// The transaction can modify the state by calling contracts.
///
/// Use the [TransactionBuilder] to create new transactions.
#[derive(Default, Clone, Debug)]
pub struct Transaction {
state: State,
stack: Vec<Frame>,
}
impl Transaction {
pub fn default_address() -> Address {
Address::default().create2(B256::default(), keccak256([]).0)
}
fn top_frame(&self) -> &Frame {
self.stack.last().expect("transactions should have a frame")
}
fn top_frame_mut(&mut self) -> &mut Frame {
self.stack
.last_mut()
.expect("transactions should have a frame")
}
fn top_account_mut(&mut self) -> &mut Account {
let account = self.top_frame_mut().callee;
self.state
.accounts
.get_mut(&account)
.unwrap_or_else(|| panic!("callee has no associated account: {account}"))
}
fn create2(&self, salt: U256, blob_hash: U256) -> Address {
self.top_frame()
.callee
.create2(salt.to_be_bytes(), blob_hash.to_be_bytes())
}
}
/// Helper to create valid transactions.
#[derive(Default, Clone, Debug)]
pub struct TransactionBuilder {
context: Transaction,
state_before: State,
}
impl TransactionBuilder {
/// Set the caller account.
pub fn caller(mut self, account: Address) -> Self {
self.context.top_frame_mut().caller = account;
self
}
/// Set the callee account.
pub fn callee(mut self, account: Address) -> Self {
self.context.top_frame_mut().callee = account;
self
}
/// Set the transferred callvalue.
pub fn callvalue(mut self, amount: U256) -> Self {
self.context.top_frame_mut().callvalue = amount;
self
}
/// Set the calldata.
pub fn calldata(mut self, data: Vec<u8>) -> Self {
self.context.top_frame_mut().input = data;
self
}
/// Set the transaction to deploy code.
pub fn deploy(mut self, code: &[u8]) -> Self {
let blob_hash = self.context.state.upload_code(code);
self.context.top_frame_mut().export = Export::Deploy(blob_hash);
self
}
/// Set the account at [Transaction::default_address] to the given `code`.
///
/// Useful helper to spare the deploy transaction.
pub fn with_default_account(mut self, code: &[u8]) -> Self {
self.context.state.upload_code(code);
self.context.state.create_account(
Transaction::default_address(),
Default::default(),
keccak256(code).into(),
);
self
}
/// Execute the transaction with a default config backend.
///
/// Reverts any state changes if the contract reverts or the exuection traps.
pub fn call(mut self) -> (State, CallOutput) {
let blob_hash = match self.context.top_frame().export {
Export::Call => self.context.top_account_mut().contract.unwrap_or_else(|| {
panic!(
"not a contract account: {}",
self.context.top_frame().callee
)
}),
Export::Deploy(blob_hash) => blob_hash,
};
let code = self
.context
.state
.blobs
.get(&blob_hash)
.unwrap_or_else(|| panic!("contract code not found: {blob_hash}"));
let (mut instance, export) = prepare(code, None);
self.call_on(&mut instance, export)
}
/// Execute the transaction on a given instance and export.
/// The `instance` and `export` are expected to match that of the `Transaction`.
/// Reverts any state changes if the contract reverts or the exuection traps.
pub fn call_on(
mut self,
instance: &mut Instance<Transaction>,
export: ExportIndex,
) -> (State, CallOutput) {
let mut state_args = polkavm::StateArgs::default();
state_args.set_gas(polkavm::Gas::MAX);
if let Export::Deploy(blob_hash) = self.context.top_frame().export {
self.context.state.create_account(
self.context.create2(Default::default(), blob_hash),
self.context.top_frame().callvalue,
blob_hash,
);
}
let callvalue = self.context.top_frame().callvalue;
self.context.top_account_mut().value += callvalue;
let call_args = polkavm::CallArgs::new(&mut self.context, export);
init_logs();
match instance.call(state_args, call_args) {
Err(polkavm::ExecutionError::Trap(_)) => self.finalize(),
Err(other) => panic!("unexpected error: {other}"),
Ok(_) => panic!("unexpected return"),
}
}
/// Commits or reverts the state changes based on the call flags.
fn finalize(mut self) -> (State, CallOutput) {
let state = match self.context.top_frame().output.flags {
ReturnFlags::Success => self.context.state,
_ => self.state_before,
};
let output = self.context.stack.pop().unwrap().output;
(state, output)
}
}
impl From<State> for TransactionBuilder {
fn from(state: State) -> Self {
TransactionBuilder {
state_before: state.clone(),
context: Transaction {
state,
stack: Default::default(),
},
}
}
}
/// The mocked blockchain state.
#[derive(Default, Clone, Debug)]
pub struct State {
blobs: HashMap<U256, Vec<u8>>,
accounts: HashMap<Address, Account>,
}
impl State {
pub fn new(input: Vec<u8>) -> Self {
Self {
input,
..Default::default()
pub const BLOCK_NUMBER: u64 = 123;
pub const BLOCK_TIMESTAMP: u64 = 456;
pub fn transaction(self) -> TransactionBuilder {
TransactionBuilder {
state_before: self.clone(),
context: Transaction {
state: self,
stack: vec![Default::default()],
},
}
}
pub fn reset_output(&mut self) {
self.output = Default::default();
pub fn upload_code(&mut self, code: &[u8]) -> U256 {
let blob_hash = keccak256(code).into();
self.blobs.insert(blob_hash, code.to_vec());
blob_hash
}
pub fn assert_storage_key(&self, at: U256, expect: U256) {
assert_eq!(self.storage[&at], expect);
pub fn assert_storage_key(&self, account: Address, key: U256, expected: U256) {
assert_eq!(
self.accounts
.get(&account)
.unwrap_or_else(|| panic!("unknown account: {account}"))
.storage
.get(&key)
.copied()
.unwrap_or_default(),
expected
);
}
pub fn create_account(&mut self, address: Address, value: U256, blob_hash: U256) {
self.accounts.insert(
address,
Account {
value,
contract: Some(blob_hash),
storage: HashMap::new(),
},
);
}
}
fn link_host_functions(engine: &Engine) -> Linker<State> {
fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
let mut linker = Linker::new(engine);
linker
.func_wrap(
"input",
|caller: Caller<State>, out_ptr: u32, out_len_ptr: u32| -> Result<(), Trap> {
let (mut caller, state) = caller.split();
runtime_api::INPUT,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| -> Result<(), Trap> {
let (mut caller, transaction) = caller.split();
assert!(state.input.len() <= caller.read_u32(out_len_ptr).unwrap() as usize);
let input = &transaction.top_frame().input;
assert!(input.len() <= caller.read_u32(out_len_ptr).unwrap() as usize);
caller.write_memory(out_ptr, &state.input)?;
caller.write_memory(out_len_ptr, &(state.input.len() as u32).to_le_bytes())?;
caller.write_memory(out_ptr, input)?;
caller.write_memory(out_len_ptr, &(input.len() as u32).to_le_bytes())?;
Ok(())
},
@@ -69,12 +342,17 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
linker
.func_wrap(
"seal_return",
|caller: Caller<State>, flags: u32, data_ptr: u32, data_len: u32| -> Result<(), Trap> {
let (caller, state) = caller.split();
runtime_api::RETURN,
|caller: Caller<Transaction>,
flags: u32,
data_ptr: u32,
data_len: u32|
-> Result<(), Trap> {
let (caller, transaction) = caller.split();
state.output.flags = flags;
state.output.data = caller.read_memory_into_vec(data_ptr, data_len)?;
let frame = transaction.top_frame_mut();
frame.output.flags = flags.into();
frame.output.data = caller.read_memory_into_vec(data_ptr, data_len)?;
Err(Default::default())
},
@@ -83,12 +361,18 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
linker
.func_wrap(
"value_transferred",
|caller: Caller<State>, out_ptr: u32, out_len_ptr: u32| -> Result<(), Trap> {
let (mut caller, state) = caller.split();
runtime_api::VALUE_TRANSFERRED,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| -> Result<(), Trap> {
let (mut caller, transaction) = caller.split();
let value = state.value.to_le_bytes();
let out_len = caller.read_u32(out_len_ptr)? as usize;
assert_eq!(
out_len,
revive_common::BYTE_LENGTH_VALUE,
"spurious output buffer size: {out_len}"
);
let value = transaction.top_frame().callvalue.as_le_bytes();
caller.write_memory(out_ptr, &value)?;
caller.write_memory(out_len_ptr, &(value.len() as u32).to_le_bytes())?;
@@ -100,7 +384,7 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
linker
.func_wrap(
"debug_message",
|caller: Caller<State>, str_ptr: u32, str_len: u32| -> Result<u32, Trap> {
|caller: Caller<Transaction>, str_ptr: u32, str_len: u32| -> Result<u32, Trap> {
let (caller, _) = caller.split();
let data = caller.read_memory_into_vec(str_ptr, str_len)?;
@@ -113,22 +397,30 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
linker
.func_wrap(
"set_storage",
|caller: Caller<State>,
runtime_api::SET_STORAGE,
|caller: Caller<Transaction>,
key_ptr: u32,
key_len: u32,
value_ptr: u32,
value_len: u32|
-> Result<u32, Trap> {
let (caller, state) = caller.split();
let (caller, transaction) = caller.split();
assert_eq!(key_len, 32, "storage key must be 32 bytes");
assert_eq!(value_len, 32, "storage value must be 32 bytes");
assert_eq!(
key_len as usize,
revive_common::BYTE_LENGTH_WORD,
"storage key must be 32 bytes"
);
assert_eq!(
value_len as usize,
revive_common::BYTE_LENGTH_WORD,
"storage value must be 32 bytes"
);
let key = caller.read_memory_into_vec(key_ptr, key_len)?;
let value = caller.read_memory_into_vec(value_ptr, value_len)?;
state.storage.insert(
transaction.top_account_mut().storage.insert(
U256::from_be_bytes::<32>(key.try_into().unwrap()),
U256::from_be_bytes::<32>(value.try_into().unwrap()),
);
@@ -140,20 +432,25 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
linker
.func_wrap(
"get_storage",
|caller: Caller<State>,
runtime_api::GET_STORAGE,
|caller: Caller<Transaction>,
key_ptr: u32,
key_len: u32,
out_ptr: u32,
out_len_ptr: u32|
-> Result<u32, Trap> {
let (mut caller, state) = caller.split();
let (mut caller, transaction) = caller.split();
let key = caller.read_memory_into_vec(key_ptr, key_len)?;
let out_len = caller.read_u32(out_len_ptr)?;
assert!(out_len >= 32);
let out_len = caller.read_u32(out_len_ptr)? as usize;
assert_eq!(
out_len,
revive_common::BYTE_LENGTH_WORD,
"spurious output buffer size: {out_len}"
);
let value = state
let value = transaction
.top_account_mut()
.storage
.get(&U256::from_be_bytes::<32>(key.try_into().unwrap()))
.map(U256::to_be_bytes::<32>)
@@ -169,8 +466,8 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
linker
.func_wrap(
"hash_keccak_256",
|caller: Caller<State>,
runtime_api::HASH_KECCAK_256,
|caller: Caller<Transaction>,
input_ptr: u32,
input_len: u32,
out_ptr: u32|
@@ -188,6 +485,129 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
)
.unwrap();
linker
.func_wrap(
runtime_api::NOW,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| {
let (mut caller, _) = caller.split();
let out_len = caller.read_u32(out_len_ptr)? as usize;
assert_eq!(
out_len,
revive_common::BYTE_LENGTH_BLOCK_TIMESTAMP,
"spurious output buffer size: {out_len}"
);
caller.write_memory(out_ptr, &State::BLOCK_TIMESTAMP.to_le_bytes())?;
caller.write_memory(out_len_ptr, &64u32.to_le_bytes())?;
Ok(())
},
)
.unwrap();
linker
.func_wrap(
runtime_api::BLOCK_NUMBER,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| {
let (mut caller, _) = caller.split();
let out_len = caller.read_u32(out_len_ptr)? as usize;
assert_eq!(
out_len,
revive_common::BYTE_LENGTH_BLOCK_NUMBER,
"spurious output buffer size: {out_len}"
);
caller.write_memory(out_ptr, &State::BLOCK_NUMBER.to_le_bytes())?;
caller.write_memory(out_len_ptr, &64u32.to_le_bytes())?;
Ok(())
},
)
.unwrap();
linker
.func_wrap(
runtime_api::ADDRESS,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| {
let (mut caller, transaction) = caller.split();
let out_len = caller.read_u32(out_len_ptr)? as usize;
assert_eq!(
out_len,
revive_common::BYTE_LENGTH_ETH_ADDRESS,
"spurious output buffer size: {out_len}"
);
let address = transaction.top_frame().callee.as_slice();
caller.write_memory(out_ptr, address)?;
caller.write_memory(out_len_ptr, &(address.len() as u32).to_le_bytes())?;
Ok(())
},
)
.unwrap();
linker
.func_wrap(
runtime_api::CALLER,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| {
let (mut caller, transaction) = caller.split();
let out_len = caller.read_u32(out_len_ptr)? as usize;
assert_eq!(
out_len,
revive_common::BYTE_LENGTH_ETH_ADDRESS,
"spurious output buffer size: {out_len}"
);
let address = transaction.top_frame().caller.as_slice();
caller.write_memory(out_ptr, address)?;
caller.write_memory(out_len_ptr, &(address.len() as u32).to_le_bytes())?;
Ok(())
},
)
.unwrap();
linker
.func_wrap(
runtime_api::DEPOSIT_EVENT,
|caller: Caller<Transaction>,
topics_ptr: u32,
topics_len: u32,
data_ptr: u32,
data_len: u32| {
let (caller, transaction) = caller.split();
let address = transaction.top_frame().callee;
let data = if data_len != 0 {
caller.read_memory_into_vec(data_ptr, data_len)?
} else {
Default::default()
};
let topics = if topics_len != 0 {
caller
.read_memory_into_vec(topics_ptr, topics_len)?
.chunks(32)
.map(|chunk| U256::from_be_slice(chunk))
.collect()
} else {
Default::default()
};
transaction.top_frame_mut().output.events.push(Event {
address,
data,
topics,
});
Ok(())
},
)
.unwrap();
linker
}
@@ -202,15 +622,18 @@ pub fn recompile_code(code: &[u8], engine: &Engine) -> Module {
Module::new(engine, &module_config, code).unwrap()
}
pub fn instantiate_module(module: &Module, engine: &Engine) -> (Instance<State>, ExportIndex) {
let export = module.lookup_export("call").unwrap();
pub fn instantiate_module(
module: &Module,
engine: &Engine,
) -> (Instance<Transaction>, ExportIndex) {
let export = module.lookup_export(runtime_api::CALL).unwrap();
let func = link_host_functions(engine).instantiate_pre(module).unwrap();
let instance = func.instantiate().unwrap();
(instance, export)
}
pub fn prepare(code: &[u8], config: Option<Config>) -> (Instance<State>, ExportIndex) {
pub fn prepare(code: &[u8], config: Option<Config>) -> (Instance<Transaction>, ExportIndex) {
let blob = ProgramBlob::parse(code).unwrap();
let engine = Engine::new(&config.unwrap_or_default()).unwrap();
@@ -219,7 +642,7 @@ pub fn prepare(code: &[u8], config: Option<Config>) -> (Instance<State>, ExportI
module_config.set_gas_metering(Some(GasMeteringKind::Sync));
let module = Module::from_blob(&engine, &module_config, &blob).unwrap();
let export = module.lookup_export("call").unwrap();
let export = module.lookup_export(runtime_api::CALL).unwrap();
let func = link_host_functions(&engine)
.instantiate_pre(&module)
.unwrap();
@@ -228,23 +651,6 @@ pub fn prepare(code: &[u8], config: Option<Config>) -> (Instance<State>, ExportI
(instance, export)
}
pub fn call(mut state: State, on: &mut Instance<State>, export: ExportIndex) -> State {
state.reset_output();
let mut state_args = polkavm::StateArgs::default();
state_args.set_gas(polkavm::Gas::MAX);
let call_args = polkavm::CallArgs::new(&mut state, export);
init_logs();
match on.call(state_args, call_args) {
Err(polkavm::ExecutionError::Trap(_)) => state,
Err(other) => panic!("unexpected error: {other}"),
Ok(_) => panic!("unexpected return"),
}
}
fn init_logs() {
if std::env::var("RUST_LOG").is_ok() {
#[cfg(test)]
+220 -64
View File
@@ -1,11 +1,12 @@
use alloy_primitives::{FixedBytes, Keccak256, I256, U256};
use alloy_primitives::{keccak256, Address, FixedBytes, I256, U256};
use alloy_sol_types::{sol, SolCall};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use sha1::Digest;
use crate::{
assert_success,
cases::Contract,
mock_runtime::{self, State},
mock_runtime::{self, ReturnFlags, State, Transaction},
};
#[test]
@@ -17,8 +18,8 @@ fn fibonacci() {
Contract::fib_iterative(parameter),
Contract::fib_binet(parameter),
] {
let state = assert_success(contract, true);
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
let (_, output) = assert_success(&contract, true);
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
let expected = U256::from(8);
assert_eq!(received, expected);
}
@@ -26,17 +27,16 @@ fn fibonacci() {
#[test]
fn flipper() {
let code = crate::compile_blob("Flipper", include_str!("../contracts/flipper.sol"));
let state = State::new(0xcde4efa9u32.to_be_bytes().to_vec());
let (mut instance, export) = mock_runtime::prepare(&code, None);
let contract = Contract::flipper();
let address = Transaction::default_address();
let state = crate::mock_runtime::call(state, &mut instance, export);
assert_eq!(state.output.flags, 0);
assert_eq!(state.storage[&U256::ZERO], U256::try_from(1).unwrap());
let (state, output) = contract.execute();
assert_eq!(output.flags, ReturnFlags::Success);
state.assert_storage_key(address, U256::ZERO, U256::from(1));
let state = crate::mock_runtime::call(state, &mut instance, export);
assert_eq!(state.output.flags, 0);
assert_eq!(state.storage[&U256::ZERO], U256::ZERO);
let (state, output) = state.transaction().calldata(contract.calldata).call();
assert_eq!(output.flags, ReturnFlags::Success);
state.assert_storage_key(address, U256::ZERO, U256::ZERO);
}
#[test]
@@ -57,16 +57,16 @@ fn hash_keccak_256() {
let param = "hello";
let input = TestSha3::testCall::new((param.to_string(),)).abi_encode();
let state = State::new(input);
let (mut instance, export) = mock_runtime::prepare(&code, None);
let state = crate::mock_runtime::call(state, &mut instance, export);
let (_, output) = State::default()
.transaction()
.with_default_account(&code)
.calldata(input)
.call();
assert_eq!(state.output.flags, 0);
assert_eq!(output.flags, ReturnFlags::Success);
let mut hasher = Keccak256::new();
hasher.update(param);
let expected = hasher.finalize();
let received = FixedBytes::<32>::from_slice(&state.output.data);
let expected = keccak256(param.as_bytes());
let received = FixedBytes::<32>::from_slice(&output.data);
assert_eq!(received, expected);
}
@@ -77,16 +77,16 @@ fn erc20() {
#[test]
fn triangle_number() {
let state = assert_success(Contract::triangle_number(13), true);
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
let (_, output) = assert_success(&Contract::triangle_number(13), true);
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
let expected = U256::try_from(91).unwrap();
assert_eq!(received, expected);
}
#[test]
fn odd_product() {
let state = assert_success(Contract::odd_product(5), true);
let received = I256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
let (_, output) = assert_success(&Contract::odd_product(5), true);
let received = I256::from_be_bytes::<32>(output.data.try_into().unwrap());
let expected = I256::try_from(945i64).unwrap();
assert_eq!(received, expected);
}
@@ -105,16 +105,19 @@ fn msize_plain() {
false,
revive_solidity::SolcPipeline::EVMLA,
);
let (mut instance, export) = mock_runtime::prepare(&code, None);
let input = MSize::mSizeCall::new(()).abi_encode();
let state = crate::mock_runtime::call(State::new(input), &mut instance, export);
let (_, output) = State::default()
.transaction()
.calldata(input)
.with_default_account(&code)
.call();
assert_eq!(state.output.flags, 0);
assert_eq!(output.flags, ReturnFlags::Success);
// Solidity always stores the "free memory pointer" (32 byte int) at offset 64.
let expected = U256::try_from(64 + 32).unwrap();
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
assert_eq!(received, expected);
}
@@ -126,16 +129,18 @@ fn transferred_value() {
}
);
let code = crate::compile_blob("Value", include_str!("../contracts/Value.sol"));
let mut state = State::new(Value::valueCall::SELECTOR.to_vec());
state.value = 0x1;
let (mut instance, export) = mock_runtime::prepare(&code, None);
let state = crate::mock_runtime::call(state, &mut instance, export);
let (_, output) = State::default()
.transaction()
.calldata(Value::valueCall::SELECTOR.to_vec())
.callvalue(U256::from(123))
.with_default_account(&code)
.call();
assert_eq!(state.output.flags, 0);
assert_eq!(output.flags, ReturnFlags::Success);
let expected = I256::try_from(state.value).unwrap();
let received = I256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
let expected = I256::try_from(123).unwrap();
let received = I256::from_be_bytes::<32>(output.data.try_into().unwrap());
assert_eq!(received, expected);
}
@@ -153,43 +158,27 @@ fn msize_non_word_sized_access() {
false,
revive_solidity::SolcPipeline::Yul,
);
let (mut instance, export) = mock_runtime::prepare(&code, None);
let input = MSize::mStore100Call::new(()).abi_encode();
let state = crate::mock_runtime::call(State::new(input), &mut instance, export);
let (_, output) = State::default()
.transaction()
.with_default_account(&code)
.calldata(input)
.call();
assert_eq!(state.output.flags, 0);
assert_eq!(output.flags, ReturnFlags::Success);
// https://docs.zksync.io/build/developer-reference/differences-with-ethereum.html#mstore-mload
// "Unlike EVM, where the memory growth is in words, on zkEVM the memory growth is counted in bytes."
// "For example, if you write mstore(100, 0) the msize on zkEVM will be 132, but on the EVM it will be 160."
let expected = U256::try_from(132).unwrap();
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
assert_eq!(received, expected);
}
#[test]
fn mstore8() {
sol!(
#[derive(Debug, PartialEq, Eq)]
contract MStore8 {
function mStore8(uint value) public pure returns (uint256 word);
}
);
let code = crate::compile_blob("MStore8", include_str!("../contracts/mStore8.sol"));
let (mut instance, export) = mock_runtime::prepare(&code, None);
let mut assert = |parameter, expected| {
let input = MStore8::mStore8Call::new((parameter,)).abi_encode();
let state = crate::mock_runtime::call(State::new(input), &mut instance, export);
assert_eq!(state.output.flags, 0);
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
assert_eq!(received, expected);
};
for (parameter, expected) in [
for (received, expected) in [
(U256::MIN, U256::MIN),
(
U256::from(1),
@@ -248,8 +237,16 @@ fn mstore8() {
)
.unwrap(),
),
] {
assert(parameter, expected);
]
.par_iter()
.map(|(parameter, expected)| {
let (_, output) = assert_success(&Contract::mstore8(*parameter), true);
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
(received, *expected)
})
.collect::<Vec<_>>()
{
assert_eq!(received, expected);
}
}
@@ -260,8 +257,167 @@ fn sha1() {
hasher.update(&pre);
let hash = hasher.finalize();
let state = assert_success(Contract::sha1(pre), true);
let (_, output) = assert_success(&Contract::sha1(pre), true);
let expected = FixedBytes::<20>::from_slice(&hash[..]);
let received = FixedBytes::<20>::from_slice(&state.output.data[..20]);
let received = FixedBytes::<20>::from_slice(&output.data[..20]);
assert_eq!(received, expected);
}
#[test]
fn block_number() {
let (_, output) = assert_success(&Contract::block_number(), true);
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
let expected = U256::from(mock_runtime::State::BLOCK_NUMBER);
assert_eq!(received, expected);
}
#[test]
fn block_timestamp() {
let (_, output) = assert_success(&Contract::block_timestamp(), true);
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
let expected = U256::from(mock_runtime::State::BLOCK_TIMESTAMP);
assert_eq!(received, expected);
}
#[test]
fn address() {
let contract = Contract::context_address();
let (_, output) = assert_success(&contract, true);
let received = Address::from_slice(&output.data[12..]);
let expected = Transaction::default_address();
assert_eq!(received, expected);
}
#[test]
fn caller() {
let (_, output) = assert_success(&Contract::context_caller(), true);
let received = Address::from_slice(&output.data[12..]);
let expected = Transaction::default_address();
assert_eq!(received, expected);
}
#[test]
fn unsigned_division() {
let one = U256::from(1);
let two = U256::from(2);
let five = U256::from(5);
for (received, expected) in [
(five, five, one),
(five, one, five),
(U256::ZERO, U256::MAX, U256::ZERO),
(five, two, two),
(one, U256::ZERO, U256::ZERO),
]
.par_iter()
.map(|(n, d, q)| {
let (_, output) = assert_success(&Contract::division_arithmetics_div(*n, *d), true);
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
(received, *q)
})
.collect::<Vec<_>>()
{
assert_eq!(received, expected)
}
}
#[test]
fn signed_division() {
let one = I256::try_from(1).unwrap();
let two = I256::try_from(2).unwrap();
let minus_two = I256::try_from(-2).unwrap();
let five = I256::try_from(5).unwrap();
let minus_five = I256::try_from(-5).unwrap();
for (received, expected) in [
(five, five, one),
(five, one, five),
(I256::ZERO, I256::MAX, I256::ZERO),
(I256::ZERO, I256::MINUS_ONE, I256::ZERO),
(five, two, two),
(five, I256::MINUS_ONE, minus_five),
(I256::MINUS_ONE, minus_two, I256::ZERO),
(minus_five, minus_five, one),
(minus_five, two, minus_two),
(I256::MINUS_ONE, I256::MIN, I256::ZERO),
(one, I256::ZERO, I256::ZERO),
]
.par_iter()
.map(|(n, d, q)| {
let (_, output) = assert_success(&Contract::division_arithmetics_sdiv(*n, *d), true);
let received = I256::from_be_bytes::<32>(output.data.try_into().unwrap());
(received, *q)
})
.collect::<Vec<_>>()
{
assert_eq!(received, expected);
}
}
#[test]
fn unsigned_remainder() {
let one = U256::from(1);
let two = U256::from(2);
let five = U256::from(5);
for (received, expected) in [
(five, five, U256::ZERO),
(five, one, U256::ZERO),
(U256::ZERO, U256::MAX, U256::ZERO),
(U256::MAX, U256::MAX, U256::ZERO),
(five, two, one),
(two, five, two),
(U256::MAX, U256::ZERO, U256::ZERO),
]
.par_iter()
.map(|(n, d, q)| {
let (_, output) = assert_success(&Contract::division_arithmetics_mod(*n, *d), true);
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
(received, *q)
})
.collect::<Vec<_>>()
{
assert_eq!(received, expected);
}
}
#[test]
fn signed_remainder() {
let one = I256::try_from(1).unwrap();
let two = I256::try_from(2).unwrap();
let minus_two = I256::try_from(-2).unwrap();
let five = I256::try_from(5).unwrap();
let minus_five = I256::try_from(-5).unwrap();
for (received, expected) in [
(five, five, I256::ZERO),
(five, one, I256::ZERO),
(I256::ZERO, I256::MAX, I256::ZERO),
(I256::MAX, I256::MAX, I256::ZERO),
(five, two, one),
(two, five, two),
(five, minus_five, I256::ZERO),
(five, I256::MINUS_ONE, I256::ZERO),
(five, minus_two, one),
(minus_five, two, I256::MINUS_ONE),
(minus_two, five, minus_two),
(minus_five, minus_five, I256::ZERO),
(minus_five, I256::MINUS_ONE, I256::ZERO),
(minus_five, minus_two, I256::MINUS_ONE),
(minus_two, minus_five, minus_two),
(I256::MIN, I256::MINUS_ONE, I256::ZERO),
(I256::ZERO, I256::ZERO, I256::ZERO),
]
.par_iter()
.map(|(n, d, q)| {
let (_, output) = assert_success(&Contract::division_arithmetics_smod(*n, *d), true);
let received = I256::from_be_bytes::<32>(output.data.try_into().unwrap());
(received, *q)
})
.collect::<Vec<_>>()
{
assert_eq!(received, expected);
}
}
#[test]
fn events() {
assert_success(&Contract::event(U256::ZERO), true);
assert_success(&Contract::event(U256::from(123)), true);
}
+7 -2
View File
@@ -1,5 +1,5 @@
[package]
name = "era-compiler-llvm-context"
name = "revive-llvm-context"
version = "1.4.1"
authors = [
"Oleksandr Zarudnyi <a.zarudnyy@matterlabs.dev>",
@@ -7,11 +7,14 @@ authors = [
]
license = "MIT OR Apache-2.0"
edition = "2021"
description = "Shared front end code of the EraVM compilers"
description = "Shared front end code of the revive PolkaVM compilers"
[lib]
doctest = false
[features]
riscv-zbb = []
[dependencies]
anyhow = { workspace = true }
semver = { workspace = true }
@@ -25,6 +28,8 @@ sha2 = { workspace = true }
sha3 = { workspace = true }
md5 = { workspace = true }
inkwell = { workspace = true }
polkavm-disassembler = { workspace = true }
polkavm-common = { workspace = true }
zkevm_opcode_defs = { git = "https://github.com/matter-labs/era-zkevm_opcode_defs", branch = "v1.4.1" }
revive-common = { path = "../common" }
@@ -1,10 +1,6 @@
//!
//! The debug IR type.
//!
///
/// The debug IR type.
///
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -24,9 +20,7 @@ pub enum IRType {
}
impl IRType {
///
/// Returns the file extension for the specified IR.
///
pub fn file_extension(&self) -> &'static str {
match self {
Self::Yul => revive_common::EXTENSION_YUL,
@@ -34,7 +28,7 @@ impl IRType {
Self::EVMLA => revive_common::EXTENSION_EVMLA,
Self::LLL => revive_common::EXTENSION_LLL,
Self::LLVM => revive_common::EXTENSION_LLVM_SOURCE,
Self::Assembly => revive_common::EXTENSION_ERAVM_ASSEMBLY,
Self::Assembly => revive_common::EXTENSION_POLKAVM_ASSEMBLY,
}
}
}
@@ -1,6 +1,4 @@
//!
//! The debug configuration.
//!
pub mod ir_type;
@@ -11,9 +9,7 @@ use serde::Serialize;
use self::ir_type::IRType;
///
/// The debug configuration.
///
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct DebugConfig {
/// The directory to dump the IRs to.
@@ -21,16 +17,12 @@ pub struct DebugConfig {
}
impl DebugConfig {
///
/// A shortcut constructor.
///
pub fn new(output_directory: PathBuf) -> Self {
Self { output_directory }
}
///
/// Dumps the Yul IR.
///
pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
@@ -40,9 +32,7 @@ impl DebugConfig {
Ok(())
}
///
/// Dumps the EVM legacy assembly IR.
///
pub fn dump_evmla(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::EVMLA);
@@ -52,9 +42,7 @@ impl DebugConfig {
Ok(())
}
///
/// Dumps the Ethereal IR.
///
pub fn dump_ethir(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::EthIR);
@@ -64,9 +52,7 @@ impl DebugConfig {
Ok(())
}
///
/// Dumps the LLL IR.
///
pub fn dump_lll(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::LLL);
@@ -76,9 +62,7 @@ impl DebugConfig {
Ok(())
}
///
/// Dumps the unoptimized LLVM IR.
///
pub fn dump_llvm_ir_unoptimized(
&self,
contract_path: &str,
@@ -94,9 +78,7 @@ impl DebugConfig {
Ok(())
}
///
/// Dumps the optimized LLVM IR.
///
pub fn dump_llvm_ir_optimized(
&self,
contract_path: &str,
@@ -112,9 +94,7 @@ impl DebugConfig {
Ok(())
}
///
/// Dumps the assembly.
///
pub fn dump_assembly(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::Assembly);
@@ -124,9 +104,7 @@ impl DebugConfig {
Ok(())
}
///
/// Creates a full file name, given the contract full path, suffix, and extension.
///
fn full_file_name(contract_path: &str, suffix: Option<&str>, ir_type: IRType) -> String {
let mut full_file_name = contract_path.replace('/', "_").replace(':', ".");
if let Some(suffix) = suffix {
@@ -1,149 +0,0 @@
//!
//! Translates the arithmetic operations.
//!
use inkwell::values::BasicValue;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
///
/// Translates the arithmetic addition.
///
pub fn addition<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_add(operand_1, operand_2, "addition_result")?
.as_basic_value_enum())
}
///
/// Translates the arithmetic subtraction.
///
pub fn subtraction<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_sub(operand_1, operand_2, "subtraction_result")?
.as_basic_value_enum())
}
///
/// Translates the arithmetic multiplication.
///
pub fn multiplication<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_mul(operand_1, operand_2, "multiplication_result")?
.as_basic_value_enum())
}
///
/// Translates the arithmetic division.
///
pub fn division<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_unsigned_div(operand_1, operand_2, "udiv")?
.into())
}
///
/// Translates the arithmetic remainder.
///
pub fn remainder<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.build_call(
context.llvm_runtime().r#mod,
&[
operand_1.as_basic_value_enum(),
operand_2.as_basic_value_enum(),
],
"add_mod_call",
)
.expect("Always exists"))
}
///
/// Translates the signed arithmetic division.
///
/// Two differences between the EVM and LLVM IR:
/// 1. In case of division by zero, 0 is returned.
/// 2. In case of overflow, the first argument is returned.
///
pub fn division_signed<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.build_call(
context.llvm_runtime().sdiv,
&[
operand_1.as_basic_value_enum(),
operand_2.as_basic_value_enum(),
],
"add_mod_call",
)
.expect("Always exists"))
}
///
/// Translates the signed arithmetic remainder.
///
pub fn remainder_signed<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.build_call(
context.llvm_runtime().smod,
&[
operand_1.as_basic_value_enum(),
operand_2.as_basic_value_enum(),
],
"add_mod_call",
)
.expect("Always exists"))
}
@@ -1,158 +0,0 @@
//!
//! Translates the context getter instructions.
//!
use inkwell::values::BasicValue;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
///
/// Translates the `gas_limit` instruction.
///
pub fn gas_limit<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `gas_price` instruction.
///
pub fn gas_price<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `tx.origin` instruction.
///
pub fn origin<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `chain_id` instruction.
///
pub fn chain_id<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `block_number` instruction.
///
pub fn block_number<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `block_timestamp` instruction.
///
pub fn block_timestamp<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `block_hash` instruction.
///
pub fn block_hash<'ctx, D>(
_context: &mut Context<'ctx, D>,
_index: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `difficulty` instruction.
///
pub fn difficulty<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `coinbase` instruction.
///
pub fn coinbase<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `basefee` instruction.
///
pub fn basefee<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
///
/// Translates the `msize` instruction.
///
pub fn msize<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let heap_end = context.build_sbrk(context.xlen_type().const_zero())?;
let heap_start = context
.get_global(crate::eravm::GLOBAL_HEAP_MEMORY_POINTER)?
.value
.as_pointer_value();
let heap_size = context.builder().build_int_nuw_sub(
context
.builder()
.build_ptr_to_int(heap_end, context.xlen_type(), "heap_end")?,
context
.builder()
.build_ptr_to_int(heap_start, context.xlen_type(), "heap_start")?,
"heap_size",
)?;
Ok(context
.builder()
.build_int_z_extend(heap_size, context.field_type(), "heap_size_extended")?
.as_basic_value_enum())
}
@@ -1,51 +0,0 @@
//!
//! Translates the cryptographic operations.
//!
use crate::eravm::context::Context;
use crate::eravm::Dependency;
///
/// Translates the `sha3` instruction.
///
pub fn sha3<'ctx, D>(
context: &mut Context<'ctx, D>,
offset: inkwell::values::IntValue<'ctx>,
length: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let offset_casted = context.safe_truncate_int_to_i32(offset)?;
let length_casted = context.safe_truncate_int_to_i32(length)?;
let input_pointer = context.build_heap_gep(offset_casted, length_casted)?;
let input_pointer_casted = context.builder().build_ptr_to_int(
input_pointer.value,
context.xlen_type(),
"input_pointer_casted",
)?;
let output_pointer = context.build_alloca(context.field_type(), "output_pointer");
let output_pointer_casted = context.builder().build_ptr_to_int(
output_pointer.value,
context.xlen_type(),
"output_pointer_casted",
)?;
let function = context
.module()
.get_function("hash_keccak_256")
.expect("is declared");
context.builder().build_call(
function,
&[
input_pointer_casted.into(),
length_casted.into(),
output_pointer_casted.into(),
],
"call_seal_hash_keccak_256",
)?;
Ok(context.build_byte_swap(context.build_load(output_pointer, "sha3_output")?))
}
@@ -1,81 +0,0 @@
//!
//! Translates the value and balance operations.
//!
use inkwell::values::BasicValue;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
///
/// Translates the `gas` instruction.
///
pub fn gas<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context.integer_const(256, 0).as_basic_value_enum())
}
///
/// Translates the `value` instruction.
///
pub fn value<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let output_pointer = context.build_alloca(context.value_type(), "output_pointer");
let output_pointer_casted = context.builder().build_ptr_to_int(
output_pointer.value,
context.xlen_type(),
"output_pointer_casted",
)?;
let output_length_pointer = context.build_alloca(context.xlen_type(), "output_len_pointer");
let output_length_pointer_casted = context.builder().build_ptr_to_int(
output_length_pointer.value,
context.xlen_type(),
"output_pointer_casted",
)?;
context.build_store(
output_length_pointer,
context.integer_const(crate::eravm::XLEN, revive_common::BYTE_LENGTH_VALUE as u64),
)?;
context.builder().build_call(
context
.module()
.get_function("value_transferred")
.expect("is declared"),
&[
output_pointer_casted.into(),
output_length_pointer_casted.into(),
],
"call_seal_value_transferred",
)?;
let value = context.build_load(output_pointer, "transferred_value")?;
let value_extended = context.builder().build_int_z_extend(
value.into_int_value(),
context.field_type(),
"transferred_value_extended",
)?;
Ok(value_extended.as_basic_value_enum())
}
///
/// Translates the `balance` instructions.
///
pub fn balance<'ctx, D>(
_context: &mut Context<'ctx, D>,
_address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
@@ -1,78 +0,0 @@
//!
//! Translates a log or event call.
//!
use crate::eravm::context::Context;
use crate::eravm::Dependency;
///
/// Translates a log or event call.
///
/// The decoding logic is implemented in a system contract, which is called from here.
///
/// There are several cases of the translation for the sake of efficiency, since the front-end
/// emits topics and values sequentially by one, but the LLVM intrinsic and bytecode instruction
/// accept two at once.
///
pub fn log<'ctx, D>(
_context: &mut Context<'ctx, D>,
_input_offset: inkwell::values::IntValue<'ctx>,
_input_length: inkwell::values::IntValue<'ctx>,
_topics: Vec<inkwell::values::IntValue<'ctx>>,
) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
/*
let failure_block = context.append_basic_block("event_failure_block");
let join_block = context.append_basic_block("event_join_block");
let gas = crate::eravm::evm::ether_gas::gas(context)?.into_int_value();
let abi_data = crate::eravm::utils::abi_data(
context,
input_offset,
input_length,
Some(gas),
AddressSpace::Heap,
true,
)?;
let mut extra_abi_data = Vec::with_capacity(1 + topics.len());
extra_abi_data.push(context.field_const(topics.len() as u64));
extra_abi_data.extend(topics);
let result = context
.build_call(
context.llvm_runtime().far_call,
crate::eravm::utils::external_call_arguments(
context,
abi_data.as_basic_value_enum(),
context.field_const(zkevm_opcode_defs::ADDRESS_EVENT_WRITER as u64),
extra_abi_data,
None,
)
.as_slice(),
"event_writer_call_external",
)
.expect("Always returns a value");
let result_status_code_boolean = context
.builder()
.build_extract_value(
result.into_struct_value(),
1,
"event_writer_external_result_status_code_boolean",
)
.expect("Always exists");
context.build_conditional_branch(
result_status_code_boolean.into_int_value(),
join_block,
failure_block,
)?;
context.set_basic_block(failure_block);
crate::eravm::evm::r#return::revert(context, context.field_const(0), context.field_const(0))?;
context.set_basic_block(join_block);
*/
Ok(())
}
+55 -59
View File
@@ -1,78 +1,74 @@
//!
//! The LLVM context library.
//!
pub(crate) mod debug_config;
pub(crate) mod eravm;
pub(crate) mod optimizer;
pub(crate) mod polkavm;
pub(crate) mod target_machine;
pub use self::debug_config::ir_type::IRType as DebugConfigIR;
pub use self::debug_config::DebugConfig;
pub use self::eravm::build_assembly_text as eravm_build_assembly_text;
pub use self::eravm::context::address_space::AddressSpace as EraVMAddressSpace;
pub use self::eravm::context::argument::Argument as EraVMArgument;
pub use self::eravm::context::attribute::Attribute as EraVMAttribute;
pub use self::eravm::context::build::Build as EraVMBuild;
pub use self::eravm::context::code_type::CodeType as EraVMCodeType;
pub use self::eravm::context::evmla_data::EVMLAData as EraVMContextEVMLAData;
pub use self::eravm::context::function::block::evmla_data::key::Key as EraVMFunctionBlockKey;
pub use self::eravm::context::function::block::evmla_data::EVMLAData as EraVMFunctionBlockEVMLAData;
pub use self::eravm::context::function::block::Block as EraVMFunctionBlock;
pub use self::eravm::context::function::declaration::Declaration as EraVMFunctionDeclaration;
pub use self::eravm::context::function::evmla_data::EVMLAData as EraVMFunctionEVMLAData;
pub use self::eravm::context::function::intrinsics::Intrinsics as EraVMIntrinsicFunction;
pub use self::eravm::context::function::llvm_runtime::LLVMRuntime as EraVMLLVMRuntime;
pub use self::eravm::context::function::r#return::Return as EraVMFunctionReturn;
pub use self::eravm::context::function::runtime::deploy_code::DeployCode as EraVMDeployCodeFunction;
pub use self::eravm::context::function::runtime::entry::Entry as EraVMEntryFunction;
pub use self::eravm::context::function::runtime::runtime_code::RuntimeCode as EraVMRuntimeCodeFunction;
pub use self::eravm::context::function::runtime::Runtime as EraVMRuntime;
pub use self::eravm::context::function::vyper_data::VyperData as EraVMFunctionVyperData;
pub use self::eravm::context::function::yul_data::YulData as EraVMFunctionYulData;
pub use self::eravm::context::function::Function as EraVMFunction;
pub use self::eravm::context::global::Global as EraVMGlobal;
pub use self::eravm::context::pointer::Pointer as EraVMPointer;
pub use self::eravm::context::r#loop::Loop as EraVMLoop;
pub use self::eravm::context::solidity_data::SolidityData as EraVMContextSolidityData;
pub use self::eravm::context::vyper_data::VyperData as EraVMContextVyperData;
pub use self::eravm::context::yul_data::YulData as EraVMContextYulData;
pub use self::eravm::context::Context as EraVMContext;
pub use self::eravm::evm::arithmetic as eravm_evm_arithmetic;
pub use self::eravm::evm::bitwise as eravm_evm_bitwise;
pub use self::eravm::evm::call as eravm_evm_call;
pub use self::eravm::evm::calldata as eravm_evm_calldata;
pub use self::eravm::evm::comparison as eravm_evm_comparison;
pub use self::eravm::evm::context as eravm_evm_contract_context;
pub use self::eravm::evm::create as eravm_evm_create;
pub use self::eravm::evm::crypto as eravm_evm_crypto;
pub use self::eravm::evm::ether_gas as eravm_evm_ether_gas;
pub use self::eravm::evm::event as eravm_evm_event;
pub use self::eravm::evm::ext_code as eravm_evm_ext_code;
pub use self::eravm::evm::immutable as eravm_evm_immutable;
pub use self::eravm::evm::math as eravm_evm_math;
pub use self::eravm::evm::memory as eravm_evm_memory;
pub use self::eravm::evm::r#return as eravm_evm_return;
pub use self::eravm::evm::return_data as eravm_evm_return_data;
pub use self::eravm::evm::storage as eravm_evm_storage;
pub use self::eravm::metadata_hash::MetadataHash as EraVMMetadataHash;
pub use self::eravm::r#const as eravm_const;
pub use self::eravm::utils as eravm_utils;
pub use self::eravm::Dependency as EraVMDependency;
pub use self::eravm::DummyDependency as EraVMDummyDependency;
pub use self::eravm::DummyLLVMWritable as EraVMDummyLLVMWritable;
pub use self::eravm::WriteLLVM as EraVMWriteLLVM;
pub use self::optimizer::settings::size_level::SizeLevel as OptimizerSettingsSizeLevel;
pub use self::optimizer::settings::Settings as OptimizerSettings;
pub use self::optimizer::Optimizer;
pub use self::polkavm::build_assembly_text as polkavm_build_assembly_text;
pub use self::polkavm::context::address_space::AddressSpace as PolkaVMAddressSpace;
pub use self::polkavm::context::argument::Argument as PolkaVMArgument;
pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute;
pub use self::polkavm::context::build::Build as PolkaVMBuild;
pub use self::polkavm::context::code_type::CodeType as PolkaVMCodeType;
pub use self::polkavm::context::evmla_data::EVMLAData as PolkaVMContextEVMLAData;
pub use self::polkavm::context::function::block::evmla_data::key::Key as PolkaVMFunctionBlockKey;
pub use self::polkavm::context::function::block::evmla_data::EVMLAData as PolkaVMFunctionBlockEVMLAData;
pub use self::polkavm::context::function::block::Block as PolkaVMFunctionBlock;
pub use self::polkavm::context::function::declaration::Declaration as PolkaVMFunctionDeclaration;
pub use self::polkavm::context::function::evmla_data::EVMLAData as PolkaVMFunctionEVMLAData;
pub use self::polkavm::context::function::intrinsics::Intrinsics as PolkaVMIntrinsicFunction;
pub use self::polkavm::context::function::llvm_runtime::LLVMRuntime as PolkaVMLLVMRuntime;
pub use self::polkavm::context::function::r#return::Return as PolkaVMFunctionReturn;
pub use self::polkavm::context::function::runtime::deploy_code::DeployCode as PolkaVMDeployCodeFunction;
pub use self::polkavm::context::function::runtime::entry::Entry as PolkaVMEntryFunction;
pub use self::polkavm::context::function::runtime::runtime_code::RuntimeCode as PolkaVMRuntimeCodeFunction;
pub use self::polkavm::context::function::runtime::Runtime as PolkaVMRuntime;
pub use self::polkavm::context::function::vyper_data::VyperData as PolkaVMFunctionVyperData;
pub use self::polkavm::context::function::yul_data::YulData as PolkaVMFunctionYulData;
pub use self::polkavm::context::function::Function as PolkaVMFunction;
pub use self::polkavm::context::global::Global as PolkaVMGlobal;
pub use self::polkavm::context::pointer::Pointer as PolkaVMPointer;
pub use self::polkavm::context::r#loop::Loop as PolkaVMLoop;
pub use self::polkavm::context::solidity_data::SolidityData as PolkaVMContextSolidityData;
pub use self::polkavm::context::vyper_data::VyperData as PolkaVMContextVyperData;
pub use self::polkavm::context::yul_data::YulData as PolkaVMContextYulData;
pub use self::polkavm::context::Context as PolkaVMContext;
pub use self::polkavm::evm::arithmetic as polkavm_evm_arithmetic;
pub use self::polkavm::evm::bitwise as polkavm_evm_bitwise;
pub use self::polkavm::evm::call as polkavm_evm_call;
pub use self::polkavm::evm::calldata as polkavm_evm_calldata;
pub use self::polkavm::evm::comparison as polkavm_evm_comparison;
pub use self::polkavm::evm::context as polkavm_evm_contract_context;
pub use self::polkavm::evm::create as polkavm_evm_create;
pub use self::polkavm::evm::crypto as polkavm_evm_crypto;
pub use self::polkavm::evm::ether_gas as polkavm_evm_ether_gas;
pub use self::polkavm::evm::event as polkavm_evm_event;
pub use self::polkavm::evm::ext_code as polkavm_evm_ext_code;
pub use self::polkavm::evm::immutable as polkavm_evm_immutable;
pub use self::polkavm::evm::math as polkavm_evm_math;
pub use self::polkavm::evm::memory as polkavm_evm_memory;
pub use self::polkavm::evm::r#return as polkavm_evm_return;
pub use self::polkavm::evm::return_data as polkavm_evm_return_data;
pub use self::polkavm::evm::storage as polkavm_evm_storage;
pub use self::polkavm::metadata_hash::MetadataHash as PolkaVMMetadataHash;
pub use self::polkavm::r#const as polkavm_const;
pub use self::polkavm::utils as polkavm_utils;
pub use self::polkavm::Dependency as PolkaVMDependency;
pub use self::polkavm::DummyDependency as PolkaVMDummyDependency;
pub use self::polkavm::DummyLLVMWritable as PolkaVMDummyLLVMWritable;
pub use self::polkavm::WriteLLVM as PolkaVMWriteLLVM;
pub use self::target_machine::target::Target;
pub use self::target_machine::TargetMachine;
///
/// Initializes the target machine.
///
pub fn initialize_target(target: Target) {
match target {
Target::PVM => self::eravm::initialize_target(),
Target::PVM => self::polkavm::initialize_target(),
}
}
-10
View File
@@ -1,6 +1,4 @@
//!
//! The LLVM optimizing tools.
//!
pub mod settings;
@@ -11,9 +9,7 @@ use crate::target_machine::TargetMachine;
use self::settings::Settings;
///
/// The LLVM optimizing tools.
///
#[derive(Debug, Serialize, Deserialize)]
pub struct Optimizer {
/// The optimizer settings.
@@ -21,16 +17,12 @@ pub struct Optimizer {
}
impl Optimizer {
///
/// A shortcut constructor.
///
pub fn new(settings: Settings) -> Self {
Self { settings }
}
///
/// Runs the new pass manager.
///
pub fn run(
&self,
target_machine: &TargetMachine,
@@ -42,9 +34,7 @@ impl Optimizer {
)
}
///
/// Returns the optimizer settings reference.
///
pub fn settings(&self) -> &Settings {
&self.settings
}
@@ -1,6 +1,4 @@
//!
//! The LLVM optimizer settings.
//!
pub mod size_level;
@@ -11,9 +9,7 @@ use itertools::Itertools;
use self::size_level::SizeLevel;
///
/// The LLVM optimizer settings.
///
#[derive(Debug, Serialize, Deserialize, Clone, Eq)]
pub struct Settings {
/// The middle-end optimization level.
@@ -35,9 +31,7 @@ pub struct Settings {
}
impl Settings {
///
/// A shortcut constructor.
///
pub fn new(
level_middle_end: inkwell::OptimizationLevel,
level_middle_end_size: SizeLevel,
@@ -56,9 +50,7 @@ impl Settings {
}
}
///
/// A shortcut constructor with debugging tools.
///
pub fn new_debug(
level_middle_end: inkwell::OptimizationLevel,
level_middle_end_size: SizeLevel,
@@ -80,9 +72,7 @@ impl Settings {
}
}
///
/// Creates settings from a CLI optimization parameter.
///
pub fn try_from_cli(value: char) -> anyhow::Result<Self> {
Ok(match value {
'0' => Self::new(
@@ -126,9 +116,7 @@ impl Settings {
})
}
///
/// Returns the settings without optimizations.
///
pub fn none() -> Self {
Self::new(
inkwell::OptimizationLevel::None,
@@ -137,9 +125,7 @@ impl Settings {
)
}
///
/// Returns the settings for the optimal number of VM execution cycles.
///
pub fn cycles() -> Self {
Self::new(
inkwell::OptimizationLevel::Aggressive,
@@ -148,9 +134,7 @@ impl Settings {
)
}
///
/// Returns the settings for the optimal size.
///
pub fn size() -> Self {
Self::new(
inkwell::OptimizationLevel::Default,
@@ -159,9 +143,7 @@ impl Settings {
)
}
///
/// Returns the middle-end optimization parameter as string.
///
pub fn middle_end_as_string(&self) -> String {
match self.level_middle_end_size {
SizeLevel::Zero => (self.level_middle_end as u8).to_string(),
@@ -169,19 +151,14 @@ impl Settings {
}
}
///
/// Checks whether there are middle-end optimizations enabled.
///
pub fn is_middle_end_enabled(&self) -> bool {
self.level_middle_end != inkwell::OptimizationLevel::None
|| self.level_middle_end_size != SizeLevel::Zero
}
///
/// Returns all possible combinations of the optimizer settings.
///
/// Used only for testing purposes.
///
pub fn combinations() -> Vec<Self> {
let performance_combinations: Vec<Self> = vec![
inkwell::OptimizationLevel::None,
@@ -224,30 +201,22 @@ impl Settings {
combinations
}
///
/// Sets the fallback to optimizing for size if the bytecode is too large.
///
pub fn enable_fallback_to_size(&mut self) {
self.is_fallback_to_size_enabled = true;
}
///
/// Disables the system request memoization.
///
pub fn disable_system_request_memoization(&mut self) {
self.is_system_request_memoization_disabled = true;
}
///
/// Whether the fallback to optimizing for size is enabled.
///
pub fn is_fallback_to_size_enabled(&self) -> bool {
self.is_fallback_to_size_enabled
}
///
/// Whether the system request memoization is disabled.
///
pub fn is_system_request_memoization_disabled(&self) -> bool {
self.is_system_request_memoization_disabled
}
@@ -1,13 +1,9 @@
//!
//! The LLVM optimizer settings size level.
//!
use serde::Deserialize;
use serde::Serialize;
///
/// The LLVM optimizer settings size level.
///
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum SizeLevel {
/// No size optimizations.
@@ -1,11 +1,12 @@
//!
//! The LLVM context constants.
//!
/// Runtime API methods.
pub mod runtime_api;
/// The LLVM framework version.
pub const LLVM_VERSION: semver::Version = semver::Version::new(18, 1, 4);
/// The EraVM version.
/// The PolkaVM version.
pub const ZKEVM_VERSION: semver::Version = semver::Version::new(1, 3, 2);
/// The register width sized type
@@ -41,12 +42,21 @@ pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_";
/// The global verbatim getter identifier prefix.
pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::";
/// The static word size.
pub static GLOBAL_I256_SIZE: &str = "i256_size";
/// The static value size.
pub static GLOBAL_I160_SIZE: &str = "i160_size";
/// The static i64 size.
pub static GLOBAL_I64_SIZE: &str = "i64_size";
/// The external call data offset in the auxiliary heap.
pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0;
/// The constructor return data offset in the auxiliary heap.
pub const HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA: u64 =
8 * (revive_common::BYTE_LENGTH_FIELD as u64);
8 * (revive_common::BYTE_LENGTH_WORD as u64);
/// The number of the extra ABI data arguments.
pub const EXTRA_ABI_DATA_SIZE: usize = 0;
@@ -63,13 +73,11 @@ pub const NO_SYSTEM_CALL_BIT: bool = false;
/// The system call bit.
pub const SYSTEM_CALL_BIT: bool = true;
///
/// The deployer call header size that consists of:
/// - selector (4 bytes)
/// - salt (32 bytes)
/// - bytecode hash (32 bytes)
/// - constructor arguments offset (32 bytes)
/// - constructor arguments length (32 bytes)
///
pub const DEPLOYER_CALL_HEADER_SIZE: usize =
revive_common::BYTE_LENGTH_X32 + (revive_common::BYTE_LENGTH_FIELD * 4);
revive_common::BYTE_LENGTH_X32 + (revive_common::BYTE_LENGTH_WORD * 4);
@@ -0,0 +1,52 @@
//! Runtime API import and export symbols.
/// The contract deploy export.
pub static CALL: &str = "call";
/// The contract call export.
pub static DEPLOY: &str = "deploy";
/// All exported symbols.
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
pub static ADDRESS: &str = "address";
pub static BLOCK_NUMBER: &str = "block_number";
pub static CALLER: &str = "caller";
pub static DEPOSIT_EVENT: &str = "deposit_event";
pub static GET_STORAGE: &str = "get_storage";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static INPUT: &str = "input";
pub static NOW: &str = "now";
pub static RETURN: &str = "seal_return";
pub static SET_STORAGE: &str = "set_storage";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
/// All imported runtime API symbols..
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 11] = [
ADDRESS,
BLOCK_NUMBER,
CALLER,
DEPOSIT_EVENT,
GET_STORAGE,
HASH_KECCAK_256,
INPUT,
NOW,
RETURN,
SET_STORAGE,
VALUE_TRANSFERRED,
];
/// PolkaVM __sbrk API symbol to extend the heap memory.
pub static SBRK: &str = "__sbrk";
@@ -1,10 +1,6 @@
//!
//! The address space aliases.
//!
///
/// The address space aliases.
///
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AddressSpace {
/// The stack memory.
@@ -1,10 +1,6 @@
//!
//! The LLVM argument with metadata.
//!
///
/// The LLVM argument with metadata.
///
#[derive(Debug, Clone)]
pub struct Argument<'ctx> {
/// The actual LLVM operand.
@@ -22,9 +18,7 @@ impl<'ctx> Argument<'ctx> {
/// The calldata length argument index.
pub const ARGUMENT_INDEX_CALLDATA_LENGTH: usize = 1;
///
/// A shortcut constructor.
///
pub fn new(value: inkwell::values::BasicValueEnum<'ctx>) -> Self {
Self {
value,
@@ -33,9 +27,7 @@ impl<'ctx> Argument<'ctx> {
}
}
///
/// A shortcut constructor.
///
pub fn new_with_original(
value: inkwell::values::BasicValueEnum<'ctx>,
original: String,
@@ -47,9 +39,7 @@ impl<'ctx> Argument<'ctx> {
}
}
///
/// A shortcut constructor.
///
pub fn new_with_constant(
value: inkwell::values::BasicValueEnum<'ctx>,
constant: num::BigUint,
@@ -61,9 +51,7 @@ impl<'ctx> Argument<'ctx> {
}
}
///
/// Returns the inner LLVM value.
///
pub fn to_llvm(&self) -> inkwell::values::BasicValueEnum<'ctx> {
self.value
}
@@ -4,11 +4,8 @@ use serde::Deserialize;
use serde::Serialize;
/// The LLVM attribute.
///
/// In order to check the real order in a new major version of LLVM, find the `Attributes.inc` file
/// inside of the LLVM build directory. This order is actually generated during the building.
///
/// FIXME: Generate this in build.rs?
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Attribute {
Unused = 0,
@@ -84,7 +81,7 @@ pub enum Attribute {
Writable = 70,
WriteOnly = 71,
ZExt = 72,
//LastEnumAttr = 72,
// LastEnumAttr = 72,
// FirstTypeAttr = 73,
ByRef = 73,
ByVal = 74,
@@ -119,12 +116,7 @@ impl TryFrom<&str> for Attribute {
"OptimizeForSize" => Ok(Attribute::OptimizeForSize),
"NoInline" => Ok(Attribute::NoInline),
"WillReturn" => Ok(Attribute::WillReturn),
"WriteOnly" => Ok(Attribute::WriteOnly),
"ReadNone" => Ok(Attribute::ReadNone),
"ReadOnly" => Ok(Attribute::ReadOnly),
"NoReturn" => Ok(Attribute::NoReturn),
// FIXME: Not in Attributes.inc
//"InaccessibleMemOnly" => Ok(Attribute::InaccessibleMemOnly),
"MustProgress" => Ok(Attribute::MustProgress),
_ => Err(value.to_owned()),
}
@@ -1,36 +1,30 @@
//!
//! The LLVM module build.
//!
use std::collections::BTreeMap;
use serde::Deserialize;
use serde::Serialize;
///
/// The LLVM module build.
///
#[derive(Debug, Serialize, Deserialize)]
pub struct Build {
/// The EraVM text assembly.
/// The PolkaVM text assembly.
pub assembly_text: String,
/// The metadata hash.
pub metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_FIELD]>,
/// The EraVM binary bytecode.
pub metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]>,
/// The PolkaVM binary bytecode.
pub bytecode: Vec<u8>,
/// The EraVM bytecode hash.
/// The PolkaVM bytecode hash.
pub bytecode_hash: String,
/// The hash-to-full-path mapping of the contract factory dependencies.
pub factory_dependencies: BTreeMap<String, String>,
}
impl Build {
///
/// A shortcut constructor.
///
pub fn new(
assembly_text: String,
metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_FIELD]>,
metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]>,
bytecode: Vec<u8>,
bytecode_hash: String,
) -> Self {
@@ -1,13 +1,8 @@
//!
//! The contract code types.
//!
///
/// The contract code types (deploy and runtime).
///
/// They do not represent any entities in the final bytecode, but this separation is always present
/// in the IRs used for translation to the EVM bytecode.
///
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CodeType {
/// The deploy code.
@@ -1,13 +1,9 @@
//!
//! The LLVM debug information.
//!
use inkwell::debug_info::AsDIScope;
use num::Zero;
///
/// The LLVM debug information.
///
pub struct DebugInfo<'ctx> {
/// The compile unit.
compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
@@ -16,9 +12,7 @@ pub struct DebugInfo<'ctx> {
}
impl<'ctx> DebugInfo<'ctx> {
///
/// A shortcut constructor.
///
pub fn new(module: &inkwell::module::Module<'ctx>) -> Self {
let (builder, compile_unit) = module.create_debug_info_builder(
true,
@@ -44,16 +38,14 @@ impl<'ctx> DebugInfo<'ctx> {
}
}
///
/// Creates a function info.
///
pub fn create_function(
&self,
name: &str,
) -> anyhow::Result<inkwell::debug_info::DISubprogram<'ctx>> {
let subroutine_type = self.builder.create_subroutine_type(
self.compile_unit.get_file(),
Some(self.create_type(era_compiler_common::BIT_LENGTH_FIELD)?),
Some(self.create_type(revive_common::BIT_LENGTH_FIELD)?),
&[],
inkwell::debug_info::DIFlags::zero(),
);
@@ -82,9 +74,7 @@ impl<'ctx> DebugInfo<'ctx> {
Ok(function)
}
///
/// Creates a primitive type info.
///
pub fn create_type(
&self,
bit_length: usize,
@@ -100,9 +90,7 @@ impl<'ctx> DebugInfo<'ctx> {
.map_err(|error| anyhow::anyhow!("Debug info error: {}", error))
}
///
/// Finalizes the builder.
///
pub fn finalize(&self) {
self.builder.finalize();
}
@@ -1,14 +1,9 @@
//!
//! The LLVM IR generator EVM legacy assembly data.
//!
use crate::eravm::context::argument::Argument;
use crate::polkavm::context::argument::Argument;
///
/// The LLVM IR generator EVM legacy assembly data.
///
/// Describes some data that is only relevant to the EVM legacy assembly.
///
#[derive(Debug, Clone)]
pub struct EVMLAData<'ctx> {
/// The Solidity compiler version.
@@ -22,9 +17,7 @@ impl<'ctx> EVMLAData<'ctx> {
/// The default stack size.
pub const DEFAULT_STACK_SIZE: usize = 64;
///
/// A shortcut constructor.
///
pub fn new(version: semver::Version) -> Self {
Self {
version,
@@ -1,14 +1,9 @@
//!
//! The LLVM IR generator function block key.
//!
use crate::eravm::context::code_type::CodeType;
use crate::polkavm::context::code_type::CodeType;
///
/// The LLVM IR generator function block key.
///
/// Is only relevant to the EVM legacy assembly.
///
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Key {
/// The block code type.
@@ -18,9 +13,7 @@ pub struct Key {
}
impl Key {
///
/// A shortcut constructor.
///
pub fn new(code_type: CodeType, tag: num::BigUint) -> Self {
Self { code_type, tag }
}
@@ -1,14 +1,9 @@
//!
//! The LLVM function block EVM legacy assembly data.
//!
pub mod key;
///
/// The LLVM function block EVM legacy assembly data.
///
/// Describes some data that is only relevant to the EVM legacy assembly.
///
#[derive(Debug, Clone)]
pub struct EVMLAData {
/// The initial hashes of the allowed stack states.
@@ -16,9 +11,7 @@ pub struct EVMLAData {
}
impl EVMLAData {
///
/// A shortcut constructor.
///
pub fn new(stack_hashes: Vec<md5::Digest>) -> Self {
Self { stack_hashes }
}
@@ -1,14 +1,10 @@
//!
//! The LLVM IR generator function block.
//!
pub mod evmla_data;
use self::evmla_data::EVMLAData;
///
/// The LLVM IR generator function block.
///
#[derive(Debug, Clone)]
pub struct Block<'ctx> {
/// The inner block.
@@ -18,9 +14,7 @@ pub struct Block<'ctx> {
}
impl<'ctx> Block<'ctx> {
///
/// A shortcut constructor.
///
pub fn new(inner: inkwell::basic_block::BasicBlock<'ctx>) -> Self {
Self {
inner,
@@ -28,38 +22,28 @@ impl<'ctx> Block<'ctx> {
}
}
///
/// Sets the EVM legacy assembly data.
///
pub fn set_evmla_data(&mut self, data: EVMLAData) {
self.evmla_data = Some(data);
}
///
/// The LLVM object reference.
///
pub fn inner(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.inner
}
///
/// Returns the EVM data reference.
///
/// # Panics
/// If the EVM data has not been initialized.
///
pub fn evm(&self) -> &EVMLAData {
self.evmla_data
.as_ref()
.expect("The EVM data must have been initialized")
}
///
/// Returns the EVM data mutable reference.
///
/// # Panics
/// If the EVM data has not been initialized.
///
pub fn evm_mut(&mut self) -> &mut EVMLAData {
self.evmla_data
.as_mut()
@@ -1,10 +1,6 @@
//!
//! The LLVM function declaration.
//!
///
/// The LLVM function declaration.
///
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Declaration<'ctx> {
/// The function type.
@@ -14,9 +10,7 @@ pub struct Declaration<'ctx> {
}
impl<'ctx> Declaration<'ctx> {
///
/// A shortcut constructor.
///
pub fn new(
r#type: inkwell::types::FunctionType<'ctx>,
value: inkwell::values::FunctionValue<'ctx>,
@@ -1,17 +1,12 @@
//!
//! The LLVM function EVM legacy assembly data.
//!
use std::collections::BTreeMap;
use crate::eravm::context::function::block::evmla_data::key::Key as BlockKey;
use crate::eravm::context::function::block::Block;
use crate::polkavm::context::function::block::evmla_data::key::Key as BlockKey;
use crate::polkavm::context::function::block::Block;
///
/// The LLVM function EVM legacy assembly data.
///
/// Describes some data that is only relevant to the EVM legacy assembly.
///
#[derive(Debug)]
pub struct EVMLAData<'ctx> {
/// The ordinary blocks with numeric tags.
@@ -22,9 +17,7 @@ pub struct EVMLAData<'ctx> {
}
impl<'ctx> EVMLAData<'ctx> {
///
/// A shortcut constructor.
///
pub fn new(stack_size: usize) -> Self {
Self {
blocks: BTreeMap::new(),
@@ -32,9 +25,7 @@ impl<'ctx> EVMLAData<'ctx> {
}
}
///
/// Inserts a function block.
///
pub fn insert_block(&mut self, key: BlockKey, block: Block<'ctx>) {
if let Some(blocks) = self.blocks.get_mut(&key) {
blocks.push(block);
@@ -43,11 +34,8 @@ impl<'ctx> EVMLAData<'ctx> {
}
}
///
/// Returns the block with the specified tag and initial stack pattern.
///
/// If there is only one block, it is returned unconditionally.
///
pub fn find_block(
&self,
key: &BlockKey,
@@ -1,17 +1,12 @@
//!
//! The LLVM intrinsic functions.
//!
use inkwell::types::BasicType;
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
///
/// The LLVM intrinsic functions, implemented in the LLVM back-end.
///
/// Most of them are translated directly into bytecode instructions.
///
#[derive(Debug)]
pub struct Intrinsics<'ctx> {
/// The trap.
@@ -21,7 +16,9 @@ pub struct Intrinsics<'ctx> {
/// The memory copy from a generic page.
pub memory_copy_from_generic: FunctionDeclaration<'ctx>,
/// Performs endianness swaps on i256 values
pub byte_swap: FunctionDeclaration<'ctx>,
pub byte_swap_word: FunctionDeclaration<'ctx>,
/// Performs endianness swaps on i160 values
pub byte_swap_eth_address: FunctionDeclaration<'ctx>,
}
impl<'ctx> Intrinsics<'ctx> {
@@ -35,18 +32,20 @@ impl<'ctx> Intrinsics<'ctx> {
pub const FUNCTION_MEMORY_COPY_FROM_GENERIC: &'static str = "llvm.memcpy.p3.p1.i256";
/// The corresponding intrinsic function name.
pub const FUNCTION_BYTE_SWAP: &'static str = "llvm.bswap.i256";
pub const FUNCTION_BYTE_SWAP_WORD: &'static str = "llvm.bswap.i256";
/// The corresponding intrinsic function name.
pub const FUNCTION_BYTE_SWAP_ETH_ADDRESS: &'static str = "llvm.bswap.i160";
///
/// A shortcut constructor.
///
pub fn new(
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
) -> Self {
let void_type = llvm.void_type();
let bool_type = llvm.bool_type();
let field_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32);
let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
let address_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_ETH_ADDRESS as u32);
let _stack_field_pointer_type = llvm.ptr_type(AddressSpace::Stack.into());
let heap_field_pointer_type = llvm.ptr_type(AddressSpace::Heap.into());
let generic_byte_pointer_type = llvm.ptr_type(AddressSpace::Generic.into());
@@ -65,7 +64,7 @@ impl<'ctx> Intrinsics<'ctx> {
&[
heap_field_pointer_type.as_basic_type_enum().into(),
heap_field_pointer_type.as_basic_type_enum().into(),
field_type.as_basic_type_enum().into(),
word_type.as_basic_type_enum().into(),
bool_type.as_basic_type_enum().into(),
],
false,
@@ -79,30 +78,35 @@ impl<'ctx> Intrinsics<'ctx> {
&[
heap_field_pointer_type.as_basic_type_enum().into(),
generic_byte_pointer_type.as_basic_type_enum().into(),
field_type.as_basic_type_enum().into(),
word_type.as_basic_type_enum().into(),
bool_type.as_basic_type_enum().into(),
],
false,
),
);
let byte_swap = Self::declare(
let byte_swap_word = Self::declare(
llvm,
module,
Self::FUNCTION_BYTE_SWAP,
field_type.fn_type(&[field_type.as_basic_type_enum().into()], false),
Self::FUNCTION_BYTE_SWAP_WORD,
word_type.fn_type(&[word_type.as_basic_type_enum().into()], false),
);
let byte_swap_eth_address = Self::declare(
llvm,
module,
Self::FUNCTION_BYTE_SWAP_ETH_ADDRESS,
address_type.fn_type(&[address_type.as_basic_type_enum().into()], false),
);
Self {
trap,
memory_copy,
memory_copy_from_generic,
byte_swap,
byte_swap_word,
byte_swap_eth_address,
}
}
///
/// Finds the specified LLVM intrinsic function in the target and returns its declaration.
///
pub fn declare(
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
@@ -118,14 +122,12 @@ impl<'ctx> Intrinsics<'ctx> {
FunctionDeclaration::new(r#type, value)
}
///
/// Returns the LLVM types for selecting via the signature.
///
pub fn argument_types(
llvm: &'ctx inkwell::context::Context,
name: &str,
) -> Vec<inkwell::types::BasicTypeEnum<'ctx>> {
let field_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32);
let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
match name {
name if name == Self::FUNCTION_MEMORY_COPY => vec![
@@ -133,16 +135,21 @@ impl<'ctx> Intrinsics<'ctx> {
.as_basic_type_enum(),
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum(),
field_type.as_basic_type_enum(),
word_type.as_basic_type_enum(),
],
name if name == Self::FUNCTION_MEMORY_COPY_FROM_GENERIC => vec![
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum(),
llvm.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum(),
field_type.as_basic_type_enum(),
word_type.as_basic_type_enum(),
],
name if name == Self::FUNCTION_BYTE_SWAP => vec![field_type.as_basic_type_enum()],
name if name == Self::FUNCTION_BYTE_SWAP_WORD => vec![word_type.as_basic_type_enum()],
name if name == Self::FUNCTION_BYTE_SWAP_ETH_ADDRESS => {
vec![llvm
.custom_width_int_type(revive_common::BIT_LENGTH_ETH_ADDRESS as u32)
.as_basic_type_enum()]
}
_ => vec![],
}
}
@@ -1,19 +1,14 @@
//!
//! The LLVM runtime functions.
//!
use inkwell::types::BasicType;
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::eravm::context::function::Function;
use crate::optimizer::Optimizer;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::function::Function;
///
/// The runtime functions, implemented on the LLVM side.
///
/// The functions are automatically linked to the LLVM implementations if the signatures match.
///
#[derive(Debug)]
pub struct LLVMRuntime<'ctx> {
/// The LLVM personality function, used for exception handling.
@@ -21,15 +16,6 @@ pub struct LLVMRuntime<'ctx> {
/// The LLVM exception throwing function.
pub cxa_throw: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub div: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub sdiv: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub r#mod: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub smod: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub shl: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
@@ -87,18 +73,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
/// The LLVM exception throwing function name.
pub const FUNCTION_CXA_THROW: &'static str = "__cxa_throw";
/// The corresponding runtime function name.
pub const FUNCTION_DIV: &'static str = "__div";
/// The corresponding runtime function name.
pub const FUNCTION_SDIV: &'static str = "__sdiv";
/// The corresponding runtime function name.
pub const FUNCTION_MOD: &'static str = "__mod";
/// The corresponding runtime function name.
pub const FUNCTION_SMOD: &'static str = "__smod";
/// The corresponding runtime function name.
pub const FUNCTION_SHL: &'static str = "__shl";
@@ -159,9 +133,7 @@ impl<'ctx> LLVMRuntime<'ctx> {
/// The corresponding runtime function name.
pub const FUNCTION_REVERT: &'static str = "__revert";
///
/// A shortcut constructor.
///
pub fn new(
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
@@ -191,89 +163,13 @@ impl<'ctx> LLVMRuntime<'ctx> {
);
Function::set_cxa_throw_attributes(llvm, cxa_throw);
let div = Self::declare(
module,
Self::FUNCTION_DIV,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, div, optimizer);
Function::set_pure_function_attributes(llvm, div);
let r#mod = Self::declare(
module,
Self::FUNCTION_MOD,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, r#mod, optimizer);
Function::set_pure_function_attributes(llvm, r#mod);
let sdiv = Self::declare(
module,
Self::FUNCTION_SDIV,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, sdiv, optimizer);
Function::set_pure_function_attributes(llvm, sdiv);
let smod = Self::declare(
module,
Self::FUNCTION_SMOD,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, smod, optimizer);
Function::set_pure_function_attributes(llvm, smod);
let shl = Self::declare(
module,
Self::FUNCTION_SHL,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
2
@@ -289,10 +185,10 @@ impl<'ctx> LLVMRuntime<'ctx> {
let shr = Self::declare(
module,
Self::FUNCTION_SHR,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
2
@@ -308,10 +204,10 @@ impl<'ctx> LLVMRuntime<'ctx> {
let sar = Self::declare(
module,
Self::FUNCTION_SAR,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
2
@@ -327,10 +223,10 @@ impl<'ctx> LLVMRuntime<'ctx> {
let byte = Self::declare(
module,
Self::FUNCTION_BYTE,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
2
@@ -365,13 +261,13 @@ impl<'ctx> LLVMRuntime<'ctx> {
let sha3 = Self::declare(
module,
Self::FUNCTION_SHA3,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_BOOLEAN as u32)
@@ -395,16 +291,16 @@ impl<'ctx> LLVMRuntime<'ctx> {
let system_request = Self::declare(
module,
Self::FUNCTION_SYSTEM_REQUEST,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.ptr_type(AddressSpace::Stack.into())
@@ -417,24 +313,17 @@ impl<'ctx> LLVMRuntime<'ctx> {
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, system_request, optimizer);
Function::set_attributes(
llvm,
system_request,
//vec![Attribute::ArgMemOnly, Attribute::ReadOnly],
vec![],
false,
);
let external_call_arguments: Vec<inkwell::types::BasicMetadataTypeEnum> = vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
crate::eravm::context::function::runtime::entry::Entry::MANDATORY_ARGUMENTS_COUNT
+ crate::eravm::EXTRA_ABI_DATA_SIZE
crate::polkavm::context::function::runtime::entry::Entry::MANDATORY_ARGUMENTS_COUNT
+ crate::polkavm::EXTRA_ABI_DATA_SIZE
];
let mut mimic_call_arguments = external_call_arguments.clone();
mimic_call_arguments.push(
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
);
@@ -443,21 +332,21 @@ impl<'ctx> LLVMRuntime<'ctx> {
llvm.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
];
external_call_arguments_by_ref.extend::<Vec<inkwell::types::BasicMetadataTypeEnum>>(vec![
llvm.custom_width_int_type(
revive_common::BIT_LENGTH_FIELD as u32
revive_common::BIT_LENGTH_WORD as u32
)
.as_basic_type_enum()
.into();
crate::eravm::EXTRA_ABI_DATA_SIZE
crate::polkavm::EXTRA_ABI_DATA_SIZE
]);
let mut mimic_call_arguments_by_ref = external_call_arguments_by_ref.clone();
mimic_call_arguments_by_ref.push(
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
);
@@ -536,7 +425,7 @@ impl<'ctx> LLVMRuntime<'ctx> {
Self::FUNCTION_RETURN,
llvm.void_type().fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
3
@@ -552,7 +441,7 @@ impl<'ctx> LLVMRuntime<'ctx> {
Self::FUNCTION_REVERT,
llvm.void_type().fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
3
@@ -568,11 +457,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
personality,
cxa_throw,
div,
sdiv,
r#mod,
smod,
shl,
shr,
sar,
@@ -602,9 +486,7 @@ impl<'ctx> LLVMRuntime<'ctx> {
}
}
///
/// Declares an LLVM runtime function in the `module`,
///
pub fn declare(
module: &inkwell::module::Module<'ctx>,
name: &str,
@@ -625,9 +507,7 @@ impl<'ctx> LLVMRuntime<'ctx> {
FunctionDeclaration::new(value.get_type(), value).into()
}
///
/// Modifies the external call function with `is_byref` and `is_system` modifiers.
///
pub fn modify(
&self,
function: FunctionDeclaration<'ctx>,
@@ -1,6 +1,4 @@
//!
//! The LLVM IR generator function.
//!
pub mod block;
pub mod declaration;
@@ -14,10 +12,10 @@ pub mod yul_data;
use std::collections::HashMap;
use crate::eravm::context::attribute::Attribute;
use crate::eravm::context::pointer::Pointer;
use crate::optimizer::settings::size_level::SizeLevel;
use crate::optimizer::Optimizer;
use crate::polkavm::context::attribute::Attribute;
use crate::polkavm::context::pointer::Pointer;
use self::declaration::Declaration;
use self::evmla_data::EVMLAData;
@@ -26,9 +24,7 @@ use self::runtime::Runtime;
use self::vyper_data::VyperData;
use self::yul_data::YulData;
///
/// The LLVM IR generator function.
///
#[derive(Debug)]
pub struct Function<'ctx> {
/// The high-level source code name.
@@ -66,9 +62,7 @@ impl<'ctx> Function<'ctx> {
/// The stack hashmap default capacity.
const STACK_HASHMAP_INITIAL_CAPACITY: usize = 64;
///
/// A shortcut constructor.
///
pub fn new(
name: String,
declaration: Declaration<'ctx>,
@@ -92,16 +86,12 @@ impl<'ctx> Function<'ctx> {
}
}
///
/// Returns the function name reference.
///
pub fn name(&self) -> &str {
self.name.as_str()
}
///
/// Checks whether the function is defined outside of the front-end.
///
pub fn is_name_external(name: &str) -> bool {
name.starts_with("llvm.")
|| (name.starts_with("__")
@@ -110,24 +100,18 @@ impl<'ctx> Function<'ctx> {
&& name != Runtime::FUNCTION_RUNTIME_CODE)
}
///
/// Checks whether the function is related to the near call ABI.
///
pub fn is_near_call_abi(name: &str) -> bool {
name.starts_with(Self::ZKSYNC_NEAR_CALL_ABI_PREFIX)
|| name == Self::ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER
}
///
/// Returns the LLVM function declaration.
///
pub fn declaration(&self) -> Declaration<'ctx> {
self.declaration
}
///
/// Returns the N-th parameter of the function.
///
pub fn get_nth_param(&self, index: usize) -> inkwell::values::BasicValueEnum<'ctx> {
self.declaration()
.value
@@ -135,9 +119,7 @@ impl<'ctx> Function<'ctx> {
.expect("Always exists")
}
///
/// Sets the memory writer function attributes.
///
pub fn set_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
@@ -146,6 +128,7 @@ impl<'ctx> Function<'ctx> {
) {
for attribute_kind in attributes.into_iter() {
match attribute_kind {
Attribute::Memory => todo!("`memory` attributes are not yet implemented"),
attribute_kind @ Attribute::AlwaysInline if force => {
let is_optimize_none_set = declaration
.value
@@ -183,17 +166,36 @@ impl<'ctx> Function<'ctx> {
}
}
///
/// Remove specified attributes existing on the given declaration.
pub fn remove_attributes(declaration: Declaration, attributes: &[Attribute]) {
for attribute in attributes.iter().filter(|attribute| {
declaration
.value
.get_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
**attribute as u32,
)
.is_some()
}) {
declaration.value.remove_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
*attribute as u32,
);
}
}
/// Sets the default attributes.
///
/// The attributes only affect the LLVM optimizations.
///
pub fn set_default_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
optimizer: &Optimizer,
) {
if optimizer.settings().level_middle_end == inkwell::OptimizationLevel::None {
Self::remove_attributes(
declaration,
&[Attribute::OptimizeForSize, Attribute::AlwaysInline],
);
Self::set_attributes(
llvm,
declaration,
@@ -212,9 +214,7 @@ impl<'ctx> Function<'ctx> {
Self::set_attributes(llvm, declaration, vec![Attribute::NoFree], false);
}
///
/// Sets the front-end runtime attributes.
///
pub fn set_frontend_runtime_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
@@ -225,9 +225,7 @@ impl<'ctx> Function<'ctx> {
}
}
///
/// Sets the exception handler attributes.
///
pub fn set_exception_handler_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
@@ -235,9 +233,7 @@ impl<'ctx> Function<'ctx> {
Self::set_attributes(llvm, declaration, vec![Attribute::NoInline], false);
}
///
/// Sets the CXA-throw attributes.
///
pub fn set_cxa_throw_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
@@ -245,9 +241,7 @@ impl<'ctx> Function<'ctx> {
Self::set_attributes(llvm, declaration, vec![Attribute::NoProfile], false);
}
///
/// Sets the pure function attributes.
///
pub fn set_pure_function_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
@@ -258,18 +252,14 @@ impl<'ctx> Function<'ctx> {
vec![
Attribute::MustProgress,
Attribute::NoUnwind,
// FIXME: LLVM complains about ReadNone being not valid for fns
// Attribute::ReadNone,
Attribute::WillReturn,
],
false,
);
}
///
/// Saves the pointer to a stack variable, returning the pointer to the shadowed variable,
/// if it exists.
///
pub fn insert_stack_pointer(
&mut self,
name: String,
@@ -278,148 +268,108 @@ impl<'ctx> Function<'ctx> {
self.stack.insert(name, pointer)
}
///
/// Gets the pointer to a stack variable.
///
pub fn get_stack_pointer(&self, name: &str) -> Option<Pointer<'ctx>> {
self.stack.get(name).copied()
}
///
/// Removes the pointer to a stack variable.
///
pub fn remove_stack_pointer(&mut self, name: &str) {
self.stack.remove(name);
}
///
/// Returns the return entity representation.
///
pub fn r#return(&self) -> Return<'ctx> {
self.r#return
}
///
/// Returns the pointer to the function return value.
///
/// # Panics
/// If the pointer has not been set yet.
///
pub fn return_pointer(&self) -> Option<Pointer<'ctx>> {
self.r#return.return_pointer()
}
///
/// Returns the return data size in bytes, based on the default stack alignment.
///
/// # Panics
/// If the pointer has not been set yet.
///
pub fn return_data_size(&self) -> usize {
self.r#return.return_data_size()
}
///
/// Returns the function entry block.
///
pub fn entry_block(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.entry_block
}
///
/// Returns the function return block.
///
pub fn return_block(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.return_block
}
///
/// Sets the EVM legacy assembly data.
///
pub fn set_evmla_data(&mut self, data: EVMLAData<'ctx>) {
self.evmla_data = Some(data);
}
///
/// Returns the EVM legacy assembly data reference.
///
/// # Panics
/// If the EVM data has not been initialized.
///
pub fn evmla(&self) -> &EVMLAData<'ctx> {
self.evmla_data
.as_ref()
.expect("The EVM data must have been initialized")
}
///
/// Returns the EVM legacy assembly data mutable reference.
///
/// # Panics
/// If the EVM data has not been initialized.
///
pub fn evmla_mut(&mut self) -> &mut EVMLAData<'ctx> {
self.evmla_data
.as_mut()
.expect("The EVM data must have been initialized")
}
///
/// Sets the Vyper data.
///
pub fn set_vyper_data(&mut self, data: VyperData) {
self.vyper_data = Some(data);
}
///
/// Returns the Vyper data reference.
///
/// # Panics
/// If the Vyper data has not been initialized.
///
pub fn vyper(&self) -> &VyperData {
self.vyper_data
.as_ref()
.expect("The Vyper data must have been initialized")
}
///
/// Returns the Vyper data mutable reference.
///
/// # Panics
/// If the Vyper data has not been initialized.
///
pub fn vyper_mut(&mut self) -> &mut VyperData {
self.vyper_data
.as_mut()
.expect("The Vyper data must have been initialized")
}
///
/// Sets the Yul data.
///
pub fn set_yul_data(&mut self, data: YulData) {
self.yul_data = Some(data);
}
///
/// Returns the Yul data reference.
///
/// # Panics
/// If the Yul data has not been initialized.
///
pub fn yul(&self) -> &YulData {
self.yul_data
.as_ref()
.expect("The Yul data must have been initialized")
}
///
/// Returns the Yul data mutable reference.
///
/// # Panics
/// If the Yul data has not been initialized.
///
pub fn yul_mut(&mut self) -> &mut YulData {
self.yul_data
.as_mut()
@@ -1,12 +1,8 @@
//!
//! The LLVM IR generator function return entity.
//!
use crate::eravm::context::pointer::Pointer;
use crate::polkavm::context::pointer::Pointer;
///
/// The LLVM IR generator function return entity.
///
#[derive(Debug, Clone, Copy)]
pub enum Return<'ctx> {
/// The function does not return a value.
@@ -27,30 +23,22 @@ pub enum Return<'ctx> {
}
impl<'ctx> Return<'ctx> {
///
/// A shortcut constructor.
///
pub fn none() -> Self {
Self::None
}
///
/// A shortcut constructor.
///
pub fn primitive(pointer: Pointer<'ctx>) -> Self {
Self::Primitive { pointer }
}
///
/// A shortcut constructor.
///
pub fn compound(pointer: Pointer<'ctx>, size: usize) -> Self {
Self::Compound { pointer, size }
}
///
/// Returns the pointer to the function return value.
///
pub fn return_pointer(&self) -> Option<Pointer<'ctx>> {
match self {
Return::None => None,
@@ -59,11 +47,9 @@ impl<'ctx> Return<'ctx> {
}
}
///
/// Returns the return data size in bytes, based on the default stack alignment.
///
pub fn return_data_size(&self) -> usize {
revive_common::BYTE_LENGTH_FIELD
revive_common::BYTE_LENGTH_WORD
* match self {
Self::None => 0,
Self::Primitive { .. } => 1,
@@ -1,21 +1,16 @@
//!
//! The `default_call` function.
//!
use inkwell::types::BasicType;
use crate::eravm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::eravm::context::function::llvm_runtime::LLVMRuntime;
use crate::eravm::context::function::Function;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::eravm::WriteLLVM;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::function::llvm_runtime::LLVMRuntime;
use crate::polkavm::context::function::Function;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
///
/// The `default_call` function.
///
/// Generates a default contract call, if the `msg.value` is zero.
///
#[derive(Debug)]
pub struct DefaultCall {
/// The name of the inner function used for the low-level call.
@@ -44,9 +39,7 @@ impl DefaultCall {
/// The output length argument index.
pub const ARGUMENT_INDEX_OUTPUT_LENGTH: usize = 5;
///
/// A shortcut constructor.
///
pub fn new(call_function: FunctionDeclaration) -> Self {
let inner_name = call_function.value.get_name().to_string_lossy().to_string();
let name = Self::name(call_function);
@@ -54,9 +47,7 @@ impl DefaultCall {
Self { inner_name, name }
}
///
/// Returns the function name.
///
pub fn name(call_function: FunctionDeclaration) -> String {
let suffix = match call_function.value.get_name().to_string_lossy() {
name if name == LLVMRuntime::FUNCTION_FARCALL => "far",
@@ -67,9 +58,7 @@ impl DefaultCall {
format!("__default_{suffix}_call")
}
///
/// Returns the low-level call function.
///
fn inner_function<'ctx, D>(&self, context: &Context<'ctx, D>) -> FunctionDeclaration<'ctx>
where
D: Dependency + Clone,
@@ -92,12 +81,12 @@ where
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let function_type = context.function_type(
vec![
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
],
1,
false,
@@ -155,13 +144,13 @@ where
context.set_basic_block(context.current_function().borrow().entry_block());
let status_code_result_pointer = context.build_alloca(
context.field_type(),
context.word_type(),
"contract_call_result_status_code_pointer",
);
/*
context.build_store(status_code_result_pointer, context.field_const(0));
let abi_data = crate::eravm::utils::abi_data(
let abi_data = crate::polkavm::utils::abi_data(
context,
input_offset,
input_length,
@@ -174,7 +163,7 @@ where
let result = context
.build_call(
self.inner_function(context),
crate::eravm::utils::external_call_arguments(
crate::polkavm::utils::external_call_arguments(
context,
abi_data.as_basic_value_enum(),
address,
@@ -236,11 +225,11 @@ where
context.write_abi_pointer(
result_abi_data_pointer,
crate::eravm::GLOBAL_RETURN_DATA_POINTER,
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
);
context.write_abi_data_size(
result_abi_data_pointer,
crate::eravm::GLOBAL_RETURN_DATA_SIZE,
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
);
*/
context.build_unconditional_branch(context.current_function().borrow().return_block());
@@ -1,22 +1,17 @@
//!
//! The deploy code function.
//!
use std::marker::PhantomData;
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::code_type::CodeType;
use crate::eravm::context::function::runtime::Runtime;
use crate::eravm::context::pointer::Pointer;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::eravm::WriteLLVM;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime::Runtime;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
///
/// The deploy code function.
///
/// Is a special function that is only used by the front-end generated code.
///
#[derive(Debug)]
pub struct DeployCode<B, D>
where
@@ -34,9 +29,7 @@ where
B: WriteLLVM<D>,
D: Dependency + Clone,
{
///
/// A shortcut constructor.
///
pub fn new(inner: B) -> Self {
Self {
inner,
@@ -69,19 +62,19 @@ where
context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Deploy);
if let Some(vyper) = context.vyper_data.as_ref() {
for index in 0..vyper.immutables_size() / revive_common::BYTE_LENGTH_FIELD {
let offset = (crate::eravm::r#const::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
for index in 0..vyper.immutables_size() / revive_common::BYTE_LENGTH_WORD {
let offset = (crate::polkavm::r#const::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
as usize)
+ (1 + index) * 2 * revive_common::BYTE_LENGTH_FIELD;
let value = index * revive_common::BYTE_LENGTH_FIELD;
+ (1 + index) * 2 * revive_common::BYTE_LENGTH_WORD;
let value = index * revive_common::BYTE_LENGTH_WORD;
let pointer = Pointer::new_with_offset(
context,
AddressSpace::HeapAuxiliary,
context.field_type(),
context.field_const(offset as u64),
context.word_type(),
context.word_const(offset as u64),
"immutable_index_initializer",
);
context.build_store(pointer, context.field_const(value as u64))?;
context.build_store(pointer, context.word_const(value as u64))?;
}
}
@@ -1,24 +1,18 @@
//!
//! The `deployer_call` function.
//!
use inkwell::types::BasicType;
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::function::Function;
use crate::eravm::context::pointer::Pointer;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::eravm::WriteLLVM;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::Function;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
///
/// The `deployer_call` function.
///
/// Calls the deployer system contract, which returns the newly deployed contract address or 0.
///
/// The address is returned in the first 32-byte word of the return data. If it is 0, the 0 is
/// returned. If the entire call has failed, there is also a 0 returned.
///
#[derive(Debug)]
pub struct DeployerCall {
/// The address space where the calldata is allocated.
@@ -45,9 +39,7 @@ impl DeployerCall {
/// The salt argument index.
pub const ARGUMENT_INDEX_SALT: usize = 4;
///
/// A shortcut constructor.
///
pub fn new(address_space: AddressSpace) -> Self {
Self { address_space }
}
@@ -60,11 +52,11 @@ where
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let function_type = context.function_type(
vec![
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
],
1,
false,
@@ -120,7 +112,7 @@ where
let value_join_block = context.append_basic_block("deployer_call_value_join_block");
context.set_basic_block(context.current_function().borrow().entry_block());
let _abi_data = crate::eravm::utils::abi_data(
let _abi_data = crate::polkavm::utils::abi_data(
context,
input_offset,
input_length,
@@ -132,7 +124,7 @@ where
let signature_pointer = Pointer::new_with_offset(
context,
self.address_space,
context.field_type(),
context.word_type(),
input_offset,
"deployer_call_signature_pointer",
);
@@ -140,13 +132,13 @@ where
let salt_offset = context.builder().build_int_add(
input_offset,
context.field_const(revive_common::BYTE_LENGTH_X32 as u64),
context.word_const(revive_common::BYTE_LENGTH_X32 as u64),
"deployer_call_salt_offset",
)?;
let salt_pointer = Pointer::new_with_offset(
context,
self.address_space,
context.field_type(),
context.word_type(),
salt_offset,
"deployer_call_salt_pointer",
);
@@ -154,47 +146,47 @@ where
let arguments_offset_offset = context.builder().build_int_add(
salt_offset,
context.field_const((revive_common::BYTE_LENGTH_FIELD * 2) as u64),
context.word_const((revive_common::BYTE_LENGTH_WORD * 2) as u64),
"deployer_call_arguments_offset_offset",
)?;
let arguments_offset_pointer = Pointer::new_with_offset(
context,
self.address_space,
context.field_type(),
context.word_type(),
arguments_offset_offset,
"deployer_call_arguments_offset_pointer",
);
context.build_store(
arguments_offset_pointer,
context.field_const(
(crate::eravm::DEPLOYER_CALL_HEADER_SIZE
- (revive_common::BYTE_LENGTH_X32 + revive_common::BYTE_LENGTH_FIELD))
context.word_const(
(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE
- (revive_common::BYTE_LENGTH_X32 + revive_common::BYTE_LENGTH_WORD))
as u64,
),
)?;
let arguments_length_offset = context.builder().build_int_add(
arguments_offset_offset,
context.field_const(revive_common::BYTE_LENGTH_FIELD as u64),
context.word_const(revive_common::BYTE_LENGTH_WORD as u64),
"deployer_call_arguments_length_offset",
)?;
let arguments_length_pointer = Pointer::new_with_offset(
context,
self.address_space,
context.field_type(),
context.word_type(),
arguments_length_offset,
"deployer_call_arguments_length_pointer",
);
let arguments_length_value = context.builder().build_int_sub(
input_length,
context.field_const(crate::eravm::DEPLOYER_CALL_HEADER_SIZE as u64),
context.word_const(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE as u64),
"deployer_call_arguments_length",
)?;
context.build_store(arguments_length_pointer, arguments_length_value)?;
let result_pointer =
context.build_alloca(context.field_type(), "deployer_call_result_pointer");
context.build_store(result_pointer, context.field_const(0))?;
context.build_alloca(context.word_type(), "deployer_call_result_pointer");
context.build_store(result_pointer, context.word_const(0))?;
let deployer_call_result_type = context.structure_type(&[
context
.llvm()
@@ -211,7 +203,7 @@ where
let is_value_zero = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
value,
context.field_const(0),
context.word_const(0),
"deployer_call_is_value_zero",
)?;
context.build_conditional_branch(is_value_zero, value_zero_block, value_non_zero_block)?;
@@ -220,7 +212,7 @@ where
//let deployer_call_result = context
// .build_call(
// context.llvm_runtime().far_call,
// crate::eravm::utils::external_call_arguments(
// crate::polkavm::utils::external_call_arguments(
// context,
// abi_data,
// context.field_const(zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into()),
@@ -238,14 +230,14 @@ where
//let deployer_call_result = context
// .build_call(
// context.llvm_runtime().far_call,
// crate::eravm::utils::external_call_arguments(
// crate::polkavm::utils::external_call_arguments(
// context,
// abi_data.as_basic_value_enum(),
// context.field_const(zkevm_opcode_defs::ADDRESS_MSG_VALUE.into()),
// vec![
// value,
// context.field_const(zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into()),
// context.field_const(u64::from(crate::eravm::r#const::SYSTEM_CALL_BIT)),
// context.field_const(u64::from(crate::polkavm::r#const::SYSTEM_CALL_BIT)),
// ],
// None,
// )
@@ -260,7 +252,7 @@ where
let result_abi_data_pointer = context.build_gep(
deployer_call_result_pointer,
&[
context.field_const(0),
context.word_const(0),
context
.integer_type(revive_common::BIT_LENGTH_X32)
.const_zero(),
@@ -277,7 +269,7 @@ where
let result_status_code_pointer = context.build_gep(
deployer_call_result_pointer,
&[
context.field_const(0),
context.word_const(0),
context
.integer_type(revive_common::BIT_LENGTH_X32)
.const_int(1, false),
@@ -296,7 +288,7 @@ where
context.set_basic_block(success_block);
let result_abi_data_pointer = Pointer::new(
context.field_type(),
context.word_type(),
AddressSpace::Generic,
result_abi_data.into_pointer_value(),
);
@@ -315,11 +307,11 @@ where
);
context.write_abi_pointer(
result_abi_data_pointer,
crate::eravm::GLOBAL_RETURN_DATA_POINTER,
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
);
context.write_abi_data_size(
result_abi_data_pointer,
crate::eravm::GLOBAL_RETURN_DATA_SIZE,
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
);
context.build_unconditional_branch(context.current_function().borrow().return_block());
@@ -1,23 +1,18 @@
//!
//! The entry function.
//!
use inkwell::types::BasicType;
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::function::runtime::Runtime;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::eravm::WriteLLVM;
use crate::EraVMPointer as Pointer;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::runtime::Runtime;
use crate::polkavm::context::Context;
use crate::polkavm::r#const::*;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
use crate::PolkaVMPointer as Pointer;
///
/// The entry function.
///
/// The function is a wrapper managing the runtime and deploy code calling logic.
///
/// Is a special runtime function that is only used by the front-end generated code.
///
#[derive(Debug, Default)]
pub struct Entry {}
@@ -39,56 +34,83 @@ impl Entry {
{
let calldata_type = context.array_type(context.byte_type(), Self::MAX_CALLDATA_SIZE);
context.set_global(
crate::eravm::GLOBAL_CALLDATA_POINTER,
crate::polkavm::GLOBAL_CALLDATA_POINTER,
calldata_type,
AddressSpace::Stack,
calldata_type.get_undef(),
);
context.set_global(
crate::eravm::GLOBAL_HEAP_MEMORY_POINTER,
crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER,
context.llvm().ptr_type(AddressSpace::Generic.into()),
AddressSpace::Stack,
context.xlen_type().get_undef(),
);
context.build_store(
context
.get_global(crate::eravm::GLOBAL_HEAP_MEMORY_POINTER)?
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.into(),
context.build_sbrk(context.integer_const(32, 0))?,
context.build_sbrk(context.integer_const(crate::polkavm::XLEN, 0))?,
)?;
context.set_global(
crate::eravm::GLOBAL_CALLDATA_SIZE,
context.field_type(),
crate::polkavm::GLOBAL_CALLDATA_SIZE,
context.word_type(),
AddressSpace::Stack,
context.field_undef(),
context.word_undef(),
);
context.set_global(
crate::eravm::GLOBAL_RETURN_DATA_SIZE,
context.field_type(),
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
context.word_type(),
AddressSpace::Stack,
context.field_const(0),
context.word_const(0),
);
context.set_global(
crate::eravm::GLOBAL_CALL_FLAGS,
context.field_type(),
crate::polkavm::GLOBAL_CALL_FLAGS,
context.word_type(),
AddressSpace::Stack,
context.field_const(0),
context.word_const(0),
);
let extra_abi_data_type = context.array_type(
context.field_type().as_basic_type_enum(),
crate::eravm::EXTRA_ABI_DATA_SIZE,
context.word_type().as_basic_type_enum(),
crate::polkavm::EXTRA_ABI_DATA_SIZE,
);
context.set_global(
crate::eravm::GLOBAL_EXTRA_ABI_DATA,
crate::polkavm::GLOBAL_EXTRA_ABI_DATA,
extra_abi_data_type,
AddressSpace::Stack,
extra_abi_data_type.const_zero(),
);
context.set_global(
crate::polkavm::GLOBAL_I256_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_X64 as u64 * 4,
),
);
context.set_global(
crate::polkavm::GLOBAL_I160_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_X64 as u64 * 2 + revive_common::BYTE_LENGTH_X32 as u64,
),
);
context.set_global(
crate::polkavm::GLOBAL_I64_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(crate::polkavm::XLEN, revive_common::BYTE_LENGTH_X64 as u64),
);
Ok(())
}
@@ -99,7 +121,7 @@ impl Entry {
D: Dependency + Clone,
{
let input_pointer = context
.get_global(crate::eravm::GLOBAL_CALLDATA_POINTER)?
.get_global(crate::polkavm::GLOBAL_CALLDATA_POINTER)?
.value
.as_pointer_value();
let input_pointer_casted = context.builder.build_ptr_to_int(
@@ -117,13 +139,12 @@ impl Entry {
context.build_store(
length_pointer,
context.integer_const(32, Self::MAX_CALLDATA_SIZE as u64),
context.integer_const(crate::polkavm::XLEN, Self::MAX_CALLDATA_SIZE as u64),
)?;
context.builder().build_call(
context.module().get_function("input").expect("is declared"),
context.build_runtime_call(
runtime_api::INPUT,
&[input_pointer_casted.into(), length_pointer_casted.into()],
"call_seal_input",
)?;
);
// Store the calldata size
let calldata_size = context
@@ -131,12 +152,12 @@ impl Entry {
.into_int_value();
let calldata_size_casted = context.builder().build_int_z_extend(
calldata_size,
context.field_type(),
context.word_type(),
"zext_input_len",
)?;
context.set_global(
crate::eravm::GLOBAL_CALLDATA_SIZE,
context.field_type(),
crate::polkavm::GLOBAL_CALLDATA_SIZE,
context.word_type(),
AddressSpace::Stack,
calldata_size_casted,
);
@@ -158,9 +179,9 @@ impl Entry {
);
context.write_abi_pointer(
calldata_end_pointer,
crate::eravm::GLOBAL_RETURN_DATA_POINTER,
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
);
context.write_abi_pointer(calldata_end_pointer, crate::eravm::GLOBAL_ACTIVE_POINTER);
context.write_abi_pointer(calldata_end_pointer, crate::polkavm::GLOBAL_ACTIVE_POINTER);
Ok(())
}
@@ -177,7 +198,7 @@ impl Entry {
.get_nth_param(Self::ARGUMENT_INDEX_CALL_FLAGS);
context.set_global(
crate::eravm::GLOBAL_CALL_FLAGS,
crate::polkavm::GLOBAL_CALL_FLAGS,
is_deploy.get_type(),
AddressSpace::Stack,
is_deploy.into_int_value(),
@@ -224,8 +245,9 @@ where
let entry_function_type = context.function_type(entry_arguments, 0, false);
context.add_function(Runtime::FUNCTION_ENTRY, entry_function_type, 0, None)?;
context.declare_extern_function("deploy")?;
context.declare_extern_function("call")?;
for symbol in runtime_api::EXPORTS {
context.declare_extern_function(symbol)?;
}
Ok(())
}
@@ -239,14 +261,14 @@ where
.expect("the entry function should already be declared")
.borrow()
.declaration;
crate::EraVMFunction::set_attributes(
crate::PolkaVMFunction::set_attributes(
context.llvm(),
entry,
vec![crate::EraVMAttribute::NoReturn],
vec![crate::PolkaVMAttribute::NoReturn],
true,
);
context.set_current_function("deploy")?;
context.set_current_function(runtime_api::DEPLOY)?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
@@ -256,7 +278,7 @@ where
context.set_basic_block(context.current_function().borrow().return_block);
context.build_unreachable();
context.set_current_function("call")?;
context.set_current_function(runtime_api::CALL)?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
@@ -1,6 +1,4 @@
//!
//! The front-end runtime functions.
//!
pub mod default_call;
pub mod deploy_code;
@@ -8,18 +6,16 @@ pub mod deployer_call;
pub mod entry;
pub mod runtime_code;
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::eravm::WriteLLVM;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
use self::default_call::DefaultCall;
use self::deployer_call::DeployerCall;
///
/// The front-end runtime functions.
///
#[derive(Debug, Clone)]
pub struct Runtime {
/// The address space where the calldata is allocated.
@@ -37,16 +33,12 @@ impl Runtime {
/// The runtime code function name.
pub const FUNCTION_RUNTIME_CODE: &'static str = "__runtime";
///
/// A shortcut constructor.
///
pub fn new(address_space: AddressSpace) -> Self {
Self { address_space }
}
///
/// Returns the corresponding runtime function.
///
pub fn default_call<'ctx, D>(
context: &Context<'ctx, D>,
call_function: FunctionDeclaration<'ctx>,
@@ -61,9 +53,7 @@ impl Runtime {
.declaration()
}
///
/// Returns the corresponding runtime function.
///
pub fn deployer_call<'ctx, D>(context: &Context<'ctx, D>) -> FunctionDeclaration<'ctx>
where
D: Dependency + Clone,
@@ -1,20 +1,15 @@
//!
//! The runtime code function.
//!
use std::marker::PhantomData;
use crate::eravm::context::code_type::CodeType;
use crate::eravm::context::function::runtime::Runtime;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::eravm::WriteLLVM;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime::Runtime;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
///
/// The runtime code function.
///
/// Is a special function that is only used by the front-end generated code.
///
#[derive(Debug)]
pub struct RuntimeCode<B, D>
where
@@ -32,9 +27,7 @@ where
B: WriteLLVM<D>,
D: Dependency + Clone,
{
///
/// A shortcut constructor.
///
pub fn new(inner: B) -> Self {
Self {
inner,
@@ -1,14 +1,9 @@
//!
//! The LLVM function Vyper data.
//!
use std::collections::HashMap;
///
/// The LLVM function Vyper data.
///
/// Describes some data that is only relevant to Vyper.
///
#[derive(Debug)]
pub struct VyperData {
/// The block-local variables. They are still allocated at the beginning of the function,
@@ -29,23 +24,17 @@ impl VyperData {
/// The label arguments hashmap default capacity.
const LABEL_ARGUMENTS_HASHMAP_INITIAL_CAPACITY: usize = 16;
///
/// A shortcut constructor.
///
pub fn new() -> Self {
Self::default()
}
///
/// Returns the list of a Vyper label arguments.
///
pub fn label_arguments(&self, label_name: &str) -> Option<Vec<String>> {
self.label_arguments.get(label_name).cloned()
}
///
/// Inserts arguments for the specified label.
///
pub fn insert_label_arguments(&mut self, label_name: String, arguments: Vec<String>) {
self.label_arguments.insert(label_name, arguments);
}
@@ -1,16 +1,11 @@
//!
//! The LLVM function Yul data.
//!
use std::collections::HashMap;
use num::BigUint;
///
/// The LLVM function Yul data.
///
/// Describes some data that is only relevant to Yul.
///
#[derive(Debug)]
pub struct YulData {
/// The constants saved to variables. Used for peculiar cases like call simulation.
@@ -30,23 +25,17 @@ impl YulData {
/// The constants hashmap default capacity.
const CONSTANTS_HASHMAP_INITIAL_CAPACITY: usize = 16;
///
/// A shortcut constructor.
///
pub fn new() -> Self {
Self::default()
}
///
/// Returns a constant if it has been saved.
///
pub fn get_constant(&self, name: &str) -> Option<BigUint> {
self.constants.get(name).cloned()
}
///
/// Saves a constant detected with the partial constant propagation.
///
pub fn insert_constant(&mut self, name: String, value: BigUint) {
self.constants.insert(name, value);
}
@@ -1,17 +1,13 @@
//!
//! The LLVM global value.
//!
use inkwell::types::BasicType;
use inkwell::values::BasicValue;
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::Context;
use crate::EraVMDependency;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::Context;
use crate::PolkaVMDependency;
///
/// The LLVM global value.
///
#[derive(Debug, Clone, Copy)]
pub struct Global<'ctx> {
/// The global type.
@@ -21,9 +17,7 @@ pub struct Global<'ctx> {
}
impl<'ctx> Global<'ctx> {
///
/// A shortcut constructor.
///
pub fn new<D, T, V>(
context: &mut Context<'ctx, D>,
r#type: T,
@@ -32,7 +26,7 @@ impl<'ctx> Global<'ctx> {
name: &str,
) -> Self
where
D: EraVMDependency + Clone,
D: PolkaVMDependency + Clone,
T: BasicType<'ctx>,
V: BasicValue<'ctx>,
{
@@ -1,10 +1,6 @@
//!
//! The LLVM IR generator loop.
//!
///
/// The LLVM IR generator loop.
///
#[derive(Debug, Clone)]
pub struct Loop<'ctx> {
/// The loop current block.
@@ -16,9 +12,7 @@ pub struct Loop<'ctx> {
}
impl<'ctx> Loop<'ctx> {
///
/// A shortcut constructor.
///
pub fn new(
body_block: inkwell::basic_block::BasicBlock<'ctx>,
continue_block: inkwell::basic_block::BasicBlock<'ctx>,
@@ -1,17 +1,13 @@
//!
//! The LLVM pointer.
//!
use inkwell::types::BasicType;
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::global::Global;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::global::Global;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
///
/// The LLVM pointer.
///
#[derive(Debug, Clone, Copy)]
pub struct Pointer<'ctx> {
/// The pointee type.
@@ -23,9 +19,7 @@ pub struct Pointer<'ctx> {
}
impl<'ctx> Pointer<'ctx> {
///
/// A shortcut constructor.
///
pub fn new<T>(
r#type: T,
address_space: AddressSpace,
@@ -41,9 +35,7 @@ impl<'ctx> Pointer<'ctx> {
}
}
///
/// Wraps a 256-bit primitive type pointer.
///
pub fn new_stack_field<D>(
context: &Context<'ctx, D>,
value: inkwell::values::PointerValue<'ctx>,
@@ -52,15 +44,13 @@ impl<'ctx> Pointer<'ctx> {
D: Dependency + Clone,
{
Self {
r#type: context.field_type().as_basic_type_enum(),
r#type: context.word_type().as_basic_type_enum(),
address_space: AddressSpace::Stack,
value,
}
}
///
/// Creates a new pointer with the specified `offset`.
///
pub fn new_with_offset<D, T>(
context: &Context<'ctx, D>,
address_space: AddressSpace,
@@ -78,7 +68,7 @@ impl<'ctx> Pointer<'ctx> {
"Stack pointers cannot be addressed"
);
let offset = context.safe_truncate_int_to_i32(offset).unwrap();
let offset = context.safe_truncate_int_to_xlen(offset).unwrap();
let value = context
.builder
.build_int_to_ptr(offset, context.llvm().ptr_type(address_space.into()), name)
@@ -86,9 +76,7 @@ impl<'ctx> Pointer<'ctx> {
Self::new(r#type, address_space, value)
}
///
/// Casts the pointer into another type.
///
pub fn cast<T>(self, r#type: T) -> Self
where
T: BasicType<'ctx>,
@@ -100,6 +88,17 @@ impl<'ctx> Pointer<'ctx> {
}
}
/// Cast this pointer to a register sized integer value.
pub fn to_int<D>(&self, context: &Context<'ctx, D>) -> inkwell::values::IntValue<'ctx>
where
D: Dependency + Clone,
{
context
.builder()
.build_ptr_to_int(self.value, context.xlen_type(), "ptr_to_xlen")
.expect("we should be positioned")
}
pub fn address_space_cast<D>(
self,
context: &Context<'ctx, D>,
@@ -1,14 +1,9 @@
//!
//! The LLVM IR generator Solidity data.
//!
use std::collections::BTreeMap;
///
/// The LLVM IR generator Solidity data.
///
/// Describes some data that is only relevant to Solidity.
///
#[derive(Debug, Default)]
pub struct SolidityData {
/// The immutables identifier-to-offset mapping. Is only used by Solidity due to
@@ -17,39 +12,29 @@ pub struct SolidityData {
}
impl SolidityData {
///
/// A shortcut constructor.
///
pub fn new() -> Self {
Self::default()
}
///
/// Returns the current number of immutables values in the contract.
///
pub fn immutables_size(&self) -> usize {
self.immutables.len() * revive_common::BYTE_LENGTH_FIELD
self.immutables.len() * revive_common::BYTE_LENGTH_WORD
}
///
/// Allocates memory for an immutable value in the auxiliary heap.
///
/// If the identifier is already known, just returns its offset.
///
pub fn allocate_immutable(&mut self, identifier: &str) -> usize {
let number_of_elements = self.immutables.len();
let new_offset = number_of_elements * revive_common::BYTE_LENGTH_FIELD;
let new_offset = number_of_elements * revive_common::BYTE_LENGTH_WORD;
*self
.immutables
.entry(identifier.to_owned())
.or_insert(new_offset)
}
///
/// Gets the offset of the immutable value.
///
/// If the value is not yet allocated, then it is done forcibly.
///
pub fn get_or_allocate_immutable(&mut self, identifier: &str) -> usize {
match self.immutables.get(identifier).copied() {
Some(offset) => offset,
@@ -1,18 +1,16 @@
//!
//! The LLVM IR generator context tests.
//!
use crate::eravm::context::attribute::Attribute;
use crate::eravm::context::Context;
use crate::eravm::DummyDependency;
use crate::optimizer::settings::Settings as OptimizerSettings;
use crate::optimizer::Optimizer;
use crate::polkavm::context::attribute::Attribute;
use crate::polkavm::context::Context;
use crate::polkavm::DummyDependency;
pub fn create_context(
llvm: &inkwell::context::Context,
optimizer_settings: OptimizerSettings,
) -> Context<DummyDependency> {
crate::eravm::initialize_target();
crate::polkavm::initialize_target();
let module = llvm.create_module("test");
let optimizer = Optimizer::new(optimizer_settings);
@@ -29,13 +27,13 @@ pub fn check_attribute_null_pointer_is_invalid() {
.add_function(
"test",
context
.field_type()
.fn_type(&[context.field_type().into()], false),
.word_type()
.fn_type(&[context.word_type().into()], false),
1,
Some(inkwell::module::Linkage::External),
)
.expect("Failed to add function");
assert!(function
assert!(!function
.borrow()
.declaration()
.value
@@ -52,8 +50,8 @@ pub fn check_attribute_optimize_for_size_mode_3() {
.add_function(
"test",
context
.field_type()
.fn_type(&[context.field_type().into()], false),
.word_type()
.fn_type(&[context.word_type().into()], false),
1,
Some(inkwell::module::Linkage::External),
)
@@ -75,8 +73,8 @@ pub fn check_attribute_optimize_for_size_mode_z() {
.add_function(
"test",
context
.field_type()
.fn_type(&[context.field_type().into()], false),
.word_type()
.fn_type(&[context.word_type().into()], false),
1,
Some(inkwell::module::Linkage::External),
)
@@ -98,8 +96,8 @@ pub fn check_attribute_min_size_mode_3() {
.add_function(
"test",
context
.field_type()
.fn_type(&[context.field_type().into()], false),
.word_type()
.fn_type(&[context.word_type().into()], false),
1,
Some(inkwell::module::Linkage::External),
)
@@ -121,8 +119,8 @@ pub fn check_attribute_min_size_mode_z() {
.add_function(
"test",
context
.field_type()
.fn_type(&[context.field_type().into()], false),
.word_type()
.fn_type(&[context.word_type().into()], false),
1,
Some(inkwell::module::Linkage::External),
)
@@ -1,12 +1,7 @@
//!
//! The LLVM IR generator Vyper data.
//!
///
/// The LLVM IR generator Vyper data.
///
/// Describes some data that is only relevant to Vyper.
///
#[derive(Debug)]
pub struct VyperData {
/// The immutables size tracker. Stores the size in bytes.
@@ -17,9 +12,7 @@ pub struct VyperData {
}
impl VyperData {
///
/// A shortcut constructor.
///
pub fn new(immutables_size: usize, is_forwarder_used: bool) -> Self {
Self {
immutables_size,
@@ -27,23 +20,17 @@ impl VyperData {
}
}
///
/// Returns the size of the immutables data of the contract.
///
pub fn immutables_size(&self) -> usize {
self.immutables_size
}
///
/// Sets the forwarder usage flag.
///
pub fn set_is_forwarder_used(&mut self) {
self.is_forwarder_used = true;
}
///
/// Returns the forwarder usage flag.
///
pub fn is_forwarder_used(&self) -> bool {
self.is_forwarder_used
}
@@ -1,16 +1,11 @@
//!
//! The LLVM IR generator Yul data.
//!
use std::collections::BTreeMap;
use num::Zero;
///
/// The LLVM IR generator Yul data.
///
/// Describes some data that is only relevant to Yul.
///
#[derive(Debug, Default)]
pub struct YulData {
/// The system mode flag.
@@ -22,9 +17,7 @@ pub struct YulData {
}
impl YulData {
///
/// A shortcut constructor.
///
pub fn new(is_system_mode: bool) -> Self {
Self {
is_system_mode,
@@ -32,16 +25,12 @@ impl YulData {
}
}
///
/// Whether the system mode is enabled.
///
pub fn is_system_mode(&self) -> bool {
self.is_system_mode
}
///
/// Declares a temporary constant array representation.
///
pub fn const_array_declare(&mut self, index: u8, size: u16) -> anyhow::Result<()> {
if self.const_arrays.contains_key(&index) {
anyhow::bail!(
@@ -56,9 +45,7 @@ impl YulData {
Ok(())
}
///
/// Sets a value in the constant array representation.
///
pub fn const_array_set(
&mut self,
index: u8,
@@ -81,9 +68,7 @@ impl YulData {
Ok(())
}
///
/// Finalizes the constant array declaration.
///
pub fn const_array_take(&mut self, index: u8) -> anyhow::Result<Vec<num::BigUint>> {
self.const_arrays.remove(&index).ok_or_else(|| {
anyhow::anyhow!("The constant array with index {} is not declared", index)
@@ -0,0 +1,205 @@
//! Translates the arithmetic operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the arithmetic addition.
pub fn addition<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_add(operand_1, operand_2, "addition_result")?
.as_basic_value_enum())
}
/// Translates the arithmetic subtraction.
pub fn subtraction<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_sub(operand_1, operand_2, "subtraction_result")?
.as_basic_value_enum())
}
/// Translates the arithmetic multiplication.
pub fn multiplication<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_mul(operand_1, operand_2, "multiplication_result")?
.as_basic_value_enum())
}
/// Translates the arithmetic division.
pub fn division<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
wrapped_division(context, operand_2, || {
Ok(context
.builder()
.build_int_unsigned_div(operand_1, operand_2, "DIV")?)
})
}
/// Translates the arithmetic remainder.
pub fn remainder<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
wrapped_division(context, operand_2, || {
Ok(context
.builder()
.build_int_unsigned_rem(operand_1, operand_2, "MOD")?)
})
}
/// Translates the signed arithmetic division.
/// Two differences between the EVM and LLVM IR:
/// 1. In case of division by zero, 0 is returned.
/// 2. In case of overflow, the first argument is returned.
pub fn division_signed<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
assert_eq!(
operand_2.get_type().get_bit_width(),
revive_common::BIT_LENGTH_WORD as u32
);
let block_calculate = context.append_basic_block("calculate");
let block_overflow = context.append_basic_block("overflow");
let block_select = context.append_basic_block("select_result");
let block_origin = context.basic_block();
context.builder().build_switch(
operand_2,
block_calculate,
&[
(context.word_type().const_zero(), block_select),
(context.word_type().const_all_ones(), block_overflow),
],
)?;
context.set_basic_block(block_calculate);
let quotient = context
.builder()
.build_int_signed_div(operand_1, operand_2, "SDIV")?;
context.build_unconditional_branch(block_select);
context.set_basic_block(block_overflow);
let max_uint = context.builder().build_int_z_extend(
context
.integer_type(revive_common::BIT_LENGTH_WORD - 1)
.const_all_ones(),
context.word_type(),
"max_uint",
)?;
let is_operand_1_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
operand_1,
context.builder().build_int_neg(max_uint, "min_uint")?,
"is_operand_1_overflow",
)?;
context.build_conditional_branch(is_operand_1_overflow, block_select, block_calculate)?;
context.set_basic_block(block_select);
let result = context.builder().build_phi(context.word_type(), "result")?;
result.add_incoming(&[
(&operand_1, block_overflow),
(&context.word_const(0), block_origin),
(&quotient.as_basic_value_enum(), block_calculate),
]);
Ok(result.as_basic_value())
}
/// Translates the signed arithmetic remainder.
pub fn remainder_signed<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
operand_2: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
wrapped_division(context, operand_2, || {
Ok(context
.builder()
.build_int_signed_rem(operand_1, operand_2, "SMOD")?)
})
}
/// Wrap division operations so that zero will be returned if the
/// denominator is zero (see also Ethereum YP Appendix H.2).
///
/// The closure is expected to calculate and return the quotient.
///
/// The result is either the calculated quotient or zero,
/// selected at runtime.
fn wrapped_division<'ctx, D, F, T>(
context: &Context<'ctx, D>,
denominator: inkwell::values::IntValue<'ctx>,
f: F,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
F: FnOnce() -> anyhow::Result<T>,
T: inkwell::values::IntMathValue<'ctx>,
{
assert_eq!(
denominator.get_type().get_bit_width(),
revive_common::BIT_LENGTH_WORD as u32
);
let block_calculate = context.append_basic_block("calculate");
let block_select = context.append_basic_block("select");
let block_origin = context.basic_block();
context.builder().build_switch(
denominator,
block_calculate,
&[(context.word_const(0), block_select)],
)?;
context.set_basic_block(block_calculate);
let calculated_value = f()?.as_basic_value_enum();
context.build_unconditional_branch(block_select);
context.set_basic_block(block_select);
let result = context.builder().build_phi(context.word_type(), "result")?;
result.add_incoming(&[
(&context.word_const(0), block_origin),
(&calculated_value, block_calculate),
]);
Ok(result.as_basic_value())
}
@@ -1,15 +1,11 @@
//!
//! Translates the bitwise operations.
//!
use inkwell::values::BasicValue;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
///
/// Translates the bitwise OR.
///
pub fn or<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
@@ -24,9 +20,7 @@ where
.as_basic_value_enum())
}
///
/// Translates the bitwise XOR.
///
pub fn xor<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
@@ -41,9 +35,7 @@ where
.as_basic_value_enum())
}
///
/// Translates the bitwise AND.
///
pub fn and<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
@@ -58,9 +50,7 @@ where
.as_basic_value_enum())
}
///
/// Translates the bitwise shift left.
///
pub fn shift_left<'ctx, D>(
context: &mut Context<'ctx, D>,
shift: inkwell::values::IntValue<'ctx>,
@@ -73,17 +63,17 @@ where
let non_overflow_block = context.append_basic_block("shift_left_non_overflow");
let join_block = context.append_basic_block("shift_left_join");
let result_pointer = context.build_alloca(context.field_type(), "shift_left_result_pointer");
let result_pointer = context.build_alloca(context.word_type(), "shift_left_result_pointer");
let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
shift,
context.field_const((revive_common::BIT_LENGTH_FIELD - 1) as u64),
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
"shift_left_is_overflow",
)?;
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
context.set_basic_block(overflow_block);
context.build_store(result_pointer, context.field_const(0))?;
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block);
@@ -98,9 +88,7 @@ where
context.build_load(result_pointer, "shift_left_result")
}
///
/// Translates the bitwise shift right.
///
pub fn shift_right<'ctx, D>(
context: &mut Context<'ctx, D>,
shift: inkwell::values::IntValue<'ctx>,
@@ -113,17 +101,17 @@ where
let non_overflow_block = context.append_basic_block("shift_right_non_overflow");
let join_block = context.append_basic_block("shift_right_join");
let result_pointer = context.build_alloca(context.field_type(), "shift_right_result_pointer");
let result_pointer = context.build_alloca(context.word_type(), "shift_right_result_pointer");
let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
shift,
context.field_const((revive_common::BIT_LENGTH_FIELD - 1) as u64),
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
"shift_right_is_overflow",
)?;
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
context.set_basic_block(overflow_block);
context.build_store(result_pointer, context.field_const(0))?;
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block);
@@ -140,9 +128,7 @@ where
context.build_load(result_pointer, "shift_right_result")
}
///
/// Translates the arithmetic bitwise shift right.
///
pub fn shift_right_arithmetic<'ctx, D>(
context: &mut Context<'ctx, D>,
shift: inkwell::values::IntValue<'ctx>,
@@ -159,14 +145,12 @@ where
let non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow");
let join_block = context.append_basic_block("shift_right_arithmetic_join");
let result_pointer = context.build_alloca(
context.field_type(),
"shift_right_arithmetic_result_pointer",
);
let result_pointer =
context.build_alloca(context.word_type(), "shift_right_arithmetic_result_pointer");
let condition_is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
shift,
context.field_const((revive_common::BIT_LENGTH_FIELD - 1) as u64),
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
"shift_right_arithmetic_is_overflow",
)?;
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
@@ -174,7 +158,7 @@ where
context.set_basic_block(overflow_block);
let sign_bit = context.builder().build_right_shift(
value,
context.field_const((revive_common::BIT_LENGTH_FIELD - 1) as u64),
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
false,
"shift_right_arithmetic_sign_bit",
)?;
@@ -190,11 +174,11 @@ where
)?;
context.set_basic_block(overflow_positive_block);
context.build_store(result_pointer, context.field_const(0))?;
context.build_store(result_pointer, context.word_const(0))?;
context.build_unconditional_branch(join_block);
context.set_basic_block(overflow_negative_block);
context.build_store(result_pointer, context.field_type().const_all_ones())?;
context.build_store(result_pointer, context.word_type().const_all_ones())?;
context.build_unconditional_branch(join_block);
context.set_basic_block(non_overflow_block);
@@ -211,9 +195,7 @@ where
context.build_load(result_pointer, "shift_right_arithmetic_result")
}
///
/// Translates the `byte` instruction.
///
pub fn byte<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
@@ -1,20 +1,15 @@
//!
//! Translates a contract call.
//!
use inkwell::values::BasicValue;
use crate::eravm::context::argument::Argument;
use crate::eravm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::argument::Argument;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
///
/// Translates a contract call.
///
/// If the `simulation_address` is specified, the call is substituted with another instruction
/// according to the specification.
///
#[allow(clippy::too_many_arguments)]
pub fn default<'ctx, D>(
_context: &mut Context<'ctx, D>,
@@ -95,9 +90,7 @@ where
*/
}
///
/// Translates the Yul `linkersymbol` instruction.
///
pub fn linker_symbol<'ctx, D>(
context: &mut Context<'ctx, D>,
mut arguments: [Argument<'ctx>; 1],
@@ -1,16 +1,12 @@
//!
//! Translates the calldata instructions.
//!
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::pointer::Pointer;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use inkwell::types::BasicType;
///
/// Translates the calldata load.
///
pub fn load<'ctx, D>(
context: &mut Context<'ctx, D>,
offset: inkwell::values::IntValue<'ctx>,
@@ -19,37 +15,33 @@ where
D: Dependency + Clone,
{
let calldata_pointer = context
.get_global(crate::eravm::GLOBAL_CALLDATA_POINTER)?
.get_global(crate::polkavm::GLOBAL_CALLDATA_POINTER)?
.value
.as_pointer_value();
let offset = context.build_gep(
Pointer::new(context.byte_type(), AddressSpace::Stack, calldata_pointer),
&[offset],
context.field_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
"calldata_pointer_with_offset",
);
context
.build_load(offset, "calldata_value")
.map(|value| context.build_byte_swap(value))
.and_then(|value| context.build_byte_swap(value))
}
///
/// Translates the calldata size.
///
pub fn size<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let value = context.get_global_value(crate::eravm::GLOBAL_CALLDATA_SIZE)?;
let value = context.get_global_value(crate::polkavm::GLOBAL_CALLDATA_SIZE)?;
Ok(value)
}
///
/// Translates the calldata copy.
///
pub fn copy<'ctx, D>(
context: &mut Context<'ctx, D>,
destination_offset: inkwell::values::IntValue<'ctx>,
@@ -59,17 +51,17 @@ pub fn copy<'ctx, D>(
where
D: Dependency + Clone,
{
let offset = context.safe_truncate_int_to_i32(destination_offset)?;
let size = context.safe_truncate_int_to_i32(size)?;
let offset = context.safe_truncate_int_to_xlen(destination_offset)?;
let size = context.safe_truncate_int_to_xlen(size)?;
let destination = context.build_heap_gep(offset, size)?;
let calldata_pointer = context
.get_global(crate::eravm::GLOBAL_CALLDATA_POINTER)?
.get_global(crate::polkavm::GLOBAL_CALLDATA_POINTER)?
.value
.as_pointer_value();
let source = context.build_gep(
Pointer::new(context.byte_type(), AddressSpace::Stack, calldata_pointer),
&[context.safe_truncate_int_to_i32(source_offset)?],
&[context.safe_truncate_int_to_xlen(source_offset)?],
context.byte_type(),
"calldata_pointer_with_offset",
);
@@ -1,17 +1,12 @@
//!
//! Translates the comparison operations.
//!
use inkwell::values::BasicValue;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
///
/// Translates the comparison operations.
///
/// There is not difference between the EVM and LLVM IR behaviors.
///
pub fn compare<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
@@ -29,7 +24,7 @@ where
)?;
let result = context.builder().build_int_z_extend_or_bit_cast(
result,
context.field_type(),
context.word_type(),
"comparison_result_extended",
)?;
Ok(result.as_basic_value_enum())
@@ -0,0 +1,211 @@
//! Translates the context getter instructions.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `gas_limit` instruction.
pub fn gas_limit<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
/// Translates the `gas_price` instruction.
pub fn gas_price<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
/// Translates the `tx.origin` instruction.
pub fn origin<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
/// Translates the `chain_id` instruction.
pub fn chain_id<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
/// Translates the `block_number` instruction.
pub fn block_number<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let (output_pointer, output_length_pointer) = context.build_stack_parameter(
revive_common::BIT_LENGTH_BLOCK_NUMBER,
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::BLOCK_NUMBER,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_BLOCK_NUMBER,
"block_number",
)
}
/// Translates the `block_timestamp` instruction.
pub fn block_timestamp<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let (output_pointer, output_length_pointer) = context.build_stack_parameter(
revive_common::BIT_LENGTH_BLOCK_TIMESTAMP,
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::NOW,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_BLOCK_TIMESTAMP,
"block_timestamp",
)
}
/// Translates the `block_hash` instruction.
pub fn block_hash<'ctx, D>(
_context: &mut Context<'ctx, D>,
_index: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
/// Translates the `difficulty` instruction.
pub fn difficulty<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context.word_const(2500000000000000).as_basic_value_enum())
}
/// Translates the `coinbase` instruction.
pub fn coinbase<'ctx, D>(
_context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
/// Translates the `basefee` instruction.
pub fn basefee<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context.word_const(0).as_basic_value_enum())
}
/// Translates the `msize` instruction.
pub fn msize<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let heap_end = context.build_sbrk(context.xlen_type().const_zero())?;
let heap_start = context
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.value
.as_pointer_value();
let heap_size = context.builder().build_int_nuw_sub(
context
.builder()
.build_ptr_to_int(heap_end, context.xlen_type(), "heap_end")?,
context
.builder()
.build_ptr_to_int(heap_start, context.xlen_type(), "heap_start")?,
"heap_size",
)?;
Ok(context
.builder()
.build_int_z_extend(heap_size, context.word_type(), "heap_size_extended")?
.as_basic_value_enum())
}
/// Translates the `address` instruction.
pub fn address<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_output");
context.build_runtime_call(
runtime_api::ADDRESS,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
let value = context.build_byte_swap(context.build_load(output_pointer, "address")?)?;
Ok(context
.builder()
.build_int_z_extend(value.into_int_value(), context.word_type(), "address_zext")?
.into())
}
/// Translates the `caller` instruction.
pub fn caller<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "caller_output");
context.build_runtime_call(
runtime_api::CALLER,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
let value = context.build_byte_swap(context.build_load(output_pointer, "caller")?)?;
Ok(context
.builder()
.build_int_z_extend(value.into_int_value(), context.word_type(), "caller_zext")?
.into())
}
@@ -1,21 +1,16 @@
//!
//! Translates the contract creation instructions.
//!
use inkwell::values::BasicValue;
use num::Zero;
use crate::eravm::context::argument::Argument;
use crate::eravm::context::code_type::CodeType;
use crate::eravm::context::function::runtime::Runtime;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::argument::Argument;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime::Runtime;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
///
/// Translates the contract `create` instruction.
///
/// The instruction is simulated by a call to a system contract.
///
pub fn create<'ctx, D>(
context: &mut Context<'ctx, D>,
value: inkwell::values::IntValue<'ctx>,
@@ -26,10 +21,10 @@ where
D: Dependency + Clone,
{
let signature_hash_string =
crate::eravm::utils::keccak256(crate::eravm::DEPLOYER_SIGNATURE_CREATE.as_bytes());
let signature_hash = context.field_const_str_hex(signature_hash_string.as_str());
crate::polkavm::utils::keccak256(crate::polkavm::DEPLOYER_SIGNATURE_CREATE.as_bytes());
let signature_hash = context.word_const_str_hex(signature_hash_string.as_str());
let salt = context.field_const(0);
let salt = context.word_const(0);
let function = Runtime::deployer_call(context);
let result = context
@@ -49,11 +44,8 @@ where
Ok(result)
}
///
/// Translates the contract `create2` instruction.
///
/// The instruction is simulated by a call to a system contract.
///
pub fn create2<'ctx, D>(
context: &mut Context<'ctx, D>,
value: inkwell::values::IntValue<'ctx>,
@@ -65,10 +57,10 @@ where
D: Dependency + Clone,
{
let signature_hash_string =
crate::eravm::utils::keccak256(crate::eravm::DEPLOYER_SIGNATURE_CREATE2.as_bytes());
let signature_hash = context.field_const_str_hex(signature_hash_string.as_str());
crate::polkavm::utils::keccak256(crate::polkavm::DEPLOYER_SIGNATURE_CREATE2.as_bytes());
let signature_hash = context.word_const_str_hex(signature_hash_string.as_str());
let salt = salt.unwrap_or_else(|| context.field_const(0));
let salt = salt.unwrap_or_else(|| context.word_const(0));
let function = Runtime::deployer_call(context);
let result = context
@@ -88,12 +80,9 @@ where
Ok(result)
}
///
/// Translates the contract hash instruction, which is actually used to set the hash of the contract
/// being created, or other related auxiliary data.
///
/// Represents `dataoffset` in Yul and `PUSH [$]` in the EVM legacy assembly.
///
pub fn contract_hash<'ctx, D>(
context: &mut Context<'ctx, D>,
identifier: String,
@@ -118,7 +107,7 @@ where
})?;
if contract_path.as_str() == parent {
return Ok(Argument::new_with_constant(
context.field_const(0).as_basic_value_enum(),
context.word_const(0).as_basic_value_enum(),
num::BigUint::zero(),
));
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime {
@@ -127,25 +116,21 @@ where
let hash_string = context.compile_dependency(identifier.as_str())?;
let hash_value = context
.field_const_str_hex(hash_string.as_str())
.word_const_str_hex(hash_string.as_str())
.as_basic_value_enum();
Ok(Argument::new_with_original(hash_value, hash_string))
}
///
/// Translates the deployer call header size instruction, Usually, the header consists of:
/// - the deployer contract method signature
/// - the salt if the call is `create2`, or zero if the call is `create1`
/// - the hash of the bytecode of the contract whose instance is being created
/// - the offset of the constructor arguments
/// - the length of the constructor arguments
///
/// If the call is `create1`, the space for the salt is still allocated, because the memory for the
/// header is allocated by the Yul or EVM legacy assembly before it is known which version of
/// `create` is going to be used.
///
/// Represents `datasize` in Yul and `PUSH #[$]` in the EVM legacy assembly.
///
pub fn header_size<'ctx, D>(
context: &mut Context<'ctx, D>,
identifier: String,
@@ -170,16 +155,16 @@ where
})?;
if contract_path.as_str() == parent {
return Ok(Argument::new_with_constant(
context.field_const(0).as_basic_value_enum(),
context.word_const(0).as_basic_value_enum(),
num::BigUint::zero(),
));
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime {
anyhow::bail!("type({}).runtimeCode is not supported", identifier);
}
let size_bigint = num::BigUint::from(crate::eravm::DEPLOYER_CALL_HEADER_SIZE);
let size_bigint = num::BigUint::from(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE);
let size_value = context
.field_const(crate::eravm::DEPLOYER_CALL_HEADER_SIZE as u64)
.word_const(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE as u64)
.as_basic_value_enum();
Ok(Argument::new_with_constant(size_value, size_bigint))
}
@@ -0,0 +1,31 @@
//! Translates the cryptographic operations.
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `sha3` instruction.
pub fn sha3<'ctx, D>(
context: &mut Context<'ctx, D>,
offset: inkwell::values::IntValue<'ctx>,
length: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let offset_casted = context.safe_truncate_int_to_xlen(offset)?;
let length_casted = context.safe_truncate_int_to_xlen(length)?;
let input_pointer = context.build_heap_gep(offset_casted, length_casted)?;
let output_pointer = context.build_alloca(context.word_type(), "output_pointer");
context.build_runtime_call(
runtime_api::HASH_KECCAK_256,
&[
input_pointer.to_int(context).into(),
length_casted.into(),
output_pointer.to_int(context).into(),
],
);
context.build_byte_swap(context.build_load(output_pointer, "sha3_output")?)
}
@@ -0,0 +1,51 @@
//! Translates the value and balance operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `gas` instruction.
pub fn gas<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context.integer_const(256, 0).as_basic_value_enum())
}
/// Translates the `value` instruction.
pub fn value<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_VALUE, "value_transferred_output");
context.build_runtime_call(
runtime_api::VALUE_TRANSFERRED,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_VALUE,
"value_transferred",
)
}
/// Translates the `balance` instructions.
pub fn balance<'ctx, D>(
_context: &mut Context<'ctx, D>,
_address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
}
@@ -0,0 +1,133 @@
//! Translates a log or event call.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates a log or event call.
pub fn log<'ctx, D>(
context: &mut Context<'ctx, D>,
input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>,
topics: Vec<inkwell::values::IntValue<'ctx>>,
) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
let input_pointer = context.builder().build_ptr_to_int(
context.build_heap_gep(input_offset, input_length)?.value,
context.xlen_type(),
"event_input_offset",
)?;
if topics.is_empty() {
let _ = context.build_runtime_call(
runtime_api::DEPOSIT_EVENT,
&[
context.xlen_type().const_zero().as_basic_value_enum(),
context.xlen_type().const_zero().as_basic_value_enum(),
input_pointer.as_basic_value_enum(),
input_length.as_basic_value_enum(),
],
);
return Ok(());
}
let name = match topics.len() {
1 => "__log_1",
2 => "__log_2",
3 => "__log_3",
4 => "__log_4",
_ => unreachable!(),
};
let function = context.module().get_function(name).unwrap_or_else(|| {
let position = context.basic_block();
let mut parameters = vec![context.xlen_type().into(), context.xlen_type().into()];
parameters.extend_from_slice(
&topics
.iter()
.map(|_| context.word_type().into())
.collect::<Vec<_>>(),
);
let function = context.module().add_function(
name,
context.void_type().fn_type(&parameters, false),
None,
);
let block_entry = context.llvm().append_basic_block(function, "entry");
context.set_basic_block(block_entry);
let parameters = function.get_param_iter().collect::<Vec<_>>();
let topics = &parameters[2..];
let input_offset = parameters.first().unwrap();
let input_length = parameters.get(1).unwrap();
let topics_buffer_size = topics.len() * revive_common::BYTE_LENGTH_WORD;
let topics_buffer_pointer = context.build_alloca(
context.byte_type().array_type(topics_buffer_size as u32),
"topics_buffer",
);
for (n, topic) in topics.iter().enumerate() {
let topic_buffer_offset = context
.xlen_type()
.const_int((n * revive_common::BYTE_LENGTH_WORD) as u64, false);
context
.build_store(
context.build_gep(
topics_buffer_pointer,
&[context.xlen_type().const_zero(), topic_buffer_offset],
context.byte_type(),
"topic_buffer_gep",
),
context
.build_byte_swap(topic.as_basic_value_enum())
.unwrap(),
)
.unwrap();
}
let arguments = [
context
.builder()
.build_ptr_to_int(
topics_buffer_pointer.value,
context.xlen_type(),
"event_topics_offset",
)
.unwrap()
.as_basic_value_enum(),
context
.xlen_type()
.const_int(topics_buffer_size as u64, false)
.as_basic_value_enum(),
input_offset.as_basic_value_enum(),
input_length.as_basic_value_enum(),
];
let _ = context.build_runtime_call(runtime_api::DEPOSIT_EVENT, &arguments);
context.builder().build_return(None).unwrap();
context.set_basic_block(position);
function
});
let mut arguments = vec![
input_pointer.as_basic_value_enum().into(),
input_length.as_basic_value_enum().into(),
];
arguments.extend_from_slice(
&topics
.iter()
.map(|value| value.as_basic_value_enum().into())
.collect::<Vec<_>>(),
);
let _ = context
.builder()
.build_direct_call(function, &arguments[..], "call_log");
Ok(())
}
@@ -1,13 +1,9 @@
//!
//! Translates the external code operations.
//!
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
///
/// Translates the `extcodesize` instruction.
///
pub fn size<'ctx, D>(
_context: &mut Context<'ctx, D>,
_address: inkwell::values::IntValue<'ctx>,
@@ -18,9 +14,7 @@ where
todo!()
}
///
/// Translates the `extcodehash` instruction.
///
pub fn hash<'ctx, D>(
_context: &mut Context<'ctx, D>,
_address: inkwell::values::IntValue<'ctx>,
@@ -1,19 +1,14 @@
//!
//! Translates the contract immutable operations.
//!
use crate::eravm::context::address_space::AddressSpace;
use crate::eravm::context::code_type::CodeType;
use crate::eravm::context::pointer::Pointer;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
///
/// Translates the contract immutable load.
///
/// In the deploy code the values are read from the auxiliary heap.
/// In the runtime code they are requested from the system contract.
///
pub fn load<'ctx, D>(
context: &mut Context<'ctx, D>,
index: inkwell::values::IntValue<'ctx>,
@@ -28,21 +23,21 @@ where
Some(CodeType::Deploy) => {
let index_double = context.builder().build_int_mul(
index,
context.field_const(2),
context.word_const(2),
"immutable_load_index_double",
)?;
let offset_absolute = context.builder().build_int_add(
index_double,
context.field_const(
crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
+ (3 * revive_common::BYTE_LENGTH_FIELD) as u64,
context.word_const(
crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
+ (3 * revive_common::BYTE_LENGTH_WORD) as u64,
),
"immutable_offset_absolute",
)?;
let immutable_pointer = Pointer::new_with_offset(
context,
AddressSpace::HeapAuxiliary,
context.field_type(),
context.word_type(),
offset_absolute,
"immutable_pointer",
);
@@ -54,14 +49,10 @@ where
}
}
///
/// Translates the contract immutable store.
///
/// In the deploy code the values are written to the auxiliary heap at the predefined offset,
/// being prepared for returning to the system contract for saving.
///
/// Ignored in the runtime code.
///
pub fn store<'ctx, D>(
context: &mut Context<'ctx, D>,
index: inkwell::values::IntValue<'ctx>,
@@ -77,21 +68,21 @@ where
Some(CodeType::Deploy) => {
let index_double = context.builder().build_int_mul(
index,
context.field_const(2),
context.word_const(2),
"immutable_load_index_double",
)?;
let index_offset_absolute = context.builder().build_int_add(
index_double,
context.field_const(
crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
+ (2 * revive_common::BYTE_LENGTH_FIELD) as u64,
context.word_const(
crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
+ (2 * revive_common::BYTE_LENGTH_WORD) as u64,
),
"index_offset_absolute",
)?;
let index_offset_pointer = Pointer::new_with_offset(
context,
AddressSpace::HeapAuxiliary,
context.field_type(),
context.word_type(),
index_offset_absolute,
"immutable_index_pointer",
);
@@ -99,13 +90,13 @@ where
let value_offset_absolute = context.builder().build_int_add(
index_offset_absolute,
context.field_const(revive_common::BYTE_LENGTH_FIELD as u64),
context.word_const(revive_common::BYTE_LENGTH_WORD as u64),
"value_offset_absolute",
)?;
let value_offset_pointer = Pointer::new_with_offset(
context,
AddressSpace::HeapAuxiliary,
context.field_type(),
context.word_type(),
value_offset_absolute,
"immutable_value_pointer",
);
@@ -1,15 +1,11 @@
//!
//! Translates the mathematical operations.
//!
use inkwell::values::BasicValue;
use crate::eravm::context::Context;
use crate::eravm::Dependency;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
///
/// Translates the `addmod` instruction.
///
pub fn add_mod<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
@@ -32,9 +28,7 @@ where
.expect("Always exists"))
}
///
/// Translates the `mulmod` instruction.
///
pub fn mul_mod<'ctx, D>(
context: &mut Context<'ctx, D>,
operand_1: inkwell::values::IntValue<'ctx>,
@@ -57,9 +51,7 @@ where
.expect("Always exists"))
}
///
/// Translates the `exp` instruction.
///
pub fn exponent<'ctx, D>(
context: &mut Context<'ctx, D>,
value: inkwell::values::IntValue<'ctx>,
@@ -77,9 +69,7 @@ where
.expect("Always exists"))
}
///
/// Translates the `signextend` instruction.
///
pub fn sign_extend<'ctx, D>(
context: &mut Context<'ctx, D>,
bytes: inkwell::values::IntValue<'ctx>,

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