mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-20 02:11:03 +00:00
Merge remote-tracking branch 'origin/main' into resolc.js
This commit is contained in:
Generated
+484
-483
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -67,7 +67,7 @@ log = { version = "0.4" }
|
|||||||
# polkadot-sdk and friends
|
# polkadot-sdk and friends
|
||||||
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
|
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
|
||||||
scale-info = { version = "2.11.1", default-features = false }
|
scale-info = { version = "2.11.1", default-features = false }
|
||||||
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "a77940bac783108fcae783c553528c8d5328e5b2" }
|
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "0449b214accd0f0fbf7ea3e8f3a8d8b7f99445e4" }
|
||||||
|
|
||||||
# llvm
|
# llvm
|
||||||
[workspace.dependencies.inkwell]
|
[workspace.dependencies.inkwell]
|
||||||
|
|||||||
@@ -41,3 +41,6 @@ pub static EXTENSION_POLKAVM_ASSEMBLY: &str = "pvmasm";
|
|||||||
|
|
||||||
/// The PolkaVM bytecode file extension.
|
/// The PolkaVM bytecode file extension.
|
||||||
pub static EXTENSION_POLKAVM_BINARY: &str = "pvm";
|
pub static EXTENSION_POLKAVM_BINARY: &str = "pvm";
|
||||||
|
|
||||||
|
/// The ELF shared object file extension.
|
||||||
|
pub static EXTENSION_SHARED_OBJECT: &str = "so";
|
||||||
|
|||||||
@@ -557,6 +557,7 @@ allocated bytes: 3711"#;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore] // https://github.com/ethereum/go-ethereum/issues/30778
|
||||||
fn bench_flipper() {
|
fn bench_flipper() {
|
||||||
let log_runtime = Evm::default()
|
let log_runtime = Evm::default()
|
||||||
.code_blob(EVM_BIN_RUNTIME_FIXTURE.as_bytes().to_vec())
|
.code_blob(EVM_BIN_RUNTIME_FIXTURE.as_bytes().to_vec())
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": true,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Upload": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Logic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Tester"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Call": {
|
||||||
|
"dest": {
|
||||||
|
"Instantiated": 0
|
||||||
|
},
|
||||||
|
"value": 123,
|
||||||
|
"data": "6466414b0000000000000000000000000000000000000000000000000000000000000020"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract Logic {
|
||||||
|
// NOTE: storage layout must be the same as contract Tester
|
||||||
|
uint256 public num;
|
||||||
|
address public sender;
|
||||||
|
uint256 public value;
|
||||||
|
|
||||||
|
uint public immutable multiplier = 4;
|
||||||
|
|
||||||
|
event DidSetVars();
|
||||||
|
|
||||||
|
function setVars(uint256 _num) public payable returns (uint256) {
|
||||||
|
num = _num * multiplier;
|
||||||
|
sender = msg.sender;
|
||||||
|
value = msg.value;
|
||||||
|
emit DidSetVars();
|
||||||
|
return _num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Tester {
|
||||||
|
uint256 public num;
|
||||||
|
address public sender;
|
||||||
|
uint256 public value;
|
||||||
|
|
||||||
|
uint public immutable multiplier = 2;
|
||||||
|
|
||||||
|
function setVars(uint256 _num) public payable returns (bool, bytes memory) {
|
||||||
|
Logic impl = new Logic();
|
||||||
|
|
||||||
|
// Tester's storage is set, Logic is not modified.
|
||||||
|
(bool success, bytes memory data) = address(impl).delegatecall(
|
||||||
|
abi.encodeWithSignature("setVars(uint256)", _num)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(success);
|
||||||
|
assert(impl.num() == 0);
|
||||||
|
assert(impl.sender() == address(0));
|
||||||
|
assert(impl.value() == 0);
|
||||||
|
assert(num == _num * 4);
|
||||||
|
assert(sender == msg.sender);
|
||||||
|
assert(value == msg.value);
|
||||||
|
|
||||||
|
return (success, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ test_spec!(return_data_oob, "ReturnDataOob", "ReturnDataOob.sol");
|
|||||||
test_spec!(immutables, "Immutables", "Immutables.sol");
|
test_spec!(immutables, "Immutables", "Immutables.sol");
|
||||||
test_spec!(transaction, "Transaction", "Transaction.sol");
|
test_spec!(transaction, "Transaction", "Transaction.sol");
|
||||||
test_spec!(block_hash, "BlockHash", "BlockHash.sol");
|
test_spec!(block_hash, "BlockHash", "BlockHash.sol");
|
||||||
|
test_spec!(delegate, "Delegate", "Delegate.sol");
|
||||||
|
|
||||||
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
|
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
|
||||||
vec![Instantiate {
|
vec![Instantiate {
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ fn invoke_lld(cmd_args: &[&str]) -> bool {
|
|||||||
unsafe { LLDELFLink(args.as_ptr(), args.len()) == 0 }
|
unsafe { LLDELFLink(args.as_ptr(), args.len()) == 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn polkavm_linker<T: AsRef<[u8]>>(code: T) -> anyhow::Result<Vec<u8>> {
|
pub fn polkavm_linker<T: AsRef<[u8]>>(code: T, strip_binary: bool) -> anyhow::Result<Vec<u8>> {
|
||||||
let mut config = polkavm_linker::Config::default();
|
let mut config = polkavm_linker::Config::default();
|
||||||
config.set_strip(true);
|
config.set_strip(strip_binary);
|
||||||
config.set_optimize(true);
|
config.set_optimize(true);
|
||||||
|
|
||||||
polkavm_linker::program_from_elf(config, code.as_ref())
|
polkavm_linker::program_from_elf(config, code.as_ref())
|
||||||
@@ -79,10 +79,5 @@ pub fn link<T: AsRef<[u8]>>(input: T) -> anyhow::Result<Vec<u8>> {
|
|||||||
return Err(anyhow::anyhow!("ld.lld failed"));
|
return Err(anyhow::anyhow!("ld.lld failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if env::var("PVM_LINKER_DUMP_SO").is_ok() {
|
Ok(fs::read(&output_path)?)
|
||||||
fs::copy(&output_path, "/tmp/out.so")?;
|
|
||||||
};
|
|
||||||
|
|
||||||
let blob = fs::read(&output_path)?;
|
|
||||||
polkavm_linker(blob)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ pub enum IRType {
|
|||||||
LLVM,
|
LLVM,
|
||||||
/// Whether to dump the assembly code.
|
/// Whether to dump the assembly code.
|
||||||
Assembly,
|
Assembly,
|
||||||
|
/// Whether to dump the ELF shared object
|
||||||
|
SO,
|
||||||
/// Whether to jump JSON
|
/// Whether to jump JSON
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
JSON,
|
JSON,
|
||||||
@@ -31,6 +33,7 @@ impl IRType {
|
|||||||
Self::Assembly => revive_common::EXTENSION_POLKAVM_ASSEMBLY,
|
Self::Assembly => revive_common::EXTENSION_POLKAVM_ASSEMBLY,
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
Self::JSON => revive_common::EXTENSION_JSON,
|
Self::JSON => revive_common::EXTENSION_JSON,
|
||||||
|
Self::SO => revive_common::EXTENSION_SHARED_OBJECT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,41 +13,52 @@ use self::ir_type::IRType;
|
|||||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
pub struct DebugConfig {
|
pub struct DebugConfig {
|
||||||
/// The directory to dump the IRs to.
|
/// The directory to dump the IRs to.
|
||||||
pub output_directory: PathBuf,
|
pub output_directory: Option<PathBuf>,
|
||||||
|
/// Whether debug info should be emitted.
|
||||||
|
pub emit_debug_info: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugConfig {
|
impl DebugConfig {
|
||||||
/// A shortcut constructor.
|
/// A shortcut constructor.
|
||||||
pub fn new(output_directory: PathBuf) -> Self {
|
pub const fn new(output_directory: Option<PathBuf>, emit_debug_info: bool) -> Self {
|
||||||
Self { output_directory }
|
Self {
|
||||||
|
output_directory,
|
||||||
|
emit_debug_info,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dumps the Yul IR.
|
/// Dumps the Yul IR.
|
||||||
pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
||||||
let mut file_path = self.output_directory.to_owned();
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
|
let mut file_path = output_directory.to_owned();
|
||||||
file_path.push(full_file_name);
|
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
|
||||||
std::fs::write(file_path, code)?;
|
file_path.push(full_file_name);
|
||||||
|
std::fs::write(file_path, code)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dumps the EVM legacy assembly IR.
|
/// Dumps the EVM legacy assembly IR.
|
||||||
pub fn dump_evmla(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
pub fn dump_evmla(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
||||||
let mut file_path = self.output_directory.to_owned();
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
let full_file_name = Self::full_file_name(contract_path, None, IRType::EVMLA);
|
let mut file_path = output_directory.to_owned();
|
||||||
file_path.push(full_file_name);
|
let full_file_name = Self::full_file_name(contract_path, None, IRType::EVMLA);
|
||||||
std::fs::write(file_path, code)?;
|
file_path.push(full_file_name);
|
||||||
|
std::fs::write(file_path, code)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dumps the Ethereal IR.
|
/// Dumps the Ethereal IR.
|
||||||
pub fn dump_ethir(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
pub fn dump_ethir(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
||||||
let mut file_path = self.output_directory.to_owned();
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
let full_file_name = Self::full_file_name(contract_path, None, IRType::EthIR);
|
let mut file_path = output_directory.to_owned();
|
||||||
file_path.push(full_file_name);
|
let full_file_name = Self::full_file_name(contract_path, None, IRType::EthIR);
|
||||||
std::fs::write(file_path, code)?;
|
file_path.push(full_file_name);
|
||||||
|
std::fs::write(file_path, code)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -58,12 +69,15 @@ impl DebugConfig {
|
|||||||
contract_path: &str,
|
contract_path: &str,
|
||||||
module: &inkwell::module::Module,
|
module: &inkwell::module::Module,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let llvm_code = module.print_to_string().to_string();
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
|
let llvm_code = module.print_to_string().to_string();
|
||||||
|
|
||||||
let mut file_path = self.output_directory.to_owned();
|
let mut file_path = output_directory.to_owned();
|
||||||
let full_file_name = Self::full_file_name(contract_path, Some("unoptimized"), IRType::LLVM);
|
let full_file_name =
|
||||||
file_path.push(full_file_name);
|
Self::full_file_name(contract_path, Some("unoptimized"), IRType::LLVM);
|
||||||
std::fs::write(file_path, llvm_code)?;
|
file_path.push(full_file_name);
|
||||||
|
std::fs::write(file_path, llvm_code)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -74,22 +88,39 @@ impl DebugConfig {
|
|||||||
contract_path: &str,
|
contract_path: &str,
|
||||||
module: &inkwell::module::Module,
|
module: &inkwell::module::Module,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let llvm_code = module.print_to_string().to_string();
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
|
let llvm_code = module.print_to_string().to_string();
|
||||||
|
|
||||||
let mut file_path = self.output_directory.to_owned();
|
let mut file_path = output_directory.to_owned();
|
||||||
let full_file_name = Self::full_file_name(contract_path, Some("optimized"), IRType::LLVM);
|
let full_file_name =
|
||||||
file_path.push(full_file_name);
|
Self::full_file_name(contract_path, Some("optimized"), IRType::LLVM);
|
||||||
std::fs::write(file_path, llvm_code)?;
|
file_path.push(full_file_name);
|
||||||
|
std::fs::write(file_path, llvm_code)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dumps the assembly.
|
/// Dumps the assembly.
|
||||||
pub fn dump_assembly(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
pub fn dump_assembly(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
||||||
let mut file_path = self.output_directory.to_owned();
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
let full_file_name = Self::full_file_name(contract_path, None, IRType::Assembly);
|
let mut file_path = output_directory.to_owned();
|
||||||
file_path.push(full_file_name);
|
let full_file_name = Self::full_file_name(contract_path, None, IRType::Assembly);
|
||||||
std::fs::write(file_path, code)?;
|
file_path.push(full_file_name);
|
||||||
|
std::fs::write(file_path, code)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dumps the code object.
|
||||||
|
pub fn dump_object(&self, contract_path: &str, code: &[u8]) -> anyhow::Result<()> {
|
||||||
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
|
let mut file_path = output_directory.to_owned();
|
||||||
|
let full_file_name = Self::full_file_name(contract_path, None, IRType::SO);
|
||||||
|
file_path.push(full_file_name);
|
||||||
|
std::fs::write(file_path, code)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -102,10 +133,12 @@ impl DebugConfig {
|
|||||||
contract_suffix: Option<&str>,
|
contract_suffix: Option<&str>,
|
||||||
stage_json: &Vec<u8>,
|
stage_json: &Vec<u8>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let mut file_path = self.output_directory.to_owned();
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
let full_file_name = Self::full_file_name(contract_path, contract_suffix, IRType::JSON);
|
let mut file_path = output_directory.to_owned();
|
||||||
file_path.push(full_file_name);
|
let full_file_name = Self::full_file_name(contract_path, contract_suffix, IRType::JSON);
|
||||||
std::fs::write(file_path, stage_json)?;
|
file_path.push(full_file_name);
|
||||||
|
std::fs::write(file_path, stage_json)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub use self::polkavm::context::argument::Argument as PolkaVMArgument;
|
|||||||
pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute;
|
pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute;
|
||||||
pub use self::polkavm::context::build::Build as PolkaVMBuild;
|
pub use self::polkavm::context::build::Build as PolkaVMBuild;
|
||||||
pub use self::polkavm::context::code_type::CodeType as PolkaVMCodeType;
|
pub use self::polkavm::context::code_type::CodeType as PolkaVMCodeType;
|
||||||
|
pub use self::polkavm::context::debug_info::DebugInfo;
|
||||||
pub use self::polkavm::context::evmla_data::EVMLAData as PolkaVMContextEVMLAData;
|
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::key::Key as PolkaVMFunctionBlockKey;
|
||||||
pub use self::polkavm::context::function::block::evmla_data::EVMLAData as PolkaVMFunctionBlockEVMLAData;
|
pub use self::polkavm::context::function::block::evmla_data::EVMLAData as PolkaVMFunctionBlockEVMLAData;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use itertools::Itertools;
|
|||||||
|
|
||||||
use self::size_level::SizeLevel;
|
use self::size_level::SizeLevel;
|
||||||
|
|
||||||
/// The LLVM optimizer settings.
|
/// The LLVM optimizer and code-gen settings.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
/// The middle-end optimization level.
|
/// The middle-end optimization level.
|
||||||
|
|||||||
@@ -1,7 +1,43 @@
|
|||||||
//! The LLVM debug information.
|
//! The LLVM debug information.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use inkwell::debug_info::AsDIScope;
|
use inkwell::debug_info::AsDIScope;
|
||||||
use num::Zero;
|
use inkwell::debug_info::DIScope;
|
||||||
|
|
||||||
|
/// Debug info scope stack
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ScopeStack<'ctx> {
|
||||||
|
stack: Vec<DIScope<'ctx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract the type of the DIScope stack.
|
||||||
|
impl<'ctx> ScopeStack<'ctx> {
|
||||||
|
pub fn from(item: DIScope<'ctx>) -> Self {
|
||||||
|
Self { stack: vec![item] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the top of the scope stack, or None if the stack is empty.
|
||||||
|
pub fn top(&self) -> Option<DIScope<'ctx>> {
|
||||||
|
self.stack.last().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a scope onto the stack.
|
||||||
|
pub fn push(&mut self, scope: DIScope<'ctx>) {
|
||||||
|
self.stack.push(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pop the scope at the top of the stack and return it.
|
||||||
|
/// Return None if the stack is empty.
|
||||||
|
pub fn pop(&mut self) -> Option<DIScope<'ctx>> {
|
||||||
|
self.stack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of scopes on the stack.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.stack.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The LLVM debug information.
|
/// The LLVM debug information.
|
||||||
pub struct DebugInfo<'ctx> {
|
pub struct DebugInfo<'ctx> {
|
||||||
@@ -9,6 +45,8 @@ pub struct DebugInfo<'ctx> {
|
|||||||
compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
|
compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
|
||||||
/// The debug info builder.
|
/// The debug info builder.
|
||||||
builder: inkwell::debug_info::DebugInfoBuilder<'ctx>,
|
builder: inkwell::debug_info::DebugInfoBuilder<'ctx>,
|
||||||
|
/// Enclosing debug info scopes.
|
||||||
|
scope_stack: RefCell<ScopeStack<'ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> DebugInfo<'ctx> {
|
impl<'ctx> DebugInfo<'ctx> {
|
||||||
@@ -35,19 +73,43 @@ impl<'ctx> DebugInfo<'ctx> {
|
|||||||
Self {
|
Self {
|
||||||
compile_unit,
|
compile_unit,
|
||||||
builder,
|
builder,
|
||||||
|
scope_stack: RefCell::new(ScopeStack::from(compile_unit.as_debug_info_scope())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepare an LLVM-IR module for debug-info generation
|
||||||
|
pub fn initialize_module(
|
||||||
|
&self,
|
||||||
|
llvm: &'ctx inkwell::context::Context,
|
||||||
|
module: &inkwell::module::Module<'ctx>,
|
||||||
|
) {
|
||||||
|
let debug_metadata_value = llvm
|
||||||
|
.i32_type()
|
||||||
|
.const_int(inkwell::debug_info::debug_metadata_version() as u64, false);
|
||||||
|
module.add_basic_value_flag(
|
||||||
|
"Debug Info Version",
|
||||||
|
inkwell::module::FlagBehavior::Warning,
|
||||||
|
debug_metadata_value,
|
||||||
|
);
|
||||||
|
self.push_scope(self.compilation_unit().get_file().as_debug_info_scope());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finalize debug-info for an LLVM-IR module.
|
||||||
|
pub fn finalize_module(&self) {
|
||||||
|
self.builder().finalize()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a function info.
|
/// Creates a function info.
|
||||||
pub fn create_function(
|
pub fn create_function(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> anyhow::Result<inkwell::debug_info::DISubprogram<'ctx>> {
|
) -> anyhow::Result<inkwell::debug_info::DISubprogram<'ctx>> {
|
||||||
|
let flags = inkwell::debug_info::DIFlagsConstants::ZERO;
|
||||||
let subroutine_type = self.builder.create_subroutine_type(
|
let subroutine_type = self.builder.create_subroutine_type(
|
||||||
self.compile_unit.get_file(),
|
self.compile_unit.get_file(),
|
||||||
Some(self.create_type(revive_common::BIT_LENGTH_FIELD)?),
|
Some(self.create_word_type(Some(flags))?.as_type()),
|
||||||
&[],
|
&[],
|
||||||
inkwell::debug_info::DIFlags::zero(),
|
flags,
|
||||||
);
|
);
|
||||||
|
|
||||||
let function = self.builder.create_function(
|
let function = self.builder.create_function(
|
||||||
@@ -60,7 +122,7 @@ impl<'ctx> DebugInfo<'ctx> {
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
1,
|
1,
|
||||||
inkwell::debug_info::DIFlags::zero(),
|
flags,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -74,24 +136,55 @@ impl<'ctx> DebugInfo<'ctx> {
|
|||||||
Ok(function)
|
Ok(function)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a primitive type info.
|
/// Creates primitive integer type debug-info.
|
||||||
pub fn create_type(
|
pub fn create_primitive_type(
|
||||||
&self,
|
&self,
|
||||||
bit_length: usize,
|
bit_length: usize,
|
||||||
) -> anyhow::Result<inkwell::debug_info::DIType<'ctx>> {
|
flags: Option<inkwell::debug_info::DIFlags>,
|
||||||
|
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
|
||||||
|
let di_flags = flags.unwrap_or(inkwell::debug_info::DIFlagsConstants::ZERO);
|
||||||
|
let di_encoding: u32 = 0;
|
||||||
|
let type_name = String::from("U") + bit_length.to_string().as_str();
|
||||||
self.builder
|
self.builder
|
||||||
.create_basic_type(
|
.create_basic_type(type_name.as_str(), bit_length as u64, di_encoding, di_flags)
|
||||||
"U256",
|
|
||||||
bit_length as u64,
|
|
||||||
0,
|
|
||||||
inkwell::debug_info::DIFlags::zero(),
|
|
||||||
)
|
|
||||||
.map(|basic_type| basic_type.as_type())
|
|
||||||
.map_err(|error| anyhow::anyhow!("Debug info error: {}", error))
|
.map_err(|error| anyhow::anyhow!("Debug info error: {}", error))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalizes the builder.
|
/// Returns the debug-info model of word-sized integer types.
|
||||||
pub fn finalize(&self) {
|
pub fn create_word_type(
|
||||||
self.builder.finalize();
|
&self,
|
||||||
|
flags: Option<inkwell::debug_info::DIFlags>,
|
||||||
|
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
|
||||||
|
self.create_primitive_type(revive_common::BIT_LENGTH_WORD, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the DIBuilder.
|
||||||
|
pub fn builder(&self) -> &inkwell::debug_info::DebugInfoBuilder<'ctx> {
|
||||||
|
&self.builder
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the compilation unit. {
|
||||||
|
pub fn compilation_unit(&self) -> &inkwell::debug_info::DICompileUnit<'ctx> {
|
||||||
|
&self.compile_unit
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a debug-info scope onto the stack.
|
||||||
|
pub fn push_scope(&self, scope: DIScope<'ctx>) {
|
||||||
|
self.scope_stack.borrow_mut().push(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pop the top of the debug-info scope stack and return it.
|
||||||
|
pub fn pop_scope(&self) -> Option<DIScope<'ctx>> {
|
||||||
|
self.scope_stack.borrow_mut().pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the top of the debug-info scope stack.
|
||||||
|
pub fn top_scope(&self) -> Option<DIScope<'ctx>> {
|
||||||
|
self.scope_stack.borrow().top()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of debug-info scopes on the scope stack.
|
||||||
|
pub fn num_scopes(&self) -> usize {
|
||||||
|
self.scope_stack.borrow().len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,4 +17,8 @@ impl<'ctx> Declaration<'ctx> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self { r#type, value }
|
Self { r#type, value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn function_value(&self) -> inkwell::values::FunctionValue<'ctx> {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ pub mod yul_data;
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use inkwell::debug_info::AsDIScope;
|
||||||
|
|
||||||
use crate::optimizer::settings::size_level::SizeLevel;
|
use crate::optimizer::settings::size_level::SizeLevel;
|
||||||
use crate::optimizer::Optimizer;
|
use crate::optimizer::Optimizer;
|
||||||
use crate::polkavm::context::attribute::Attribute;
|
use crate::polkavm::context::attribute::Attribute;
|
||||||
@@ -94,6 +96,14 @@ impl<'ctx> Function<'ctx> {
|
|||||||
self.declaration
|
self.declaration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the debug-info scope.
|
||||||
|
pub fn get_debug_scope(&self) -> Option<inkwell::debug_info::DIScope<'ctx>> {
|
||||||
|
self.declaration()
|
||||||
|
.function_value()
|
||||||
|
.get_subprogram()
|
||||||
|
.map(|scp| scp.as_debug_info_scope())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the N-th parameter of the function.
|
/// Returns the N-th parameter of the function.
|
||||||
pub fn get_nth_param(&self, index: usize) -> inkwell::values::BasicValueEnum<'ctx> {
|
pub fn get_nth_param(&self, index: usize) -> inkwell::values::BasicValueEnum<'ctx> {
|
||||||
self.declaration()
|
self.declaration()
|
||||||
|
|||||||
@@ -54,12 +54,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
context.set_current_function(runtime::FUNCTION_DEPLOY_CODE)?;
|
context.set_current_function(runtime::FUNCTION_DEPLOY_CODE, None)?;
|
||||||
|
|
||||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||||
context.set_code_type(CodeType::Deploy);
|
context.set_code_type(CodeType::Deploy);
|
||||||
|
|
||||||
self.inner.into_llvm(context)?;
|
self.inner.into_llvm(context)?;
|
||||||
|
context.set_debug_location(0, 0, None)?;
|
||||||
|
|
||||||
match context
|
match context
|
||||||
.basic_block()
|
.basic_block()
|
||||||
.get_last_instruction()
|
.get_last_instruction()
|
||||||
@@ -72,8 +74,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.set_basic_block(context.current_function().borrow().return_block());
|
context.set_basic_block(context.current_function().borrow().return_block());
|
||||||
|
context.set_debug_location(0, 0, None)?;
|
||||||
context.build_return(None);
|
context.build_return(None);
|
||||||
|
|
||||||
|
context.pop_debug_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ impl Entry {
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
context.set_debug_location(0, 0, None)?;
|
||||||
|
|
||||||
let is_deploy = context
|
let is_deploy = context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow()
|
.borrow()
|
||||||
@@ -214,7 +216,7 @@ where
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
context.set_current_function(runtime::FUNCTION_ENTRY)?;
|
context.set_current_function(runtime::FUNCTION_ENTRY, None)?;
|
||||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||||
|
|
||||||
Self::initialize_globals(context)?;
|
Self::initialize_globals(context)?;
|
||||||
@@ -225,6 +227,8 @@ where
|
|||||||
context.set_basic_block(context.current_function().borrow().return_block());
|
context.set_basic_block(context.current_function().borrow().return_block());
|
||||||
context.build_unreachable();
|
context.build_unreachable();
|
||||||
|
|
||||||
|
context.pop_debug_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
context.set_current_function(runtime::FUNCTION_LOAD_IMMUTABLE_DATA)?;
|
context.set_current_function(runtime::FUNCTION_LOAD_IMMUTABLE_DATA, None)?;
|
||||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||||
|
|
||||||
let immutable_data_size_pointer = context
|
let immutable_data_size_pointer = context
|
||||||
@@ -111,6 +111,8 @@ where
|
|||||||
context.set_basic_block(return_block);
|
context.set_basic_block(return_block);
|
||||||
context.build_return(None);
|
context.build_return(None);
|
||||||
|
|
||||||
|
context.pop_debug_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,11 +54,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
context.set_current_function(runtime::FUNCTION_RUNTIME_CODE)?;
|
context.set_current_function(runtime::FUNCTION_RUNTIME_CODE, None)?;
|
||||||
|
|
||||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||||
context.set_code_type(CodeType::Runtime);
|
context.set_code_type(CodeType::Runtime);
|
||||||
|
|
||||||
self.inner.into_llvm(context)?;
|
self.inner.into_llvm(context)?;
|
||||||
|
context.set_debug_location(0, 0, None)?;
|
||||||
|
|
||||||
match context
|
match context
|
||||||
.basic_block()
|
.basic_block()
|
||||||
.get_last_instruction()
|
.get_last_instruction()
|
||||||
@@ -73,6 +76,8 @@ where
|
|||||||
context.set_basic_block(context.current_function().borrow().return_block());
|
context.set_basic_block(context.current_function().borrow().return_block());
|
||||||
context.build_unreachable();
|
context.build_unreachable();
|
||||||
|
|
||||||
|
context.pop_debug_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ pub mod argument;
|
|||||||
pub mod attribute;
|
pub mod attribute;
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub mod code_type;
|
pub mod code_type;
|
||||||
// pub mod debug_info;
|
pub mod debug_info;
|
||||||
pub mod evmla_data;
|
pub mod evmla_data;
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod global;
|
pub mod global;
|
||||||
@@ -21,6 +21,8 @@ use std::cell::RefCell;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use inkwell::debug_info::AsDIScope;
|
||||||
|
use inkwell::debug_info::DIScope;
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
use inkwell::values::BasicValue;
|
use inkwell::values::BasicValue;
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ use self::address_space::AddressSpace;
|
|||||||
use self::attribute::Attribute;
|
use self::attribute::Attribute;
|
||||||
use self::build::Build;
|
use self::build::Build;
|
||||||
use self::code_type::CodeType;
|
use self::code_type::CodeType;
|
||||||
// use self::debug_info::DebugInfo;
|
use self::debug_info::DebugInfo;
|
||||||
use self::evmla_data::EVMLAData;
|
use self::evmla_data::EVMLAData;
|
||||||
use self::function::declaration::Declaration as FunctionDeclaration;
|
use self::function::declaration::Declaration as FunctionDeclaration;
|
||||||
use self::function::intrinsics::Intrinsics;
|
use self::function::intrinsics::Intrinsics;
|
||||||
@@ -85,9 +87,9 @@ where
|
|||||||
/// Whether to append the metadata hash at the end of bytecode.
|
/// Whether to append the metadata hash at the end of bytecode.
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
/// The debug info of the current module.
|
/// The debug info of the current module.
|
||||||
// debug_info: DebugInfo<'ctx>,
|
debug_info: Option<DebugInfo<'ctx>>,
|
||||||
/// The debug configuration telling whether to dump the needed IRs.
|
/// The debug configuration telling whether to dump the needed IRs.
|
||||||
debug_config: Option<DebugConfig>,
|
debug_config: DebugConfig,
|
||||||
|
|
||||||
/// The Solidity data.
|
/// The Solidity data.
|
||||||
solidity_data: Option<SolidityData>,
|
solidity_data: Option<SolidityData>,
|
||||||
@@ -207,7 +209,7 @@ where
|
|||||||
optimizer: Optimizer,
|
optimizer: Optimizer,
|
||||||
dependency_manager: Option<D>,
|
dependency_manager: Option<D>,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
debug_config: Option<DebugConfig>,
|
debug_config: DebugConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::link_stdlib_module(llvm, &module);
|
Self::link_stdlib_module(llvm, &module);
|
||||||
Self::link_polkavm_imports(llvm, &module);
|
Self::link_polkavm_imports(llvm, &module);
|
||||||
@@ -216,6 +218,11 @@ where
|
|||||||
|
|
||||||
let intrinsics = Intrinsics::new(llvm, &module);
|
let intrinsics = Intrinsics::new(llvm, &module);
|
||||||
let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer);
|
let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer);
|
||||||
|
let debug_info = debug_config.emit_debug_info.then(|| {
|
||||||
|
let debug_info = DebugInfo::new(&module);
|
||||||
|
debug_info.initialize_module(llvm, &module);
|
||||||
|
debug_info
|
||||||
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
llvm,
|
llvm,
|
||||||
@@ -232,7 +239,8 @@ where
|
|||||||
|
|
||||||
dependency_manager,
|
dependency_manager,
|
||||||
include_metadata_hash,
|
include_metadata_hash,
|
||||||
// debug_info,
|
|
||||||
|
debug_info,
|
||||||
debug_config,
|
debug_config,
|
||||||
|
|
||||||
solidity_data: None,
|
solidity_data: None,
|
||||||
@@ -255,9 +263,9 @@ where
|
|||||||
let target_machine = TargetMachine::new(Target::PVM, self.optimizer.settings())?;
|
let target_machine = TargetMachine::new(Target::PVM, self.optimizer.settings())?;
|
||||||
target_machine.set_target_data(self.module());
|
target_machine.set_target_data(self.module());
|
||||||
|
|
||||||
if let Some(ref debug_config) = self.debug_config {
|
self.debug_config
|
||||||
debug_config.dump_llvm_ir_unoptimized(contract_path, self.module())?;
|
.dump_llvm_ir_unoptimized(contract_path, self.module())?;
|
||||||
}
|
|
||||||
self.verify().map_err(|error| {
|
self.verify().map_err(|error| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
"The contract `{}` unoptimized LLVM IR verification error: {}",
|
"The contract `{}` unoptimized LLVM IR verification error: {}",
|
||||||
@@ -275,9 +283,10 @@ where
|
|||||||
error
|
error
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
if let Some(ref debug_config) = self.debug_config {
|
|
||||||
debug_config.dump_llvm_ir_optimized(contract_path, self.module())?;
|
self.debug_config
|
||||||
}
|
.dump_llvm_ir_optimized(contract_path, self.module())?;
|
||||||
|
|
||||||
self.verify().map_err(|error| {
|
self.verify().map_err(|error| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
"The contract `{}` optimized LLVM IR verification error: {}",
|
"The contract `{}` optimized LLVM IR verification error: {}",
|
||||||
@@ -296,11 +305,17 @@ where
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let bytecode = revive_linker::link(buffer.as_slice())?;
|
let shared_object = revive_linker::link(buffer.as_slice())?;
|
||||||
|
|
||||||
|
self.debug_config
|
||||||
|
.dump_object(contract_path, &shared_object)?;
|
||||||
|
|
||||||
|
let polkavm_bytecode =
|
||||||
|
revive_linker::polkavm_linker(shared_object, !self.debug_config().emit_debug_info)?;
|
||||||
|
|
||||||
let build = match crate::polkavm::build_assembly_text(
|
let build = match crate::polkavm::build_assembly_text(
|
||||||
contract_path,
|
contract_path,
|
||||||
&bytecode,
|
&polkavm_bytecode,
|
||||||
metadata_hash,
|
metadata_hash,
|
||||||
self.debug_config(),
|
self.debug_config(),
|
||||||
) {
|
) {
|
||||||
@@ -422,6 +437,21 @@ where
|
|||||||
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
|
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
|
||||||
let value = self.module().add_function(name, r#type, linkage);
|
let value = self.module().add_function(name, r#type, linkage);
|
||||||
|
|
||||||
|
if self.debug_info().is_some() {
|
||||||
|
self.builder().unset_current_debug_location();
|
||||||
|
let func_scope = match value.get_subprogram() {
|
||||||
|
None => {
|
||||||
|
let fn_name = value.get_name().to_str()?;
|
||||||
|
let scp = self.build_function_debug_info(fn_name, 0)?;
|
||||||
|
value.set_subprogram(scp);
|
||||||
|
scp
|
||||||
|
}
|
||||||
|
Some(scp) => scp,
|
||||||
|
};
|
||||||
|
self.push_debug_scope(func_scope.as_debug_info_scope());
|
||||||
|
self.set_debug_location(0, 0, Some(func_scope.as_debug_info_scope()))?;
|
||||||
|
}
|
||||||
|
|
||||||
let entry_block = self.llvm.append_basic_block(value, "entry");
|
let entry_block = self.llvm.append_basic_block(value, "entry");
|
||||||
let return_block = self.llvm.append_basic_block(value, "return");
|
let return_block = self.llvm.append_basic_block(value, "return");
|
||||||
|
|
||||||
@@ -455,6 +485,8 @@ where
|
|||||||
let function = Rc::new(RefCell::new(function));
|
let function = Rc::new(RefCell::new(function));
|
||||||
self.functions.insert(name.to_string(), function.clone());
|
self.functions.insert(name.to_string(), function.clone());
|
||||||
|
|
||||||
|
self.pop_debug_scope();
|
||||||
|
|
||||||
Ok(function)
|
Ok(function)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,15 +502,95 @@ where
|
|||||||
.expect("Must be declared before use")
|
.expect("Must be declared before use")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the current active function.
|
/// Sets the current active function. If debug-info generation is enabled,
|
||||||
pub fn set_current_function(&mut self, name: &str) -> anyhow::Result<()> {
|
/// constructs a debug-scope and pushes in on the scope-stack.
|
||||||
|
pub fn set_current_function(&mut self, name: &str, line: Option<u32>) -> anyhow::Result<()> {
|
||||||
let function = self.functions.get(name).cloned().ok_or_else(|| {
|
let function = self.functions.get(name).cloned().ok_or_else(|| {
|
||||||
anyhow::anyhow!("Failed to activate an undeclared function `{}`", name)
|
anyhow::anyhow!("Failed to activate an undeclared function `{}`", name)
|
||||||
})?;
|
})?;
|
||||||
self.current_function = Some(function);
|
self.current_function = Some(function);
|
||||||
|
|
||||||
|
if let Some(scope) = self.current_function().borrow().get_debug_scope() {
|
||||||
|
self.push_debug_scope(scope);
|
||||||
|
}
|
||||||
|
self.set_debug_location(line.unwrap_or_default(), 0, None)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a debug-info scope for a function.
|
||||||
|
pub fn build_function_debug_info(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
line_no: u32,
|
||||||
|
) -> anyhow::Result<inkwell::debug_info::DISubprogram<'ctx>> {
|
||||||
|
let Some(debug_info) = self.debug_info() else {
|
||||||
|
anyhow::bail!("expected debug-info builders");
|
||||||
|
};
|
||||||
|
let builder = debug_info.builder();
|
||||||
|
let file = debug_info.compilation_unit().get_file();
|
||||||
|
let scope = file.as_debug_info_scope();
|
||||||
|
let flags = inkwell::debug_info::DIFlagsConstants::PUBLIC;
|
||||||
|
let return_type = debug_info.create_word_type(Some(flags))?.as_type();
|
||||||
|
let subroutine_type = builder.create_subroutine_type(file, Some(return_type), &[], flags);
|
||||||
|
|
||||||
|
Ok(builder.create_function(
|
||||||
|
scope,
|
||||||
|
name,
|
||||||
|
None,
|
||||||
|
file,
|
||||||
|
line_no,
|
||||||
|
subroutine_type,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
1,
|
||||||
|
flags,
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the debug info location.
|
||||||
|
///
|
||||||
|
/// No-op if the emitting debug info is disabled.
|
||||||
|
///
|
||||||
|
/// If `scope` is `None` the top scope will be used.
|
||||||
|
pub fn set_debug_location(
|
||||||
|
&self,
|
||||||
|
line: u32,
|
||||||
|
column: u32,
|
||||||
|
scope: Option<DIScope<'ctx>>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let Some(debug_info) = self.debug_info() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let scope = match scope {
|
||||||
|
Some(scp) => scp,
|
||||||
|
None => debug_info.top_scope().expect("expected a debug-info scope"),
|
||||||
|
};
|
||||||
|
let location =
|
||||||
|
debug_info
|
||||||
|
.builder()
|
||||||
|
.create_debug_location(self.llvm(), line, column, scope, None);
|
||||||
|
|
||||||
|
self.builder().set_current_debug_location(location);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes a debug-info scope to the stack.
|
||||||
|
pub fn push_debug_scope(&self, scope: DIScope<'ctx>) {
|
||||||
|
if let Some(debug_info) = self.debug_info() {
|
||||||
|
debug_info.push_scope(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pops the top of the debug-info scope stack.
|
||||||
|
pub fn pop_debug_scope(&self) {
|
||||||
|
if let Some(debug_info) = self.debug_info() {
|
||||||
|
debug_info.pop_scope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Pushes a new loop context to the stack.
|
/// Pushes a new loop context to the stack.
|
||||||
pub fn push_loop(
|
pub fn push_loop(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -548,9 +660,14 @@ where
|
|||||||
.expect("The dependency manager is unset")
|
.expect("The dependency manager is unset")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the debug info.
|
||||||
|
pub fn debug_info(&self) -> Option<&DebugInfo<'ctx>> {
|
||||||
|
self.debug_info.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the debug config reference.
|
/// Returns the debug config reference.
|
||||||
pub fn debug_config(&self) -> Option<&DebugConfig> {
|
pub fn debug_config(&self) -> &DebugConfig {
|
||||||
self.debug_config.as_ref()
|
&self.debug_config
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends a new basic block to the current function.
|
/// Appends a new basic block to the current function.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub fn create_context(
|
|||||||
let module = llvm.create_module("test");
|
let module = llvm.create_module("test");
|
||||||
let optimizer = Optimizer::new(optimizer_settings);
|
let optimizer = Optimizer::new(optimizer_settings);
|
||||||
|
|
||||||
Context::<DummyDependency>::new(llvm, module, optimizer, None, true, None)
|
Context::<DummyDependency>::new(llvm, module, optimizer, None, true, Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn call<'ctx, D>(
|
pub fn call<'ctx, D>(
|
||||||
context: &mut Context<'ctx, D>,
|
context: &mut Context<'ctx, D>,
|
||||||
_gas: inkwell::values::IntValue<'ctx>,
|
gas: inkwell::values::IntValue<'ctx>,
|
||||||
address: inkwell::values::IntValue<'ctx>,
|
address: inkwell::values::IntValue<'ctx>,
|
||||||
value: Option<inkwell::values::IntValue<'ctx>>,
|
value: Option<inkwell::values::IntValue<'ctx>>,
|
||||||
input_offset: inkwell::values::IntValue<'ctx>,
|
input_offset: inkwell::values::IntValue<'ctx>,
|
||||||
@@ -40,7 +40,7 @@ where
|
|||||||
// TODO: What to supply here? Is there a weight to gas?
|
// TODO: What to supply here? Is there a weight to gas?
|
||||||
let _gas = context
|
let _gas = context
|
||||||
.builder()
|
.builder()
|
||||||
.build_int_truncate(_gas, context.integer_type(64), "gas")?;
|
.build_int_truncate(gas, context.integer_type(64), "gas")?;
|
||||||
|
|
||||||
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
|
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||||
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
|
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
|
||||||
@@ -102,20 +102,80 @@ where
|
|||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn delegate_call<'ctx, D>(
|
pub fn delegate_call<'ctx, D>(
|
||||||
_context: &mut Context<'ctx, D>,
|
context: &mut Context<'ctx, D>,
|
||||||
_gas: inkwell::values::IntValue<'ctx>,
|
gas: inkwell::values::IntValue<'ctx>,
|
||||||
_address: inkwell::values::IntValue<'ctx>,
|
address: inkwell::values::IntValue<'ctx>,
|
||||||
_value: Option<inkwell::values::IntValue<'ctx>>,
|
input_offset: inkwell::values::IntValue<'ctx>,
|
||||||
_input_offset: inkwell::values::IntValue<'ctx>,
|
input_length: inkwell::values::IntValue<'ctx>,
|
||||||
_input_length: inkwell::values::IntValue<'ctx>,
|
output_offset: inkwell::values::IntValue<'ctx>,
|
||||||
_output_offset: inkwell::values::IntValue<'ctx>,
|
output_length: inkwell::values::IntValue<'ctx>,
|
||||||
_output_length: inkwell::values::IntValue<'ctx>,
|
|
||||||
_constants: Vec<Option<num::BigUint>>,
|
_constants: Vec<Option<num::BigUint>>,
|
||||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
todo!()
|
let address_pointer = context.build_address_argument_store(address)?;
|
||||||
|
|
||||||
|
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
||||||
|
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
||||||
|
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
|
||||||
|
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
|
||||||
|
|
||||||
|
// TODO: What to supply here? Is there a weight to gas?
|
||||||
|
let _gas = context
|
||||||
|
.builder()
|
||||||
|
.build_int_truncate(gas, context.integer_type(64), "gas")?;
|
||||||
|
|
||||||
|
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||||
|
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
|
||||||
|
|
||||||
|
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
||||||
|
context.build_store(output_length_pointer, output_length)?;
|
||||||
|
|
||||||
|
let flags = context.xlen_type().const_int(0u64, false);
|
||||||
|
|
||||||
|
let argument_type = revive_runtime_api::calling_convention::delegate_call(context.llvm());
|
||||||
|
let argument_pointer = context.build_alloca_at_entry(argument_type, "delegate_call_arguments");
|
||||||
|
let arguments = &[
|
||||||
|
flags.as_basic_value_enum(),
|
||||||
|
address_pointer.value.as_basic_value_enum(),
|
||||||
|
context.integer_const(64, 0).as_basic_value_enum(),
|
||||||
|
context.integer_const(64, 0).as_basic_value_enum(),
|
||||||
|
context.sentinel_pointer().value.as_basic_value_enum(),
|
||||||
|
input_pointer.value.as_basic_value_enum(),
|
||||||
|
input_length.as_basic_value_enum(),
|
||||||
|
output_pointer.value.as_basic_value_enum(),
|
||||||
|
output_length_pointer.value.as_basic_value_enum(),
|
||||||
|
];
|
||||||
|
revive_runtime_api::calling_convention::spill(
|
||||||
|
context.builder(),
|
||||||
|
argument_pointer.value,
|
||||||
|
argument_type,
|
||||||
|
arguments,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let name = revive_runtime_api::polkavm_imports::DELEGATE_CALL;
|
||||||
|
let argument_pointer = context.builder().build_ptr_to_int(
|
||||||
|
argument_pointer.value,
|
||||||
|
context.xlen_type(),
|
||||||
|
"delegate_call_argument_pointer",
|
||||||
|
)?;
|
||||||
|
let success = context
|
||||||
|
.build_runtime_call(name, &[argument_pointer.into()])
|
||||||
|
.unwrap_or_else(|| panic!("{name} should return a value"))
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
|
let is_success = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::EQ,
|
||||||
|
success,
|
||||||
|
context.xlen_type().const_zero(),
|
||||||
|
"is_success",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(context
|
||||||
|
.builder()
|
||||||
|
.build_int_z_extend(is_success, context.word_type(), "success")?
|
||||||
|
.as_basic_value_enum())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the Yul `linkersymbol` instruction.
|
/// Translates the Yul `linkersymbol` instruction.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ pub fn build_assembly_text(
|
|||||||
contract_path: &str,
|
contract_path: &str,
|
||||||
bytecode: &[u8],
|
bytecode: &[u8],
|
||||||
metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]>,
|
metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]>,
|
||||||
debug_config: Option<&DebugConfig>,
|
debug_config: &DebugConfig,
|
||||||
) -> anyhow::Result<Build> {
|
) -> anyhow::Result<Build> {
|
||||||
let program_blob = ProgramBlob::parse(bytecode.into())
|
let program_blob = ProgramBlob::parse(bytecode.into())
|
||||||
.map_err(anyhow::Error::msg)
|
.map_err(anyhow::Error::msg)
|
||||||
@@ -49,9 +49,7 @@ pub fn build_assembly_text(
|
|||||||
format!("Failed to convert disassembled code to string for contract: {contract_path}")
|
format!("Failed to convert disassembled code to string for contract: {contract_path}")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if let Some(debug_config) = debug_config {
|
debug_config.dump_assembly(contract_path, &assembly_text)?;
|
||||||
debug_config.dump_assembly(contract_path, &assembly_text)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Build::new(
|
Ok(Build::new(
|
||||||
assembly_text.to_owned(),
|
assembly_text.to_owned(),
|
||||||
@@ -98,7 +96,7 @@ pub trait Dependency {
|
|||||||
path: &str,
|
path: &str,
|
||||||
optimizer_settings: OptimizerSettings,
|
optimizer_settings: OptimizerSettings,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
debug_config: Option<DebugConfig>,
|
debug_config: DebugConfig,
|
||||||
) -> anyhow::Result<String>;
|
) -> anyhow::Result<String>;
|
||||||
|
|
||||||
/// Resolves a full contract path.
|
/// Resolves a full contract path.
|
||||||
@@ -118,7 +116,7 @@ impl Dependency for DummyDependency {
|
|||||||
_path: &str,
|
_path: &str,
|
||||||
_optimizer_settings: OptimizerSettings,
|
_optimizer_settings: OptimizerSettings,
|
||||||
_include_metadata_hash: bool,
|
_include_metadata_hash: bool,
|
||||||
_debug_config: Option<DebugConfig>,
|
_debug_config: DebugConfig,
|
||||||
) -> anyhow::Result<String> {
|
) -> anyhow::Result<String> {
|
||||||
Ok(String::new())
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,3 +106,30 @@ pub fn call(context: &Context) -> StructType {
|
|||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a packed struct argument type for the `delegate_call` API.
|
||||||
|
pub fn delegate_call(context: &Context) -> StructType {
|
||||||
|
context.struct_type(
|
||||||
|
&[
|
||||||
|
// flags: u32,
|
||||||
|
context.i32_type().as_basic_type_enum(),
|
||||||
|
// address_ptr:
|
||||||
|
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||||
|
// ref_time_limit: u64,
|
||||||
|
context.i64_type().as_basic_type_enum(),
|
||||||
|
// proof_size_limit: u64,
|
||||||
|
context.i64_type().as_basic_type_enum(),
|
||||||
|
// deposit_ptr: u32,
|
||||||
|
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||||
|
// input_data_ptr: u32,
|
||||||
|
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||||
|
// input_data_len: u32,
|
||||||
|
context.i32_type().as_basic_type_enum(),
|
||||||
|
// output_ptr: u32,
|
||||||
|
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||||
|
// output_len_ptr: u32,
|
||||||
|
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ POLKAVM_IMPORT(void, block_number, uint32_t)
|
|||||||
|
|
||||||
POLKAVM_IMPORT(uint32_t, call, uint32_t)
|
POLKAVM_IMPORT(uint32_t, call, uint32_t)
|
||||||
|
|
||||||
|
POLKAVM_IMPORT(uint32_t, delegate_call, uint32_t)
|
||||||
|
|
||||||
POLKAVM_IMPORT(void, caller, uint32_t)
|
POLKAVM_IMPORT(void, caller, uint32_t)
|
||||||
|
|
||||||
POLKAVM_IMPORT(void, chain_id, uint32_t)
|
POLKAVM_IMPORT(void, chain_id, uint32_t)
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ pub static BLOCK_NUMBER: &str = "block_number";
|
|||||||
|
|
||||||
pub static CALL: &str = "call";
|
pub static CALL: &str = "call";
|
||||||
|
|
||||||
|
pub static DELEGATE_CALL: &str = "delegate_call";
|
||||||
|
|
||||||
pub static CALLER: &str = "caller";
|
pub static CALLER: &str = "caller";
|
||||||
|
|
||||||
pub static CHAIN_ID: &str = "chain_id";
|
pub static CHAIN_ID: &str = "chain_id";
|
||||||
@@ -64,7 +66,7 @@ pub static VALUE_TRANSFERRED: &str = "value_transferred";
|
|||||||
|
|
||||||
/// All imported runtime API symbols.
|
/// All imported runtime API symbols.
|
||||||
/// Useful for configuring common attributes and linkage.
|
/// Useful for configuring common attributes and linkage.
|
||||||
pub static IMPORTS: [&str; 26] = [
|
pub static IMPORTS: [&str; 27] = [
|
||||||
SBRK,
|
SBRK,
|
||||||
MEMORY_SIZE,
|
MEMORY_SIZE,
|
||||||
ADDRESS,
|
ADDRESS,
|
||||||
@@ -73,6 +75,7 @@ pub static IMPORTS: [&str; 26] = [
|
|||||||
BLOCK_HASH,
|
BLOCK_HASH,
|
||||||
BLOCK_NUMBER,
|
BLOCK_NUMBER,
|
||||||
CALL,
|
CALL,
|
||||||
|
DELEGATE_CALL,
|
||||||
CALLER,
|
CALLER,
|
||||||
CHAIN_ID,
|
CHAIN_ID,
|
||||||
CODE_SIZE,
|
CODE_SIZE,
|
||||||
|
|||||||
@@ -212,9 +212,10 @@ where
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let full_path = self.full_path().to_owned();
|
let full_path = self.full_path().to_owned();
|
||||||
|
|
||||||
if let Some(debug_config) = context.debug_config() {
|
context
|
||||||
debug_config.dump_evmla(full_path.as_str(), self.to_string().as_str())?;
|
.debug_config()
|
||||||
}
|
.dump_evmla(full_path.as_str(), self.to_string().as_str())?;
|
||||||
|
|
||||||
let deploy_code_blocks = EtherealIR::get_blocks(
|
let deploy_code_blocks = EtherealIR::get_blocks(
|
||||||
context.evmla().version.to_owned(),
|
context.evmla().version.to_owned(),
|
||||||
revive_llvm_context::PolkaVMCodeType::Deploy,
|
revive_llvm_context::PolkaVMCodeType::Deploy,
|
||||||
@@ -228,9 +229,11 @@ where
|
|||||||
.ok_or_else(|| anyhow::anyhow!("Runtime code data not found"))?
|
.ok_or_else(|| anyhow::anyhow!("Runtime code data not found"))?
|
||||||
.remove("0")
|
.remove("0")
|
||||||
.expect("Always exists");
|
.expect("Always exists");
|
||||||
if let Some(debug_config) = context.debug_config() {
|
|
||||||
debug_config.dump_evmla(full_path.as_str(), data.to_string().as_str())?;
|
context
|
||||||
}
|
.debug_config()
|
||||||
|
.dump_evmla(full_path.as_str(), data.to_string().as_str())?;
|
||||||
|
|
||||||
let runtime_code_instructions = match data {
|
let runtime_code_instructions = match data {
|
||||||
Data::Assembly(assembly) => assembly
|
Data::Assembly(assembly) => assembly
|
||||||
.code
|
.code
|
||||||
@@ -253,9 +256,11 @@ where
|
|||||||
blocks.extend(runtime_code_blocks);
|
blocks.extend(runtime_code_blocks);
|
||||||
let mut ethereal_ir =
|
let mut ethereal_ir =
|
||||||
EtherealIR::new(context.evmla().version.to_owned(), extra_metadata, blocks)?;
|
EtherealIR::new(context.evmla().version.to_owned(), extra_metadata, blocks)?;
|
||||||
if let Some(debug_config) = context.debug_config() {
|
|
||||||
debug_config.dump_ethir(full_path.as_str(), ethereal_ir.to_string().as_str())?;
|
context
|
||||||
}
|
.debug_config()
|
||||||
|
.dump_ethir(full_path.as_str(), ethereal_ir.to_string().as_str())?;
|
||||||
|
|
||||||
ethereal_ir.declare(context)?;
|
ethereal_ir.declare(context)?;
|
||||||
ethereal_ir.into_llvm(context)?;
|
ethereal_ir.into_llvm(context)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1096,7 +1096,6 @@ where
|
|||||||
context,
|
context,
|
||||||
gas,
|
gas,
|
||||||
address,
|
address,
|
||||||
None,
|
|
||||||
input_offset,
|
input_offset,
|
||||||
input_size,
|
input_size,
|
||||||
output_offset,
|
output_offset,
|
||||||
|
|||||||
@@ -1175,7 +1175,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
||||||
context.set_current_function(self.name.as_str())?;
|
context.set_current_function(self.name.as_str(), None)?;
|
||||||
|
|
||||||
for (key, blocks) in self.blocks.iter() {
|
for (key, blocks) in self.blocks.iter() {
|
||||||
for (index, block) in blocks.iter().enumerate() {
|
for (index, block) in blocks.iter().enumerate() {
|
||||||
@@ -1297,6 +1297,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.pop_debug_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ pub fn yul<T: Compiler>(
|
|||||||
solc: &mut T,
|
solc: &mut T,
|
||||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
) -> anyhow::Result<Build> {
|
) -> anyhow::Result<Build> {
|
||||||
let path = match input_files.len() {
|
let path = match input_files.len() {
|
||||||
1 => input_files.first().expect("Always exists"),
|
1 => input_files.first().expect("Always exists"),
|
||||||
@@ -92,7 +92,7 @@ pub fn llvm_ir(
|
|||||||
input_files: &[PathBuf],
|
input_files: &[PathBuf],
|
||||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
) -> anyhow::Result<Build> {
|
) -> anyhow::Result<Build> {
|
||||||
let path = match input_files.len() {
|
let path = match input_files.len() {
|
||||||
1 => input_files.first().expect("Always exists"),
|
1 => input_files.first().expect("Always exists"),
|
||||||
@@ -126,7 +126,7 @@ pub fn standard_output<T: Compiler>(
|
|||||||
allow_paths: Option<String>,
|
allow_paths: Option<String>,
|
||||||
remappings: Option<BTreeSet<String>>,
|
remappings: Option<BTreeSet<String>>,
|
||||||
suppressed_warnings: Option<Vec<Warning>>,
|
suppressed_warnings: Option<Vec<Warning>>,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
) -> anyhow::Result<Build> {
|
) -> anyhow::Result<Build> {
|
||||||
let solc_version = solc.version()?;
|
let solc_version = solc.version()?;
|
||||||
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
|
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
|
||||||
@@ -185,7 +185,7 @@ pub fn standard_output<T: Compiler>(
|
|||||||
libraries,
|
libraries,
|
||||||
solc_pipeline,
|
solc_pipeline,
|
||||||
&solc_version,
|
&solc_version,
|
||||||
debug_config.as_ref(),
|
&debug_config,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
|
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
|
||||||
@@ -202,7 +202,7 @@ pub fn standard_json<T: Compiler>(
|
|||||||
base_path: Option<String>,
|
base_path: Option<String>,
|
||||||
include_paths: Vec<String>,
|
include_paths: Vec<String>,
|
||||||
allow_paths: Option<String>,
|
allow_paths: Option<String>,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let solc_version = solc.version()?;
|
let solc_version = solc.version()?;
|
||||||
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
|
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
|
||||||
@@ -247,7 +247,7 @@ pub fn standard_json<T: Compiler>(
|
|||||||
libraries,
|
libraries,
|
||||||
solc_pipeline,
|
solc_pipeline,
|
||||||
&solc_version,
|
&solc_version,
|
||||||
debug_config.as_ref(),
|
&debug_config,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if detect_missing_libraries {
|
if detect_missing_libraries {
|
||||||
@@ -278,7 +278,7 @@ pub fn combined_json<T: Compiler>(
|
|||||||
allow_paths: Option<String>,
|
allow_paths: Option<String>,
|
||||||
remappings: Option<BTreeSet<String>>,
|
remappings: Option<BTreeSet<String>>,
|
||||||
suppressed_warnings: Option<Vec<Warning>>,
|
suppressed_warnings: Option<Vec<Warning>>,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
output_directory: Option<PathBuf>,
|
output_directory: Option<PathBuf>,
|
||||||
overwrite: bool,
|
overwrite: bool,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub struct Input {
|
|||||||
/// The optimizer settings.
|
/// The optimizer settings.
|
||||||
pub optimizer_settings: revive_llvm_context::OptimizerSettings,
|
pub optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
/// The debug output config.
|
/// The debug output config.
|
||||||
pub debug_config: Option<revive_llvm_context::DebugConfig>,
|
pub debug_config: revive_llvm_context::DebugConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
@@ -29,7 +29,7 @@ impl Input {
|
|||||||
project: Project,
|
project: Project,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
contract,
|
contract,
|
||||||
|
|||||||
@@ -34,17 +34,16 @@ impl Process for NativeProcess {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if let Some(dbg_config) = &input.debug_config {
|
input
|
||||||
dbg_config
|
.debug_config
|
||||||
.dump_stage_output(&input.contract.path, Some("stage"), &input_json)
|
.dump_stage_output(&input.contract.path, Some("stage"), &input_json)
|
||||||
.map_err(|error| {
|
.map_err(|error| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
"{:?} failed to log the recursive process output: {:?}",
|
"{:?} failed to log the recursive process output: {:?}",
|
||||||
executable,
|
executable,
|
||||||
error,
|
error,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
}
|
|
||||||
|
|
||||||
process
|
process
|
||||||
.stdin
|
.stdin
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ impl Contract {
|
|||||||
project: Project,
|
project: Project,
|
||||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
) -> anyhow::Result<ContractBuild> {
|
) -> anyhow::Result<ContractBuild> {
|
||||||
let llvm = inkwell::context::Context::create();
|
let llvm = inkwell::context::Context::create();
|
||||||
let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings);
|
let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings);
|
||||||
@@ -104,6 +104,7 @@ impl Contract {
|
|||||||
|
|
||||||
let module = match self.ir {
|
let module = match self.ir {
|
||||||
IR::LLVMIR(ref llvm_ir) => {
|
IR::LLVMIR(ref llvm_ir) => {
|
||||||
|
// Create the output module
|
||||||
let memory_buffer =
|
let memory_buffer =
|
||||||
inkwell::memory_buffer::MemoryBuffer::create_from_memory_range_copy(
|
inkwell::memory_buffer::MemoryBuffer::create_from_memory_range_copy(
|
||||||
llvm_ir.source.as_bytes(),
|
llvm_ir.source.as_bytes(),
|
||||||
@@ -114,6 +115,7 @@ impl Contract {
|
|||||||
}
|
}
|
||||||
_ => llvm.create_module(self.path.as_str()),
|
_ => llvm.create_module(self.path.as_str()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut context = revive_llvm_context::PolkaVMContext::new(
|
let mut context = revive_llvm_context::PolkaVMContext::new(
|
||||||
&llvm,
|
&llvm,
|
||||||
module,
|
module,
|
||||||
@@ -151,6 +153,10 @@ impl Contract {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if let Some(debug_info) = context.debug_info() {
|
||||||
|
debug_info.finalize_module()
|
||||||
|
}
|
||||||
|
|
||||||
let build = context.build(self.path.as_str(), metadata_hash)?;
|
let build = context.build(self.path.as_str(), metadata_hash)?;
|
||||||
|
|
||||||
Ok(ContractBuild::new(
|
Ok(ContractBuild::new(
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ impl Project {
|
|||||||
self,
|
self,
|
||||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
) -> anyhow::Result<Build> {
|
) -> anyhow::Result<Build> {
|
||||||
let project = self.clone();
|
let project = self.clone();
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
@@ -251,7 +251,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
|
|||||||
identifier: &str,
|
identifier: &str,
|
||||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
) -> anyhow::Result<String> {
|
) -> anyhow::Result<String> {
|
||||||
let contract_path = project.resolve_path(identifier)?;
|
let contract_path = project.resolve_path(identifier)?;
|
||||||
let contract = project
|
let contract = project
|
||||||
|
|||||||
@@ -138,6 +138,11 @@ pub struct Arguments {
|
|||||||
#[structopt(long = "suppress-warnings")]
|
#[structopt(long = "suppress-warnings")]
|
||||||
pub suppress_warnings: Option<Vec<String>>,
|
pub suppress_warnings: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Generate source based debug information in the output code file. This only has an effect
|
||||||
|
/// with the LLVM-IR code generator and is ignored otherwise.
|
||||||
|
#[structopt(short = 'g')]
|
||||||
|
pub emit_source_debug_info: bool,
|
||||||
|
|
||||||
/// Dump all IRs to files in the specified directory.
|
/// Dump all IRs to files in the specified directory.
|
||||||
/// Only for testing and debugging.
|
/// Only for testing and debugging.
|
||||||
#[structopt(long = "debug-output-dir")]
|
#[structopt(long = "debug-output-dir")]
|
||||||
|
|||||||
@@ -83,11 +83,12 @@ fn main_inner() -> anyhow::Result<()> {
|
|||||||
let debug_config = match arguments.debug_output_directory {
|
let debug_config = match arguments.debug_output_directory {
|
||||||
Some(ref debug_output_directory) => {
|
Some(ref debug_output_directory) => {
|
||||||
std::fs::create_dir_all(debug_output_directory.as_path())?;
|
std::fs::create_dir_all(debug_output_directory.as_path())?;
|
||||||
Some(revive_llvm_context::DebugConfig::new(
|
revive_llvm_context::DebugConfig::new(
|
||||||
debug_output_directory.to_owned(),
|
Some(debug_output_directory.to_owned()),
|
||||||
))
|
arguments.emit_source_debug_info,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
None => None,
|
None => revive_llvm_context::DebugConfig::new(None, arguments.emit_source_debug_info),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (input_files, remappings) = arguments.split_input_files_and_remappings()?;
|
let (input_files, remappings) = arguments.split_input_files_and_remappings()?;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ impl Output {
|
|||||||
libraries: BTreeMap<String, BTreeMap<String, String>>,
|
libraries: BTreeMap<String, BTreeMap<String, String>>,
|
||||||
pipeline: SolcPipeline,
|
pipeline: SolcPipeline,
|
||||||
solc_version: &SolcVersion,
|
solc_version: &SolcVersion,
|
||||||
debug_config: Option<&revive_llvm_context::DebugConfig>,
|
debug_config: &revive_llvm_context::DebugConfig,
|
||||||
) -> anyhow::Result<Project> {
|
) -> anyhow::Result<Project> {
|
||||||
if let SolcPipeline::EVMLA = pipeline {
|
if let SolcPipeline::EVMLA = pipeline {
|
||||||
self.preprocess_dependencies()?;
|
self.preprocess_dependencies()?;
|
||||||
@@ -90,9 +90,7 @@ impl Output {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(debug_config) = debug_config {
|
debug_config.dump_yul(full_path.as_str(), ir_optimized.as_str())?;
|
||||||
debug_config.dump_yul(full_path.as_str(), ir_optimized.as_str())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut lexer = Lexer::new(ir_optimized.to_owned());
|
let mut lexer = Lexer::new(ir_optimized.to_owned());
|
||||||
let object = Object::parse(&mut lexer, None).map_err(|error| {
|
let object = Object::parse(&mut lexer, None).map_err(|error| {
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ static EVM_BLOB_CACHE: Lazy<Mutex<HashMap<CachedBlob, Vec<u8>>>> = Lazy::new(Def
|
|||||||
static EVM_RUNTIME_BLOB_CACHE: Lazy<Mutex<HashMap<CachedBlob, Vec<u8>>>> =
|
static EVM_RUNTIME_BLOB_CACHE: Lazy<Mutex<HashMap<CachedBlob, Vec<u8>>>> =
|
||||||
Lazy::new(Default::default);
|
Lazy::new(Default::default);
|
||||||
|
|
||||||
|
const DEBUG_CONFIG: revive_llvm_context::DebugConfig =
|
||||||
|
revive_llvm_context::DebugConfig::new(None, true);
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq)]
|
#[derive(Hash, PartialEq, Eq)]
|
||||||
struct CachedBlob {
|
struct CachedBlob {
|
||||||
contract_name: String,
|
contract_name: String,
|
||||||
@@ -104,9 +107,10 @@ pub fn build_solidity_with_options(
|
|||||||
|
|
||||||
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?;
|
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?;
|
||||||
|
|
||||||
let project = output.try_to_project(sources, libraries, pipeline, &solc_version, None)?;
|
let project =
|
||||||
|
output.try_to_project(sources, libraries, pipeline, &solc_version, &DEBUG_CONFIG)?;
|
||||||
|
|
||||||
let build: crate::Build = project.compile(optimizer_settings, false, None)?;
|
let build: crate::Build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?;
|
||||||
build.write_to_standard_json(&mut output, &solc_version)?;
|
build.write_to_standard_json(&mut output, &solc_version)?;
|
||||||
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
@@ -198,7 +202,8 @@ pub fn build_solidity_and_detect_missing_libraries(
|
|||||||
|
|
||||||
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?;
|
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?;
|
||||||
|
|
||||||
let project = output.try_to_project(sources, libraries, pipeline, &solc_version, None)?;
|
let project =
|
||||||
|
output.try_to_project(sources, libraries, pipeline, &solc_version, &DEBUG_CONFIG)?;
|
||||||
|
|
||||||
let missing_libraries = project.get_missing_libraries();
|
let missing_libraries = project.get_missing_libraries();
|
||||||
missing_libraries.write_to_standard_json(&mut output, &solc.version()?)?;
|
missing_libraries.write_to_standard_json(&mut output, &solc.version()?)?;
|
||||||
@@ -219,7 +224,7 @@ pub fn build_yul(source_code: &str) -> anyhow::Result<()> {
|
|||||||
source_code,
|
source_code,
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
let _build = project.compile(optimizer_settings, false, None)?;
|
let _build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import * as path from 'path';
|
|||||||
const outputDir = 'artifacts';
|
const outputDir = 'artifacts';
|
||||||
const binExtension = ':C.pvm';
|
const binExtension = ':C.pvm';
|
||||||
const asmExtension = ':C.pvmasm';
|
const asmExtension = ':C.pvmasm';
|
||||||
|
const llvmExtension = '.ll';
|
||||||
const contractSolFilename = 'contract.sol';
|
const contractSolFilename = 'contract.sol';
|
||||||
const contractYulFilename = 'contract.yul';
|
const contractYulFilename = 'contract.yul';
|
||||||
|
const contractOptimizedLLVMFilename = contractSolFilename + '.C.optimized';
|
||||||
|
const contractUnoptimizedLLVMFilename = contractSolFilename + '.C.unoptimized';
|
||||||
const pathToOutputDir = path.join(__dirname, '..', outputDir);
|
const pathToOutputDir = path.join(__dirname, '..', outputDir);
|
||||||
const pathToContracts = path.join(__dirname, '..', 'src', 'contracts');
|
const pathToContracts = path.join(__dirname, '..', 'src', 'contracts');
|
||||||
const pathToBasicYulContract = path.join(pathToContracts, 'yul', contractYulFilename);
|
const pathToBasicYulContract = path.join(pathToContracts, 'yul', contractYulFilename);
|
||||||
@@ -16,8 +19,11 @@ export const paths = {
|
|||||||
outputDir: outputDir,
|
outputDir: outputDir,
|
||||||
binExtension: binExtension,
|
binExtension: binExtension,
|
||||||
asmExtension: asmExtension,
|
asmExtension: asmExtension,
|
||||||
|
llvmExtension: llvmExtension,
|
||||||
contractSolFilename: contractSolFilename,
|
contractSolFilename: contractSolFilename,
|
||||||
contractYulFilename: contractYulFilename,
|
contractYulFilename: contractYulFilename,
|
||||||
|
contractOptimizedLLVMFilename: contractOptimizedLLVMFilename,
|
||||||
|
contractUnoptimizedLLVMFilename: contractUnoptimizedLLVMFilename,
|
||||||
pathToOutputDir: pathToOutputDir,
|
pathToOutputDir: pathToOutputDir,
|
||||||
pathToContracts: pathToContracts,
|
pathToContracts: pathToContracts,
|
||||||
pathToBasicSolContract: pathToBasicSolContract,
|
pathToBasicSolContract: pathToBasicSolContract,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {executeCommand, isFolderExist, isFileExist, isFileEmpty} from "../src/helper";
|
import { executeCommand, isFolderExist, isFileExist, isFileEmpty } from "../src/helper";
|
||||||
import { paths } from '../src/entities';
|
import { paths } from '../src/entities';
|
||||||
|
|
||||||
|
|
||||||
@@ -76,3 +76,69 @@ describe("Default run a command from the help", () => {
|
|||||||
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
|
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Run resolc with source debug information", () => {
|
||||||
|
const commands = [
|
||||||
|
`resolc -g ${paths.pathToBasicSolContract} --bin --asm --output-dir "${paths.pathToOutputDir}"`,
|
||||||
|
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --bin --asm --output-dir "${paths.pathToOutputDir}"`
|
||||||
|
]; // potential issue on resolc with full path on Windows cmd`;
|
||||||
|
|
||||||
|
for (var idx in commands) {
|
||||||
|
const command = commands[idx];
|
||||||
|
const result = executeCommand(command);
|
||||||
|
|
||||||
|
it("Compiler run successful", () => {
|
||||||
|
expect(result.output).toMatch(/(Compiler run successful.)/i);
|
||||||
|
});
|
||||||
|
it("Exit code = 0", () => {
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
it("Output dir is created", () => {
|
||||||
|
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
|
||||||
|
});
|
||||||
|
it("Output files are created", () => { // a bug on windows
|
||||||
|
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
|
||||||
|
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
|
||||||
|
});
|
||||||
|
it("the output files are not empty", () => {
|
||||||
|
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
|
||||||
|
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
|
||||||
|
});
|
||||||
|
it("No 'Error'/'Fail' in the output", () => {
|
||||||
|
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Run resolc with source debug information, check LLVM debug-info", () => {
|
||||||
|
const commands = [
|
||||||
|
`resolc -g ${paths.pathToBasicSolContract} --debug-output-dir="${paths.pathToOutputDir}"`,
|
||||||
|
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --debug-output-dir="${paths.pathToOutputDir}"`
|
||||||
|
]; // potential issue on resolc with full path on Windows cmd`;
|
||||||
|
|
||||||
|
for (var idx in commands) {
|
||||||
|
const command = commands[idx];
|
||||||
|
const result = executeCommand(command);
|
||||||
|
|
||||||
|
it("Compiler run successful", () => {
|
||||||
|
expect(result.output).toMatch(/(Compiler run successful.)/i);
|
||||||
|
});
|
||||||
|
it("Exit code = 0", () => {
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
it("Output dir is created", () => {
|
||||||
|
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
|
||||||
|
});
|
||||||
|
it("Output files are created", () => { // a bug on windows
|
||||||
|
expect(isFileExist(paths.pathToOutputDir, paths.contractOptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
|
||||||
|
expect(isFileExist(paths.pathToOutputDir, paths.contractUnoptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
|
||||||
|
});
|
||||||
|
it("the output files are not empty", () => {
|
||||||
|
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
|
||||||
|
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
|
||||||
|
});
|
||||||
|
it("No 'Error'/'Fail' in the output", () => {
|
||||||
|
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub struct Lexer {
|
|||||||
/// The input source code.
|
/// The input source code.
|
||||||
input: String,
|
input: String,
|
||||||
/// The number of characters processed so far.
|
/// The number of characters processed so far.
|
||||||
offset: usize,
|
offset: u32,
|
||||||
/// The current location.
|
/// The current location.
|
||||||
location: Location,
|
location: Location,
|
||||||
/// The peeked lexeme, waiting to be fetched.
|
/// The peeked lexeme, waiting to be fetched.
|
||||||
@@ -48,8 +48,17 @@ impl Lexer {
|
|||||||
return Ok(peeked);
|
return Ok(peeked);
|
||||||
}
|
}
|
||||||
|
|
||||||
while self.offset < self.input.len() {
|
while self.offset
|
||||||
let input = &self.input[self.offset..];
|
< self
|
||||||
|
.input
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::InvalidLexeme {
|
||||||
|
location: self.location,
|
||||||
|
sequence: Default::default(),
|
||||||
|
})?
|
||||||
|
{
|
||||||
|
let input = &self.input[(self.offset as usize)..];
|
||||||
|
|
||||||
if input.starts_with(|character| char::is_ascii_whitespace(&character)) {
|
if input.starts_with(|character| char::is_ascii_whitespace(&character)) {
|
||||||
if input.starts_with('\n') {
|
if input.starts_with('\n') {
|
||||||
@@ -101,12 +110,13 @@ impl Lexer {
|
|||||||
return Ok(token);
|
return Ok(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
let end = self.input[self.offset..]
|
let end = self.input[(self.offset as usize)..]
|
||||||
.find(char::is_whitespace)
|
.find(char::is_whitespace)
|
||||||
.unwrap_or(self.input.len());
|
.unwrap_or(self.input.len());
|
||||||
return Err(Error::InvalidLexeme {
|
return Err(Error::InvalidLexeme {
|
||||||
location: self.location,
|
location: self.location,
|
||||||
sequence: self.input[self.offset..self.offset + end].to_owned(),
|
sequence: self.input[(self.offset as usize)..(self.offset as usize) + end]
|
||||||
|
.to_owned(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,20 @@ impl Comment {
|
|||||||
let end_position = input.find(Self::END).unwrap_or(input.len());
|
let end_position = input.find(Self::END).unwrap_or(input.len());
|
||||||
let input = &input[..end_position];
|
let input = &input[..end_position];
|
||||||
|
|
||||||
let length = end_position + Self::END.len();
|
let length = (end_position + Self::END.len())
|
||||||
let lines = input.matches('\n').count();
|
.try_into()
|
||||||
|
.expect("the YUL should be of reasonable size");
|
||||||
|
let lines = input
|
||||||
|
.matches('\n')
|
||||||
|
.count()
|
||||||
|
.try_into()
|
||||||
|
.expect("the YUL should be of reasonable size");
|
||||||
let columns = match input.rfind('\n') {
|
let columns = match input.rfind('\n') {
|
||||||
Some(new_line) => end_position - (new_line + 1),
|
Some(new_line) => end_position - (new_line + 1),
|
||||||
None => end_position,
|
None => end_position,
|
||||||
};
|
}
|
||||||
|
.try_into()
|
||||||
|
.expect("the YUL should be of reasonable size");
|
||||||
|
|
||||||
Token::new(Location::new(lines, columns), Lexeme::Comment, length)
|
Token::new(Location::new(lines, columns), Lexeme::Comment, length)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ impl Comment {
|
|||||||
/// Returns the comment's length, including the trimmed whitespace around it.
|
/// Returns the comment's length, including the trimmed whitespace around it.
|
||||||
pub fn parse(input: &str) -> Token {
|
pub fn parse(input: &str) -> Token {
|
||||||
let end_position = input.find(Self::END).unwrap_or(input.len());
|
let end_position = input.find(Self::END).unwrap_or(input.len());
|
||||||
let length = end_position + Self::END.len();
|
let length = (end_position + Self::END.len())
|
||||||
|
.try_into()
|
||||||
|
.expect("the YUL should be of reasonable size");
|
||||||
|
|
||||||
Token::new(Location::new(1, 1), Lexeme::Comment, length)
|
Token::new(Location::new(1, 1), Lexeme::Comment, length)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ impl Identifier {
|
|||||||
let end = input.find(Self::cannot_continue).unwrap_or(input.len());
|
let end = input.find(Self::cannot_continue).unwrap_or(input.len());
|
||||||
|
|
||||||
let inner = input[..end].to_string();
|
let inner = input[..end].to_string();
|
||||||
let length = inner.len();
|
let length = inner
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.expect("the YUL should be of reasonable size");
|
||||||
|
|
||||||
if let Some(token) = Keyword::parse(inner.as_str()) {
|
if let Some(token) = Keyword::parse(inner.as_str()) {
|
||||||
return Some(token);
|
return Some(token);
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ impl Keyword {
|
|||||||
if length != input.len() {
|
if length != input.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let length = length
|
||||||
|
.try_into()
|
||||||
|
.expect("the YUL should be of reasonable size");
|
||||||
|
|
||||||
Some(Token::new(Location::new(0, length), lexeme, length))
|
Some(Token::new(Location::new(0, length), lexeme, length))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ impl Integer {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let length = length
|
||||||
|
.try_into()
|
||||||
|
.expect("the YUL should be of reasonable size");
|
||||||
|
|
||||||
let token = Token::new(
|
let token = Token::new(
|
||||||
Location::new(0, length),
|
Location::new(0, length),
|
||||||
Lexeme::Literal(Literal::Integer(value)),
|
Lexeme::Literal(Literal::Integer(value)),
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ impl String {
|
|||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
let literal = Self::new(string, is_hex_string);
|
let literal = Self::new(string, is_hex_string);
|
||||||
|
let length = length
|
||||||
|
.try_into()
|
||||||
|
.expect("the YUL should be of reasonable size");
|
||||||
|
|
||||||
Some(Token::new(
|
Some(Token::new(
|
||||||
Location::new(0, length),
|
Location::new(0, length),
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ use serde::Serialize;
|
|||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq)]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
/// The line number, starting from 1.
|
/// The line number, starting from 1.
|
||||||
pub line: usize,
|
pub line: u32,
|
||||||
/// The column number, starting from 1.
|
/// The column number, starting from 1.
|
||||||
pub column: usize,
|
pub column: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Location {
|
impl Default for Location {
|
||||||
@@ -20,13 +20,13 @@ impl Default for Location {
|
|||||||
|
|
||||||
impl Location {
|
impl Location {
|
||||||
/// Creates a default location.
|
/// Creates a default location.
|
||||||
pub fn new(line: usize, column: usize) -> Self {
|
pub fn new(line: u32, column: u32) -> Self {
|
||||||
Self { line, column }
|
Self { line, column }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutates the location by shifting the original one down by `lines` and
|
/// Mutates the location by shifting the original one down by `lines` and
|
||||||
/// setting the column to `column`.
|
/// setting the column to `column`.
|
||||||
pub fn shift_down(&mut self, lines: usize, column: usize) {
|
pub fn shift_down(&mut self, lines: u32, column: u32) {
|
||||||
if lines == 0 {
|
if lines == 0 {
|
||||||
self.shift_right(column);
|
self.shift_right(column);
|
||||||
return;
|
return;
|
||||||
@@ -37,7 +37,7 @@ impl Location {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Mutates the location by shifting the original one rightward by `columns`.
|
/// Mutates the location by shifting the original one rightward by `columns`.
|
||||||
pub fn shift_right(&mut self, columns: usize) {
|
pub fn shift_right(&mut self, columns: u32) {
|
||||||
self.column += columns;
|
self.column += columns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ pub struct Token {
|
|||||||
/// The lexeme.
|
/// The lexeme.
|
||||||
pub lexeme: Lexeme,
|
pub lexeme: Lexeme,
|
||||||
/// The token length, including whitespaces.
|
/// The token length, including whitespaces.
|
||||||
pub length: usize,
|
pub length: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
/// A shortcut constructor.
|
/// A shortcut constructor.
|
||||||
pub fn new(location: Location, lexeme: Lexeme, length: usize) -> Self {
|
pub fn new(location: Location, lexeme: Lexeme, length: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
location,
|
location,
|
||||||
lexeme,
|
lexeme,
|
||||||
|
|||||||
@@ -57,4 +57,7 @@ pub enum Error {
|
|||||||
/// The list of invalid attributes.
|
/// The list of invalid attributes.
|
||||||
values: BTreeSet<String>,
|
values: BTreeSet<String>,
|
||||||
},
|
},
|
||||||
|
/// Invalid code length.
|
||||||
|
#[error("The line or column length exceed the maximum of u32::MAX")]
|
||||||
|
InvalidLength,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,11 @@ impl Assignment {
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let length = identifier.inner.len();
|
let length = identifier
|
||||||
|
.inner
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::Parser(ParserError::InvalidLength))?;
|
||||||
|
|
||||||
match lexer.peek()? {
|
match lexer.peek()? {
|
||||||
Token {
|
Token {
|
||||||
@@ -115,6 +119,8 @@ where
|
|||||||
mut self,
|
mut self,
|
||||||
context: &mut revive_llvm_context::PolkaVMContext<D>,
|
context: &mut revive_llvm_context::PolkaVMContext<D>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
|
|
||||||
let value = match self.initializer.into_llvm(context)? {
|
let value = match self.initializer.into_llvm(context)? {
|
||||||
Some(value) => value,
|
Some(value) => value,
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
@@ -142,6 +148,8 @@ where
|
|||||||
context.build_store(tuple_pointer, value.to_llvm())?;
|
context.build_store(tuple_pointer, value.to_llvm())?;
|
||||||
|
|
||||||
for (index, binding) in self.bindings.into_iter().enumerate() {
|
for (index, binding) in self.bindings.into_iter().enumerate() {
|
||||||
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
|
|
||||||
let field_pointer = context.build_gep(
|
let field_pointer = context.build_gep(
|
||||||
tuple_pointer,
|
tuple_pointer,
|
||||||
&[
|
&[
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use std::collections::HashSet;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use inkwell::debug_info::AsDIScope;
|
||||||
|
|
||||||
use crate::yul::error::Error;
|
use crate::yul::error::Error;
|
||||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||||
@@ -153,9 +155,26 @@ where
|
|||||||
function.into_llvm(context)?;
|
function.into_llvm(context)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.set_current_function(current_function.as_str())?;
|
context.set_current_function(current_function.as_str(), Some(self.location.line))?;
|
||||||
|
|
||||||
|
if let Some(debug_info) = context.debug_info() {
|
||||||
|
let di_builder = debug_info.builder();
|
||||||
|
let di_scope = debug_info.top_scope().expect("expected a debug-info scope");
|
||||||
|
let di_block_scope = di_builder
|
||||||
|
.create_lexical_block(
|
||||||
|
di_scope,
|
||||||
|
debug_info.compilation_unit().get_file(),
|
||||||
|
self.location.line,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.as_debug_info_scope();
|
||||||
|
context.push_debug_scope(di_block_scope);
|
||||||
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
|
}
|
||||||
|
|
||||||
context.set_basic_block(current_block);
|
context.set_basic_block(current_block);
|
||||||
for statement in local_statements.into_iter() {
|
for statement in local_statements.into_iter() {
|
||||||
|
context.set_debug_location(statement.location().line, 0, None)?;
|
||||||
if context.basic_block().get_terminator().is_some() {
|
if context.basic_block().get_terminator().is_some() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -194,6 +213,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.pop_debug_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -807,7 +807,6 @@ impl FunctionCall {
|
|||||||
context,
|
context,
|
||||||
gas,
|
gas,
|
||||||
address,
|
address,
|
||||||
None,
|
|
||||||
input_offset,
|
input_offset,
|
||||||
input_size,
|
input_size,
|
||||||
output_offset,
|
output_offset,
|
||||||
|
|||||||
@@ -55,7 +55,11 @@ impl Expression {
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let length = identifier.inner.len();
|
let length = identifier
|
||||||
|
.inner
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::Parser(ParserError::InvalidLength))?;
|
||||||
|
|
||||||
match lexer.peek()? {
|
match lexer.peek()? {
|
||||||
Token {
|
Token {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::collections::BTreeSet;
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@@ -229,14 +230,15 @@ where
|
|||||||
mut self,
|
mut self,
|
||||||
context: &mut revive_llvm_context::PolkaVMContext<D>,
|
context: &mut revive_llvm_context::PolkaVMContext<D>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
context.set_current_function(self.identifier.as_str())?;
|
context.set_current_function(self.identifier.as_str(), Some(self.location.line))?;
|
||||||
let r#return = context.current_function().borrow().r#return();
|
|
||||||
|
|
||||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||||
|
|
||||||
|
let r#return = context.current_function().borrow().r#return();
|
||||||
match r#return {
|
match r#return {
|
||||||
revive_llvm_context::PolkaVMFunctionReturn::None => {}
|
revive_llvm_context::PolkaVMFunctionReturn::None => {}
|
||||||
revive_llvm_context::PolkaVMFunctionReturn::Primitive { pointer } => {
|
revive_llvm_context::PolkaVMFunctionReturn::Primitive { pointer } => {
|
||||||
let identifier = self.result.pop().expect("Always exists");
|
let identifier = self.result.pop().expect("Always exists");
|
||||||
|
|
||||||
let r#type = identifier.r#type.unwrap_or_default();
|
let r#type = identifier.r#type.unwrap_or_default();
|
||||||
context.build_store(pointer, r#type.into_llvm(context).const_zero())?;
|
context.build_store(pointer, r#type.into_llvm(context).const_zero())?;
|
||||||
context
|
context
|
||||||
@@ -288,6 +290,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.body.into_llvm(context)?;
|
self.body.into_llvm(context)?;
|
||||||
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
|
|
||||||
match context
|
match context
|
||||||
.basic_block()
|
.basic_block()
|
||||||
.get_last_instruction()
|
.get_last_instruction()
|
||||||
@@ -314,6 +318,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.pop_debug_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use inkwell::debug_info::AsDIScope;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@@ -217,16 +219,30 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
||||||
|
if let Some(debug_info) = context.debug_info() {
|
||||||
|
let di_builder = debug_info.builder();
|
||||||
|
let object_name: &str = self.identifier.as_str();
|
||||||
|
let di_parent_scope = debug_info
|
||||||
|
.top_scope()
|
||||||
|
.expect("expected an existing debug-info scope");
|
||||||
|
let object_scope = di_builder.create_namespace(di_parent_scope, object_name, true);
|
||||||
|
context.push_debug_scope(object_scope.as_debug_info_scope());
|
||||||
|
}
|
||||||
|
|
||||||
if self.identifier.ends_with("_deployed") {
|
if self.identifier.ends_with("_deployed") {
|
||||||
revive_llvm_context::PolkaVMImmutableDataLoadFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMImmutableDataLoadFunction.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMRuntimeCodeFunction::new(self.code).into_llvm(context)?;
|
revive_llvm_context::PolkaVMRuntimeCodeFunction::new(self.code).into_llvm(context)?;
|
||||||
} else {
|
} else {
|
||||||
revive_llvm_context::PolkaVMDeployCodeFunction::new(self.code).into_llvm(context)?;
|
revive_llvm_context::PolkaVMDeployCodeFunction::new(self.code).into_llvm(context)?;
|
||||||
}
|
}
|
||||||
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
|
|
||||||
if let Some(object) = self.inner_object {
|
if let Some(object) = self.inner_object {
|
||||||
object.into_llvm(context)?;
|
object.into_llvm(context)?;
|
||||||
}
|
}
|
||||||
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
|
|
||||||
|
context.pop_debug_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,9 @@ where
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
if self.bindings.len() == 1 {
|
if self.bindings.len() == 1 {
|
||||||
let identifier = self.bindings.remove(0);
|
let identifier = self.bindings.remove(0);
|
||||||
let r#type = identifier.r#type.unwrap_or_default().into_llvm(context);
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
|
let identifier_type = identifier.r#type.clone().unwrap_or_default();
|
||||||
|
let r#type = identifier_type.into_llvm(context);
|
||||||
let pointer = context.build_alloca(r#type, identifier.inner.as_str());
|
let pointer = context.build_alloca(r#type, identifier.inner.as_str());
|
||||||
context
|
context
|
||||||
.current_function()
|
.current_function()
|
||||||
@@ -116,7 +118,7 @@ where
|
|||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.yul_mut()
|
.yul_mut()
|
||||||
.insert_constant(identifier.inner, constant);
|
.insert_constant(identifier.inner.clone(), constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
value.to_llvm()
|
value.to_llvm()
|
||||||
@@ -131,6 +133,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (index, binding) in self.bindings.iter().enumerate() {
|
for (index, binding) in self.bindings.iter().enumerate() {
|
||||||
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
|
|
||||||
let yul_type = binding
|
let yul_type = binding
|
||||||
.r#type
|
.r#type
|
||||||
.to_owned()
|
.to_owned()
|
||||||
|
|||||||
Reference in New Issue
Block a user