Skip to content

Commit

Permalink
Improve optimization of duplicate wide expressions (verilator#5637)
Browse files Browse the repository at this point in the history
Prevent inlining of expensive wide expressions in V3Gate (verilator#5637)
  • Loading branch information
b-chmiel authored Dec 11, 2024
1 parent 9656311 commit 58ddf99
Show file tree
Hide file tree
Showing 15 changed files with 346 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/V3Gate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,44 @@ class GateInline final {
std::unordered_map<AstNode*, size_t> m_hasPending;
size_t m_statInlined = 0; // Statistic tracking - signals inlined
size_t m_statRefs = 0; // Statistic tracking
size_t m_statExcluded = 0; // Statistic tracking

// METHODS
static bool isCheapWide(const AstNodeExpr* exprp) {
if (const AstSel* const selp = VN_CAST(exprp, Sel)) {
if (selp->lsbConst() % VL_EDATASIZE != 0) return false;
exprp = selp->fromp();
}
if (const AstArraySel* const aselp = VN_CAST(exprp, ArraySel)) exprp = aselp->fromp();
return VN_IS(exprp, Const) || VN_IS(exprp, NodeVarRef);
}
static bool excludedWide(GateVarVertex* const vVtxp, const AstNodeExpr* const rhsp) {
// Handle wides with logic drivers that are too wide for V3Expand.
if (!vVtxp->varScp()->isWide() //
|| vVtxp->varScp()->widthWords() <= v3Global.opt.expandLimit() //
|| vVtxp->inEmpty() //
|| isCheapWide(rhsp))
return false;

const GateLogicVertex* const lVtxp
= vVtxp->inEdges().frontp()->fromp()->as<GateLogicVertex>();

// Exclude from inlining variables READ multiple times.
// To decouple actives thus simplifying scheduling, exclude only those
// VarRefs that are referenced under the same active as they were assigned.
if (const AstActive* const primaryActivep = lVtxp->activep()) {
size_t reads = 0;
for (const V3GraphEdge& edge : vVtxp->outEdges()) {
const GateLogicVertex* const lvp = edge.top()->as<GateLogicVertex>();
if (lvp->activep() != primaryActivep) continue;

reads += edge.weight();
if (reads > 1) return true;
}
}
return false;
}

void recordSubstitution(AstVarScope* vscp, AstNodeExpr* substp, AstNode* logicp) {
m_hasPending.emplace(logicp, ++m_ord); // It's OK if already present
const auto pair = m_substitutions(logicp).emplace(vscp, nullptr);
Expand Down Expand Up @@ -777,6 +813,12 @@ class GateInline final {
if (!okVisitor.isSimple()) continue;
// If the varScope is already removed from logicp, no need to try substitution.
if (!okVisitor.varAssigned(vVtxp->varScp())) continue;
if (excludedWide(vVtxp, okVisitor.substitutionp())) {
++m_statExcluded;
UINFO(9, "Gate inline exclude '" << vVtxp->name() << "'" << endl);
vVtxp->clearReducible("Excluded wide"); // Check once.
continue;
}

// Does it read multiple source variables?
if (okVisitor.readVscps().size() > 1) {
Expand Down Expand Up @@ -876,6 +918,7 @@ class GateInline final {
~GateInline() {
V3Stats::addStat("Optimizations, Gate sigs deleted", m_statInlined);
V3Stats::addStat("Optimizations, Gate inputs replaced", m_statRefs);
V3Stats::addStat("Optimizations, Gate excluded wide expressions", m_statExcluded);
}

public:
Expand Down
19 changes: 19 additions & 0 deletions test_regress/t/t_gate_inline_wide_exclude_multiple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

import vltest_bootstrap

test.scenarios('vlt')

test.lint(verilator_flags2=['--stats', '--expand-limit 5'])

test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 2)
test.file_grep(test.stats, r'Optimizations, Gate sigs deleted\s+(\d+)', 4)

test.passes()
26 changes: 26 additions & 0 deletions test_regress/t/t_gate_inline_wide_exclude_multiple.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0

localparam N = 256; // Wider than expand limit.

module t(
input wire [N-1:0] i,
output logic [N-1:0] o_multiple1,
output logic [N-1:0] o_multiple2,
output wire [N-1:0] o
);

// Exclude from inline wide expressions referenced multiple times.
wire [N-1:0] wide_multiple_assigns = N >> i;
wire [N-1:0] wide = N << i;

for (genvar n = 0; n < N - 1; ++n) begin
assign o[n] = i[N-1-n] | wide[N-1-n];
end

assign o_multiple1 = wide_multiple_assigns | i + 1;
assign o_multiple2 = wide_multiple_assigns | i + 2;
endmodule
19 changes: 19 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_arraysel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

import vltest_bootstrap

test.scenarios('vlt')

test.lint(verilator_flags2=['--stats', '--expand-limit 5'])

test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 0)
test.file_grep(test.stats, r'Optimizations, Gate sigs deleted\s+(\d+)', 1)

test.passes()
19 changes: 19 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_arraysel.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0

module t;
logic [255:0] arrd [0:0] = '{ 1 };
logic [255:0] y0;

// Do not exclude from inlining wide arraysels.
always_comb y0 = arrd[0];

always_comb begin
if (y0 != 1 && y0 != 0) begin
$stop;
end
end
endmodule
19 changes: 19 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

import vltest_bootstrap

test.scenarios('vlt')

test.lint(verilator_flags2=['--stats', '--expand-limit 5'])

test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 0)
test.file_grep(test.stats, r'Optimizations, Gate sigs deleted\s+(\d+)', 2)

test.passes()
19 changes: 19 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_const.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0

module t;
logic [255:0] arrd = 256'b0;
logic [255:0] y0;

// Do not exclude from inlining wide variables with const assignments.
always_comb y0 = 256'(arrd[0]);

always_comb begin
if (y0 != 1 && y0 != 0) begin
$stop;
end
end
endmodule
18 changes: 18 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_other_scope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

import vltest_bootstrap

test.scenarios('vlt')

test.lint(verilator_flags2=['--stats', '--expand-limit 5'])

test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 0)

test.passes()
26 changes: 26 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_other_scope.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0

localparam N = 256; // Wider than expand limit.

module t(
input wire [N-1:0] i,
output wire [N-1:0] o
);

// Do not exclude from inlining wides referenced in different scope.
wire [N-1:0] wide = N ~^ i;

sub sub(i, wide, o);
endmodule

module sub(input wire [N-1:0] i, input wire [N-1:0] wide, output logic [N-1:0] o);
initial begin
for (integer n = 0; n < N ; ++n) begin
o[n] = i[N-1-n] | wide[N-1-n];
end
end
endmodule
19 changes: 19 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_sel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

import vltest_bootstrap

test.scenarios('vlt')

test.lint(verilator_flags2=['--stats', '--expand-limit 5'])

test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 1)
test.file_grep(test.stats, r'Optimizations, Gate sigs deleted\s+(\d+)', 9)

test.passes()
44 changes: 44 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_sel.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0

module t (
output reg [1020:0] res1,
output reg [1020:0] res2,
output reg [1022:0] res3,
output reg [1022:0] res4
);
always_inline always_inline(res1, res2);
dont_inline dont_inline(res3, res4);
endmodule

module always_inline(
output reg [1020:0] res1,
output reg [1020:0] res2
);

wire [1023:0] a;
wire [478:0] b;

assign b = a[510:32];
assign res1 = {542'b0, b};
assign res2 = {542'b1, b};
endmodule

// SEL does not have proper offset so we do not have guarantee that it will be
// emitted as '[' operator, thus we do not exclude it from inlining.
module dont_inline(
output reg [1022:0] res1,
output reg [1022:0] res2
);

wire [1023:0] a;
wire [480:0] b;

// LSB % 32 != 0
assign b = a[510:30];
assign res1 = {542'b0, b};
assign res2 = {542'b1, b};
endmodule
18 changes: 18 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_small_wide.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

import vltest_bootstrap

test.scenarios('vlt')

test.lint(verilator_flags2=['--stats', '--expand-limit 5'])

test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 0)

test.passes()
21 changes: 21 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_small_wide.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0

localparam N = 65; // Wide but narrower than expand limit

module t(
input wire [N-1:0] i,
output wire [N-1:0] o
);

// Do not exclude from inlining wides small enough to be handled by
// V3Expand.
wire [65:0] wide_small = N << i * i / N;

for (genvar n = 0; n < N; ++n) begin
assign o[n] = i[n] ^ wide_small[n];
end
endmodule
19 changes: 19 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_varref.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

import vltest_bootstrap

test.scenarios('vlt')

test.lint(verilator_flags2=['--stats', '--expand-limit 5'])

test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 0)
test.file_grep(test.stats, r'Optimizations, Gate sigs deleted\s+(\d+)', 3)

test.passes()
17 changes: 17 additions & 0 deletions test_regress/t/t_gate_inline_wide_noexclude_varref.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro.
// SPDX-License-Identifier: CC0-1.0

module t(input [255:0] clk);
// Do not exclude from inlining wide reference assignments.
mod1 mod1(clk);
mod2 mod2(clk);
endmodule

module mod1(input [255:0] clk);
endmodule

module mod2(input [255:0] clk);
endmodule

0 comments on commit 58ddf99

Please sign in to comment.