14 #include <catch2/catch.hpp>
18 Module god_module(
"m",
"", {0,0}, 32);
20 MRRG makeMRRG_4nodeLinear(
int II,
int latency_of_a,
int latency_of_c,
ConfigStore options = {}) {
21 const int cycle_after_a = (0 + latency_of_a) % II;
22 const int cycle_after_c = (cycle_after_a + latency_of_c) % II;
25 std::map<std::string,MRRGNode::SupportedOpTags> operand_tags;
26 for (
auto& n : {
"a",
"b",
"c",
"d"}) {
27 auto& tags_for_n = operand_tags[n];
28 for (
auto& i : {
"1",
"2",
"3"}) {
29 const auto& key = std::string(
"operand-") + n + i;
30 if (options.hasKey(key)) {
31 tags_for_n.
insert(options.getString(key));
38 const auto b = mrrg.insert(a,
MRRGNode::make_operand_pin(&god_module, 32, cycle_after_a,
"b", operand_tags.at(
"b"), 0 )).first;
39 const auto c = mrrg.insert(b,
MRRGNode::make_operand_pin(&god_module, 32, cycle_after_a,
"c", operand_tags.at(
"c"), latency_of_c)).first;
40 const auto d = mrrg.insert(c,
MRRGNode::make_operand_pin(&god_module, 32, cycle_after_c,
"d", operand_tags.at(
"d"), 0 )).first;
47 MRRG makeMRRG_2nodeFUAndReg(
int II,
int latency_of_fu,
int latency_of_reg) {
49 const int cycle_after_fu = (0 + latency_of_fu) % II;
52 const auto reg = mrrg.insert(fu,
MRRGNode::make_routing(
nullptr, 32, cycle_after_fu,
"reg", latency_of_reg)).first;
60 MRRG makeMRRG_2112DAG(
int II,
int latency_of_a,
int latency_of_c) {
61 const int cycle_after_a = (0 + latency_of_a) % II;
62 const int cycle_after_c = (cycle_after_a + latency_of_c) % II;
66 const auto b = mrrg.insertMultiFanin({a1,a2},
MRRGNode::make_routing(
nullptr, 32, cycle_after_a,
"b", 0)).first;
67 const auto c = mrrg.insert(b,
MRRGNode::make_routing(
nullptr, 32, cycle_after_a,
"c", latency_of_c)).first;
77 MRRG makeMRRG_112221DAG(
int II,
int latency_of_a,
int latency_of_c) {
78 const int cycle_after_a = (0 + latency_of_a) % II;
79 const int cycle_after_c = (cycle_after_a + latency_of_c) % II;
83 const auto c1 = mrrg.insert(b,
MRRGNode::make_routing(
nullptr, 32, cycle_after_a,
"c1", latency_of_c)).first;
84 const auto c2 = mrrg.insert(b,
MRRGNode::make_routing(
nullptr, 32, cycle_after_a,
"c2", latency_of_c)).first;
89 const auto f = mrrg.insertMultiFanin({e1,e2},
MRRGNode::make_routing(
nullptr, 32, latency_of_c,
"f", 0)).first;
99 const auto II = GENERATE(1,2,3);
100 const auto latency_of_a = GENERATE(0,1,2,3,5);
101 const auto latency_of_c = GENERATE(0,1,2,3,5);
102 GIVEN(
"a 4 node linear II == " << II <<
" MRRG with latency " << latency_of_a <<
" on node a and latency of " << latency_of_c <<
" for node c") {
103 const auto& mrrg = makeMRRG_4nodeLinear(II, latency_of_a, latency_of_c);
104 const auto node_a = mrrg.getNode(0,
"a");
105 const auto node_b = *mrrg.fanout(node_a).begin();
106 const auto node_c = mrrg.getNode(mrrg.getNodeRef(node_b).getContextNum(),
"c");
107 const auto walk = std::vector<MRRG::NodeDescriptor>{node_a, node_b, node_c, *mrrg.fanout(node_c).begin()};
108 const auto total_trip_count = (latency_of_c + latency_of_a)/II;
109 auto walk_len = GENERATE_REF(range(2, (
int)walk.size()-1+1));
110 GIVEN(
"a walk that we will spit after " << walk_len <<
" nodes") {
111 const auto expected_trip_count = (walk_len >= 4 ? total_trip_count : latency_of_a/II);
112 THEN(
"the first " << walk_len <<
"nodes should have the expected trip count") {
113 CHECK(expected_trip_count ==
tripCountOfWalk(mrrg, decltype(walk){walk.begin(), std::next(walk.begin(), walk_len)}));
115 THEN(
"the last " << walk.size() - walk_len <<
" nodes should have the expected trip count") {
116 CHECK(total_trip_count - expected_trip_count ==
tripCountOfWalk(mrrg, decltype(walk){std::next(walk.begin(), walk_len-1), walk.end()}));
123 struct OperandCompatibilityTest {
129 const auto mrrg = makeMRRG_4nodeLinear(1, 0, 0, {
136 const auto& node_a = mrrg.getNode(0,
"a");
137 const auto& node_b = mrrg.getNode(0,
"b");
138 const auto& node_c = mrrg.getNode(0,
"c");
139 const auto& node_d = mrrg.getNode(0,
"d");
163 const auto nodes_a_to_a = std::vector<MRRG::NodeDescriptor>{node_a, node_b};
164 const auto nodes_a_to_b = std::vector<MRRG::NodeDescriptor>{node_a, node_b, node_c};
165 const auto nodes_b_to_c = std::vector<MRRG::NodeDescriptor>{ node_b, node_c, node_d};
166 const auto nodes_a_to_c = std::vector<MRRG::NodeDescriptor>{node_a, node_b, node_c, node_d};
198 struct FindNRoutingPathsBetweenTest {
199 std::pair<int,int> num_paths_to_search_for_min_max;
200 int max_paths_expected;
201 std::pair<int,std::string> source;
202 std::pair<int,std::string> sink;
203 std::pair<int,int> cycle_path_latency_min_max;
204 std::string mrrg_description;
205 std::function<
MRRG()> mrrg_generator;
211 const auto test = GENERATE(values<FindNRoutingPathsBetweenTest>({
212 { {1,3}, 1, {0,
"fu"}, {0,
"fu"}, {1,1},
"2 node loop with II=1, fu latency 0, reg latency 1", []{
return makeMRRG_2nodeFUAndReg(1, 0, 1); } },
215 const auto num_paths_to_search_for = GENERATE_REF(range(
216 test.num_paths_to_search_for_min_max.first, test.num_paths_to_search_for_min_max.second+1
218 const auto mrrg = test.mrrg_generator();
219 const auto src = mrrg.getNode(test.source.first, test.source.second);
220 const auto sink = mrrg.getNode(test.sink.first, test.sink.second);
222 num_paths_to_search_for, mrrg, src, sink, test.cycle_path_latency_min_max, {},
nullptr);
223 CHECK(actual.size() <= test.max_paths_expected);
224 CHECK(actual.size() <= num_paths_to_search_for);
229 template<
typename VertexNameLists>
230 auto resolve_node_name_lists(
bool create_if_not_exist,
MRRG& mrrg,
const VertexNameLists& vertex_lists) {
231 std::vector<std::vector<MRRG::NodeDescriptor>> result;
232 for (
const auto& vlist : vertex_lists) {
233 std::vector<MRRG::NodeDescriptor> resolved_list;
234 for (
const auto& v_name : vlist) {
235 const auto curr_ndesc_and_is_new = mrrg.
insert(
237 if (curr_ndesc_and_is_new.second && not create_if_not_exist) {
238 throw make_from_stream<std::logic_error>([&](
auto&& s) {
239 s <<
"used non-existant node " << v_name;
242 resolved_list.push_back(curr_ndesc_and_is_new.first);
243 if (resolved_list.size() > 1) {
244 const auto prev_ndesc = *std::next(resolved_list.rbegin());
245 const auto& prev_fo = mrrg.
fanout(prev_ndesc);
247 if (std::find(prev_fo.begin(), prev_fo.end(), curr_ndesc_and_is_new.first) == prev_fo.end()) {
248 if (not create_if_not_exist) {
throw make_from_stream<std::logic_error>([&](
auto&& s) {
249 s <<
"used non-existant edge to " << v_name;
251 mrrg.
link(prev_ndesc, curr_ndesc_and_is_new.first);
255 result.push_back(std::move(resolved_list));
260 struct MergeMRRGWalksTest {
261 std::string test_description;
263 std::vector<std::vector<std::string>> input;
264 std::vector<std::vector<std::string>> expected;
270 const auto test = GENERATE(values<MergeMRRGWalksTest>({
274 {
"two isolated paths", {
282 {
"two diverging paths", {
290 {
"two re-converging paths -- same start and end", {
291 {
"a",
"b1",
"c",
"z" },
297 {
"two re-converging paths -- same start, different end", {
298 {
"a",
"b1",
"c",
"d",
"e",
"z1" },
299 {
"a",
"b2",
"d",
"z2" },
301 {
"a",
"b2",
"d",
"z2" },
305 {
"two re-converging paths -- different start, different end", {
306 {
"x1",
"y",
"a",
"b1",
"c",
"d",
"e",
"z1" },
307 {
"x2",
"a",
"b2",
"d",
"z2" },
309 {
"x2",
"a",
"b2",
"d",
"z2" },
315 {
"a",
"b",
"c",
"a"},
317 {
"a",
"b",
"c",
"a"},
320 {
"a loop, plus a line -- only sharing a source", {
321 {
"a",
"b",
"c",
"a"},
324 {
"a",
"b",
"c",
"a"},
328 {
"a loop, plus a line -- sharing the source and a fanout", {
329 {
"a",
"b",
"c",
"a"},
330 {
"a",
"b",
"d",
"e",
"z"},
332 {
"a",
"b",
"c",
"a"},
333 {
"b",
"d",
"e",
"z"},
338 GIVEN(
"A '" << test.test_description <<
"' test") {
340 const auto input = resolve_node_name_lists(
true, mrrg, test.input);
341 auto expected = resolve_node_name_lists(
false, mrrg, test.expected);
342 std::sort(expected.begin(), expected.end());
345 std::sort(actual.begin(), actual.end());
347 CHECK(actual == expected);
353 (void)makeMRRG_2112DAG;
354 (void)makeMRRG_112221DAG;