Skip to content

Commit 51e06ef

Browse files
author
Ethan Pailes
committed
Factor OnePassCompiler::forwards into local var.
Iteration of a `Forwards` object is destructive, which previously meant that we had to clone it in order to iterate over it. Once the compiler iterates over the forwarding jobs, it never touches them again, so this was an extra copy. This patch plays a little type tetris to get rid of that extra copy.
1 parent 223e14c commit 51e06ef

File tree

1 file changed

+59
-66
lines changed

1 file changed

+59
-66
lines changed

src/onepass.rs

+59-66
Original file line numberDiff line numberDiff line change
@@ -478,11 +478,6 @@ pub struct OnePassCompiler {
478478
/// A mapping from instruction indices to flags indicating
479479
/// if they should have the STATE_MATCH flag set.
480480
accepting_states: Vec<bool>,
481-
482-
/// A DAG of forwarding relationships indicating when
483-
/// a state needs to be forwarded to an Action state
484-
/// once that Action state has been fully constructed.
485-
forwards: Forwards,
486481
}
487482

488483
#[derive(Debug)]
@@ -568,27 +563,33 @@ impl OnePassCompiler {
568563
x
569564
},
570565
accepting_states: vec![false; prog.len()],
571-
forwards: Forwards::new(),
572566
prog: prog,
573567
})
574568
}
575569

576570
/// Attempt to compile the regex to a OnePass DFA
577571
pub fn compile(mut self) -> Result<OnePass, OnePassError> {
572+
// A DAG of forwarding relationships indicating when
573+
// a state needs to be forwarded to an Action state
574+
// once that Action state has been fully constructed.
575+
let mut forwards = Forwards::new();
576+
578577
// Compute the prioritized transition tables for all of the
579578
// instructions which get states.
580579
let mut state_edge = vec![0];
581580
while let Some(i) = state_edge.pop() {
582-
state_edge.extend(try!(self.inst_trans(i)));
581+
state_edge.extend(try!(self.inst_trans(i, &mut forwards)));
583582
}
584583

585584
// Solve the dependency relationships between all the
586585
// forwarding directives that were emitted by inst_trans.
587-
try!(self.solve_forwards());
586+
for fwd in forwards.into_iter_topo() {
587+
self.perform_forward(try!(fwd));
588+
}
588589

589590
// Now emit the transitions in a form that we can actually
590591
// execute.
591-
self.emit_transitions();
592+
self.bake_transitions();
592593

593594
Ok(OnePass {
594595
table: self.table,
@@ -611,7 +612,8 @@ impl OnePassCompiler {
611612
/// via `inst_trans`.
612613
fn inst_trans(
613614
&mut self,
614-
inst_idx: usize
615+
inst_idx: usize,
616+
forwards: &mut Forwards,
615617
) -> Result<Vec<usize>, OnePassError> {
616618
trace!("::inst_trans inst_idx={}", inst_idx);
617619

@@ -642,7 +644,7 @@ impl OnePassCompiler {
642644
while let Some(child_idx) = resume.pop() {
643645
match &self.prog[child_idx] {
644646
&Inst::EmptyLook(_) | &Inst::Save(_) => {
645-
self.forwards.forward(inst_idx, child_idx, priority);
647+
forwards.forward(inst_idx, child_idx, priority);
646648
children.push(child_idx);
647649
}
648650
&Inst::Bytes(ref inst) => {
@@ -685,10 +687,7 @@ impl OnePassCompiler {
685687
Ok(children)
686688
}
687689

688-
/// Topologically sort the forwarding jobs so that we
689-
/// start with jobs that have no dependencies and then
690-
/// shuffle the transitions over. Mutate `self.transitions`
691-
/// in place.
690+
/// Execute a forwarding job.
692691
///
693692
/// To make that a little more concrete, consider the program snippet:
694693
///
@@ -724,71 +723,65 @@ impl OnePassCompiler {
724723
/// The arrow flows in a funny direction because we want the jobs
725724
/// with no dependencies to live at the roots of the DAG so that
726725
/// we can process them first.
727-
fn solve_forwards(&mut self) -> Result<(), OnePassError> {
728-
// TODO(ethan):yakshaving drop the clone
729-
for fwd in self.forwards.clone().into_iter_topo() {
730-
let fwd = try!(fwd);
731-
debug_assert!(fwd.from != fwd.to);
726+
fn perform_forward(&mut self, fwd: Forward) {
727+
debug_assert!(fwd.from != fwd.to);
732728

733-
let tgt = match &self.prog[fwd.to] {
734-
&Inst::EmptyLook(_) | &Inst::Save(_) =>
735-
TransitionTarget::ActionInst(fwd.to),
736-
_ =>
737-
TransitionTarget::BytesInst(fwd.to),
738-
};
739-
740-
let (from_ts, to_ts) = if fwd.from < fwd.to {
741-
let (stub, tail) = self.transitions.split_at_mut(fwd.to);
742-
(&mut stub[fwd.from], &mut tail[0])
743-
} else {
744-
let (stub, tail) = self.transitions.split_at_mut(fwd.from);
745-
(&mut tail[0], &mut stub[fwd.to])
746-
};
729+
let tgt = match &self.prog[fwd.to] {
730+
&Inst::EmptyLook(_) | &Inst::Save(_) =>
731+
TransitionTarget::ActionInst(fwd.to),
732+
_ => TransitionTarget::BytesInst(fwd.to),
733+
};
747734

748-
let (from_ts, to_ts) = match (from_ts, to_ts) {
749-
(&mut Some(ref mut from_ts), &mut Some(ref to_ts)) => {
750-
(from_ts, to_ts)
751-
}
752-
_ => unreachable!("forwards must be between real nodes."),
753-
};
735+
// Get a pair of mutable references to the two different
736+
// transition tables in borrow checker approved fashion.
737+
let (from_ts, to_ts) = if fwd.from < fwd.to {
738+
let (stub, tail) = self.transitions.split_at_mut(fwd.to);
739+
(&mut stub[fwd.from], &mut tail[0])
740+
} else {
741+
let (stub, tail) = self.transitions.split_at_mut(fwd.from);
742+
(&mut tail[0], &mut stub[fwd.to])
743+
};
744+
let (from_ts, to_ts) = match (from_ts, to_ts) {
745+
(&mut Some(ref mut from_ts), &mut Some(ref to_ts)) => {
746+
(from_ts, to_ts)
747+
}
748+
_ => unreachable!("forwards must be between real nodes."),
749+
};
754750

755-
// now shuffle the transitions from `to` to `from`.
756-
for (to_t, from_t) in to_ts.0.iter().zip(from_ts.0.iter_mut()) {
757-
if to_t.tgt == TransitionTarget::Die {
758-
continue;
759-
}
760-
if from_t.priority > fwd.priority {
761-
continue;
762-
}
751+
// now shuffle the transitions from `to` to `from`.
752+
for (to_t, from_t) in to_ts.0.iter().zip(from_ts.0.iter_mut()) {
753+
if to_t.tgt == TransitionTarget::Die {
754+
continue;
755+
}
756+
if from_t.priority > fwd.priority {
757+
continue;
758+
}
763759

764-
// we should never encounter equal priorities
765-
debug_assert!(from_t.priority != fwd.priority);
760+
// we should never encounter equal priorities
761+
debug_assert!(from_t.priority != fwd.priority);
766762

767-
*from_t = Transition {
768-
tgt: tgt.clone(),
769-
priority: fwd.priority,
770-
};
771-
}
763+
*from_t = Transition {
764+
tgt: tgt.clone(),
765+
priority: fwd.priority,
766+
};
767+
}
772768

773-
// Finally, if a match instruction is reachable through
774-
// a save fwd (which can never fail), the from state is accepting.
775-
match &self.prog[fwd.to] {
776-
&Inst::Save(_) => {
777-
self.accepting_states[fwd.from] =
778-
self.accepting_states[fwd.to];
779-
}
780-
_ => {}
769+
// Finally, if a match instruction is reachable through
770+
// a save fwd (which can never fail), the from state is accepting.
771+
match &self.prog[fwd.to] {
772+
&Inst::Save(_) => {
773+
self.accepting_states[fwd.from] =
774+
self.accepting_states[fwd.to];
781775
}
776+
_ => {}
782777
}
783-
784-
Ok(())
785778
}
786779

787780
/// Once all the per-instruction transition tables have been worked
788781
/// out, we can bake them into the single flat transition table we
789782
/// are going to use for the actual DFA. This function creates the
790783
/// baked form, storing it in `self.table`.
791-
fn emit_transitions(&mut self) {
784+
fn bake_transitions(&mut self) {
792785
// pre-compute the state indices
793786
let mut state_starts = Vec::with_capacity(self.prog.len());
794787
let mut off = 0;

0 commit comments

Comments
 (0)