-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
260 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#ifndef VERILOGAST_ASSIGN_INLINER_H | ||
#define VERILOGAST_ASSIGN_INLINER_H | ||
|
||
#include "verilogAST.hpp" | ||
#include "verilogAST/transformer.hpp" | ||
|
||
namespace verilogAST { | ||
|
||
class ConcatCoalescer : public Transformer { | ||
public: | ||
using Transformer::visit; | ||
|
||
std::unique_ptr<Expression> visit(std::unique_ptr<Expression> node) override; | ||
}; | ||
|
||
} // namespace verilogAST | ||
|
||
#endif // VERILOGAST_ASSIGN_INLINER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
#include <cassert> | ||
#include "verilogAST/concat_coalescer.hpp" | ||
|
||
namespace verilogAST { | ||
|
||
namespace { | ||
|
||
struct Run { | ||
std::string name; | ||
int first; // inclusive | ||
int last; // inclusive | ||
}; | ||
|
||
class RunOrExpr { | ||
public: | ||
explicit RunOrExpr(const Expression* expr) : run_(), expr_(expr) {} | ||
explicit RunOrExpr(std::string name, int first, int last) | ||
: run_({name, first, last}), expr_(nullptr) {} | ||
|
||
bool isRun() const { return expr_ == nullptr; } | ||
|
||
// Tries to merge in @other into this run if it is contiguous. Returns true if | ||
// merged, falses otherwise. | ||
bool tryMerge(const RunOrExpr& other) { | ||
if (!isRun() or !other.isRun()) return false; | ||
if (run_.name != other.run_.name) return false; | ||
if (run_.last != other.run_.first + 1) return false; | ||
run_.last = other.run_.first; | ||
return true; | ||
} | ||
|
||
// Returns the expression corresponding to this run. If this is not a run, | ||
// then we return a clone of the contained expression. Otherwise, we return an | ||
// Index expresssion if the run contains only one index, or a Slice if it | ||
// contains > 1. | ||
std::unique_ptr<Expression> generateExpression() const { | ||
if (not isRun()) return expr_->clone(); | ||
auto first = std::unique_ptr<Expression>( | ||
new NumericLiteral(std::to_string(run_.first))); | ||
if (run_.first == run_.last) { | ||
return std::unique_ptr<Expression>( | ||
new Index(std::make_unique<Identifier>(run_.name), std::move(first))); | ||
} | ||
auto id = std::unique_ptr<Expression>(new Identifier(run_.name)); | ||
auto last = std::unique_ptr<Expression>( | ||
new NumericLiteral(std::to_string(run_.last))); | ||
return std::unique_ptr<Expression>( | ||
new Slice(std::move(id), std::move(first), std::move(last))); | ||
} | ||
|
||
private: | ||
Run run_; | ||
const Expression* expr_ = nullptr; | ||
}; | ||
|
||
// Tries to extract a NumericLiteral (int) from @expr. Returns a pair of <true, | ||
// value> if successful; otherwise, returns <false, 0>. | ||
std::pair<bool, int> expr_to_int(const Expression* expr) { | ||
auto ptr = dynamic_cast<const NumericLiteral*>(expr); | ||
if (not ptr) return std::make_pair(false, 0); | ||
return std::make_pair(true, std::atoi(ptr->value.c_str())); | ||
} | ||
|
||
// Consumes a Concat node argument @arg and tries to make a run out of it. If it | ||
// is of the form Index(Identifier name, NumericLiteral val) then we return | ||
// RunOrExpr(name, val, val); otherwise we return RunOrExpr(arg). | ||
RunOrExpr makeRunOrExpr(const Expression* arg) { | ||
auto index = dynamic_cast<const Index*>(arg); | ||
if (not index) return RunOrExpr(arg); | ||
auto as_int = expr_to_int(index->index.get()); | ||
if (not as_int.first) return RunOrExpr(arg); | ||
return RunOrExpr(index->id->value, as_int.second, as_int.second); | ||
} | ||
|
||
} // namespace | ||
|
||
std::unique_ptr<Expression> ConcatCoalescer::visit( | ||
std::unique_ptr<Expression> node) { | ||
auto ptr = dynamic_cast<const Concat*>(node.get()); | ||
// This pass only operates on non-empty Concat nodes. | ||
if (not ptr or ptr->args.size() == 0) return node; | ||
std::vector<RunOrExpr> runs; | ||
for (const auto& arg : ptr->args) { | ||
auto run_or_expr = makeRunOrExpr(arg.get()); | ||
// If this is the first run, then we append it. Otherwise, we try to merge | ||
// it into the previous run. If it cannot be merged, then we append it. | ||
if (runs.size() == 0 or not runs.back().tryMerge(run_or_expr)) { | ||
runs.push_back(run_or_expr); | ||
} | ||
} | ||
assert(runs.size() > 0); | ||
// If there is sonly one run, then we return that run as a standalone | ||
// expression; otherwise, we return a Concat node containing the runs. | ||
if (runs.size() == 1) return runs.front().generateExpression(); | ||
std::vector<std::unique_ptr<Expression>> args; | ||
for (const auto& run : runs) { | ||
args.push_back(run.generateExpression()); | ||
} | ||
return std::make_unique<Concat>(std::move(args)); | ||
} | ||
|
||
} // namespace verilogAST |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
#include "verilogAST/concat_coalescer.hpp" | ||
#include "common.cpp" | ||
#include "gtest/gtest.h" | ||
|
||
namespace vAST = verilogAST; | ||
|
||
namespace { | ||
|
||
using BodyElement = std::variant<std::unique_ptr<vAST::StructuralStatement>, | ||
std::unique_ptr<vAST::Declaration>>; | ||
|
||
auto makeVector(std::string name, int hi, int lo) { | ||
return std::make_unique<vAST::Vector>(vAST::make_id(name), | ||
vAST::make_num(std::to_string(hi)), | ||
vAST::make_num(std::to_string(lo))); | ||
} | ||
|
||
void runTest(std::array<int, 8> indices, std::string pre, std::string post) { | ||
std::vector<std::unique_ptr<vAST::AbstractPort>> ports; | ||
ports.push_back(std::make_unique<vAST::Port>(makeVector("I", 7, 0), | ||
vAST::INPUT, | ||
vAST::WIRE)); | ||
ports.push_back(std::make_unique<vAST::Port>(makeVector("O", 7, 0), | ||
vAST::OUTPUT, | ||
vAST::WIRE)); | ||
std::vector<BodyElement> body; | ||
|
||
std::vector<std::unique_ptr<vAST::Expression>> args = {}; | ||
for (int i = 7; i >= 0; i--) { | ||
args.emplace_back( | ||
new vAST::Index( | ||
vAST::make_id("I"), | ||
vAST::make_num(std::to_string(indices[i])))); | ||
} | ||
std::unique_ptr<vAST::Expression> rhs(new vAST::Concat(std::move(args))); | ||
|
||
body.push_back(std::make_unique<vAST::ContinuousAssign>( | ||
std::make_unique<vAST::Identifier>("O"), std::move(rhs))); | ||
|
||
std::unique_ptr<vAST::AbstractModule> module = std::make_unique<vAST::Module>( | ||
"test_module", | ||
std::move(ports), | ||
std::move(body)); | ||
|
||
EXPECT_EQ(module->toString(), pre); | ||
|
||
// Run ConcatCoalescer transformer. | ||
vAST::ConcatCoalescer transformer; | ||
module = transformer.visit(std::move(module)); | ||
|
||
EXPECT_EQ(module->toString(), post); | ||
} | ||
|
||
TEST(ConcatCoalescerTests, TestBasic) { | ||
auto pre = | ||
"module test_module (\n" | ||
" input [7:0] I,\n" | ||
" output [7:0] O\n" | ||
");\n" | ||
"assign O = {I[7],I[6],I[5],I[4],I[3],I[2],I[1],I[0]};\n" | ||
"endmodule\n"; | ||
auto post = | ||
"module test_module (\n" | ||
" input [7:0] I,\n" | ||
" output [7:0] O\n" | ||
");\n" | ||
"assign O = I[7:0];\n" | ||
"endmodule\n"; | ||
runTest({0, 1, 2, 3, 4, 5, 6, 7}, pre, post); | ||
} | ||
|
||
TEST(ConcatCoalescerTests, TestMultipleRuns) { | ||
auto pre = | ||
"module test_module (\n" | ||
" input [7:0] I,\n" | ||
" output [7:0] O\n" | ||
");\n" | ||
"assign O = {I[7],I[6],I[5],I[2],I[1],I[0],I[4],I[3]};\n" | ||
"endmodule\n"; | ||
auto post = | ||
"module test_module (\n" | ||
" input [7:0] I,\n" | ||
" output [7:0] O\n" | ||
");\n" | ||
"assign O = {I[7:5],I[2:0],I[4:3]};\n" | ||
"endmodule\n"; | ||
runTest({3, 4, 0, 1, 2, 5, 6, 7}, pre, post); | ||
} | ||
|
||
TEST(ConcatCoalescerTests, TestNoRuns) { | ||
auto pre = | ||
"module test_module (\n" | ||
" input [7:0] I,\n" | ||
" output [7:0] O\n" | ||
");\n" | ||
"assign O = {I[7],I[5],I[3],I[1],I[6],I[4],I[2],I[0]};\n" | ||
"endmodule\n"; | ||
auto post = | ||
"module test_module (\n" | ||
" input [7:0] I,\n" | ||
" output [7:0] O\n" | ||
");\n" | ||
"assign O = {I[7],I[5],I[3],I[1],I[6],I[4],I[2],I[0]};\n" | ||
"endmodule\n"; | ||
// Sequence has no contiguous runs. | ||
runTest({0, 2, 4, 6, 1, 3, 5, 7}, pre, post); | ||
} | ||
|
||
TEST(ConcatCoalescerTests, ReverseRun) { | ||
auto pre = | ||
"module test_module (\n" | ||
" input [7:0] I,\n" | ||
" output [7:0] O\n" | ||
");\n" | ||
"assign O = {I[4],I[5],I[6],I[7],I[3],I[2],I[1],I[0]};\n" | ||
"endmodule\n"; | ||
// ConcatCoalescer doesn't coalese reverse runs right now. | ||
auto post = | ||
"module test_module (\n" | ||
" input [7:0] I,\n" | ||
" output [7:0] O\n" | ||
");\n" | ||
"assign O = {I[4],I[5],I[6],I[7],I[3:0]};\n" | ||
"endmodule\n"; | ||
// Sequence has no contiguous runs. | ||
runTest({0, 1, 2, 3, 7, 6, 5, 4}, pre, post); | ||
} | ||
|
||
} // namespace | ||
|
||
int main(int argc, char **argv) { | ||
::testing::InitGoogleTest(&argc, argv); | ||
return RUN_ALL_TESTS(); | ||
} |