Skip to content

Commit

Permalink
Merge 7acf0f7 into 9ae39d2
Browse files Browse the repository at this point in the history
  • Loading branch information
rsetaluri committed Feb 28, 2020
2 parents 9ae39d2 + 7acf0f7 commit 090e43b
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 1 deletion.
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ include_directories(
${PROJECT_SOURCE_DIR}/include/
)

set(LIB_SOURCES src/verilogAST.cpp src/transformer.cpp src/assign_inliner.cpp)
set(LIB_SOURCES src/verilogAST.cpp src/transformer.cpp src/assign_inliner.cpp
src/concat_coalescer.cpp)

set(LIBRARY_NAME verilogAST)
add_library(${LIBRARY_NAME} SHARED ${LIB_SOURCES})
Expand Down Expand Up @@ -65,6 +66,10 @@ if (VERILOGAST_BUILD_TESTS)
add_executable(assign_inliner tests/assign_inliner.cpp)
target_link_libraries(assign_inliner gtest_main ${LIBRARY_NAME})
add_test(NAME assign_inliner_tests COMMAND assign_inliner)

add_executable(concat_coalescer tests/concat_coalescer.cpp)
target_link_libraries(concat_coalescer gtest_main ${LIBRARY_NAME})
add_test(NAME concat_coalescer_tests COMMAND concat_coalescer)
endif()

install(TARGETS ${LIBRARY_NAME} DESTINATION lib)
Expand Down
18 changes: 18 additions & 0 deletions include/verilogAST/concat_coalescer.hpp
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
102 changes: 102 additions & 0 deletions src/concat_coalescer.cpp
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
134 changes: 134 additions & 0 deletions tests/concat_coalescer.cpp
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();
}

0 comments on commit 090e43b

Please sign in to comment.