Skip to content
This repository has been archived by the owner on Mar 29, 2024. It is now read-only.

Commit

Permalink
sim: Refcount signals, only generate temps when refcount > 1
Browse files Browse the repository at this point in the history
This is to reduce the amount of (unnecessary) bindings in the generated prop fn, as this has been found to have nonlinear time and mem scaling in the rust compiler currently (see rust-lang/rust#79671 and an initial/related workaround in 7c2ff85), and it's been recommended to reduce these. As expected, this appears to be quite effective!

A side effect of this is that IR expressions can now have unbounded depth, so lowering those also has to be iterative instead of recursive. I've introduced parens in some cases which may be unnecessary (to avoid the complexity of determining whether or not we actually need them), so we now mark the impl item with an attribute to ignore these.

There are a couple cases like Signal::bits which ends up being lowered to a shift and a bitmask in addition to a cast (if necessary). If a user builds a graph with several of these calls on the same Signal, the intermediates between these steps aren't refcounted like normal Signals are (refcounting happens too early for that) - so we won't be able to flatten these to a single temporary. There are a few other such cases as well. I've chosen to simply ignore all of them - they're not practically an issue, and we can always revisit them later if need be (though this will likey require the introduction of yet another IR and perhaps more complex passes such as CSE on it to be effective).

Also rename Node -> Frame for the intermediate types used when traversing the graph iteratively, as this less ambiguously describes what these types represent.
  • Loading branch information
yupferris committed Dec 6, 2020
1 parent 7c2ff85 commit 534c9d4
Show file tree
Hide file tree
Showing 7 changed files with 695 additions and 602 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- Mark generated rust simulator impl's with `#[automatically_derived]` to skip expensive lints during compilation.
- Reduced the amount of temporary bindings used in the generated sim code, which reduces rustc compile time dramatically.

## [0.1.14] - 2020-11-29
### Changed
Expand Down
11 changes: 9 additions & 2 deletions kaze/src/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ pub fn generate<'a, W: Write>(
let root_context = context_arena.alloc(ModuleContext::new());

let mut state_elements = StateElements::new();
let mut signal_reference_counts = HashMap::new();
for (_, output) in m.outputs.borrow().iter() {
state_elements.gather(&output, root_context, &context_arena);
state_elements.gather(
&output,
root_context,
&context_arena,
&mut signal_reference_counts,
);
}

struct TraceSignal {
Expand All @@ -63,7 +69,7 @@ pub fn generate<'a, W: Write>(
};

let mut prop_context = AssignmentContext::new();
let mut c = Compiler::new(&state_elements, &context_arena);
let mut c = Compiler::new(&state_elements, &signal_reference_counts, &context_arena);
for (name, input) in m.inputs.borrow().iter() {
add_trace_signal(root_context, name.clone(), name.clone(), input.bit_width());
}
Expand Down Expand Up @@ -280,6 +286,7 @@ pub fn generate<'a, W: Write>(
w.append_line("}")?;
w.append_newline()?;

w.append_line("#[allow(unused_parens)]")?;
w.append_line("#[automatically_derived]")?;
w.append_indent()?;
w.append("impl")?;
Expand Down
822 changes: 425 additions & 397 deletions kaze/src/sim/compiler.rs

Large diffs are not rendered by default.

240 changes: 143 additions & 97 deletions kaze/src/sim/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@ impl AssignmentContext {
}

pub fn gen_temp(&mut self, expr: Expr) -> Expr {
let name = format!("__temp_{}", self.local_count);
self.local_count += 1;
match expr {
// We don't need to generate a temp for Constants or Refs
Expr::Constant { .. } | Expr::Ref { .. } => expr,
_ => {
let name = format!("__temp_{}", self.local_count);
self.local_count += 1;

self.assignments.push(Assignment {
target: Expr::Ref {
name: name.clone(),
scope: Scope::Local,
},
expr,
});
self.assignments.push(Assignment {
target: Expr::Ref {
name: name.clone(),
scope: Scope::Local,
},
expr,
});

Expr::Ref {
name,
scope: Scope::Local,
Expr::Ref {
name,
scope: Scope::Local,
}
}
}
}

Expand Down Expand Up @@ -84,15 +90,15 @@ pub enum Expr {
target: Box<Expr>,
index: Box<Expr>,
},
Cast {
source: Box<Expr>,
target_type: ValueType,
},
BinaryFunctionCall {
name: String,
lhs: Box<Expr>,
rhs: Box<Expr>,
},
Cast {
source: Box<Expr>,
target_type: ValueType,
},
Constant {
value: Constant,
},
Expand Down Expand Up @@ -138,86 +144,126 @@ impl Expr {
}

pub fn write<W: Write>(&self, w: &mut code_writer::CodeWriter<W>) -> Result<()> {
match self {
Expr::ArrayIndex { target, index } => {
target.write(w)?;
w.append("[")?;
index.write(w)?;
w.append(" as usize]")?;
}
Expr::Cast {
source,
target_type,
} => {
source.write(w)?;
w.append(&format!(" as {}", target_type.name()))?;
}
Expr::BinaryFunctionCall { name, lhs, rhs } => {
w.append(&format!("{}(", name))?;
lhs.write(w)?;
w.append(", ")?;
rhs.write(w)?;
w.append(")")?;
}
Expr::Constant { value } => {
w.append(&match value {
Constant::Bool(value) => format!("{}", value),
Constant::U32(value) => format!("0x{:x}u32", value),
Constant::U64(value) => format!("0x{:x}u64", value),
Constant::U128(value) => format!("0x{:x}u128", value),
})?;
}
Expr::InfixBinOp { lhs, rhs, op } => {
lhs.write(w)?;
w.append(&format!(
" {} ",
match op {
InfixBinOp::BitAnd => "&",
InfixBinOp::BitOr => "|",
InfixBinOp::BitXor => "^",
InfixBinOp::Equal => "==",
InfixBinOp::NotEqual => "!=",
InfixBinOp::LessThan => "<",
InfixBinOp::LessThanEqual => "<=",
InfixBinOp::GreaterThan => ">",
InfixBinOp::GreaterThanEqual => ">=",
InfixBinOp::Shl => "<<",
InfixBinOp::Shr => ">>",
InfixBinOp::Mul => "*",
enum Command<'a> {
Expr { expr: &'a Expr },
Str { s: &'a str },
}

let mut commands = Vec::new();
commands.push(Command::Expr { expr: self });

while let Some(command) = commands.pop() {
match command {
Command::Expr { expr } => match *expr {
Expr::ArrayIndex {
ref target,
ref index,
} => {
commands.push(Command::Str { s: " as usize]" });
commands.push(Command::Expr { expr: index });
commands.push(Command::Str { s: "[" });
commands.push(Command::Expr { expr: target });
}
))?;
rhs.write(w)?;
}
Expr::Ref { name, scope } => {
if let Scope::Member = scope {
w.append("self.")?;
Expr::BinaryFunctionCall {
ref name,
ref lhs,
ref rhs,
} => {
commands.push(Command::Str { s: ")" });
commands.push(Command::Expr { expr: rhs });
commands.push(Command::Str { s: ", " });
commands.push(Command::Expr { expr: lhs });
w.append(&format!("{}(", name))?;
}
Expr::Cast {
ref source,
target_type,
} => {
commands.push(Command::Str { s: ")" });
commands.push(Command::Str {
s: &target_type.name(),
});
commands.push(Command::Str { s: " as " });
commands.push(Command::Expr { expr: source });
w.append("(")?;
}
Expr::Constant { ref value } => {
w.append(&match value {
Constant::Bool(value) => format!("{}", value),
Constant::U32(value) => format!("0x{:x}u32", value),
Constant::U64(value) => format!("0x{:x}u64", value),
Constant::U128(value) => format!("0x{:x}u128", value),
})?;
}
Expr::InfixBinOp {
ref lhs,
ref rhs,
op,
} => {
commands.push(Command::Str { s: ")" });
commands.push(Command::Expr { expr: rhs });
commands.push(Command::Str { s: " " });
commands.push(Command::Str {
s: match op {
InfixBinOp::BitAnd => "&",
InfixBinOp::BitOr => "|",
InfixBinOp::BitXor => "^",
InfixBinOp::Equal => "==",
InfixBinOp::NotEqual => "!=",
InfixBinOp::LessThan => "<",
InfixBinOp::LessThanEqual => "<=",
InfixBinOp::GreaterThan => ">",
InfixBinOp::GreaterThanEqual => ">=",
InfixBinOp::Shl => "<<",
InfixBinOp::Shr => ">>",
InfixBinOp::Mul => "*",
},
});
commands.push(Command::Str { s: " " });
commands.push(Command::Expr { expr: lhs });
w.append("(")?;
}
Expr::Ref { ref name, scope } => {
if let Scope::Member = scope {
w.append("self.")?;
}
w.append(name)?;
}
Expr::Ternary {
ref cond,
ref when_true,
ref when_false,
} => {
commands.push(Command::Str { s: "}" });
commands.push(Command::Expr { expr: when_false });
commands.push(Command::Str { s: " } else { " });
commands.push(Command::Expr { expr: when_true });
commands.push(Command::Str { s: " { " });
commands.push(Command::Expr { expr: cond });
w.append("if ")?;
}
Expr::UnaryMemberCall {
ref target,
ref name,
ref arg,
} => {
commands.push(Command::Str { s: ")" });
commands.push(Command::Expr { expr: arg });
commands.push(Command::Str { s: "(" });
commands.push(Command::Str { s: name });
commands.push(Command::Str { s: "." });
commands.push(Command::Expr { expr: target });
}
Expr::UnOp { ref source, op } => {
w.append(match op {
UnOp::Not => "!",
})?;
commands.push(Command::Expr { expr: source });
}
},
Command::Str { s } => {
w.append(s)?;
}
w.append(name)?;
}
Expr::Ternary {
cond,
when_true,
when_false,
} => {
w.append("if ")?;
cond.write(w)?;
w.append(" { ")?;
when_true.write(w)?;
w.append(" } else { ")?;
when_false.write(w)?;
w.append(" }")?;
}
Expr::UnaryMemberCall { target, name, arg } => {
target.write(w)?;
w.append(&format!(".{}(", name))?;
arg.write(w)?;
w.append(")")?;
}
Expr::UnOp { source, op } => {
w.append(match op {
UnOp::Not => "!",
})?;
source.write(w)?;
}
}

Expand All @@ -233,7 +279,7 @@ pub enum Constant {
U128(u128),
}

#[derive(Clone)]
#[derive(Clone, Copy)]
pub enum InfixBinOp {
BitAnd,
BitOr,
Expand All @@ -249,13 +295,13 @@ pub enum InfixBinOp {
Mul,
}

#[derive(Clone)]
#[derive(Clone, Copy)]
pub enum Scope {
Local,
Member,
}

#[derive(Clone)]
#[derive(Clone, Copy)]
pub enum UnOp {
Not,
}
Expand Down
Loading

0 comments on commit 534c9d4

Please sign in to comment.