verilator/test_regress/t/t_dfg_peephole.v

228 lines
10 KiB
Systemverilog
Raw Normal View History

Introduce DFG based combinational logic optimizer (#3527) Added a new data-flow graph (DFG) based combinational logic optimizer. The capabilities of this covers a combination of V3Const and V3Gate, but is also more capable of transforming combinational logic into simplified forms and more. This entail adding a new internal representation, `DfgGraph`, and appropriate `astToDfg` and `dfgToAst` conversion functions. The graph represents some of the combinational equations (~continuous assignments) in a module, and for the duration of the DFG passes, it takes over the role of AstModule. A bulk of the Dfg vertices represent expressions. These vertex classes, and the corresponding conversions to/from AST are mostly auto-generated by astgen, together with a DfgVVisitor that can be used for dynamic dispatch based on vertex (operation) types. The resulting combinational logic graph (a `DfgGraph`) is then optimized in various ways. Currently we perform common sub-expression elimination, variable inlining, and some specific peephole optimizations, but there is scope for more optimizations in the future using the same representation. The optimizer is run directly before and after inlining. The pre inline pass can operate on smaller graphs and hence converges faster, but still has a chance of substantially reducing the size of the logic on some designs, making inlining both faster and less memory intensive. The post inline pass can then optimize across the inlined module boundaries. No optimization is performed across a module boundary. For debugging purposes, each peephole optimization can be disabled individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one of the optimizations listed in V3DfgPeephole.h, for example -fno-dfg-peephole-remove-not-not. The peephole patterns currently implemented were mostly picked based on the design that inspired this work, and on that design the optimizations yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As you can imagine not having to haul around redundant combinational networks in the rest of the compilation pipeline also helps with memory consumption, and up to 30% peak memory usage of Verilator was observed on the same design. Gains on other arbitrary designs are smaller (and can be improved by analyzing those designs). For example OpenTitan gains between 1-15% speedup depending on build type.
2022-09-23 15:46:22 +00:00
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Geza Lore.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
`define signal(name, expr) wire [$bits(expr)-1:0] dfg_``name = expr;
module t (/*AUTOARG*/
// Outputs
dfg_SWAP_CONST_IN_COMMUTATIVE_BINARY,
dfg_SWAP_NOT_IN_COMMUTATIVE_BINARY,
dfg_SWAP_VAR_IN_COMMUTATIVE_BINARY,
dfg_PUSH_BITWISE_OP_THROUGH_CONCAT,
dfg_PUSH_BITWISE_OP_THROUGH_CONCAT_2,
dfg_PUSH_COMPARE_OP_THROUGH_CONCAT, dfg_REMOVE_WIDTH_ONE_REDUCTION,
dfg_PUSH_REDUCTION_THROUGH_COND_WITH_CONST_BRANCH,
dfg_REPLACE_REDUCTION_OF_CONST_AND,
dfg_REPLACE_REDUCTION_OF_CONST_OR,
dfg_REPLACE_REDUCTION_OF_CONST_XOR, dfg_REPLACE_EXTEND,
dfg_PUSH_NOT_THROUGH_COND, dfg_REMOVE_NOT_NOT, dfg_REPLACE_NOT_NEQ,
dfg_REPLACE_NOT_OF_CONST, dfg_REPLACE_AND_OF_NOT_AND_NOT,
dfg_REPLACE_AND_OF_CONST_AND_CONST, dfg_REPLACE_AND_WITH_ZERO,
dfg_REMOVE_AND_WITH_ONES, dfg_REPLACE_CONTRADICTORY_AND,
dfg_REPLACE_OR_OF_NOT_AND_NOT, dfg_REPLACE_OR_OF_NOT_AND_NEQ,
dfg_REPLACE_OR_OF_CONCAT_ZERO_LHS_AND_CONCAT_RHS_ZERO,
dfg_REPLACE_OR_OF_CONCAT_LHS_ZERO_AND_CONCAT_ZERO_RHS,
dfg_REPLACE_OR_OF_CONST_AND_CONST, dfg_REMOVE_OR_WITH_ZERO,
dfg_REPLACE_OR_WITH_ONES, dfg_REPLACE_TAUTOLOGICAL_OR,
dfg_REMOVE_SUB_ZERO, dfg_REPLACE_SUB_WITH_NOT,
dfg_REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT,
dfg_REPLACE_EQ_OF_CONST_AND_CONST, dfg_REMOVE_FULL_WIDTH_SEL,
dfg_REMOVE_SEL_FROM_RHS_OF_CONCAT,
dfg_REMOVE_SEL_FROM_LHS_OF_CONCAT, dfg_PUSH_SEL_THROUGH_CONCAT,
dfg_PUSH_SEL_THROUGH_REPLICATE, dfg_REPLACE_SEL_FROM_CONST,
dfg_REPLACE_CONCAT_OF_CONSTS,
dfg_REPLACE_NESTED_CONCAT_OF_CONSTS_ON_RHS,
dfg_REPLACE_NESTED_CONCAT_OF_CONSTS_ON_LHS,
dfg_REPLACE_CONCAT_ZERO_AND_SEL_TOP_WITH_SHIFTR,
dfg_REPLACE_CONCAT_SEL_BOTTOM_AND_ZERO_WITH_SHIFTL,
dfg_PUSH_CONCAT_THROUGH_NOTS, dfg_REMOVE_CONCAT_OF_ADJOINING_SELS,
dfg_REPLACE_NESTED_CONCAT_OF_ADJOINING_SELS_ON_LHS,
dfg_REPLACE_NESTED_CONCAT_OF_ADJOINING_SELS_ON_RHS,
dfg_REMOVE_COND_WITH_FALSE_CONDITION,
dfg_REMOVE_COND_WITH_TRUE_CONDITION,
dfg_SWAP_COND_WITH_NOT_CONDITION, dfg_SWAP_COND_WITH_NEQ_CONDITION,
dfg_PULL_NOTS_THROUGH_COND, dfg_REPLACE_COND_WITH_THEN_BRANCH_ZERO,
dfg_REPLACE_COND_WITH_THEN_BRANCH_ONES,
dfg_REPLACE_COND_WITH_ELSE_BRANCH_ZERO,
dfg_REPLACE_COND_WITH_ELSE_BRANCH_ONES, dfg_PUSH_SEL_THROUGH_COND,
dfg_PUSH_SEL_THROUGH_SHIFTL, dfg_REPLACE_SEL_FROM_SEL,
// Inputs
clk
);
input clk;
// Sadly verilog-mode cannot look in macros so need to define these
// separately
output dfg_SWAP_CONST_IN_COMMUTATIVE_BINARY;
output dfg_SWAP_NOT_IN_COMMUTATIVE_BINARY;
output dfg_SWAP_VAR_IN_COMMUTATIVE_BINARY;
output dfg_PUSH_BITWISE_OP_THROUGH_CONCAT;
output dfg_PUSH_BITWISE_OP_THROUGH_CONCAT_2;
output dfg_PUSH_COMPARE_OP_THROUGH_CONCAT;
output dfg_REMOVE_WIDTH_ONE_REDUCTION;
output dfg_PUSH_REDUCTION_THROUGH_COND_WITH_CONST_BRANCH;
output dfg_REPLACE_REDUCTION_OF_CONST_AND;
output dfg_REPLACE_REDUCTION_OF_CONST_OR;
output dfg_REPLACE_REDUCTION_OF_CONST_XOR;
output dfg_REPLACE_EXTEND;
output dfg_PUSH_NOT_THROUGH_COND;
output dfg_REMOVE_NOT_NOT;
output dfg_REPLACE_NOT_NEQ;
output dfg_REPLACE_NOT_OF_CONST;
output dfg_REPLACE_AND_OF_NOT_AND_NOT;
output dfg_REPLACE_AND_OF_CONST_AND_CONST;
output dfg_REPLACE_AND_WITH_ZERO;
output dfg_REMOVE_AND_WITH_ONES;
output dfg_REPLACE_CONTRADICTORY_AND;
output dfg_REPLACE_OR_OF_NOT_AND_NOT;
output dfg_REPLACE_OR_OF_NOT_AND_NEQ;
output dfg_REPLACE_OR_OF_CONCAT_ZERO_LHS_AND_CONCAT_RHS_ZERO;
output dfg_REPLACE_OR_OF_CONCAT_LHS_ZERO_AND_CONCAT_ZERO_RHS;
output dfg_REPLACE_OR_OF_CONST_AND_CONST;
output dfg_REMOVE_OR_WITH_ZERO;
output dfg_REPLACE_OR_WITH_ONES;
output dfg_REPLACE_TAUTOLOGICAL_OR;
output dfg_REMOVE_SUB_ZERO;
output dfg_REPLACE_SUB_WITH_NOT;
output dfg_REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT;
output dfg_REPLACE_EQ_OF_CONST_AND_CONST;
output dfg_REMOVE_FULL_WIDTH_SEL;
output dfg_REMOVE_SEL_FROM_RHS_OF_CONCAT;
output dfg_REMOVE_SEL_FROM_LHS_OF_CONCAT;
output dfg_PUSH_SEL_THROUGH_CONCAT;
output dfg_PUSH_SEL_THROUGH_REPLICATE;
output dfg_REPLACE_SEL_FROM_CONST;
output dfg_REPLACE_CONCAT_OF_CONSTS;
output dfg_REPLACE_NESTED_CONCAT_OF_CONSTS_ON_RHS;
output dfg_REPLACE_NESTED_CONCAT_OF_CONSTS_ON_LHS;
output dfg_REPLACE_CONCAT_ZERO_AND_SEL_TOP_WITH_SHIFTR;
output dfg_REPLACE_CONCAT_SEL_BOTTOM_AND_ZERO_WITH_SHIFTL;
output dfg_PUSH_CONCAT_THROUGH_NOTS;
output dfg_REMOVE_CONCAT_OF_ADJOINING_SELS;
output dfg_REPLACE_NESTED_CONCAT_OF_ADJOINING_SELS_ON_LHS;
output dfg_REPLACE_NESTED_CONCAT_OF_ADJOINING_SELS_ON_RHS;
output dfg_REMOVE_COND_WITH_FALSE_CONDITION;
output dfg_REMOVE_COND_WITH_TRUE_CONDITION;
output dfg_SWAP_COND_WITH_NOT_CONDITION;
output dfg_SWAP_COND_WITH_NEQ_CONDITION;
output dfg_PULL_NOTS_THROUGH_COND;
output dfg_REPLACE_COND_WITH_THEN_BRANCH_ZERO;
output dfg_REPLACE_COND_WITH_THEN_BRANCH_ONES;
output dfg_REPLACE_COND_WITH_ELSE_BRANCH_ZERO;
output dfg_REPLACE_COND_WITH_ELSE_BRANCH_ONES;
output dfg_PUSH_SEL_THROUGH_COND;
output dfg_PUSH_SEL_THROUGH_SHIFTL;
output dfg_REPLACE_SEL_FROM_SEL;
integer cyc = 0;
reg [63:0] crc = 64'h5aef0c8d_d70a4497;
reg [63:0] rcr;
wire logic [127:0] rcr_crc = {rcr, crc};
wire logic [127:0] crc_rep = {2{crc}};
wire logic [63:0] const_a;
wire logic [63:0] const_b;
always @ (posedge clk) begin
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};
rcr <= ~crc;
`ifdef REF
if (cyc >= 100_000) begin
$write("*-* All Finished *-*\n");
$finish;
end
`endif
end
// 64'0 but don't tell V3Const
`define ZERO (const_a & ~const_a)
// 64'1 but don't tell V3Const
`define ONES (const_a | ~const_a)
// x, but in a way only DFG understands
`define DFG(x) ((|`ONES) ? (x) : (~x))
`signal(SWAP_CONST_IN_COMMUTATIVE_BINARY, crc + const_a);
`signal(SWAP_NOT_IN_COMMUTATIVE_BINARY, crc + ~crc);
`signal(SWAP_VAR_IN_COMMUTATIVE_BINARY, rcr + crc);
`signal(PUSH_BITWISE_OP_THROUGH_CONCAT, 32'h12345678 ^ {8'h0, crc[23:0]});
`signal(PUSH_BITWISE_OP_THROUGH_CONCAT_2, 32'h12345678 ^ {rcr[7:0], crc[23:0]});
`signal(PUSH_COMPARE_OP_THROUGH_CONCAT, 4'b1011 == {2'b10, crc[1:0]});
`signal(REMOVE_WIDTH_ONE_REDUCTION, &`DFG(crc[0]));
`signal(PUSH_REDUCTION_THROUGH_COND_WITH_CONST_BRANCH, |(crc[32] ? crc[3:0] : 4'h0));
`signal(REPLACE_REDUCTION_OF_CONST_AND, &const_a);
`signal(REPLACE_REDUCTION_OF_CONST_OR, |const_a);
`signal(REPLACE_REDUCTION_OF_CONST_XOR, ^const_a);
`signal(REPLACE_EXTEND, 4'(crc[0]));
`signal(PUSH_NOT_THROUGH_COND, ~(crc[0] ? crc[4:0] : 5'hb));
`signal(REMOVE_NOT_NOT, ~`DFG(~`DFG(crc)));
`signal(REPLACE_NOT_NEQ, ~`DFG(crc != rcr));
`signal(REPLACE_NOT_OF_CONST, ~4'd0);
`signal(REPLACE_AND_OF_NOT_AND_NOT, ~crc[0] & ~rcr[0]);
`signal(REPLACE_AND_OF_CONST_AND_CONST, const_a & const_b);
`signal(REPLACE_AND_WITH_ZERO, `ZERO & crc);
`signal(REMOVE_AND_WITH_ONES, `ONES & crc);
`signal(REPLACE_CONTRADICTORY_AND, crc & ~crc);
`signal(REPLACE_OR_OF_NOT_AND_NOT, ~crc[0] | ~rcr[0]);
`signal(REPLACE_OR_OF_NOT_AND_NEQ, ~crc[0] | (rcr != 64'd2));
`signal(REPLACE_OR_OF_CONCAT_ZERO_LHS_AND_CONCAT_RHS_ZERO, {2'd0, crc[1:0]} | {rcr[1:0], 2'd0});
`signal(REPLACE_OR_OF_CONCAT_LHS_ZERO_AND_CONCAT_ZERO_RHS, {crc[1:0], 2'd0} | {2'd0, rcr[1:0]});
`signal(REPLACE_OR_OF_CONST_AND_CONST, const_a | const_b);
`signal(REMOVE_OR_WITH_ZERO, `ZERO | crc);
`signal(REPLACE_OR_WITH_ONES, `ONES | crc);
`signal(REPLACE_TAUTOLOGICAL_OR, crc | ~crc);
`signal(REMOVE_SUB_ZERO, crc - `ZERO);
`signal(REPLACE_SUB_WITH_NOT, crc[0] - 1'b1);
`signal(REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT, crc << {2'b0, crc[2:0]});
`signal(REPLACE_EQ_OF_CONST_AND_CONST, 4'd0 == 4'd1);
`signal(REMOVE_FULL_WIDTH_SEL, crc[63:0]);
`signal(REMOVE_SEL_FROM_RHS_OF_CONCAT, rcr_crc[63:0]);
`signal(REMOVE_SEL_FROM_LHS_OF_CONCAT, rcr_crc[127:64]);
`signal(PUSH_SEL_THROUGH_CONCAT, rcr_crc[120:0]);
`signal(PUSH_SEL_THROUGH_REPLICATE, crc_rep[0]);
`signal(REPLACE_SEL_FROM_CONST, const_a[2]);
`signal(REPLACE_CONCAT_OF_CONSTS, {const_a, const_b});
`signal(REPLACE_NESTED_CONCAT_OF_CONSTS_ON_RHS, {`DFG({crc, const_a}), const_b});
`signal(REPLACE_NESTED_CONCAT_OF_CONSTS_ON_LHS, {const_a, `DFG({const_b, crc})});
`signal(REPLACE_CONCAT_ZERO_AND_SEL_TOP_WITH_SHIFTR, {62'd0, crc[63:62]});
`signal(REPLACE_CONCAT_SEL_BOTTOM_AND_ZERO_WITH_SHIFTL, {crc[1:0], 62'd0});
`signal(PUSH_CONCAT_THROUGH_NOTS, {~crc, ~rcr} );
`signal(REMOVE_CONCAT_OF_ADJOINING_SELS, {`DFG(crc[10:3]), `DFG(crc[2:1])});
`signal(REPLACE_NESTED_CONCAT_OF_ADJOINING_SELS_ON_LHS, {crc[10:3], {crc[2:1], rcr}});
`signal(REPLACE_NESTED_CONCAT_OF_ADJOINING_SELS_ON_RHS, {`DFG({rcr, crc[10:3]}), crc[2:1]});
`signal(REMOVE_COND_WITH_FALSE_CONDITION, &`ZERO ? crc : rcr);
`signal(REMOVE_COND_WITH_TRUE_CONDITION, |`ONES ? crc : rcr);
`signal(SWAP_COND_WITH_NOT_CONDITION, (~crc[0] & |`ONES) ? crc : rcr);
`signal(SWAP_COND_WITH_NEQ_CONDITION, rcr != crc ? crc : rcr);
`signal(PULL_NOTS_THROUGH_COND, crc[0] ? ~crc[4:0] : ~rcr[4:0]);
`signal(REPLACE_COND_WITH_THEN_BRANCH_ZERO, crc[0] ? |`ZERO : crc[1]);
`signal(REPLACE_COND_WITH_THEN_BRANCH_ONES, crc[0] ? |`ONES : crc[1]);
`signal(REPLACE_COND_WITH_ELSE_BRANCH_ZERO, crc[0] ? crc[1] : |`ZERO);
`signal(REPLACE_COND_WITH_ELSE_BRANCH_ONES, crc[0] ? crc[1] : |`ONES);
assign const_a = (crc | ~crc) & 64'h0123456789abcdef;
assign const_b = ~(crc & ~crc) & 64'h98badefc10325647;
// Some selects need extra temporaries
wire [63:0] sel_from_cond = crc[0] ? crc : const_a;
wire [63:0] sel_from_shiftl = crc << 10;
wire [31:0] sel_from_sel = crc[10+:32];
`signal(PUSH_SEL_THROUGH_COND, sel_from_cond[2]);
`signal(PUSH_SEL_THROUGH_SHIFTL, sel_from_shiftl[20:0]);
`signal(REPLACE_SEL_FROM_SEL, sel_from_sel[4:3]);
// Sel from not requires the operand to have a sinle sink, so can't use
// the chekc due to the raw expression referencing the operand
wire [63:0] sel_from_not_tmp = ~(crc >> rcr[2:0] << crc[3:0]);
wire sel_from_not = sel_from_not_tmp[2];
always @(posedge clk) if ($c(0)) $display(sel_from_not); // Do not remove signal
endmodule