Rather than using paths from the root instruction to the instruction we are matching against or checking if it is constant or whatever, use temporary variables. When we successfully match an instruction's opcode, we simultaneously define these temporaries for the instruction's operands. This is similar to how open-coding these matches in Rust would use `match` expressions with pattern matching to bind the operands to variables at the same time. This saves about 1.8% of instructions retired when Peepmatic is enabled.
141 lines
4.9 KiB
Rust
141 lines
4.9 KiB
Rust
//! Formatting a peephole optimizer's automata for GraphViz Dot.
|
|
//!
|
|
//! See also `crates/automata/src/dot.rs`.
|
|
|
|
use peepmatic_automata::dot::DotFmt;
|
|
use peepmatic_runtime::{
|
|
cc::ConditionCode,
|
|
integer_interner::{IntegerId, IntegerInterner},
|
|
linear,
|
|
};
|
|
use std::convert::{TryFrom, TryInto};
|
|
use std::fmt::Debug;
|
|
use std::io::{self, Write};
|
|
use std::num::{NonZeroU16, NonZeroU32};
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct PeepholeDotFmt<'a>(pub(crate) &'a IntegerInterner);
|
|
|
|
impl<TOperator> DotFmt<linear::MatchResult, linear::MatchOp, Box<[linear::Action<TOperator>]>>
|
|
for PeepholeDotFmt<'_>
|
|
where
|
|
TOperator: Debug + TryFrom<NonZeroU32>,
|
|
{
|
|
fn fmt_transition(
|
|
&self,
|
|
w: &mut impl Write,
|
|
from: Option<&linear::MatchOp>,
|
|
input: &linear::MatchResult,
|
|
_to: Option<&linear::MatchOp>,
|
|
) -> io::Result<()> {
|
|
let from = from.expect("we should have match op for every state");
|
|
if let Some(x) = input.ok() {
|
|
match from {
|
|
linear::MatchOp::Opcode { .. } => {
|
|
let opcode = TOperator::try_from(x)
|
|
.map_err(|_| ())
|
|
.expect("we shouldn't generate non-opcode edges");
|
|
write!(w, "{:?}", opcode)
|
|
}
|
|
linear::MatchOp::ConditionCode { .. } => {
|
|
let cc = ConditionCode::try_from(x.get())
|
|
.expect("we shouldn't generate non-CC edges");
|
|
write!(w, "{}", cc)
|
|
}
|
|
linear::MatchOp::IntegerValue { .. } => {
|
|
let x = self.0.lookup(IntegerId(
|
|
NonZeroU16::new(x.get().try_into().unwrap()).unwrap(),
|
|
));
|
|
write!(w, "{}", x)
|
|
}
|
|
_ => write!(w, "Ok({})", x),
|
|
}
|
|
} else {
|
|
write!(w, "(else)")
|
|
}
|
|
}
|
|
|
|
fn fmt_state(&self, w: &mut impl Write, op: &linear::MatchOp) -> io::Result<()> {
|
|
use linear::MatchOp::*;
|
|
|
|
write!(w, r#"<font face="monospace">"#)?;
|
|
|
|
match op {
|
|
Opcode(id) => write!(w, "opcode $lhs{}", id.0)?,
|
|
IsConst(id) => write!(w, "is-const? $lhs{}", id.0)?,
|
|
IsPowerOfTwo(id) => write!(w, "is-power-of-two? $lhs{}", id.0)?,
|
|
BitWidth(id) => write!(w, "bit-width $lhs{}", id.0)?,
|
|
FitsInNativeWord(id) => write!(w, "fits-in-native-word $lhs{}", id.0)?,
|
|
Eq(a, b) => write!(w, "$lhs{} == $lhs{}", a.0, b.0)?,
|
|
IntegerValue(id) => write!(w, "integer-value $lhs{}", id.0)?,
|
|
BooleanValue(id) => write!(w, "boolean-value $lhs{}", id.0)?,
|
|
ConditionCode(id) => write!(w, "condition-code $lhs{}", id.0)?,
|
|
Nop => write!(w, "nop")?,
|
|
}
|
|
|
|
writeln!(w, "</font>")
|
|
}
|
|
|
|
fn fmt_output(
|
|
&self,
|
|
w: &mut impl Write,
|
|
actions: &Box<[linear::Action<TOperator>]>,
|
|
) -> io::Result<()> {
|
|
use linear::Action::*;
|
|
|
|
if actions.is_empty() {
|
|
return writeln!(w, "(no output)");
|
|
}
|
|
|
|
write!(w, r#"<font face="monospace">"#)?;
|
|
|
|
for a in actions.iter() {
|
|
match a {
|
|
GetLhs { lhs } => write!(w, "get-lhs $lhs{}<br/>", lhs.0)?,
|
|
UnaryUnquote { operator, operand } => {
|
|
write!(w, "eval {:?} $rhs{}<br/>", operator, operand.0)?
|
|
}
|
|
BinaryUnquote { operator, operands } => write!(
|
|
w,
|
|
"eval {:?} $rhs{}, $rhs{}<br/>",
|
|
operator, operands[0].0, operands[1].0,
|
|
)?,
|
|
MakeIntegerConst {
|
|
value,
|
|
bit_width: _,
|
|
} => write!(w, "make {}<br/>", self.0.lookup(*value))?,
|
|
MakeBooleanConst {
|
|
value,
|
|
bit_width: _,
|
|
} => write!(w, "make {}<br/>", value)?,
|
|
MakeConditionCode { cc } => write!(w, "{}<br/>", cc)?,
|
|
MakeUnaryInst {
|
|
operand,
|
|
operator,
|
|
r#type: _,
|
|
} => write!(w, "make {:?} $rhs{}<br/>", operator, operand.0,)?,
|
|
MakeBinaryInst {
|
|
operator,
|
|
operands,
|
|
r#type: _,
|
|
} => write!(
|
|
w,
|
|
"make {:?} $rhs{}, $rhs{}<br/>",
|
|
operator, operands[0].0, operands[1].0,
|
|
)?,
|
|
MakeTernaryInst {
|
|
operator,
|
|
operands,
|
|
r#type: _,
|
|
} => write!(
|
|
w,
|
|
"make {:?} $rhs{}, $rhs{}, $rhs{}<br/>",
|
|
operator, operands[0].0, operands[1].0, operands[2].0,
|
|
)?,
|
|
}
|
|
}
|
|
|
|
writeln!(w, "</font>")
|
|
}
|
|
}
|