Add optimization of operators between concats, msg1447.

This commit is contained in:
Wilson Snyder 2014-10-22 21:44:41 -04:00
parent 85c3179dbd
commit cf6d07aafa
6 changed files with 183 additions and 0 deletions

View File

@ -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]

View File

@ -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 "<<lselp <<" and "<<rselp<< " to one "<<newselp<<endl);
nodep->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 <<endl);
rp->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)");

View File

@ -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

View File

@ -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; }

18
test_regress/t/t_concat_opt.pl Executable file
View File

@ -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;

View File

@ -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