Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0f341cf
start porting graph optimizations to use the analysis info system
BramOtte Sep 19, 2025
12b92d3
Use bram's ss_range_analyser
BramOtte Sep 19, 2025
bb27993
Use better signatures
BramOtte Sep 19, 2025
92fab84
use bitset instead of low and high for SSRange
BramOtte Sep 24, 2025
a096c84
Speedup coalesce2 pass by not operating on the CompileGraph directly
BramOtte Dec 27, 2025
693017b
Use normalized distance instead of signature and construct new range_…
BramOtte Dec 28, 2025
49e5269
run cargo fmt
BramOtte Dec 28, 2025
e51d35e
Optimize coalesce2 pass by reducing allocations and clones
BramOtte Dec 29, 2025
53d504e
run cargo fmt
BramOtte Dec 29, 2025
a8b5e39
Coalesce output components and keep a list of all the blocks that hav…
BramOtte Dec 29, 2025
d257be0
Optimize ss_range_analysis by only iterating over nodes that are actu…
BramOtte Dec 30, 2025
acd179e
coalesce2: Use split_at_mut instead of from_raw_parts to allocate inp…
BramOtte Dec 31, 2025
702c4e3
run cargo fmt
BramOtte Dec 31, 2025
983a89e
coalesce2: remove unsafe clone
BramOtte Dec 31, 2025
ebb3ad8
coalesce2: use single insert on nod_map instead of get + insert
BramOtte Dec 31, 2025
a7601a4
coalesce2: use or_insert instead of insert for nod_map
BramOtte Jan 1, 2026
8bfd327
run cargo fmt
BramOtte Jan 1, 2026
c5c5ea6
coalesce2: use vec of bool to dedup outputs being selected for the ne…
BramOtte Jan 1, 2026
396b9e8
Incorporate pending ticks into compile_graph::NodeState
BramOtte Feb 13, 2026
c834bee
run cargo fmt
BramOtte Feb 13, 2026
1be3f4c
Fix breakage from rebase
BramOtte Mar 25, 2026
c0b3339
make coalesce2 dedup links
BramOtte Mar 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
/target
/test_proxy
/generators
/.direnv
/.direnv
/Config.toml
/world
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ hmac = "0.12"
sha2 = "0.10"
bitvec = "1"
flate2 = "1"
smallvec = { version = "1.15.1", features = ["union"] }
enum_dispatch = "0.3"
petgraph = "0.8"
thiserror = "2"
Expand Down
1 change: 1 addition & 0 deletions crates/redpiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ itertools = { workspace = true }
rustc-hash = { workspace = true }
enum_dispatch = { workspace = true }
indexmap = { workspace = true }
smallvec = { workspace = true }
45 changes: 45 additions & 0 deletions crates/redpiler/ril_tests/opt/constant_fold2.ril
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
test_args "--passes=constant-fold-2"

circuit @simple_input {
%constant = constant 1
%repeater1 = repeater 1, false, false, true, [%constant:0], []
%repeater2 = repeater 1, false, false, true, [%repeater1:0], []
%lamp = lamp true, [%repeater2:0]
}

test(@simple_input) circuit @simple {
%lamp = lamp true, [%GLOBAL_CONSTANT15:0]
%GLOBAL_CONSTANT15 = constant 15
}

circuit @comparator_input {
%constant5 = constant 5
%constant4 = constant 4
%constant10 = constant 10
# 5 - 4
%comp_sub1 = comparator subtract, none, false, 1, [%constant5:0], [%constant4:0]
# 4 - 5
%comp_sub2 = comparator subtract, none, false, 1, [%constant4:0], [%constant5:0]
# (10 - 1) - (10 - 5)
%comp_sub3 = comparator subtract, none, false, 1, [%constant10:1], [%constant10:5]
}

test(@comparator_input) circuit @comparator {
%comp_sub2 = comparator subtract, none, false, 1, [%GLOBAL_CONSTANT15:11], [%GLOBAL_CONSTANT15:10]
%comp_sub3 = comparator subtract, none, false, 1, [%GLOBAL_CONSTANT15:6], [%GLOBAL_CONSTANT15:10]
%GLOBAL_CONSTANT15 = constant 15
}

circuit @locking_rep_input {
%constant = constant 1
%lever = lever false
%side_rep = repeater 1, true, false, false, [%lever:0], []
# This repeater should be untouched
%locking_rep = repeater 1, false, false, true, [%constant:0], [%side_rep:0]
}

test(@locking_rep_input) circuit @locking_rep {
%lever = lever false
%side_rep = repeater 1, true, false, false, [%lever:0], []
%GLOBAL_CONSTANT15 = constant 15
}
19 changes: 15 additions & 4 deletions crates/redpiler/src/backend/direct/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use mchprs_world::TickEntry;
use petgraph::visit::EdgeRef;
use petgraph::Direction;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use std::sync::Arc;
use tracing::trace;

Expand All @@ -26,7 +27,7 @@ fn compile_node(
node_idx: NodeIdx,
nodes_len: usize,
nodes_map: &FxHashMap<NodeIdx, usize>,
noteblock_info: &mut Vec<(BlockPos, Instrument, u8)>,
noteblock_info: &mut Vec<(SmallVec<[BlockPos; 1]>, Instrument, u8)>,
forward_links: &mut Vec<ForwardLink>,
stats: &mut FinalGraphStats,
) -> Node {
Expand Down Expand Up @@ -123,7 +124,11 @@ fn compile_node(
CNodeType::Constant => NodeType::Constant,
CNodeType::NoteBlock { instrument, note } => {
let noteblock_id = noteblock_info.len().try_into().unwrap();
noteblock_info.push((node.block.unwrap().0, *instrument, *note));
noteblock_info.push((
node.block.iter().copied().map(|(pos, _)| pos).collect(),
*instrument,
*note,
));
NodeType::NoteBlock { noteblock_id }
}
};
Expand Down Expand Up @@ -178,13 +183,19 @@ pub fn compile(

backend.blocks = graph
.node_weights()
.map(|node| node.block.map(|(pos, id)| (pos, Block::from_id(id))))
.map(|node| {
node.block
.iter()
.copied()
.map(|(pos, id)| (pos, Block::from_id(id)))
.collect()
})
.collect();
backend.nodes = Nodes::new(nodes);

// Create a mapping from block pos to backend NodeId
for i in 0..backend.blocks.len() {
if let Some((pos, _)) = backend.blocks[i] {
for (pos, _) in backend.blocks[i].iter().copied() {
backend.pos_map.insert(pos, backend.nodes.get(i));
}
}
Expand Down
66 changes: 39 additions & 27 deletions crates/redpiler/src/backend/direct/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use mchprs_redstone::{bool_to_ss, noteblock};
use mchprs_world::{TickEntry, TickPriority, World};
use node::{Node, NodeId, NodeType, Nodes};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use std::fmt::Write;
use std::sync::Arc;
use std::{fmt, mem};
use tracing::{debug, warn};
Expand Down Expand Up @@ -46,7 +48,7 @@ impl TickScheduler {
const NUM_PRIORITIES: usize = 4;
const NUM_QUEUES: usize = 16;

fn reset<W: World>(&mut self, world: &mut W, blocks: &[Option<(BlockPos, Block)>]) {
fn reset<W: World>(&mut self, world: &mut W, blocks: &[impl AsRef<[(BlockPos, Block)]>]) {
for (idx, queues) in self.queues_deque.iter().enumerate() {
let delay = if self.pos >= idx {
idx + Self::NUM_QUEUES
Expand All @@ -55,11 +57,12 @@ impl TickScheduler {
} - self.pos;
for (entries, priority) in queues.0.iter().zip(Self::priorities()) {
for node in entries {
let Some((pos, _)) = blocks[node.index()] else {
if blocks[node.index()].as_ref().is_empty() {
warn!("Cannot schedule tick for node {:?} because block information is missing", node);
continue;
};
world.schedule_tick(pos, delay as u32, priority);
}
for (pos, _) in blocks[node.index()].as_ref().iter().copied() {
world.schedule_tick(pos, delay as u32, priority);
}
}
}
}
Expand Down Expand Up @@ -115,11 +118,11 @@ enum Event {
pub struct DirectBackend {
nodes: Nodes,
forward_links: Vec<ForwardLink>,
blocks: Vec<Option<(BlockPos, Block)>>,
blocks: Vec<SmallVec<[(BlockPos, Block); 1]>>,
pos_map: FxHashMap<BlockPos, NodeId>,
scheduler: TickScheduler,
events: Vec<Event>,
noteblock_info: Vec<(BlockPos, Instrument, u8)>,
noteblock_info: Vec<(SmallVec<[BlockPos; 1]>, Instrument, u8)>,
}

impl DirectBackend {
Expand Down Expand Up @@ -186,18 +189,17 @@ impl JITBackend for DirectBackend {
let nodes = std::mem::take(&mut self.nodes);

for (i, node) in nodes.into_inner().iter().enumerate() {
let Some((pos, block)) = self.blocks[i] else {
continue;
};
if matches!(node.ty, NodeType::Comparator { .. }) {
let block_entity = BlockEntity::Comparator {
output_strength: node.output_power,
};
world.set_block_entity(pos, block_entity);
}
for (pos, block) in self.blocks[i].iter().copied() {
if matches!(node.ty, NodeType::Comparator { .. }) {
let block_entity = BlockEntity::Comparator {
output_strength: node.output_power,
};
world.set_block_entity(pos, block_entity);
}

if io_only && !node.is_io {
world.set_block(pos, block);
if io_only && !node.is_io {
world.set_block(pos, block);
}
}
}

Expand Down Expand Up @@ -250,16 +252,19 @@ impl JITBackend for DirectBackend {
for event in self.events.drain(..) {
match event {
Event::NoteBlockPlay { noteblock_id } => {
let (pos, instrument, note) = self.noteblock_info[noteblock_id as usize];
noteblock::play_note(world, pos, instrument, note);
let (positions, instrument, note) = &self.noteblock_info[noteblock_id as usize];
for pos in positions.iter().copied() {
noteblock::play_note(world, pos, *instrument, *note);
}
}
}
}
for (i, node) in self.nodes.inner_mut().iter_mut().enumerate() {
let Some((pos, block)) = &mut self.blocks[i] else {
if !node.changed || (io_only && !node.is_io) {
continue;
};
if node.changed && (!io_only || node.is_io) {
}
node.changed = false;
for (pos, block) in &mut self.blocks[i] {
if let Some(powered) = block_powered_mut(block) {
*powered = node.powered
}
Expand All @@ -271,7 +276,6 @@ impl JITBackend for DirectBackend {
}
world.set_block(*pos, *block);
}
node.changed = false;
}
}

Expand Down Expand Up @@ -343,7 +347,11 @@ fn get_all_input(node: &Node) -> (u8, u8) {

// This function is optimized for input values from 0 to 15 and does not work correctly outside that
// range
fn calculate_comparator_output(mode: ComparatorMode, input_strength: u8, power_on_sides: u8) -> u8 {
pub fn calculate_comparator_output(
mode: ComparatorMode,
input_strength: u8,
power_on_sides: u8,
) -> u8 {
let difference = input_strength.wrapping_sub(power_on_sides);
if difference <= 15 {
match mode {
Expand Down Expand Up @@ -381,8 +389,12 @@ impl fmt::Display for DirectBackend {
NodeType::Constant => format!("Constant({})", node.output_power),
NodeType::NoteBlock { .. } => "NoteBlock".to_string(),
};
let pos = if let Some((pos, _)) = self.blocks[id] {
format!("{}, {}, {}", pos.x, pos.y, pos.z)
let pos = if self.blocks[id].len() > 0 {
let mut string = String::new();
for (pos, _) in self.blocks[id].iter() {
write!(&mut string, "{}, {}, {}; ", pos.x, pos.y, pos.z)?;
}
string
} else {
"No Pos".to_string()
};
Expand Down
27 changes: 23 additions & 4 deletions crates/redpiler/src/compile_graph.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use mchprs_blocks::blocks::{ComparatorMode, Instrument};
use mchprs_blocks::BlockPos;
use mchprs_world::{TickEntry, TickPriority};
use petgraph::stable_graph::{NodeIndex, StableGraph};
use smallvec::SmallVec;

pub type NodeIdx = NodeIndex;

Expand Down Expand Up @@ -45,11 +47,18 @@ impl NodeType {
}
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
impl NodeType {
pub fn is_bool(&self) -> bool {
!matches!(self, NodeType::Wire | NodeType::Comparator { .. })
}
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct NodeState {
pub powered: bool,
pub repeater_locked: bool,
pub output_strength: u8,
pending_ticks: Option<(TickPriority, u32)>,
}

impl NodeState {
Expand All @@ -66,6 +75,7 @@ impl NodeState {
powered,
repeater_locked: locked,
output_strength: if powered { 15 } else { 0 },
..Default::default()
}
}

Expand All @@ -85,13 +95,13 @@ impl NodeState {
}
}

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct Annotations {}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct CompileNode {
pub ty: NodeType,
pub block: Option<(BlockPos, u32)>,
pub block: SmallVec<[(BlockPos, u32); 1]>,
pub name: Option<String>,
pub state: NodeState,

Expand All @@ -104,6 +114,15 @@ impl CompileNode {
pub fn is_removable(&self) -> bool {
!self.is_input && !self.is_output
}

pub fn add_pending_tick(&mut self, tick: &TickEntry) {
assert!(!self.has_pending_ticks());
self.state.pending_ticks = Some((tick.tick_priority, tick.ticks_left));
}

pub fn has_pending_ticks(&self) -> bool {
self.state.pending_ticks.is_some()
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down
7 changes: 6 additions & 1 deletion crates/redpiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,11 @@ impl Compiler {
debug!("Starting compile");
let start = Instant::now();

let input = CompilerInput { world, bounds };
let input = CompilerInput {
world,
bounds,
pending_ticks: &ticks,
};
let registry = PassRegistry::default();
let pass_pipeline = passes::build_pass_pipeline::<W>(&registry, &options);
let graph =
Expand Down Expand Up @@ -256,6 +260,7 @@ impl Compiler {
pub struct CompilerInput<'w, W: World> {
pub world: &'w W,
pub bounds: (BlockPos, BlockPos),
pub pending_ticks: &'w [TickEntry],
}

#[cfg(test)]
Expand Down
Loading
Loading