Compare commits

..

9 Commits

Author SHA1 Message Date
Cyrill Leutwiler b19981a513 division functions
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-07-04 16:18:30 +02:00
Cyrill Leutwiler 10e50e446b restore
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-07-04 15:51:00 +02:00
Cyrill Leutwiler 939a8f2f78 add simulated evm math opcode syscalls
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-07-04 15:45:59 +02:00
xermicus ed608699af release resolc v0.3.0 (#354)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-06-28 12:32:48 +02:00
xermicus 75fc23c810 add the missing memset builtin (#353)
Closes  #350

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

---------

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

e.g

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

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

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

This fixes it and add a test case with such a package
2025-06-05 10:05:53 +02:00
45 changed files with 1836 additions and 194 deletions
+13
View File
@@ -6,6 +6,19 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
## v0.3.0
This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
### Fixed
- llvm-context: Bugfix the SAR YUL builtin translation.
- runtime-api: Add the missing `memset` builtin.
- npm package: Bugfix the exports field defined in the `package.json`.
## v0.2.0
This is a development pre-release.
Generated
+7 -7
View File
@@ -8546,7 +8546,7 @@ dependencies = [
[[package]]
name = "resolc"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"anyhow",
"clap",
@@ -8617,7 +8617,7 @@ dependencies = [
[[package]]
name = "revive-integration"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"alloy-primitives 1.1.2",
"alloy-sol-types 1.1.2",
@@ -8645,7 +8645,7 @@ dependencies = [
[[package]]
name = "revive-llvm-builder"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"anyhow",
"assert_cmd",
@@ -8667,7 +8667,7 @@ dependencies = [
[[package]]
name = "revive-llvm-context"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"anyhow",
"hex",
@@ -8708,7 +8708,7 @@ dependencies = [
[[package]]
name = "revive-runtime-api"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"anyhow",
"inkwell",
@@ -8730,7 +8730,7 @@ dependencies = [
[[package]]
name = "revive-stdlib"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"inkwell",
"revive-build-utils",
@@ -8738,7 +8738,7 @@ dependencies = [
[[package]]
name = "revive-yul"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"anyhow",
"inkwell",
+6 -6
View File
@@ -14,21 +14,21 @@ repository = "https://github.com/paritytech/revive"
rust-version = "1.85.0"
[workspace.dependencies]
resolc = { version = "0.2.0", path = "crates/resolc" }
resolc = { version = "0.3.0", path = "crates/resolc" }
revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0", path = "crates/builtins" }
revive-common = { version = "0.1.0", path = "crates/common" }
revive-differential = { version = "0.1.0", path = "crates/differential" }
revive-integration = { version = "0.1.0", path = "crates/integration" }
revive-integration = { version = "0.1.1", path = "crates/integration" }
revive-linker = { version = "0.1.0", path = "crates/linker" }
lld-sys = { version = "0.1.0", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.2.0", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0", path = "crates/runtime-api" }
revive-llvm-context = { version = "0.3.0", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.2.0", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0", path = "crates/runner" }
revive-solc-json-interface = { version = "0.2.0", path = "crates/solc-json-interface" }
revive-stdlib = { version = "0.1.0", path = "crates/stdlib" }
revive-stdlib = { version = "0.1.1", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0", path = "crates/build-utils" }
revive-yul = { version = "0.2.0", path = "crates/yul" }
revive-yul = { version = "0.2.1", path = "crates/yul" }
hex = "0.4.3"
cc = "1.2"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "revive-integration"
version.workspace = true
version = "0.1.1"
license.workspace = true
edition.workspace = true
repository.workspace = true
+49
View File
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "SAR"
}
}
}
}
]
}
*/
contract SAR {
constructor() payable {
assert(sar(0x03, 0x01) == 0x01);
assert(
sar(
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
0x01
) == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
);
assert(
sar(
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
0xff
) == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
);
assert(
sar(
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
0x100
) == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
);
}
function sar(uint256 a, uint256 b) public pure returns (uint256 c) {
assembly {
c := sar(b, a)
}
}
}
+1
View File
@@ -60,6 +60,7 @@ test_spec!(mload, "MLoad", "MLoad.sol");
test_spec!(delegate_no_contract, "DelegateCaller", "DelegateCaller.sol");
test_spec!(function_type, "FunctionType", "FunctionType.sol");
test_spec!(layout_at, "LayoutAt", "LayoutAt.sol");
test_spec!(shift_arithmetic_right, "SAR", "SAR.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate {
+1 -1
View File
@@ -6,7 +6,7 @@ authors = [
"Anton Baliasnikov <aba@matterlabs.dev>",
"Cyrill Leutwiler <cyrill@parity.io>",
]
version.workspace = true
version = "0.2.0"
license.workspace = true
edition.workspace = true
repository.workspace = true
+1 -1
View File
@@ -22,7 +22,7 @@ impl std::str::FromStr for BuildType {
"Release" => Ok(Self::Release),
"RelWithDebInfo" => Ok(Self::RelWithDebInfo),
"MinSizeRel" => Ok(Self::MinSizeRel),
value => Err(format!("Unsupported build type: `{}`", value)),
value => Err(format!("Unsupported build type: `{value}`")),
}
}
}
+1 -1
View File
@@ -16,7 +16,7 @@ impl std::str::FromStr for CcacheVariant {
match value {
"ccache" => Ok(Self::Ccache),
"sccache" => Ok(Self::Sccache),
value => Err(format!("Unsupported ccache variant: `{}`", value)),
value => Err(format!("Unsupported ccache variant: `{value}`")),
}
}
}
+13 -14
View File
@@ -127,23 +127,22 @@ pub fn build(
sanitizer: Option<sanitizer::Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
log::trace!("build type: {:?}", build_type);
log::trace!("target env: {:?}", target_env);
log::trace!("targets: {:?}", targets);
log::trace!("llvm projects: {:?}", llvm_projects);
log::trace!("enable rtti: {:?}", enable_rtti);
log::trace!("default target: {:?}", default_target);
log::trace!("eneable tests: {:?}", enable_tests);
log::trace!("enable_coverage: {:?}", enable_coverage);
log::trace!("extra args: {:?}", extra_args);
log::trace!("sanitzer: {:?}", sanitizer);
log::trace!("enable valgrind: {:?}", enable_valgrind);
log::trace!("build type: {build_type:?}");
log::trace!("target env: {target_env:?}");
log::trace!("targets: {targets:?}");
log::trace!("llvm projects: {llvm_projects:?}");
log::trace!("enable rtti: {enable_rtti:?}");
log::trace!("default target: {default_target:?}");
log::trace!("eneable tests: {enable_tests:?}");
log::trace!("enable_coverage: {enable_coverage:?}");
log::trace!("extra args: {extra_args:?}");
log::trace!("sanitzer: {sanitizer:?}");
log::trace!("enable valgrind: {enable_valgrind:?}");
if !PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE).exists() {
log::error!(
"LLVM project source directory {} does not exist (run `revive-llvm --target-env {} clone`)",
LLVMPath::DIRECTORY_LLVM_SOURCE,
target_env
"LLVM project source directory {} does not exist (run `revive-llvm --target-env {target_env} clone`)",
LLVMPath::DIRECTORY_LLVM_SOURCE
)
}
+1 -1
View File
@@ -22,7 +22,7 @@ impl std::str::FromStr for LLVMProject {
"lld" => Ok(Self::LLD),
"lldb" => Ok(Self::LLDB),
"mlir" => Ok(Self::MLIR),
value => Err(format!("Unsupported LLVM project to enable: `{}`", value)),
value => Err(format!("Unsupported LLVM project to enable: `{value}`")),
}
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ impl TryFrom<&PathBuf> for Lock {
fn try_from(path: &PathBuf) -> Result<Self, Self::Error> {
let mut config_str = String::new();
let mut config_file =
File::open(path).with_context(|| format!("Error opening {:?} file", path))?;
File::open(path).with_context(|| format!("Error opening {path:?} file"))?;
config_file.read_to_string(&mut config_str)?;
Ok(toml::from_str(&config_str)?)
}
+1 -1
View File
@@ -29,7 +29,7 @@ impl FromStr for Platform {
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"PolkaVM" => Ok(Self::PolkaVM),
value => Err(format!("Unsupported platform: `{}`", value)),
value => Err(format!("Unsupported platform: `{value}`")),
}
}
}
+2 -2
View File
@@ -69,8 +69,8 @@ fn main_inner() -> anyhow::Result<()> {
})
.collect();
log::debug!("extra_args: {:#?}", extra_args);
log::debug!("extra_args_unescaped: {:#?}", extra_args_unescaped);
log::debug!("extra_args: {extra_args:#?}");
log::debug!("extra_args_unescaped: {extra_args_unescaped:#?}");
if let Some(ccache_variant) = ccache_variant {
revive_llvm_builder::utils::check_presence(ccache_variant.to_string().as_str())?;
+1 -1
View File
@@ -30,7 +30,7 @@ impl std::str::FromStr for Sanitizer {
"thread" => Ok(Self::Thread),
"dataflow" => Ok(Self::DataFlow),
"address;undefined" => Ok(Self::AddressUndefined),
value => Err(format!("Unsupported sanitizer: `{}`", value)),
value => Err(format!("Unsupported sanitizer: `{value}`")),
}
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ impl std::str::FromStr for TargetEnv {
"gnu" => Ok(Self::GNU),
"musl" => Ok(Self::MUSL),
"emscripten" => Ok(Self::Emscripten),
value => Err(format!("Unsupported target environment: `{}`", value)),
value => Err(format!("Unsupported target environment: `{value}`")),
}
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ impl std::str::FromStr for TargetTriple {
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"polkavm" => Ok(Self::PolkaVM),
value => Err(format!("Unsupported target triple: `{}`", value)),
value => Err(format!("Unsupported target triple: `{value}`")),
}
}
}
+2 -2
View File
@@ -92,7 +92,7 @@ pub fn unpack_tar(filename: PathBuf, path: &str) -> anyhow::Result<()> {
pub fn download_musl(name: &str) -> anyhow::Result<()> {
log::info!("downloading musl {name}");
let tar_file_name = format!("{name}.tar.gz");
let url = format!("{}/{tar_file_name}", MUSL_SNAPSHOTS_URL);
let url = format!("{MUSL_SNAPSHOTS_URL}/{tar_file_name}");
let target_path = crate::llvm_path::DIRECTORY_LLVM_TARGET
.get()
.unwrap()
@@ -223,6 +223,6 @@ pub fn install_emsdk() -> anyhow::Result<()> {
/// The LLVM target directory default path.
pub fn directory_target_llvm(target_env: crate::target_env::TargetEnv) -> PathBuf {
crate::llvm_path::DIRECTORY_LLVM_TARGET
.get_or_init(|| PathBuf::from(format!("./target-llvm/{}/", target_env)))
.get_or_init(|| PathBuf::from(format!("./target-llvm/{target_env}/")))
.clone()
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "revive-llvm-context"
version = "0.2.0"
version = "0.3.0"
license.workspace = true
edition.workspace = true
repository.workspace = true
@@ -2,13 +2,13 @@
use inkwell::values::BasicValue;
use crate::polkavm::context::runtime::RuntimeFunction;
//use crate::polkavm::context::runtime::RuntimeFunction;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::PolkaVMDivisionFunction;
use crate::PolkaVMRemainderFunction;
use crate::PolkaVMSignedDivisionFunction;
use crate::PolkaVMSignedRemainderFunction;
//use crate::PolkaVMDivisionFunction;
//use crate::PolkaVMRemainderFunction;
//use crate::PolkaVMSignedDivisionFunction;
//use crate::PolkaVMSignedRemainderFunction;
/// Translates the arithmetic addition.
pub fn addition<'ctx, D>(
@@ -64,11 +64,21 @@ pub fn division<'ctx, D>(
where
D: Dependency + Clone,
{
let name = <PolkaVMDivisionFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMDivisionFunction as RuntimeFunction<D>>::declaration(context);
Ok(context
.build_call(declaration, &[operand_1.into(), operand_2.into()], "div")
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
let result_pointer = context.build_alloca_at_entry(context.word_type(), "div_result_pointer");
let operand_1_pointer = context.build_alloca_at_entry(context.word_type(), "operand_1_pointer");
let operand_2_pointer = context.build_alloca_at_entry(context.word_type(), "operand_2_pointer");
context.build_store(operand_1_pointer, operand_1)?;
context.build_store(operand_2_pointer, operand_2)?;
let arguments = &[
result_pointer.to_int(context).into(),
operand_1_pointer.to_int(context).into(),
operand_2_pointer.to_int(context).into(),
];
context.build_runtime_call(revive_runtime_api::polkavm_imports::DIV, arguments);
context.build_load(result_pointer, "div_result")
}
/// Translates the arithmetic remainder.
@@ -80,11 +90,21 @@ pub fn remainder<'ctx, D>(
where
D: Dependency + Clone,
{
let name = <PolkaVMRemainderFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMRemainderFunction as RuntimeFunction<D>>::declaration(context);
Ok(context
.build_call(declaration, &[operand_1.into(), operand_2.into()], "rem")
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
let result_pointer = context.build_alloca_at_entry(context.word_type(), "rem_result_pointer");
let operand_1_pointer = context.build_alloca_at_entry(context.word_type(), "operand_1_pointer");
let operand_2_pointer = context.build_alloca_at_entry(context.word_type(), "operand_2_pointer");
context.build_store(operand_1_pointer, operand_1)?;
context.build_store(operand_2_pointer, operand_2)?;
let arguments = &[
result_pointer.to_int(context).into(),
operand_1_pointer.to_int(context).into(),
operand_2_pointer.to_int(context).into(),
];
context.build_runtime_call(revive_runtime_api::polkavm_imports::MOD, arguments);
context.build_load(result_pointer, "rem_result")
}
/// Translates the signed arithmetic division.
@@ -99,11 +119,21 @@ pub fn division_signed<'ctx, D>(
where
D: Dependency + Clone,
{
let name = <PolkaVMSignedDivisionFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMSignedDivisionFunction as RuntimeFunction<D>>::declaration(context);
Ok(context
.build_call(declaration, &[operand_1.into(), operand_2.into()], "sdiv")
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
let result_pointer = context.build_alloca_at_entry(context.word_type(), "sdiv_result_pointer");
let operand_1_pointer = context.build_alloca_at_entry(context.word_type(), "operand_1_pointer");
let operand_2_pointer = context.build_alloca_at_entry(context.word_type(), "operand_2_pointer");
context.build_store(operand_1_pointer, operand_1)?;
context.build_store(operand_2_pointer, operand_2)?;
let arguments = &[
result_pointer.to_int(context).into(),
operand_1_pointer.to_int(context).into(),
operand_2_pointer.to_int(context).into(),
];
context.build_runtime_call(revive_runtime_api::polkavm_imports::SDIV, arguments);
context.build_load(result_pointer, "sdiv_result")
}
/// Translates the signed arithmetic remainder.
@@ -115,9 +145,19 @@ pub fn remainder_signed<'ctx, D>(
where
D: Dependency + Clone,
{
let name = <PolkaVMSignedRemainderFunction as RuntimeFunction<D>>::NAME;
let declaration = <PolkaVMSignedRemainderFunction as RuntimeFunction<D>>::declaration(context);
Ok(context
.build_call(declaration, &[operand_1.into(), operand_2.into()], "srem")
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
let result_pointer = context.build_alloca_at_entry(context.word_type(), "srem_result_pointer");
let operand_1_pointer = context.build_alloca_at_entry(context.word_type(), "operand_1_pointer");
let operand_2_pointer = context.build_alloca_at_entry(context.word_type(), "operand_2_pointer");
context.build_store(operand_1_pointer, operand_1)?;
context.build_store(operand_2_pointer, operand_2)?;
let arguments = &[
result_pointer.to_int(context).into(),
operand_1_pointer.to_int(context).into(),
operand_2_pointer.to_int(context).into(),
];
context.build_runtime_call(revive_runtime_api::polkavm_imports::SMOD, arguments);
context.build_load(result_pointer, "rsem_result")
}
@@ -204,7 +204,7 @@ where
&context.word_type().const_all_ones(),
overflow_negative_block,
),
(&context.word_const(0), overflow_block),
(&context.word_const(0), overflow_positive_block),
]);
Ok(result.as_basic_value())
}
+72 -38
View File
@@ -1,7 +1,5 @@
//! Translates the mathematical operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
@@ -15,17 +13,26 @@ pub fn add_mod<'ctx, D>(
where
D: Dependency + Clone,
{
Ok(context
.build_call(
context.llvm_runtime().add_mod,
&[
operand_1.as_basic_value_enum(),
operand_2.as_basic_value_enum(),
modulo.as_basic_value_enum(),
],
"add_mod_call",
)
.expect("Always exists"))
let result_pointer =
context.build_alloca_at_entry(context.word_type(), "addmod_result_pointer");
let operand_1_pointer = context.build_alloca_at_entry(context.word_type(), "addmod_operand_1");
let operand_2_pointer = context.build_alloca_at_entry(context.word_type(), "addmod_operand_2");
let modulo_pointer =
context.build_alloca_at_entry(context.word_type(), "addmod_modulo_operand");
context.build_store(operand_2_pointer, operand_2)?;
context.build_store(operand_1_pointer, operand_1)?;
context.build_store(modulo_pointer, modulo)?;
let arguments = &[
result_pointer.to_int(context).into(),
operand_1_pointer.to_int(context).into(),
operand_2_pointer.to_int(context).into(),
modulo_pointer.to_int(context).into(),
];
context.build_runtime_call(revive_runtime_api::polkavm_imports::ADDMOD, arguments);
context.build_load(result_pointer, "addmod_result")
}
/// Translates the `mulmod` instruction.
@@ -38,17 +45,26 @@ pub fn mul_mod<'ctx, D>(
where
D: Dependency + Clone,
{
Ok(context
.build_call(
context.llvm_runtime().mul_mod,
&[
operand_1.as_basic_value_enum(),
operand_2.as_basic_value_enum(),
modulo.as_basic_value_enum(),
],
"mul_mod_call",
)
.expect("Always exists"))
let result_pointer =
context.build_alloca_at_entry(context.word_type(), "mulmod_result_pointer");
let operand_1_pointer = context.build_alloca_at_entry(context.word_type(), "mulmod_operand_1");
let operand_2_pointer = context.build_alloca_at_entry(context.word_type(), "mulmod_operand_2");
let modulo_pointer =
context.build_alloca_at_entry(context.word_type(), "mulmod_modulo_operand");
context.build_store(operand_2_pointer, operand_2)?;
context.build_store(operand_1_pointer, operand_1)?;
context.build_store(modulo_pointer, modulo)?;
let arguments = &[
result_pointer.to_int(context).into(),
operand_1_pointer.to_int(context).into(),
operand_2_pointer.to_int(context).into(),
modulo_pointer.to_int(context).into(),
];
context.build_runtime_call(revive_runtime_api::polkavm_imports::MULMOD, arguments);
context.build_load(result_pointer, "addmod_result")
}
/// Translates the `exp` instruction.
@@ -60,13 +76,22 @@ pub fn exponent<'ctx, D>(
where
D: Dependency + Clone,
{
Ok(context
.build_call(
context.llvm_runtime().exp,
&[value.as_basic_value_enum(), exponent.as_basic_value_enum()],
"exp_call",
)
.expect("Always exists"))
let result_pointer = context.build_alloca_at_entry(context.word_type(), "exp_result_pointer");
let value_pointer = context.build_alloca_at_entry(context.word_type(), "exp_value_pointer");
let exponent_pointer =
context.build_alloca_at_entry(context.word_type(), "exp_exponent_pointer");
context.build_store(value_pointer, value)?;
context.build_store(exponent_pointer, exponent)?;
let arguments = &[
result_pointer.to_int(context).into(),
value_pointer.to_int(context).into(),
exponent_pointer.to_int(context).into(),
];
context.build_runtime_call(revive_runtime_api::polkavm_imports::EXP, arguments);
context.build_load(result_pointer, "exponent_result")
}
/// Translates the `signextend` instruction.
@@ -78,11 +103,20 @@ pub fn sign_extend<'ctx, D>(
where
D: Dependency + Clone,
{
Ok(context
.build_call(
context.llvm_runtime().sign_extend,
&[bytes.as_basic_value_enum(), value.as_basic_value_enum()],
"sign_extend_call",
)
.expect("Always exists"))
let result_pointer =
context.build_alloca_at_entry(context.word_type(), "signext_result_pointer");
let bytes_pointer = context.build_alloca_at_entry(context.word_type(), "bytes_pointer");
let value_pointer = context.build_alloca_at_entry(context.word_type(), "signext_value_pointer");
context.build_store(bytes_pointer, bytes)?;
context.build_store(value_pointer, value)?;
let arguments = &[
result_pointer.to_int(context).into(),
bytes_pointer.to_int(context).into(),
value_pointer.to_int(context).into(),
];
context.build_runtime_call(revive_runtime_api::polkavm_imports::EXP, arguments);
context.build_load(result_pointer, "signext_mod_result")
}
+1 -1
View File
@@ -37,7 +37,7 @@ pub fn build_assembly_text(
let mut disassembled_code = Vec::new();
disassembler
.disassemble_into(&mut disassembled_code)
.with_context(|| format!("Failed to disassemble contract: {}", contract_path))?;
.with_context(|| format!("Failed to disassemble contract: {contract_path}"))?;
let assembly_text = String::from_utf8(disassembled_code).with_context(|| {
format!("Failed to convert disassembled code to string for contract: {contract_path}")
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "resolc"
version = "0.2.0"
version = "0.3.0"
license.workspace = true
edition.workspace = true
repository.workspace = true
+2 -2
View File
@@ -155,8 +155,8 @@ impl Project {
.iter()
.flat_map(|(file, names)| {
names
.iter()
.map(|(name, _address)| format!("{file}:{name}"))
.keys()
.map(|name| format!("{file}:{name}"))
.collect::<HashSet<String>>()
})
.collect::<HashSet<String>>();
+1 -3
View File
@@ -240,9 +240,7 @@ fn main_inner() -> anyhow::Result<()> {
writeln!(
std::io::stdout(),
"Contract `{}` assembly:\n\n{}",
path,
assembly_text
"Contract `{path}` assembly:\n\n{assembly_text}"
)?;
}
if arguments.output_binary {
+1 -1
View File
@@ -361,7 +361,7 @@ fn compile_evm(
.expect("source should compile");
let object = &contracts
.get(contract_name)
.unwrap_or_else(|| panic!("contract '{}' didn't produce bin-runtime", contract_name));
.unwrap_or_else(|| panic!("contract '{contract_name}' didn't produce bin-runtime"));
let code = if runtime {
object.1.object.as_str()
} else {
@@ -0,0 +1,22 @@
object "Test" {
code {
function allocate(size) -> ptr {
ptr := mload(0x40)
if iszero(ptr) { ptr := 0x60 }
mstore(0x40, add(ptr, size))
}
let size := datasize("Test_deployed")
let offset := allocate(size)
datacopy(offset, dataoffset("Test_deployed"), size)
return(offset, size)
}
object "Test_deployed" {
code {
{
let test:=0x5
mstore(2,signextend(0x8,0x0))
mstore(8,lt(0xc,test))
}
return(0, 65536)
}}}
@@ -15,6 +15,11 @@ const pathToBasicYulContract = path.join(
'yul',
contractYulFilename
)
const pathToMemsetYulContract = path.join(
pathToContracts,
'yul',
'memset.yul'
)
const pathToBasicSolContract = path.join(
pathToContracts,
'solidity',
@@ -42,6 +47,7 @@ export const paths = {
pathToContracts: pathToContracts,
pathToBasicSolContract: pathToBasicSolContract,
pathToBasicYulContract: pathToBasicYulContract,
pathToMemsetYulContract: pathToMemsetYulContract,
pathToSolBinOutputFile: pathToSolBinOutputFile,
pathToSolAsmOutputFile: pathToSolAsmOutputFile,
}
@@ -0,0 +1,18 @@
import { executeCommand } from '../src/helper'
import { paths } from '../src/entities'
describe('tests for the memset builtin to be present', () => {
// -O3 is required to reproduce.
const command = `resolc ${paths.pathToMemsetYulContract} --yul -O3`
const result = executeCommand(command)
it('Valid command exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('--yul output is presented', () => {
expect(result.output).toMatch(/(Compiler run successful)/i)
expect(result.output).toMatch(/(No output requested)/i)
})
})
+2 -2
View File
@@ -121,10 +121,10 @@ fn optimizer() {
assert!(
size_when_optimized_for_cycles < size_when_unoptimized,
"Expected the cycles-optimized bytecode to be smaller than the unoptimized. Optimized: {}B, Unoptimized: {}B", size_when_optimized_for_cycles, size_when_unoptimized,
"Expected the cycles-optimized bytecode to be smaller than the unoptimized. Optimized: {size_when_optimized_for_cycles}B, Unoptimized: {size_when_unoptimized}B",
);
assert!(
size_when_optimized_for_size < size_when_unoptimized,
"Expected the size-optimized bytecode to be smaller than the unoptimized. Optimized: {}B, Unoptimized: {}B", size_when_optimized_for_size, size_when_unoptimized,
"Expected the size-optimized bytecode to be smaller than the unoptimized. Optimized: {size_when_optimized_for_size}B, Unoptimized: {size_when_unoptimized}B",
);
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "revive-runtime-api"
version.workspace = true
version = "0.2.0"
license.workspace = true
edition.workspace = true
repository.workspace = true
+2 -3
View File
@@ -37,12 +37,11 @@ fn compile(source_path: &str, bitcode_path: &str) {
source_path,
])
.output()
.unwrap_or_else(|error| panic!("failed to execute clang: {}", error));
.unwrap_or_else(|error| panic!("failed to execute clang: {error}"));
assert!(
output.status.success(),
"failed to compile the PolkaVM C API: {:?}",
output
"failed to compile the PolkaVM C API: {output:?}"
);
}
+23
View File
@@ -30,6 +30,13 @@ void * memmove(void *dst, const void *src, size_t n) {
return dst;
}
void * memset(void *b, int c, size_t len) {
uint8_t *dest = b;
uint8_t val = (uint8_t)c;
while (len-- > 0) *dest++ = val;
return b;
}
// Imports
POLKAVM_IMPORT(void, address, uint32_t)
@@ -97,3 +104,19 @@ POLKAVM_IMPORT(uint64_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t, ui
POLKAVM_IMPORT(void, value_transferred, uint32_t)
POLKAVM_IMPORT(void, weight_to_fee, uint64_t, uint64_t, uint32_t);
POLKAVM_IMPORT(void, div, uint32_t, uint32_t, uint32_t);
POLKAVM_IMPORT(void, sdiv, uint32_t, uint32_t, uint32_t);
POLKAVM_IMPORT(void, addmod, uint32_t, uint32_t, uint32_t, uint32_t);
POLKAVM_IMPORT(void, mulmod, uint32_t, uint32_t, uint32_t, uint32_t);
POLKAVM_IMPORT(void, mod, uint32_t, uint32_t, uint32_t);
POLKAVM_IMPORT(void, smod, uint32_t, uint32_t, uint32_t);
POLKAVM_IMPORT(void, exp, uint32_t, uint32_t, uint32_t);
POLKAVM_IMPORT(void, signext, uint32_t, uint32_t, uint32_t);
+19 -1
View File
@@ -68,9 +68,27 @@ pub static VALUE_TRANSFERRED: &str = "value_transferred";
pub static WEIGHT_TO_FEE: &str = "weight_to_fee";
pub static DIV: &str = "div";
pub static SDIV: &str = "sdiv";
pub static ADDMOD: &str = "addmod";
pub static MULMOD: &str = "mulmod";
pub static MOD: &str = "mod";
pub static SMOD: &str = "smod";
pub static EXP: &str = "exp";
pub static SIGNEXT: &str = "signext";
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 33] = [
pub static IMPORTS: [&str; 41] = [
DIV,
SDIV,
ADDMOD,
MULMOD,
MOD,
SMOD,
EXP,
SIGNEXT,
//
ADDRESS,
BALANCE,
BALANCE_OF,
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "revive-stdlib"
version.workspace = true
version = "0.1.1"
license.workspace = true
edition.workspace = true
repository.workspace = true
+2 -3
View File
@@ -17,12 +17,11 @@ fn main() {
"stdlib.ll",
])
.output()
.unwrap_or_else(|error| panic!("failed to execute llvm-as: {}", error));
.unwrap_or_else(|error| panic!("failed to execute llvm-as: {error}"));
assert!(
output.status.success(),
"failed to assemble the stdlib: {:?}",
output
"failed to assemble the stdlib: {output:?}"
);
let bitcode = fs::read(bitcode_path).expect("bitcode should have been built");
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "revive-yul"
description = "The revive YUL parser library."
version = "0.2.0"
version = "0.2.1"
authors.workspace = true
license.workspace = true
edition.workspace = true
@@ -167,7 +167,7 @@ impl Literal {
unicode_char.encode_utf8(&mut unicode_bytes);
for byte in unicode_bytes.into_iter() {
hex_string.push_str(format!("{:02x}", byte).as_str());
hex_string.push_str(format!("{byte:02x}").as_str());
}
index += 5;
} else if string[index..].starts_with('t') {
+8 -8
View File
@@ -204,10 +204,10 @@ where
revive_llvm_context::PolkaVMEventLogFunction::<3>.declare(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<4>.declare(context)?;
revive_llvm_context::PolkaVMDivisionFunction.declare(context)?;
revive_llvm_context::PolkaVMSignedDivisionFunction.declare(context)?;
revive_llvm_context::PolkaVMRemainderFunction.declare(context)?;
revive_llvm_context::PolkaVMSignedRemainderFunction.declare(context)?;
//revive_llvm_context::PolkaVMDivisionFunction.declare(context)?;
//revive_llvm_context::PolkaVMSignedDivisionFunction.declare(context)?;
//revive_llvm_context::PolkaVMRemainderFunction.declare(context)?;
//revive_llvm_context::PolkaVMSignedRemainderFunction.declare(context)?;
revive_llvm_context::PolkaVMSbrkFunction.declare(context)?;
@@ -258,10 +258,10 @@ where
revive_llvm_context::PolkaVMEventLogFunction::<3>.into_llvm(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<4>.into_llvm(context)?;
revive_llvm_context::PolkaVMDivisionFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMSignedDivisionFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMSignedRemainderFunction.into_llvm(context)?;
//revive_llvm_context::PolkaVMDivisionFunction.into_llvm(context)?;
//revive_llvm_context::PolkaVMSignedDivisionFunction.into_llvm(context)?;
//revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?;
//revive_llvm_context::PolkaVMSignedRemainderFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;
+36 -35
View File
@@ -1,38 +1,39 @@
{
"name": "@parity/resolc",
"license": "Apache-2.0",
"version": "0.2.0",
"author": "Parity <admin@parity.io> (https://parity.io)",
"module": "index.ts",
"types": "./dist/index.d.ts",
"main": "./dist/index.js",
"bin": {
"resolc": "./dist/bin.js"
},
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc && cp src/resolc/** dist/resolc",
"test": "npm run build && node ./dist/index.test.js"
},
"devDependencies": {
"@openzeppelin/contracts": "5.1.0",
"globals": "^15.12.0",
"typescript": "^5.6.3"
},
"dependencies": {
"@types/node": "^22.9.0",
"commander": "^13.1.0",
"package-json": "^10.0.1",
"resolve-pkg": "^2.0.0",
"solc": ">=0.8.0 <=0.8.30"
"name": "@parity/resolc",
"license": "Apache-2.0",
"version": "0.3.0",
"author": "Parity <admin@parity.io> (https://parity.io)",
"module": "index.ts",
"types": "./dist/index.d.ts",
"main": "./dist/index.js",
"bin": {
"resolc": "./dist/bin.js"
},
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc && cp src/resolc/** dist/resolc",
"test": "npm run build && node ./dist/index.test.js"
},
"devDependencies": {
"@openzeppelin/contracts": "5.1.0",
"@redstone-finance/evm-connector": "^0.8.0",
"globals": "^15.12.0",
"typescript": "^5.6.3"
},
"dependencies": {
"@types/node": "^22.9.0",
"commander": "^13.1.0",
"package-json": "^10.0.1",
"resolve-pkg": "^2.0.0",
"solc": ">=0.8.0 <=0.8.30"
}
}
+55 -10
View File
@@ -6,6 +6,7 @@ import * as os from 'os'
import * as path from 'path'
import * as resolc from '.'
import { SolcInput } from '.'
import { execSync } from 'child_process'
async function main() {
// hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end
@@ -17,11 +18,15 @@ async function main() {
const program = new commander.Command()
program.name('solcjs')
program.name('resolcjs')
program.version(resolc.version())
program
.option('--bin', 'Binary of the contracts in hex.')
.option('--abi', 'ABI of the contracts.')
.option(
'--diff-stats',
'Print statistics about Resolc vs Solc compilation.'
)
.option(
'--base-path <path>',
'Root of the project source tree. ' +
@@ -49,8 +54,20 @@ async function main() {
outputDir?: string
prettyJson: boolean
basePath?: string
diffStats: boolean
includePath?: string[]
}>()
// when using --stats option, we want to run solc as well to compare outputs size
if (options.diffStats) {
const args = process.argv.filter((arg) => !arg.startsWith('--diff-stats'))
try {
execSync(`npx --yes solc@latest ${args.slice(2).join(' ')}`)
} catch (err) {
abort(`Failed to run solc: ${err}`)
}
}
const files: string[] = program.args
const destination = options.outputDir ?? '.'
@@ -137,7 +154,7 @@ async function main() {
if (!output) {
abort('No output from compiler')
} else if (output.errors) {
} else if (output.errors && !options.diffStats) {
for (const error in output.errors) {
const message = output.errors[error]
if (message.severity === 'warning') {
@@ -160,19 +177,41 @@ async function main() {
})
}
const contractStats = []
for (const fileName in output.contracts) {
for (const contractName in output.contracts[fileName]) {
let contractFileName = fileName + ':' + contractName
contractFileName = contractFileName.replace(/[:./\\]/g, '_')
if (options.bin) {
writeFile(
contractFileName + '.polkavm',
Buffer.from(
output.contracts[fileName][contractName].evm.bytecode.object,
'hex'
)
let polkavmSize = 0
let binSize = 0
if (
options.bin &&
output.contracts?.[fileName]?.[contractName]?.evm?.bytecode?.object
) {
const pvmData = Buffer.from(
output.contracts[fileName][contractName].evm.bytecode.object,
'hex'
)
writeFile(contractFileName + '.polkavm', pvmData)
polkavmSize = pvmData.length
const binOutPath = path.join(destination, `${contractFileName}.bin`)
if (fs.existsSync(binOutPath)) {
try {
binSize = fs.statSync(binOutPath).size || 0
} catch {}
}
contractStats.push({
file: fileName,
contract: contractName,
['resolc (kB)']: Number((polkavmSize / 1024).toFixed(2)),
['solc (kB)']: Number((binSize / 1024).toFixed(2)),
['diff (%)']:
binSize > 0
? Number(((polkavmSize / binSize - 1) * 100).toFixed(2))
: Number.NaN,
})
}
if (options.abi) {
@@ -184,6 +223,12 @@ async function main() {
}
}
if (options.diffStats && contractStats.length > 0) {
console.table(
contractStats.sort((a, b) => b['resolc (kB)'] - a['resolc (kB)'])
)
}
// Put back original exception handlers.
originalUncaughtExceptionListeners.forEach(function (listener) {
process.addListener('uncaughtException', listener)
+19 -6
View File
@@ -68,6 +68,16 @@ test('resolve import', () => {
file: './fixtures/storage.sol',
expected: resolve('fixtures/storage.sol'),
},
// package with exports
{
file: '@redstone-finance/evm-connector/contracts/data-services/PrimaryProdDataServiceConsumerBase.sol',
expected: resolve(
__dirname,
'../../..',
'node_modules/@redstone-finance/evm-connector/contracts/data-services/PrimaryProdDataServiceConsumerBase.sol'
),
},
// scopped module with version
{
file: '@openzeppelin/contracts@5.1.0/token/ERC20/ERC20.sol',
@@ -100,16 +110,19 @@ test('resolve import', () => {
]
for (const { file, expected } of cases) {
let resolved
try {
const resolved = tryResolveImport(file)
assert(
resolved === expected,
`\nGot:\n${resolved}\nExpected:\n${expected}`
)
resolved = tryResolveImport(file)
} catch (error) {
assert(
String(error) == expected,
`\nGot:\n${String(error)}\nExpected:\n${expected}`
`\nExpected:\n${expected}\nGot:\n${String(error)}\n`
)
}
if (resolved) {
assert(
resolved === expected,
`\nExpected:\n${expected}\nGot:\n${resolved}`
)
}
}
+23 -7
View File
@@ -50,11 +50,7 @@ export function resolveInputs(sources: SolcInput): SolcInput {
language: 'Solidity',
sources,
settings: {
outputSelection: {
'*': {
'*': ['evm.bytecode.object'],
},
},
outputSelection: {}, // no outputs requested, only resolves imports
},
}
@@ -106,7 +102,7 @@ export async function compile(
enabled: true,
runs: 200,
},
bin,
bin = process.env.RESOLC_BIN,
} = option
const input = JSON.stringify({
@@ -129,6 +125,26 @@ export async function compile(
return resolc(input)
}
/**
* resolve the package root
* use resolve-pkg to find the package root, and fallback to using require.resolve if the package defines an exports field
* see https://github.com/sindresorhus/resolve-pkg/issues/9
**/
function resolvePkgRoot(basePackage: string) {
const packageRoot = resolvePkg(basePackage)
if (packageRoot) {
return packageRoot
}
try {
const packageExport = require.resolve(basePackage)
const endIndex = packageExport.indexOf(basePackage) + basePackage.length
return packageExport.substring(0, endIndex)
} catch {}
return undefined
}
/**
* Resolve an import path to a file path.
* @param importPath - The import path to resolve.
@@ -150,7 +166,7 @@ export function tryResolveImport(importPath: string) {
const specifiedVersion = match[2] // "1.2.3" (optional)
const relativePath = match[3] // "/path/to/file.sol"
const packageRoot = resolvePkg(basePackage)
const packageRoot = resolvePkgRoot(basePackage)
if (!packageRoot) {
throw new Error(`Package ${basePackage} not found.`)
}
+1349 -1
View File
File diff suppressed because it is too large Load Diff