Compare commits

..

1 Commits

Author SHA1 Message Date
Cyrill Leutwiler 94dda20880 wip
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-07-15 10:15:30 +02:00
34 changed files with 161 additions and 873 deletions
-2
View File
@@ -14,9 +14,7 @@ Supported `polkadot-sdk` rev: `2503.0.1`
### Added ### Added
- Line debug information per YUL builtin and for `if` statements. - Line debug information per YUL builtin and for `if` statements.
- Column numbers in debug information.
- Support for the YUL optimizer details in the standard json input definition. - Support for the YUL optimizer details in the standard json input definition.
- The `revive-explorer` compiler utility.
### Fixed ### Fixed
- The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line. - The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line.
Generated
-10
View File
@@ -8615,16 +8615,6 @@ dependencies = [
"tempfile", "tempfile",
] ]
[[package]]
name = "revive-explorer"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"num_cpus",
"revive-yul",
]
[[package]] [[package]]
name = "revive-integration" name = "revive-integration"
version = "0.1.1" version = "0.1.1"
-1
View File
@@ -19,7 +19,6 @@ revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0", path = "crates/builtins" } revive-builtins = { version = "0.1.0", path = "crates/builtins" }
revive-common = { version = "0.1.0", path = "crates/common" } revive-common = { version = "0.1.0", path = "crates/common" }
revive-differential = { version = "0.1.0", path = "crates/differential" } revive-differential = { version = "0.1.0", path = "crates/differential" }
revive-explorer = { version = "0.1.0", path = "crates/explore" }
revive-integration = { version = "0.1.1", path = "crates/integration" } revive-integration = { version = "0.1.1", path = "crates/integration" }
revive-linker = { version = "0.1.0", path = "crates/linker" } revive-linker = { version = "0.1.0", path = "crates/linker" }
lld-sys = { version = "0.1.0", path = "crates/lld-sys" } lld-sys = { version = "0.1.0", path = "crates/lld-sys" }
+1 -5
View File
@@ -6,7 +6,6 @@
install-llvm-builder \ install-llvm-builder \
install-llvm \ install-llvm \
install-revive-runner \ install-revive-runner \
install-revive-explorer \
format \ format \
clippy \ clippy \
machete \ machete \
@@ -44,9 +43,6 @@ install-llvm: install-llvm-builder
install-revive-runner: install-revive-runner:
cargo install --locked --force --path crates/runner --no-default-features cargo install --locked --force --path crates/runner --no-default-features
install-revive-explorer:
cargo install --locked --force --path crates/explorer --no-default-features
format: format:
cargo fmt --all --check cargo fmt --all --check
@@ -57,7 +53,7 @@ machete:
cargo install cargo-machete cargo install cargo-machete
cargo machete cargo machete
test: format clippy machete test-cli test-workspace install-revive-runner install-revive-explorer test: format clippy machete test-cli test-workspace install-revive-runner
test-integration: install-bin test-integration: install-bin
cargo test --package revive-integration cargo test --package revive-integration
-20
View File
@@ -1,20 +0,0 @@
[package]
name = "revive-explorer"
version.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
authors.workspace = true
description = "Helper utility to inspect debug builds"
[[bin]]
name = "revive-explorer"
path = "src/main.rs"
[dependencies]
anyhow = { workspace = true }
clap = { workspace = true, features = ["help", "std", "derive"] }
num_cpus = { workspace = true }
revive-yul = { workspace = true }
-49
View File
@@ -1,49 +0,0 @@
# revive-explorer
The `revive-explorer` is a helper utility for exploring the compilers YUL lowering unit.
It analyzes a given shared objects from the debug dump and outputs:
- The count of each YUL statement translated.
- A per YUL statement break-down of bytecode size contributed per.
- Estimated `yul-phaser` cost parameters.
Example:
```
statements count:
block 532
Caller 20
Not 73
Gas 24
Shr 2
...
Shl 259
SetImmutable 2
CodeSize 1
CallDataLoad 87
Return 56
bytes per statement:
Or 756
CodeCopy 158
Log3 620
Return 1562
MStore 36128
...
ReturnDataCopy 2854
DataOffset 28
assignment 1194
Number 540
CallValue 4258
yul-phaser parameters:
--break-cost 1
--variable-declaration-cost 3
--function-call-cost 8
--if-cost 4
--expression-statement-cost 6
--function-definition-cost 11
--switch-cost 3
--block-cost 1
--leave-cost 1
--assignment-cost 1
```
-59
View File
@@ -1,59 +0,0 @@
//! The `llvm-dwarfdump` utility helper library.
use std::{
path::{Path, PathBuf},
process::{Command, Stdio},
};
pub static EXECUTABLE: &str = "llvm-dwarfdump";
pub static DEBUG_LINES_ARGUMENTS: [&str; 1] = ["--debug-line"];
pub static SOURCE_FILE_ARGUMENTS: [&str; 1] = ["--show-sources"];
/// Calls the `llvm-dwarfdump` tool to extract debug line information
/// from the shared object at `path`. Returns the output.
///
/// Provide `Some(dwarfdump_exectuable)` to override the default executable.
pub fn debug_lines(
shared_object: &Path,
dwarfdump_executable: &Option<PathBuf>,
) -> anyhow::Result<String> {
dwarfdump(shared_object, dwarfdump_executable, &DEBUG_LINES_ARGUMENTS)
}
/// Calls the `llvm-dwarfdump` tool to extract the source file name.
/// Returns the source file path.
///
/// Provide `Some(dwarfdump_exectuable)` to override the default executable.
pub fn source_file(
shared_object: &Path,
dwarfdump_executable: &Option<PathBuf>,
) -> anyhow::Result<PathBuf> {
let output = dwarfdump(shared_object, dwarfdump_executable, &SOURCE_FILE_ARGUMENTS)?;
Ok(output.trim().into())
}
/// The internal `llvm-dwarfdump` helper function.
fn dwarfdump(
shared_object: &Path,
dwarfdump_executable: &Option<PathBuf>,
arguments: &[&str],
) -> anyhow::Result<String> {
let executable = dwarfdump_executable
.to_owned()
.unwrap_or_else(|| PathBuf::from(EXECUTABLE));
let output = Command::new(executable)
.args(arguments)
.arg(shared_object)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?
.wait_with_output()?;
if !output.status.success() {
anyhow::bail!(String::from_utf8_lossy(&output.stderr).to_string());
}
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
-250
View File
@@ -1,250 +0,0 @@
//! The core dwarf dump analyzer library.
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
use revive_yul::lexer::token::location::Location;
use crate::location_mapper::{self, map_locations, LocationMap};
/// Unknwon code.
pub const OTHER: &str = "other";
/// Compiler internal code.
pub const INTERNAL: &str = "internal";
/// YUL block code.
pub const BLOCK: &str = "block";
/// YUL function call code.
pub const FUNCTION_CALL: &str = "function_call";
/// YUL conditional code.
pub const IF: &str = "if";
/// YUL loop code.
pub const FOR: &str = "for";
/// YUL loop continue code.
pub const CONTINUE: &str = "continue";
/// YUL loop break code.
pub const BREAK: &str = "break";
/// YUL switch code.
pub const SWITCH: &str = "switch";
/// YUL variable declaration code.
pub const DECLARATION: &str = "let";
/// YUL variable assignment code.
pub const ASSIGNMENT: &str = "assignment";
/// YUL function definition code.
pub const FUNCTION_DEFINITION: &str = "function_definition";
/// YUL function leave code.
pub const LEAVE: &str = "leave";
/// The dwarf dump analyzer.
///
/// Loads debug information from `llvm-dwarfdump` and calculates statistics
/// about the compiled YUL statements:
/// - Statements count
/// - Per-statement
#[derive(Debug, Default)]
pub struct DwarfdumpAnalyzer {
/// The YUL source file path.
source: PathBuf,
/// The YUL location to statements map.
location_map: LocationMap,
/// The `llvm-dwarfdump --debug-lines` output.
debug_lines: String,
/// The observed statements.
statements_count: HashMap<String, usize>,
/// The observed statement to instructions size.
statements_size: HashMap<String, u64>,
}
impl DwarfdumpAnalyzer {
/// The debug info analyzer constructor.
///
/// `source` is the path to the YUL source file.
/// `debug_lines` is the `llvm-dwarfdump --debug-lines` output.
pub fn new(source: &Path, debug_lines: String) -> Self {
Self {
source: source.to_path_buf(),
debug_lines,
..Default::default()
}
}
/// Run the analysis.
pub fn analyze(&mut self) -> anyhow::Result<()> {
self.map_locations()?;
self.analyze_statements()?;
Ok(())
}
/// Populate the maps so that we can always unwrap later.
fn map_locations(&mut self) -> anyhow::Result<()> {
self.location_map = map_locations(&self.source)?;
self.statements_count = HashMap::with_capacity(self.location_map.len());
self.statements_size = HashMap::with_capacity(self.location_map.len());
for statement in self.location_map.values() {
if !self.statements_size.contains_key(statement) {
self.statements_size.insert(statement.clone(), 0);
}
*self.statements_count.entry(statement.clone()).or_insert(0) += 1;
}
Ok(())
}
/// Analyze how much bytes of insturctions each statement contributes.
fn analyze_statements(&mut self) -> anyhow::Result<()> {
let mut previous_offset = 0;
let mut previous_location = Location::new(0, 0);
for line in self
.debug_lines
.lines()
.skip_while(|line| !line.starts_with("Address"))
.skip(2)
{
let mut parts = line.split_whitespace();
let (Some(offset), Some(line), Some(column)) =
(parts.next(), parts.next(), parts.next())
else {
continue;
};
let current_offset = u64::from_str_radix(offset.trim_start_matches("0x"), 16)?;
let mut current_location = Location::new(line.parse()?, column.parse()?);
// TODO: A bug? Needs further investigation.
if current_location.line == 0 && current_location.column != 0 {
current_location.line = previous_location.line;
}
if let Some(statement) = self.location_map.get(&previous_location) {
let contribution = current_offset - previous_offset;
*self.statements_size.get_mut(statement).unwrap() += contribution;
}
previous_offset = current_offset;
previous_location = current_location;
}
Ok(())
}
/// Print the per-statement count break-down.
pub fn display_statement_count(&self) {
println!("statements count:");
for (statement, count) in self.statements_count.iter() {
println!("\t{statement} {count}");
}
}
/// Print the per-statement byte size contribution break-down.
pub fn display_statement_size(&self) {
println!("bytes per statement:");
for (statement, size) in self.statements_size.iter() {
println!("\t{statement} {size}");
}
}
/// Print the estimated `yul-phaser` cost parameters.
pub fn display_phaser_costs(&self, yul_phaser_scale: u64) {
println!("yul-phaser parameters:");
for (parameter, cost) in self.phaser_costs(yul_phaser_scale) {
println!("\t{parameter} {cost}");
}
}
/// Estimate the `yul-phaser` costs using the simplified weight function:
/// `Total size / toal count = cost`
pub fn phaser_costs(&self, yul_phaser_scale: u64) -> Vec<(String, u64)> {
let mut costs: HashMap<String, (usize, u64)> = HashMap::with_capacity(16);
for (statement, count) in self
.statements_count
.iter()
.filter(|(_, count)| **count > 0)
{
let size = self.statements_size.get(statement).unwrap();
let cost = match statement.as_str() {
location_mapper::FOR => "--for-loop-cost",
location_mapper::OTHER => continue,
location_mapper::INTERNAL => continue,
location_mapper::BLOCK => "--block-cost",
location_mapper::FUNCTION_CALL => "--function-call-cost",
location_mapper::IF => "--if-cost",
location_mapper::CONTINUE => "--continue-cost",
location_mapper::BREAK => "--break-cost",
location_mapper::LEAVE => "--leave-cost",
location_mapper::SWITCH => "--switch-cost",
location_mapper::DECLARATION => "--variable-declaration-cost",
location_mapper::ASSIGNMENT => "--assignment-cost",
location_mapper::FUNCTION_DEFINITION => "--function-definition-cost",
_ => "--expression-statement-cost",
};
let entry = costs.entry(cost.to_string()).or_default();
entry.0 += count;
entry.1 += size;
}
let costs = costs
.iter()
.map(|(cost, (count, size))| {
let ratio = *size / *count as u64;
(cost.to_string(), ratio.min(100))
})
.collect::<Vec<_>>();
let scaled_costs = scale_to(
costs
.iter()
.map(|(_, ratio)| *ratio)
.collect::<Vec<_>>()
.as_slice(),
yul_phaser_scale,
);
costs
.iter()
.zip(scaled_costs)
.map(|((cost, _), scaled_ratio)| (cost.to_string(), scaled_ratio))
.collect()
}
}
/// Given a slice of u64 values, returns a Vec<u64> where each element
/// is linearly scaled into the closed interval [1, 10].
fn scale_to(data: &[u64], scale_max: u64) -> Vec<u64> {
if data.is_empty() {
return Vec::new();
}
let mut min = data[0];
let mut max = data[0];
for &x in &data[1..] {
if x < min {
min = x;
}
if x > max {
max = x;
}
}
if max < scale_max {
return data.to_vec();
}
let range = max - min;
data.iter()
.map(|&x| {
if range == 0 {
1
} else {
1 + (x - min) * scale_max / range
}
})
.collect()
}
-6
View File
@@ -1,6 +0,0 @@
//! The revive explorer leverages debug info to get insights into emitted code.
pub mod dwarfdump;
pub mod dwarfdump_analyzer;
pub mod location_mapper;
pub mod yul_phaser;
-158
View File
@@ -1,158 +0,0 @@
//! The location mapper utility maps YUL source locations to AST statements.
//!
//! TODO: Refactor when the AST visitor is implemented.
use std::{collections::HashMap, path::Path};
use revive_yul::{
lexer::{token::location::Location, Lexer},
parser::statement::{
block::Block,
expression::{function_call::name::Name, Expression},
object::Object,
Statement,
},
};
/// Code attributed to an unknown location.
pub const OTHER: &str = "other";
/// Code attributed to a compiler internal location.
pub const INTERNAL: &str = "internal";
/// Code attributed to a
pub const BLOCK: &str = "block";
pub const FUNCTION_CALL: &str = "function_call";
pub const FOR: &str = "for";
pub const IF: &str = "if";
pub const CONTINUE: &str = "continue";
pub const BREAK: &str = "break";
pub const LEAVE: &str = "leave";
pub const SWITCH: &str = "switch";
pub const DECLARATION: &str = "let";
pub const ASSIGNMENT: &str = "assignment";
pub const FUNCTION_DEFINITION: &str = "function_definition";
/// The location to statements map type alias.
pub type LocationMap = HashMap<Location, String>;
/// Construct a [LocationMap] from the given YUL `source` file.
pub fn map_locations(source: &Path) -> anyhow::Result<LocationMap> {
let mut lexer = Lexer::new(std::fs::read_to_string(source)?);
let ast = Object::parse(&mut lexer, None).map_err(|error| {
anyhow::anyhow!("Contract `{}` parsing error: {:?}", source.display(), error)
})?;
let mut location_map = HashMap::with_capacity(1024);
crate::location_mapper::object_mapper(&mut location_map, &ast);
location_map.insert(Location::new(0, 0), OTHER.to_string());
location_map.insert(Location::new(1, 0), INTERNAL.to_string());
Ok(location_map)
}
/// Map the [Block].
fn block_mapper(map: &mut LocationMap, block: &Block) {
map.insert(block.location, BLOCK.to_string());
for statement in &block.statements {
statement_mapper(map, statement);
}
}
/// Map the [Expression].
fn expression_mapper(map: &mut LocationMap, expression: &Expression) {
if let Expression::FunctionCall(call) = expression {
let id = match call.name {
Name::UserDefined(_) => FUNCTION_CALL.to_string(),
_ => format!("{:?}", call.name),
};
map.insert(expression.location(), id);
for expression in &call.arguments {
expression_mapper(map, expression);
}
}
}
/// Map the [Statement].
fn statement_mapper(map: &mut LocationMap, statement: &Statement) {
match statement {
Statement::Object(object) => object_mapper(map, object),
Statement::Code(code) => block_mapper(map, &code.block),
Statement::Block(block) => block_mapper(map, block),
Statement::ForLoop(for_loop) => {
map.insert(for_loop.location, FOR.to_string());
expression_mapper(map, &for_loop.condition);
block_mapper(map, &for_loop.body);
block_mapper(map, &for_loop.initializer);
block_mapper(map, &for_loop.finalizer);
}
Statement::IfConditional(if_conditional) => {
map.insert(if_conditional.location, IF.to_string());
expression_mapper(map, &if_conditional.condition);
block_mapper(map, &if_conditional.block);
}
Statement::Expression(expression) => expression_mapper(map, expression),
Statement::Continue(location) => {
map.insert(*location, CONTINUE.to_string());
}
Statement::Leave(location) => {
map.insert(*location, LEAVE.to_string());
}
Statement::Break(location) => {
map.insert(*location, BREAK.to_string());
}
Statement::Switch(switch) => {
map.insert(switch.expression.location(), SWITCH.to_string());
expression_mapper(map, &switch.expression);
for case in &switch.cases {
block_mapper(map, &case.block);
}
if let Some(block) = switch.default.as_ref() {
block_mapper(map, block);
}
}
Statement::Assignment(assignment) => {
map.insert(assignment.location, ASSIGNMENT.to_string());
expression_mapper(map, &assignment.initializer);
}
Statement::VariableDeclaration(declaration) => {
map.insert(declaration.location, DECLARATION.to_string());
if let Some(expression) = declaration.expression.as_ref() {
expression_mapper(map, expression);
}
}
Statement::FunctionDefinition(definition) => {
map.insert(definition.location, FUNCTION_DEFINITION.to_string());
block_mapper(map, &definition.body);
}
}
}
/// Map the [Object].
fn object_mapper(map: &mut LocationMap, object: &Object) {
map.insert(object.location, object.identifier.clone());
block_mapper(map, &object.code.block);
if let Some(object) = object.inner_object.as_ref() {
object_mapper(map, object);
}
}
-56
View File
@@ -1,56 +0,0 @@
use std::path::PathBuf;
use clap::Parser;
use revive_explorer::{dwarfdump, dwarfdump_analyzer::DwarfdumpAnalyzer, yul_phaser};
/// The `revive-explorer` is a helper utility for exploring the compilers YUL lowering unit.
///
/// It analyzes a given shared objects from the debug dump and outputs:
/// - The count of each YUL statement translated.
/// - A per YUL statement break-down of bytecode size contributed per.
/// - Estimated `yul-phaser` cost parameters.
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Path of the dwarfdump executable.
#[arg(short, long)]
dwarfdump: Option<PathBuf>,
/// The YUL phaser cost scale maximum value.
#[arg(short, long, default_value_t = 10)]
cost_scale: u64,
/// Run the provided yul-phaser executable using the estimated costs.
#[arg(short, long)]
yul_phaser: Option<PathBuf>,
/// Path of the shared object to analyze.
file: PathBuf,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let source_file = dwarfdump::source_file(&args.file, &args.dwarfdump)?;
let debug_lines = dwarfdump::debug_lines(&args.file, &args.dwarfdump)?;
let mut analyzer = DwarfdumpAnalyzer::new(source_file.as_path(), debug_lines);
analyzer.analyze()?;
if let Some(path) = args.yul_phaser.as_ref() {
yul_phaser::run(
path,
source_file.as_path(),
analyzer.phaser_costs(args.cost_scale).as_slice(),
num_cpus::get() / 2, // TODO: should be configurable.
)?;
return Ok(());
}
analyzer.display_statement_count();
analyzer.display_statement_size();
analyzer.display_phaser_costs(args.cost_scale);
Ok(())
}
-79
View File
@@ -1,79 +0,0 @@
//! The revive explorer YUL phaser utility library.
//!
//! This can be used to invoke the `yul-phaser` utility,
//! used to find better YUL optimizer sequences.
use std::{
path::{Path, PathBuf},
process::{Command, Stdio},
thread,
time::{SystemTime, UNIX_EPOCH},
};
/// The `yul-phaser` sane default arguments:
/// - Less verbose output.
/// - Sufficient rounds.
/// - Sufficient random population start.
const ARGUMENTS: [&str; 6] = [
"--hide-round",
"--rounds",
"1000",
"--random-population",
"100",
"--show-only-top-chromosome",
];
/// Run multiple YUL phaser executables in parallel.
pub fn run(
executable: &Path,
source: &Path,
costs: &[(String, u64)],
n_threads: usize,
) -> anyhow::Result<()> {
let mut handles = Vec::with_capacity(n_threads);
for n in 0..n_threads {
let executable = executable.to_path_buf();
let source = source.to_path_buf();
let costs = costs.to_vec();
handles.push(thread::spawn(move || {
spawn_process(executable, source, costs, n)
}));
}
for handle in handles {
let _ = handle.join();
}
Ok(())
}
/// The `yul-phaser` process spawning helper function.
fn spawn_process(
executable: PathBuf,
source: PathBuf,
costs: Vec<(String, u64)>,
seed: usize,
) -> anyhow::Result<()> {
let cost_parameters = costs
.iter()
.flat_map(|(parameter, cost)| vec![parameter.clone(), cost.to_string()]);
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
Command::new(executable)
.args(cost_parameters)
.args(ARGUMENTS)
.arg("--seed")
.arg((seed + secs as usize).to_string())
.arg(source)
.stdin(Stdio::null())
.spawn()?
.wait()?;
Ok(())
}
+8 -8
View File
@@ -1,10 +1,10 @@
{ {
"Baseline": 960, "Baseline": 945,
"Computation": 2367, "Computation": 2308,
"DivisionArithmetics": 9108, "DivisionArithmetics": 2334,
"ERC20": 17655, "ERC20": 21363,
"Events": 1680, "Events": 1677,
"FibonacciIterative": 1536, "FibonacciIterative": 1516,
"Flipper": 2137, "Flipper": 2099,
"SHA1": 8299 "SHA1": 8268
} }
@@ -15,6 +15,8 @@ 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;
use crate::polkavm::context::pointer::Pointer; use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use self::declaration::Declaration; use self::declaration::Declaration;
use self::r#return::Return; use self::r#return::Return;
@@ -28,7 +30,9 @@ pub struct Function<'ctx> {
/// The LLVM function declaration. /// The LLVM function declaration.
declaration: Declaration<'ctx>, declaration: Declaration<'ctx>,
/// The stack representation. /// The stack representation.
stack: HashMap<String, Pointer<'ctx>>, stack: HashMap<String, u64>,
/// The stack variables pointer.
stack_variables: Pointer<'ctx>,
/// The return value entity. /// The return value entity.
r#return: Return<'ctx>, r#return: Return<'ctx>,
@@ -56,11 +60,14 @@ impl<'ctx> Function<'ctx> {
entry_block: inkwell::basic_block::BasicBlock<'ctx>, entry_block: inkwell::basic_block::BasicBlock<'ctx>,
return_block: inkwell::basic_block::BasicBlock<'ctx>, return_block: inkwell::basic_block::BasicBlock<'ctx>,
stack_variables: Pointer<'ctx>,
) -> Self { ) -> Self {
Self { Self {
name, name,
declaration, declaration,
stack: HashMap::with_capacity(Self::STACK_HASHMAP_INITIAL_CAPACITY), stack: HashMap::with_capacity(Self::STACK_HASHMAP_INITIAL_CAPACITY),
stack_variables,
r#return, r#return,
entry_block, entry_block,
@@ -210,22 +217,49 @@ impl<'ctx> Function<'ctx> {
/// Saves the pointer to a stack variable, returning the pointer to the shadowed variable, /// Saves the pointer to a stack variable, returning the pointer to the shadowed variable,
/// if it exists. /// if it exists.
pub fn insert_stack_pointer( pub fn insert_stack_pointer<D: Dependency + Clone>(
&mut self, &mut self,
context: &mut Context<'ctx, D>,
name: String, name: String,
pointer: Pointer<'ctx>, ) -> Pointer<'ctx> {
) -> Option<Pointer<'ctx>> { let pointer_name = format!("{}_stack_pointer", &name);
self.stack.insert(name, pointer) let len = self.stack.len();
let index = *self.stack.entry(name).or_insert_with(|| len as u64);
let indices = &[
context.xlen_type().const_zero(),
context.xlen_type().const_int(index, false),
];
context.build_gep(
self.stack_variables,
indices,
context.word_type(),
&pointer_name,
)
} }
/// Gets the pointer to a stack variable. /// Gets the pointer to a stack variable.
pub fn get_stack_pointer(&self, name: &str) -> Option<Pointer<'ctx>> { pub fn get_stack_pointer<D: Dependency + Clone>(
self.stack.get(name).copied() &self,
} context: &mut Context<'ctx, D>,
name: String,
) -> Pointer<'ctx> {
let pointer_name = format!("{}_stack_pointer", &name);
let index = *self
.stack
.get(&name)
.unwrap_or_else(|| panic!("stack pointer access prior to insertion: {name}"));
let indices = &[
context.xlen_type().const_zero(),
context.xlen_type().const_int(index, false),
];
/// Removes the pointer to a stack variable. context.build_gep(
pub fn remove_stack_pointer(&mut self, name: &str) { self.stack_variables,
self.stack.remove(name); indices,
context.word_type(),
&pointer_name,
)
} }
/// Returns the return entity representation. /// Returns the return entity representation.
@@ -48,7 +48,7 @@ where
function_type, function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 1024,
)?; )?;
self.inner.declare(context) self.inner.declare(context)
@@ -75,6 +75,7 @@ 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(); context.pop_debug_scope();
@@ -145,7 +145,7 @@ where
entry_function_type, entry_function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 0,
)?; )?;
context.declare_global( context.declare_global(
@@ -48,7 +48,7 @@ where
function_type, function_type,
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 1024,
)?; )?;
self.inner.declare(context) self.inner.declare(context)
+50 -29
View File
@@ -463,7 +463,7 @@ where
r#type: inkwell::types::FunctionType<'ctx>, r#type: inkwell::types::FunctionType<'ctx>,
return_values_length: usize, return_values_length: usize,
linkage: Option<inkwell::module::Linkage>, linkage: Option<inkwell::module::Linkage>,
location: Option<(u32, u32)>, stack_variables: u32,
) -> 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);
@@ -479,22 +479,36 @@ where
Some(scp) => scp, Some(scp) => scp,
}; };
self.push_debug_scope(func_scope.as_debug_info_scope()); self.push_debug_scope(func_scope.as_debug_info_scope());
let (line, column) = location.unwrap_or((0, 0)); self.set_debug_location(0, 0, Some(func_scope.as_debug_info_scope()))?;
self.set_debug_location(line, column, 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");
self.builder().position_at_end(entry_block);
let stack_variables_pointer = self.build_alloca(
self.word_type().array_type(stack_variables),
"stack_variables",
);
let r#return = match return_values_length { let r#return = match return_values_length {
0 => FunctionReturn::none(), 0 => FunctionReturn::none(),
1 => { 1 => {
self.set_basic_block(entry_block); //self.set_basic_block(entry_block);
let pointer = self.build_alloca(self.word_type(), "return_pointer"); //let pointer = self
FunctionReturn::primitive(pointer) // .current_function()
// .borrow_mut()
// .insert_stack_pointer(self, format!("{name}_return_pointer"));
FunctionReturn::primitive(self.build_gep(
stack_variables_pointer,
&[self.xlen_type().const_zero(), self.xlen_type().const_zero()],
self.word_type(),
"return_pointer",
))
} }
size => { size => {
self.set_basic_block(entry_block); //self.set_basic_block(entry_block);
let pointer = self.build_alloca( let pointer = self.build_alloca(
self.structure_type( self.structure_type(
vec![self.word_type().as_basic_type_enum(); size].as_slice(), vec![self.word_type().as_basic_type_enum(); size].as_slice(),
@@ -511,6 +525,7 @@ where
r#return, r#return,
entry_block, entry_block,
return_block, return_block,
stack_variables_pointer,
); );
Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer); Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer);
let function = Rc::new(RefCell::new(function)); let function = Rc::new(RefCell::new(function));
@@ -535,11 +550,7 @@ where
/// Sets the current active function. If debug-info generation is enabled, /// Sets the current active function. If debug-info generation is enabled,
/// constructs a debug-scope and pushes in on the scope-stack. /// constructs a debug-scope and pushes in on the scope-stack.
pub fn set_current_function( pub fn set_current_function(&mut self, name: &str, line: Option<u32>) -> anyhow::Result<()> {
&mut self,
name: &str,
location: Option<(u32, 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)
})?; })?;
@@ -548,8 +559,7 @@ where
if let Some(scope) = self.current_function().borrow().get_debug_scope() { if let Some(scope) = self.current_function().borrow().get_debug_scope() {
self.push_debug_scope(scope); self.push_debug_scope(scope);
} }
let (line, column) = location.unwrap_or_default(); self.set_debug_location(line.unwrap_or_default(), 0, None)?;
self.set_debug_location(line, column, None)?;
Ok(()) Ok(())
} }
@@ -730,25 +740,36 @@ where
r#type: T, r#type: T,
name: &str, name: &str,
) -> Pointer<'ctx> { ) -> Pointer<'ctx> {
// TODO: Revisit. While at entry should be preferred in theory: let current_block = self.basic_block();
// - It has negligible code size impact on real word contracts. let entry_block = self.current_function().borrow().entry_block();
// - Sometimes has negative impact on code size.
// - Messes up debug information used to analyze code size issues.
self.build_alloca(r#type, name)
// let current_block = self.basic_block(); match entry_block.get_first_instruction() {
// let entry_block = self.current_function().borrow().entry_block(); Some(instruction) => self.builder().position_before(&instruction),
None => self.builder().position_at_end(entry_block),
}
// match entry_block.get_first_instruction() { let pointer = self.build_alloca(r#type, name);
// Some(instruction) => self.builder().position_before(&instruction), self.set_basic_block(current_block);
// None => self.builder().position_at_end(entry_block), pointer
// }
// let pointer = self.build_alloca(r#type, name);
// self.set_basic_block(current_block);
// pointer
} }
pub fn variable_decl<T: BasicType<'ctx> + Clone + Copy>(
&self,
r#type: T,
name: &str,
) -> Pointer<'ctx> {
let pointer = self.builder.build_alloca(r#type, name).unwrap();
pointer
.as_instruction()
.unwrap()
.set_alignment(revive_common::BYTE_LENGTH_STACK_ALIGN as u32)
.expect("Alignment is valid");
Pointer::new(r#type, AddressSpace::Stack, pointer)
}
/// Truncate `address` to the ethereum address length and store it as bytes on the stack.
/// Builds an aligned stack allocation at the current position. /// Builds an aligned stack allocation at the current position.
/// Use this if [`build_alloca_at_entry`] might change program semantics. /// Use this if [`build_alloca_at_entry`] might change program semantics.
/// Otherwise, alloca should always be built at the function prelude! /// Otherwise, alloca should always be built at the function prelude!
@@ -35,7 +35,7 @@ where
Self::r#type(context), Self::r#type(context),
0, 0,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 0,
)?; )?;
let mut attributes = Self::ATTRIBUTES.to_vec(); let mut attributes = Self::ATTRIBUTES.to_vec();
@@ -40,7 +40,7 @@ pub fn check_attribute_null_pointer_is_invalid() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 16,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -64,7 +64,7 @@ pub fn check_attribute_optimize_for_size_mode_3() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 16,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -88,7 +88,7 @@ pub fn check_attribute_optimize_for_size_mode_z() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 16,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(function assert!(function
@@ -112,7 +112,7 @@ pub fn check_attribute_min_size_mode_3() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 16,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(!function assert!(!function
@@ -136,7 +136,7 @@ pub fn check_attribute_min_size_mode_z() {
.fn_type(&[context.word_type().into()], false), .fn_type(&[context.word_type().into()], false),
1, 1,
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
None, 16,
) )
.expect("Failed to add function"); .expect("Failed to add function");
assert!(function assert!(function
@@ -14,7 +14,7 @@ use self::file::File as FileSelection;
pub struct Selection { pub struct Selection {
/// Only the 'all' wildcard is available for robustness reasons. /// Only the 'all' wildcard is available for robustness reasons.
#[serde(rename = "*", skip_serializing_if = "Option::is_none")] #[serde(rename = "*", skip_serializing_if = "Option::is_none")]
pub all: Option<FileSelection>, all: Option<FileSelection>,
#[serde(skip_serializing_if = "BTreeMap::is_empty", flatten)] #[serde(skip_serializing_if = "BTreeMap::is_empty", flatten)]
pub files: BTreeMap<String, FileSelection>, pub files: BTreeMap<String, FileSelection>,
-10
View File
@@ -1,8 +1,5 @@
//! The lexical token location. //! The lexical token location.
use std::hash::Hash;
use std::hash::Hasher;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -51,13 +48,6 @@ impl PartialEq for Location {
} }
} }
impl Hash for Location {
fn hash<H: Hasher>(&self, state: &mut H) {
self.line.hash(state);
self.column.hash(state);
}
}
impl std::fmt::Display for Location { impl std::fmt::Display for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.line, self.column) write!(f, "{}:{}", self.line, self.column)
+6 -20
View File
@@ -119,7 +119,7 @@ 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, self.location.column, None)?; 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,
@@ -130,15 +130,8 @@ where
let identifier = self.bindings.remove(0); let identifier = self.bindings.remove(0);
let pointer = context let pointer = context
.current_function() .current_function()
.borrow() .borrow_mut()
.get_stack_pointer(identifier.inner.as_str()) .get_stack_pointer(context, identifier.inner.clone());
.ok_or_else(|| {
anyhow::anyhow!(
"{} Assignment to an undeclared variable `{}`",
identifier.location,
identifier.inner,
)
})?;
context.build_store(pointer, value.access(context)?)?; context.build_store(pointer, value.access(context)?)?;
return Ok(()); return Ok(());
} }
@@ -149,7 +142,7 @@ where
context.build_store(tuple_pointer, value)?; context.build_store(tuple_pointer, value)?;
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, self.location.column, None)?; 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,
@@ -165,15 +158,8 @@ where
let binding_pointer = context let binding_pointer = context
.current_function() .current_function()
.borrow() .borrow_mut()
.get_stack_pointer(binding.inner.as_str()) .get_stack_pointer(context, binding.inner.clone());
.ok_or_else(|| {
anyhow::anyhow!(
"{} Assignment to an undeclared variable `{}`",
binding.location,
binding.inner,
)
})?;
let value = context.build_load( let value = context.build_load(
field_pointer, field_pointer,
format!("assignment_binding_{index}_value").as_str(), format!("assignment_binding_{index}_value").as_str(),
+3 -10
View File
@@ -155,10 +155,7 @@ where
function.into_llvm(context)?; function.into_llvm(context)?;
} }
context.set_current_function( context.set_current_function(current_function.as_str(), Some(self.location.line))?;
current_function.as_str(),
Some((self.location.line, self.location.column)),
)?;
if let Some(debug_info) = context.debug_info() { if let Some(debug_info) = context.debug_info() {
let di_builder = debug_info.builder(); let di_builder = debug_info.builder();
@@ -172,16 +169,12 @@ where
) )
.as_debug_info_scope(); .as_debug_info_scope();
context.push_debug_scope(di_block_scope); context.push_debug_scope(di_block_scope);
context.set_debug_location(self.location.line, self.location.column, None)?; 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( context.set_debug_location(statement.location().line, 0, None)?;
statement.location().line,
statement.location().column,
None,
)?;
if context.basic_block().get_terminator().is_some() { if context.basic_block().get_terminator().is_some() {
break; break;
} }
@@ -123,7 +123,7 @@ impl FunctionCall {
D: revive_llvm_context::PolkaVMDependency + Clone, D: revive_llvm_context::PolkaVMDependency + Clone,
{ {
let location = self.location; let location = self.location;
context.set_debug_location(location.line, location.column, None)?; context.set_debug_location(location.line, 0, None)?;
match self.name { match self.name {
Name::UserDefined(name) => { Name::UserDefined(name) => {
@@ -997,8 +997,6 @@ impl FunctionCall {
} }
arguments.reverse(); arguments.reverse();
context.set_debug_location(self.location.line, self.location.column, None)?;
Ok(arguments.try_into().expect("Always successful")) Ok(arguments.try_into().expect("Always successful"))
} }
@@ -1016,8 +1014,6 @@ impl FunctionCall {
} }
arguments.reverse(); arguments.reverse();
context.set_debug_location(self.location.line, self.location.column, None)?;
Ok(arguments.try_into().expect("Always successful")) Ok(arguments.try_into().expect("Always successful"))
} }
} }
@@ -123,11 +123,8 @@ impl Expression {
let pointer = context let pointer = context
.current_function() .current_function()
.borrow() .borrow_mut()
.get_stack_pointer(&id) .get_stack_pointer(context, id.clone());
.ok_or_else(|| {
anyhow::anyhow!("{} Undeclared variable `{}`", identifier.location, id)
})?;
let constant = context.current_function().borrow().yul().get_constant(&id); let constant = context.current_function().borrow().yul().get_constant(&id);
@@ -72,7 +72,6 @@ where
let increment_block = context.append_basic_block("for_increment"); let increment_block = context.append_basic_block("for_increment");
let join_block = context.append_basic_block("for_join"); let join_block = context.append_basic_block("for_join");
context.set_debug_location(self.location.line, self.location.column, None)?;
context.build_unconditional_branch(condition_block); context.build_unconditional_branch(condition_block);
context.set_basic_block(condition_block); context.set_basic_block(condition_block);
let condition = self let condition = self
@@ -81,7 +80,6 @@ where
.expect("Always exists") .expect("Always exists")
.access(context)? .access(context)?
.into_int_value(); .into_int_value();
context.set_debug_location(self.location.line, self.location.column, None)?;
let condition = context.builder().build_int_z_extend_or_bit_cast( let condition = context.builder().build_int_z_extend_or_bit_cast(
condition, condition,
context.word_type(), context.word_type(),
@@ -100,12 +98,10 @@ where
context.set_basic_block(body_block); context.set_basic_block(body_block);
self.body.into_llvm(context)?; self.body.into_llvm(context)?;
context.build_unconditional_branch(increment_block); context.build_unconditional_branch(increment_block);
context.set_debug_location(self.location.line, self.location.column, None)?;
context.set_basic_block(increment_block); context.set_basic_block(increment_block);
self.finalizer.into_llvm(context)?; self.finalizer.into_llvm(context)?;
context.build_unconditional_branch(condition_block); context.build_unconditional_branch(condition_block);
context.set_debug_location(self.location.line, self.location.column, None)?;
context.pop_loop(); context.pop_loop();
context.set_basic_block(join_block); context.set_basic_block(join_block);
@@ -196,7 +196,6 @@ 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, self.location.column, None)?;
let argument_types: Vec<_> = self let argument_types: Vec<_> = self
.arguments .arguments
.iter() .iter()
@@ -213,7 +212,7 @@ where
function_type, function_type,
self.result.len(), self.result.len(),
Some(inkwell::module::Linkage::External), Some(inkwell::module::Linkage::External),
Some((self.location.line, self.location.column)), 1024,
)?; )?;
revive_llvm_context::PolkaVMFunction::set_attributes( revive_llvm_context::PolkaVMFunction::set_attributes(
context.llvm(), context.llvm(),
@@ -232,10 +231,7 @@ 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( context.set_current_function(self.identifier.as_str(), Some(self.location.line))?;
self.identifier.as_str(),
Some((self.location.line, self.location.column)),
)?;
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(); let r#return = context.current_function().borrow().r#return();
@@ -249,7 +245,7 @@ where
context context
.current_function() .current_function()
.borrow_mut() .borrow_mut()
.insert_stack_pointer(identifier.inner, pointer); .insert_stack_pointer(context, identifier.inner);
} }
revive_llvm_context::PolkaVMFunctionReturn::Compound { pointer, .. } => { revive_llvm_context::PolkaVMFunctionReturn::Compound { pointer, .. } => {
for (index, identifier) in self.result.into_iter().enumerate() { for (index, identifier) in self.result.into_iter().enumerate() {
@@ -269,25 +265,24 @@ where
context context
.current_function() .current_function()
.borrow_mut() .borrow_mut()
.insert_stack_pointer(identifier.inner.clone(), pointer); .insert_stack_pointer(context, identifier.inner.clone());
} }
} }
}; };
let argument_types: Vec<_> = self //let argument_types: Vec<_> = self
.arguments // .arguments
.iter() // .iter()
.map(|argument| { // .map(|argument| {
let yul_type = argument.r#type.to_owned().unwrap_or_default(); // let yul_type = argument.r#type.to_owned().unwrap_or_default();
yul_type.into_llvm(context) // yul_type.into_llvm(context)
}) // })
.collect(); // .collect();
for (index, argument) in self.arguments.iter().enumerate() { for (index, argument) in self.arguments.iter().enumerate() {
let pointer = context.build_alloca(argument_types[index], argument.inner.as_str()); let pointer = context
context
.current_function() .current_function()
.borrow_mut() .borrow_mut()
.insert_stack_pointer(argument.inner.clone(), pointer); .insert_stack_pointer(context, argument.inner.clone());
context.build_store( context.build_store(
pointer, pointer,
context.current_function().borrow().get_nth_param(index), context.current_function().borrow().get_nth_param(index),
@@ -295,7 +290,7 @@ where
} }
self.body.into_llvm(context)?; self.body.into_llvm(context)?;
context.set_debug_location(self.location.line, self.location.column, None)?; context.set_debug_location(self.location.line, 0, None)?;
match context match context
.basic_block() .basic_block()
@@ -53,13 +53,13 @@ where
D: revive_llvm_context::PolkaVMDependency + Clone, D: revive_llvm_context::PolkaVMDependency + Clone,
{ {
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_debug_location(self.location.line, 0, None)?;
let condition = self let condition = self
.condition .condition
.into_llvm(context)? .into_llvm(context)?
.expect("Always exists") .expect("Always exists")
.access(context)? .access(context)?
.into_int_value(); .into_int_value();
context.set_debug_location(self.location.line, self.location.column, None)?;
let condition = context.builder().build_int_z_extend_or_bit_cast( let condition = context.builder().build_int_z_extend_or_bit_cast(
condition, condition,
context.word_type(), context.word_type(),
+2 -2
View File
@@ -279,17 +279,17 @@ where
context.push_debug_scope(object_scope.as_debug_info_scope()); context.push_debug_scope(object_scope.as_debug_info_scope());
} }
context.set_debug_location(self.location.line, self.location.column, None)?;
if self.identifier.ends_with("_deployed") { if self.identifier.ends_with("_deployed") {
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, self.location.column, None)?; context.set_debug_location(self.location.line, 0, None)?;
context.pop_debug_scope(); context.pop_debug_scope();
@@ -123,7 +123,6 @@ where
D: revive_llvm_context::PolkaVMDependency + Clone, D: revive_llvm_context::PolkaVMDependency + Clone,
{ {
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_debug_location(self.location.line, self.location.column, None)?;
let scrutinee = self.expression.into_llvm(context)?; let scrutinee = self.expression.into_llvm(context)?;
if self.cases.is_empty() { if self.cases.is_empty() {
@@ -144,7 +143,6 @@ where
.append_basic_block(format!("switch_case_branch_{}_block", index + 1).as_str()); .append_basic_block(format!("switch_case_branch_{}_block", index + 1).as_str());
context.set_basic_block(expression_block); context.set_basic_block(expression_block);
case.block.into_llvm(context)?; case.block.into_llvm(context)?;
context.set_debug_location(self.location.line, self.location.column, None)?;
context.build_unconditional_branch(join_block); context.build_unconditional_branch(join_block);
branches.push((constant.into_int_value(), expression_block)); branches.push((constant.into_int_value(), expression_block));
@@ -161,7 +159,6 @@ where
None => join_block, None => join_block,
}; };
context.set_debug_location(self.location.line, self.location.column, None)?;
context.set_basic_block(current_block); context.set_basic_block(current_block);
context.builder().build_switch( context.builder().build_switch(
scrutinee scrutinee
@@ -172,7 +169,6 @@ where
branches.as_slice(), branches.as_slice(),
)?; )?;
context.set_debug_location(self.location.line, self.location.column, None)?;
context.set_basic_block(join_block); context.set_basic_block(join_block);
Ok(()) Ok(())
@@ -101,14 +101,13 @@ 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);
context.set_debug_location(self.location.line, self.location.column, None)?; context.set_debug_location(self.location.line, 0, None)?;
let identifier_type = identifier.r#type.clone().unwrap_or_default(); let identifier_type = identifier.r#type.clone().unwrap_or_default();
let r#type = identifier_type.into_llvm(context); let r#type = identifier_type.into_llvm(context);
let pointer = context.build_alloca(r#type, identifier.inner.as_str()); let pointer = context
context
.current_function() .current_function()
.borrow_mut() .borrow_mut()
.insert_stack_pointer(identifier.inner.clone(), pointer); .insert_stack_pointer(context, identifier.inner.clone());
let value = if let Some(expression) = self.expression { let value = if let Some(expression) = self.expression {
match expression.into_llvm(context)? { match expression.into_llvm(context)? {
@@ -133,22 +132,18 @@ where
} }
for (index, binding) in self.bindings.iter().enumerate() { for (index, binding) in self.bindings.iter().enumerate() {
context.set_debug_location(self.location.line, self.location.column, None)?; 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()
.unwrap_or_default() .unwrap_or_default()
.into_llvm(context); .into_llvm(context);
let pointer = context.build_alloca( let pointer = context
yul_type.as_basic_type_enum(),
format!("binding_{index}_pointer").as_str(),
);
context.build_store(pointer, yul_type.const_zero())?;
context
.current_function() .current_function()
.borrow_mut() .borrow_mut()
.insert_stack_pointer(binding.inner.to_owned(), pointer); .insert_stack_pointer(context, binding.inner.to_owned());
context.build_store(pointer, yul_type.const_zero())?;
} }
let expression = match self.expression.take() { let expression = match self.expression.take() {
@@ -203,14 +198,7 @@ where
let pointer = context let pointer = context
.current_function() .current_function()
.borrow_mut() .borrow_mut()
.get_stack_pointer(binding.inner.as_str()) .get_stack_pointer(context, binding.inner.to_owned());
.ok_or_else(|| {
anyhow::anyhow!(
"{} Assignment to an undeclared variable `{}`",
binding.location,
binding.inner
)
})?;
context.build_store(pointer, value)?; context.build_store(pointer, value)?;
} }
+4 -3
View File
@@ -70,10 +70,11 @@ impl Type {
D: revive_llvm_context::PolkaVMDependency + Clone, D: revive_llvm_context::PolkaVMDependency + Clone,
{ {
match self { match self {
Self::Bool => context.integer_type(revive_common::BIT_LENGTH_BOOLEAN), //Self::Bool => context.integer_type(revive_common::BIT_LENGTH_BOOLEAN),
Self::Int(bitlength) => context.integer_type(bitlength), //Self::Int(bitlength) => context.integer_type(bitlength),
Self::UInt(bitlength) => context.integer_type(bitlength), Self::UInt(bitlength) => context.integer_type(bitlength),
Self::Custom(_) => context.word_type(), //Self::Custom(_) => context.word_type(),
_ => unreachable!("no other YUL type is supported"),
} }
} }
} }
+4 -16
View File
@@ -743,30 +743,18 @@
} }
}, },
"node_modules/@eslint/plugin-kit": { "node_modules/@eslint/plugin-kit": {
"version": "0.3.3", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
"integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/core": "^0.15.1", "@eslint/core": "^0.14.0",
"levn": "^0.4.1" "levn": "^0.4.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eth-optimism/contracts": { "node_modules/@eth-optimism/contracts": {
"version": "0.5.40", "version": "0.5.40",
"resolved": "https://registry.npmjs.org/@eth-optimism/contracts/-/contracts-0.5.40.tgz", "resolved": "https://registry.npmjs.org/@eth-optimism/contracts/-/contracts-0.5.40.tgz",