From cf6d07aafa18db0d13088b20389a79a7e0dbcafb Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 22 Oct 2014 21:44:41 -0400 Subject: [PATCH] Add optimization of operators between concats, msg1447. --- Changes | 2 + src/V3Const.cpp | 92 ++++++++++++++++++++++++++++++++++ src/V3Options.cpp | 2 + src/V3Options.h | 2 + test_regress/t/t_concat_opt.pl | 18 +++++++ test_regress/t/t_concat_opt.v | 67 +++++++++++++++++++++++++ 6 files changed, 183 insertions(+) create mode 100755 test_regress/t/t_concat_opt.pl create mode 100644 test_regress/t/t_concat_opt.v diff --git a/Changes b/Changes index 45d1dd76b..2231b0fdf 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.865 devel +*** Add optimization of operators between concats, msg1447. [Jie Xu] + **** Fix generate unrolling with function call, bug830. [Steven Slatter] **** Fix cast-to-size context-determined sizing, bug828. [Geoff Barrett] diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 9f9f18cb6..a3d2c5731 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -450,6 +450,56 @@ private: if (afterComment(lowerIfp->elsesp())) return false; return true; } + bool ifConcatMergeableBiop(AstNode* nodep) { + return (nodep->castAnd() + || nodep->castOr() + || nodep->castXor() + || nodep->castXnor()); + } + bool ifAdjacent(AstNode* lhsp, AstNode* rhsp) { + if (!v3Global.opt.oAssemble()) return false; // opt disabled + AstSel* lselp = lhsp->castSel(); + AstSel* rselp = rhsp->castSel(); + if (!lselp || !lhsp->castVarRef()) return false; + if (!rselp || !rhsp->castVarRef()) return false; + // a[a:b] a[b-1:c] are adjacent // a a[1:0] are adjacent as well + AstVarRef* lvarp = lselp->fromp()->castVarRef(); + AstVarRef* rvarp = rselp->fromp()->castVarRef(); + if (!lvarp || !rvarp || !lvarp->same(rvarp)) return false; + AstConst* lstart = lselp->lsbp()->castConst(); + AstConst* rstart = rselp->lsbp()->castConst(); + AstConst* lwidth = lselp->widthp()->castConst(); + AstConst* rwidth = rselp->widthp()->castConst(); + if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated + // a[i:j] a[j-1:k] + int rend = (rstart->toSInt() + rwidth->toSInt()); + if (rend == lstart->toSInt()) return true; + return false; + } + bool concatMergeable(AstNode* lhsp, AstNode* rhsp) { + if (!v3Global.opt.oAssemble()) return false; // opt disabled + if (lhsp->type() != rhsp->type()) return false; + if (!ifConcatMergeableBiop(lhsp)) return false; + + AstNodeBiop* lp = lhsp->castNodeBiop(); + AstNodeBiop* rp = rhsp->castNodeBiop(); + if (!lp || !rp) return false; + // {a[]&b[], a[]&b[]} + bool lad = ifAdjacent(lp->lhsp(), rp->lhsp()); + bool rad = ifAdjacent(lp->rhsp(), rp->rhsp()); + if (lad && lad) return true; + // {a[] & b[]&c[], a[] & b[]&c[]} + else if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true; + // {a[]&b[] & c[], a[]&b[] & c[]} + else if (rad && concatMergeable(lp->lhsp(), rp->lhsp())) return true; + else { + // {(a[]&b[])&(c[]&d[]), (a[]&b[])&(c[]&d[])} + if (concatMergeable(lp->lhsp(), rp->lhsp()) + && concatMergeable(lp->rhsp(), rp->rhsp())) + return true; + } + return false; + } //---------------------------------------- // Constant Replacement functions. @@ -668,6 +718,45 @@ private: rrp->deleteTree(); //nodep->dumpTree(cout, " repShiftSame_new: "); } + void replaceConcatSel(AstConcat* nodep) { + // {a[1], a[0]} -> a[1:0] + AstSel* lselp = nodep->lhsp()->unlinkFrBack()->castSel(); + AstSel* rselp = nodep->rhsp()->unlinkFrBack()->castSel(); + int lstart = lselp->lsbConst(); + int lwidth = lselp->widthConst(); + int rstart = rselp->lsbConst(); + int rwidth = rselp->widthConst(); + + if ((rstart + rwidth) != lstart) nodep->v3fatalSrc("tried to merge two selects which are not adjacent"); + AstSel* newselp = new AstSel(lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false), rstart, lwidth+rwidth); + UINFO(5, "merged two adjacent sel "<replaceWith(newselp); + lselp->deleteTree(); lselp = NULL; + rselp->deleteTree(); rselp = NULL; + nodep->deleteTree(); nodep = NULL; + } + void replaceConcatMerge(AstConcat* nodep) { + AstNodeBiop* lp = nodep->lhsp()->castNodeBiop(); + AstNodeBiop* rp = nodep->rhsp()->castNodeBiop(); + AstNode* llp = lp->lhsp()->cloneTree(false); + AstNode* lrp = lp->rhsp()->cloneTree(false); + AstNode* rlp = rp->lhsp()->cloneTree(false); + AstNode* rrp = rp->rhsp()->cloneTree(false); + if (concatMergeable(lp, rp)) { + AstConcat* newlp = new AstConcat(rlp->fileline(), llp, rlp); + AstConcat* newrp = new AstConcat(rrp->fileline(), lrp, rrp); + // use the lhs to replace the parent concat + lp->lhsp()->replaceWith(newlp); + lp->rhsp()->replaceWith(newrp); + lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), AstNumeric::fromBool(true)); + UINFO(5, "merged "<< nodep <unlinkFrBack()->deleteTree(); rp = NULL; + nodep->replaceWith(lp->unlinkFrBack()); nodep->deleteTree(); nodep = NULL; + lp->lhsp()->accept(*this); + lp->rhsp()->accept(*this); + } else nodep->v3fatalSrc("tried to merge two Concat which are not adjacent"); + } void replaceExtend (AstNode* nodep, AstNode* arg0p) { // -> EXTEND(nodep) // like a AstExtend{$rhsp}, but we need to set the width correctly from base node @@ -2064,6 +2153,9 @@ private: // CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c})) TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)"); TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())"); + // CONCAT(a[1],a[0]) -> a[1:0] + TREEOPV("AstConcat{$lhsp->castSel(), $rhsp->castSel(), ifAdjacent($lhsp,,$rhsp)}", "replaceConcatSel(nodep)"); + TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)"); // Common two-level operations that can be simplified TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 7c3167620..a958782e8 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -774,6 +774,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char case 'b': m_oCombine = flag; break; case 'c': m_oConst = flag; break; case 'd': m_oDedupe = flag; break; + case 'm': m_oAssemble = flag; break; case 'e': m_oCase = flag; break; case 'f': m_oFlopGater = flag; break; case 'g': m_oGate = flag; break; @@ -1319,6 +1320,7 @@ void V3Options::optimize(int level) { m_oSubstConst = flag; m_oTable = flag; m_oDedupe = flag; + m_oAssemble = flag; // And set specific optimization levels if (level >= 3) { m_inlineMult = -1; // Maximum inlining diff --git a/src/V3Options.h b/src/V3Options.h index a05924fe9..266fe7f04 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -139,6 +139,7 @@ class V3Options { bool m_oCombine; // main switch: -Ob: common icode packing bool m_oConst; // main switch: -Oc: constant folding bool m_oDedupe; // main switch: -Od: logic deduplication + bool m_oAssemble; // main switch: -Om: assign assemble bool m_oExpand; // main switch: -Ox: expansion of C macros bool m_oFlopGater; // main switch: -Of: flop gater detection bool m_oGate; // main switch: -Og: gate wire elimination @@ -282,6 +283,7 @@ class V3Options { bool oCombine() const { return m_oCombine; } bool oConst() const { return m_oConst; } bool oDedupe() const { return m_oDedupe; } + bool oAssemble() const { return m_oAssemble; } bool oExpand() const { return m_oExpand; } bool oFlopGater() const { return m_oFlopGater; } bool oGate() const { return m_oGate; } diff --git a/test_regress/t/t_concat_opt.pl b/test_regress/t/t_concat_opt.pl new file mode 100755 index 000000000..29bf04d49 --- /dev/null +++ b/test_regress/t/t_concat_opt.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_concat_opt.v b/test_regress/t/t_concat_opt.v new file mode 100644 index 000000000..c9be8c2a4 --- /dev/null +++ b/test_regress/t/t_concat_opt.v @@ -0,0 +1,67 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Jie Xu. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [31:0] in_a; + reg [31:0] in_b; + reg [31:0] in_c; + reg [31:0] in_d; + reg [31:0] in_e; + reg [15:0] in_f; + + + reg [31:0] out_x; + reg [31:0] out_y; + reg [31:0] out_z; + reg [31:0] out_o; + reg [31:0] out_p; + reg [31:0] out_q; + + assign out_x = {in_a[31:16] & in_f, in_a[15:0] & in_f}; + assign out_y = {in_a[31:18] & in_b[31:18], in_a[17:0] & in_b[17:0]}; + assign out_z = {in_c[31:14] & in_d[31:14] & in_e[31:14], in_c[13:0] & in_d[13:0] & in_e[13:0]}; + assign out_o = out_z | out_y; + assign out_p = {in_a[31:16] & in_f | in_e[31:16], in_a[15:0] & in_f | in_e[15:0]}; + assign out_q = {{in_a[31:25] ^ in_e[31:25], in_a[24:16] ^ in_e[24:16]}, {in_a[15:5] ^ in_e[15:5], in_a[4:0] ^ in_e[4:0]}}; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + in_a <= cyc; + in_b <= cyc + 1; + in_c <= cyc + 3; + in_d <= cyc + 8; + in_e <= cyc; + in_f <= cyc[15:0]; + + if (out_x != (in_a & {2{in_f}})) + $stop; + if (out_y != (in_a&in_b)) + $stop; + if (out_z != (in_e&in_d&in_c)) + $stop; + if (out_o != (((in_a&in_b)|(in_c&in_e&in_d)))) + $stop; + if (out_p != (in_a & {2{in_f}} | in_e)) + $stop; + if (out_q != (in_a ^ in_e)) + $stop; + + if (cyc==100) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule +