Synthetic/Generated-aware Optimizations
@prithayan is already working on this.
Since Apr 1, 2021.
- Dominant language
- C++
- Stars
- 2.2k
- Forks
- 524
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 46
Description
Hardware languages that compile to Verilog and their compilers have three implicit attributes associated with them:
- User-specified things
- Temporaries generated in the front-end, e.g., Chisel
- Synthetics generated in the middle-end, e.g., some
llvm/circtoptimization pass
User-specified things should be preserved in the output language (e.g., Verilog, VHDL, or SystemC) by default. Anything which is a temporary or a synthetic should be a candidate for any optimization available, e.g., common subexpression elimintation/global value numbering or dead code elimination.
Note: Temporaries and synthetics may be the same thing from the view of a mid-end.
I'm proposing adding this notion of "user specified"/"static"/"public" and "temporary"/"synthetic" in the IR to differentiate between things that are not optimization candidates vs. things that are optimization candidates.
This could be implemented as an attribute on specific ops and be used as a gate on optimizations. (Or: there is an optimization option which will respect this attribute like, -fpreserve-public.)
Note: Even if something is user-specified, it could be optimized around, e.g., constants could propagate through it.
Chisel/Scala FIRRTL Compiler-specific Details
Chisel emits lots of temporaries due to its software architecture. When you write something like:
/* Add 2 to "a" and connect the result to "b" */
b := a + 1.U + 1.U
Chisel is walking things in the order of Scala operations and generating temporaries along the way. You can think of this as analyzing this like so:
b.connect(a.add(1.U).add(1.U))
Each addition generates a new temporary and that temporary is connected to b.
Each temporary that Chisel generates gets a name like _T(_<int>)? producing temporaries of increasing index: [_T, _T_0, _T_1, ...]. A recent version of Chisel produces better names where temporaries are associated with a prefix, e.g., [_foo, _foo_0, _foo_1, ...].
The Scala FIRRTL Compiler may generate synthetic wires/nodes without a name using the _GEN(_<int>)? pattern like:[_GEN, _GEN_0, _GEN_1, ...].
For the purposes of integrating directly with Chisel or the Scala FIRRTL Compiler, this naming convention can be exploited to stringly infer what is a legal optimization candidate.
Example 1
Consider the following Chisel code that adds 2 to some input:
class Foo extends RawModule {
val a = IO(Input(UInt(1.W)))
val b = IO(Output(UInt(3.W)))
/** Add 2 to input "a" and connect this to output "b" */
b := a + 1.U + 1.U
}
This produces the following FIRRTL IR:
circuit Foo :
module Foo :
input a : UInt<1>
output b : UInt<3>
node _b_T = add(a, UInt<1>("h1")) @[main.scala 11:10]
node _b_T_1 = tail(_b_T, 1) @[main.scala 11:10]
node _b_T_2 = add(_b_T_1, UInt<1>("h1")) @[main.scala 11:16]
node _b_T_3 = tail(_b_T_2, 1) @[main.scala 11:16]
b <= _b_T_3 @[main.scala 11:5]
The ideal Verilog that should be produced is something with everything inlined:
module Foo(
input a,
output [3:0] b);
assign b = a + 1'h1 + 1'h1;
endmodule
llvm/circt already generates something close to this. The Scala FIRRTL compiler emits temporaries.
Example 2
Alternatively, if the user writes:
class Bar extends RawModule {
val a = IO(Input(UInt(1.W)))
val b = IO(Output(UInt(3.W)))
val x = a + 1.U
val y = x + 1.U
b := y
}
This produces the following FIRRTL IR:
circuit Bar :
module Bar :
input a : UInt<1>
output b : UInt<3>
node _x_T = add(a, UInt<1>("h1")) @[main.scala 20:13]
node x = tail(_x_T, 1) @[main.scala 20:13]
node _y_T = add(x, UInt<1>("h1")) @[main.scala 21:13]
node y = tail(_y_T, 1) @[main.scala 21:13]
b <= y @[main.scala 23:5]
The ideal Verilog would be:
module Bar(
input a,
output [2:0] b
);
wire x = a + 1'h1; // @[main.scala 17:13]
wire y = x + 1'h1; // @[main.scala 18:13]
assign b = {{2'd0}, y}; // @[main.scala 18:13]
endmodule
The Scala FIRRTL Compiler already generates something close to this. llvm/circt generates everything inlined.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.