CGRA-ME
OpGraph_tests.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * The software programs comprising "CGRA-ME" and the documentation provided
3  * with them are copyright by its authors and the University of Toronto. Only
4  * non-commercial, not-for-profit use of this software is permitted without ex-
5  * plicit permission. This software is provided "as is" with no warranties or
6  * guarantees of support. See the LICENCE for more details. You should have re-
7  * ceived a copy of the full licence along with this software. If not, see
8  * <http://cgra-me.ece.utoronto.ca/license/>.
9  ******************************************************************************/
10 
11 #include <CGRA/OpGraph.h>
12 
14 #include <catch2/catch.hpp>
15 
16 void then_verify_should_pass(const OpGraph& opgraph) {
17  THEN("OpGraph::verify should pass") {
18  CHECK(verifyAndPrintReport(opgraph, std::cout, true, false));
19  }
20 }
21 
22 SCENARIO ("removing nodes and links from OpGraphs", "") {
23  GIVEN("A linear 3-node DFG") {
24  auto opgraph = makeDFG_Linear3Node();
25  WHEN("Removing a link") {
26  const auto target_op = opgraph.getOp("op_b");
27  opgraph.unLink(opgraph.outputVal(target_op), opgraph.fanout(target_op).at(0));
28  then_verify_should_pass(opgraph);
29  }
30  WHEN("Removing an internal node") {
31  opgraph.erase(opgraph.getOp("op_b"));
32  then_verify_should_pass(opgraph);
33  }
34  WHEN("Removing a inpupt node") {
35  opgraph.erase(opgraph.getOp("op_a"));
36  then_verify_should_pass(opgraph);
37  }
38  WHEN("Removing a output node") {
39  opgraph.erase(opgraph.getOp("op_c"));
40  then_verify_should_pass(opgraph);
41  }
42  }
43 
44  GIVEN("A self loop 3-node DFG") {
45  auto opgraph = makeDFG_SelfLoop3Node();
46  WHEN("Removing a link") {
47  const auto target_op = opgraph.getOp("op_l");
48  opgraph.unLink(opgraph.outputVal(target_op), opgraph.fanout(target_op).at(0));
49  then_verify_should_pass(opgraph);
50  }
51  WHEN("Removing an internal node") {
52  opgraph.erase(opgraph.getOp("op_l"));
53  then_verify_should_pass(opgraph);
54  }
55  WHEN("Removing a inpupt node") {
56  opgraph.erase(opgraph.getOp("op_a"));
57  then_verify_should_pass(opgraph);
58  }
59  WHEN("Removing a output node") {
60  opgraph.erase(opgraph.getOp("op_c"));
61  then_verify_should_pass(opgraph);
62  }
63  }
64 
65  GIVEN("A loop 4-node DFG") {
66  auto opgraph = makeDFG_2NodeLoop4Node();
67  WHEN("Removing a link") {
68  const auto target_op = opgraph.getOp("op_l");
69  opgraph.unLink(opgraph.outputVal(target_op), opgraph.fanout(target_op).at(0));
70  then_verify_should_pass(opgraph);
71  }
72  WHEN("Removing an internal node") {
73  opgraph.erase(opgraph.getOp("op_l"));
74  then_verify_should_pass(opgraph);
75  }
76  WHEN("Removing a inpupt node") {
77  opgraph.erase(opgraph.getOp("op_a"));
78  then_verify_should_pass(opgraph);
79  }
80  WHEN("Removing a output node") {
81  opgraph.erase(opgraph.getOp("op_d"));
82  then_verify_should_pass(opgraph);
83  }
84  }
85 
86  GIVEN("A Tree 112 DFG") {
87  auto opgraph = makeDFG_Tree112();
88  WHEN("Removing a link") {
89  const auto target_op = opgraph.getOp("op_b");
90  opgraph.unLink(opgraph.outputVal(target_op), opgraph.fanout(target_op).at(0));
91  then_verify_should_pass(opgraph);
92  }
93  WHEN("Removing an internal node") {
94  opgraph.erase(opgraph.getOp("op_b"));
95  then_verify_should_pass(opgraph);
96  }
97  WHEN("Removing a inpupt node") {
98  opgraph.erase(opgraph.getOp("op_a"));
99  then_verify_should_pass(opgraph);
100  }
101  WHEN("Removing a output node") {
102  opgraph.erase(opgraph.getOp("op_c"));
103  then_verify_should_pass(opgraph);
104  }
105  }
106 
107  GIVEN("A Converging 121 DFG") {
108  auto opgraph = makeDFG_Converging121();
109  WHEN("Removing a link") {
110  const auto target_op = opgraph.getOp("op_a");
111  opgraph.unLink(opgraph.outputVal(target_op), opgraph.fanout(target_op).at(0));
112  then_verify_should_pass(opgraph);
113  }
114  WHEN("Removing an internal node") {
115  opgraph.erase(opgraph.getOp("op_a"));
116  then_verify_should_pass(opgraph);
117  }
118  WHEN("Removing a inpupt node") {
119  opgraph.erase(opgraph.getOp("op_a"));
120  then_verify_should_pass(opgraph);
121  }
122  WHEN("Removing a output node") {
123  opgraph.erase(opgraph.getOp("op_c"));
124  then_verify_should_pass(opgraph);
125  }
126  }
127 }
128 
129 TEST_CASE("OpGraphOpCode to and from Strings") {
130  GIVEN("An opcode") {
131  const auto expected_opcode = GENERATE(values<OpGraphOpCode>({
132  OpCode::NOP,
133  OpCode::SEXT,
134  OpCode::ZEXT,
138  OpCode::PHI,
140  OpCode::ADD,
141  OpCode::SUB,
142  OpCode::MUL,
143  OpCode::DIV,
144  OpCode::AND,
145  OpCode::OR,
146  OpCode::XOR,
147  OpCode::SHL,
148  OpCode::ASHR,
149  OpCode::LSHR,
150  OpCode::LOAD,
152  OpCode::GEP,
153  OpCode::ICMP,
154  OpCode::BR,
155  }));
156 
157  THEN("Converting it to a string should work") {
158  const auto as_str = to_string(expected_opcode);
159  AND_THEN("Converting back to an opcode should give the same thing") {
160  const auto actual_opcode = opcode_from_string(as_str);
161  CHECK(actual_opcode == expected_opcode);
162  }
163  }
164  }
165 
166  GIVEN("A bogus string") {
167  THEN("to_string should throw") {
168  REQUIRE_THROWS(opcode_from_string("aoeuidhtns"));
169  }
170  }
171 }
172 
173 TEST_CASE("OpGraph Equality and Copying Tests") {
174  std::map<std::string, std::function<OpGraph(ConfigStore)>> og_generators {
175  {"a;",[](ConfigStore params) {
176  OpGraph og;
177  og.insert({params.getString("name_a"), 32, opcode_from_string(params.getStringOr("op_a","add"))});
178  return finalFixup(std::move(og));
179  }},
180  {"a;b",[](ConfigStore params) {
181  OpGraph og;
182  og.insert({params.getString("name_a"), 32, opcode_from_string(params.getStringOr("op_a","add"))});
183  og.insert({params.getString("name_b"), 32, opcode_from_string(params.getStringOr("op_a","add"))});
184  return finalFixup(std::move(og));
185  }},
186  {"a->b",[](ConfigStore params) {
187  OpGraph og;
188  const auto a = og.insert({params.getString("name_a"), 32, opcode_from_string(params.getStringOr("op_a","add"))});
189  og.insert(a, {params.getString("name_b"), 32, opcode_from_string(params.getStringOr("op_b","add"))}, params.getString("ab_oper"));
190  return finalFixup(std::move(og));
191  }},
192  {"a->b with b erased",[](ConfigStore params) {
193  OpGraph og;
194  const auto a = og.insert({params.getString("name_a"), 32, opcode_from_string(params.getStringOr("op_a","add"))});
195  const auto b = og.insert(a, {params.getString("name_b"), 32, opcode_from_string(params.getStringOr("op_b","add"))}, params.getString("ab_oper")).newOp;
196  og.erase(b);
197  return finalFixup(std::move(og));
198  }},
199  {"a->{b c}",[](ConfigStore params) {
200  OpGraph og;
201  const auto a = og.insert({params.getString("name_a"), 32, opcode_from_string(params.getStringOr("op_a","add"))});
202  og.insert(a, {params.getString("name_b"), 32, opcode_from_string(params.getStringOr("op_b","add"))}, params.getString("ab_oper"));
203  og.insert(a, {params.getString("name_c"), 32, opcode_from_string(params.getStringOr("op_c","add"))}, params.getString("ac_oper"));
204  return finalFixup(std::move(og));
205  }},
206  {"a->{b c} with c erased",[](ConfigStore params) {
207  OpGraph og;
208  const auto a = og.insert({params.getString("name_a"), 32, opcode_from_string(params.getStringOr("op_a","add"))});
209  og.insert(a, {params.getString("name_b"), 32, opcode_from_string(params.getStringOr("op_b","add"))}, params.getString("ab_oper"));
210  const auto c = og.insert(a, {params.getString("name_c"), 32, opcode_from_string(params.getStringOr("op_c","add"))}, params.getString("ac_oper")).newOp;
211  og.erase(c);
212  return finalFixup(std::move(og));
213  }},
214  {"const_a;",[](ConfigStore params) {
215  OpGraph og;
216  og.insert({params.getString("name_a"), 32, opcode_from_string(params.getStringOr("op_const","const")), (unsigned)params.getInt("constVal")});
217  return finalFixup(std::move(og));
218  }},
219  };
220 
221  struct OpGraphEqualityTest {
222  std::string test_description;
223  bool expect_to_be_same;
224  OpGraph graph1;
225  OpGraph graph2;
226  };
227 
228  const OpGraphEqualityTest tests[] { // opgraph is move only...
229  { "one op vs. one op -- same name", true,
230  og_generators.at("a;")({{"name_a","a"}}),
231  og_generators.at("a;")({{"name_a","a"}}),
232  },
233  { "one op vs. one op -- same name, different op", false,
234  og_generators.at("a;")({{"name_a","a"},{"op_a","add"}}),
235  og_generators.at("a;")({{"name_a","a"},{"op_a","mul"}}),
236  },
237  { "one op vs. one op -- different name", false,
238  og_generators.at("a;")({{"name_a","a"}}),
239  og_generators.at("a;")({{"name_a","b"}}),
240  },
241  { "two ops vs. two ops -- same names", true,
242  og_generators.at("a;b")({{"name_a","a"},{"name_b","b"}}),
243  og_generators.at("a;b")({{"name_a","a"},{"name_b","b"}}),
244  },
245  { "two ops vs. two ops -- 1 different name", false,
246  og_generators.at("a;b")({{"name_a","a"},{"name_b","b"}}),
247  og_generators.at("a;b")({{"name_a","a"},{"name_b","c"}}),
248  },
249  { "a->b vs. a;b", false,
250  og_generators.at("a->b")({{"name_a","a"},{"name_b","b"},{"ab_oper",Operands::BINARY_ANY}}),
251  og_generators.at("a;b" )({{"name_a","b"},{"name_b","c"}}),
252  },
253  { "a->b vs. a->b", true,
254  og_generators.at("a->b")({{"name_a","a"},{"name_b","b"},{"ab_oper",Operands::BINARY_ANY}}),
255  og_generators.at("a->b")({{"name_a","a"},{"name_b","b"},{"ab_oper",Operands::BINARY_ANY}}),
256  },
257  { "a->b vs. a->b, but operand tag is different", false,
258  og_generators.at("a->b")({{"name_a","a"},{"name_b","b"},{"ab_oper",Operands::BINARY_LHS}}),
259  og_generators.at("a->b")({{"name_a","a"},{"name_b","b"},{"ab_oper",Operands::BINARY_RHS}}),
260  },
261  { "a->{b c} vs. a->{b c}", true,
262  og_generators.at("a->{b c}")({{"name_a","a"},{"name_b","b"},{"name_c","c"},{"ab_oper",Operands::BINARY_ANY},{"ac_oper",Operands::BINARY_ANY}}),
263  og_generators.at("a->{b c}")({{"name_a","a"},{"name_b","b"},{"name_c","c"},{"ab_oper",Operands::BINARY_ANY},{"ac_oper",Operands::BINARY_ANY}}),
264  },
265  { "a->{b c} vs. a->{b d}", false,
266  og_generators.at("a->{b c}")({{"name_a","a"},{"name_b","b"},{"name_c","c"},{"ab_oper",Operands::BINARY_ANY},{"ac_oper",Operands::BINARY_ANY}}),
267  og_generators.at("a->{b c}")({{"name_a","a"},{"name_b","b"},{"name_c","d"},{"ab_oper",Operands::BINARY_ANY},{"ac_oper",Operands::BINARY_ANY}}),
268  },
269  { "a->{b c} vs. a->{b c}, but operand tag is different", false,
270  og_generators.at("a->{b c}")({{"name_a","a"},{"name_b","b"},{"name_c","c"},{"ab_oper",Operands::BINARY_ANY},{"ac_oper",Operands::BINARY_ANY}}),
271  og_generators.at("a->{b c}")({{"name_a","a"},{"name_b","b"},{"name_c",Operands::BINARY_ANY},{"ab_oper",Operands::BINARY_LHS},{"ac_oper",Operands::BINARY_RHS}}),
272  },
273  { "one const vs. one const -- same value", true,
274  og_generators.at("const_a;")({{"name_a","a"}, {"constVal", "4"}}),
275  og_generators.at("const_a;")({{"name_a","a"}, {"constVal", "4"}}),
276  },
277  { "one const vs. one const -- different value", false,
278  og_generators.at("const_a;")({{"name_a","a"}, {"constVal", "1"}}),
279  og_generators.at("const_a;")({{"name_a","a"}, {"constVal", "4"}}),
280  },
281  { "After erasing a node and a val", true,
282  og_generators.at("a->b with b erased")({{"name_a","a"},{"name_b","b"},{"ab_oper",Operands::BINARY_ANY}}),
283  og_generators.at("a;")( {{"name_a","a"},{"name_b","b"},{"ab_oper",Operands::BINARY_ANY}}),
284  },
285  { "After erasing just a node", true,
286  og_generators.at("a->{b c} with c erased")({{"name_a","a"},{"name_b","b"},{"name_c","c"},{"ab_oper",Operands::BINARY_ANY},{"ac_oper",Operands::BINARY_ANY}}),
287  og_generators.at("a->b")( {{"name_a","a"},{"name_b","b"},{"name_c","c"},{"ab_oper",Operands::BINARY_ANY},{"ac_oper",Operands::BINARY_ANY}}),
288  },
289  };
290 
291  using std::begin; using std::end;
292  const auto& test_it = GENERATE_REF(range((std::ptrdiff_t)0, std::distance(begin(tests), end(tests))));
293  const auto& test = tests[test_it];
294 
295  GIVEN("A '" << test.test_description << "' test case") {
296  THEN("The OpGraphs should " << (test.expect_to_be_same?"":"not ") << "compare equal") {
297  if (test.expect_to_be_same) {
298  CHECK(test.graph1 == test.graph2);
299  CHECK_FALSE(test.graph1 != test.graph2);
300  } else {
301  CHECK(test.graph1 != test.graph2);
302  CHECK_FALSE(test.graph1 == test.graph2);
303  }
304  }
305  THEN("A copies should compare as expected") {
306  const auto graph1_copy = test.graph1;
307  CHECK(graph1_copy == test.graph1);
308  if (test.expect_to_be_same) {
309  CHECK(graph1_copy == test.graph2);
310  CHECK_FALSE(graph1_copy != test.graph2);
311  } else {
312  CHECK(graph1_copy != test.graph2);
313  CHECK_FALSE(graph1_copy == test.graph2);
314  }
315 
316  const auto graph2_copy = test.graph2;
317  CHECK(graph2_copy == test.graph2);
318  if (test.expect_to_be_same) {
319  CHECK(test.graph1 == graph2_copy);
320  CHECK_FALSE(test.graph1 != graph2_copy);
321  } else {
322  CHECK(test.graph1 != graph2_copy);
323  CHECK_FALSE(test.graph1 == graph2_copy);
324  }
325  }
326  }
327 }
OpCode::BR
@ BR
OpCode::DIV
@ DIV
verifyAndPrintReport
bool verifyAndPrintReport(const MRRG &mrrg, std::ostream &os, bool silent_on_no_errors, bool throw_if_errors, const ConfigStore &extra_opts)
Definition: MRRG.cpp:473
OpGraph.h
OpCode::SEXT
@ SEXT
OpCode::CONST
@ CONST
OpCode::PHI
@ PHI
Operands::BINARY_LHS
static constexpr auto & BINARY_LHS
Definition: OpGraph.h:89
OpCode::ASHR
@ ASHR
OpCode::ZEXT
@ ZEXT
OpCode::OR
@ OR
begin
auto begin(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:137
makeDFG_Converging121
OpGraph makeDFG_Converging121()
Definition: OpGraphsForTesting.cpp:151
OpCode::STORE
@ STORE
then_verify_should_pass
void then_verify_should_pass(const OpGraph &opgraph)
Definition: OpGraph_tests.cpp:16
ConfigStore
Definition: ConfigStore.h:76
OpCode::ADD
@ ADD
to_string
const std::string & to_string(const OpGraphOpCode &opcode)
Definition: OpGraph.cpp:111
OpCode::LOAD
@ LOAD
OpCode::LSHR
@ LSHR
makeDFG_SelfLoop3Node
OpGraph makeDFG_SelfLoop3Node()
Definition: OpGraphsForTesting.cpp:80
OpCode::NOP
@ NOP
makeDFG_Linear3Node
OpGraph makeDFG_Linear3Node()
Definition: OpGraphsForTesting.cpp:50
OpCode::XOR
@ XOR
Operands::BINARY_RHS
static constexpr auto & BINARY_RHS
Definition: OpGraph.h:90
OpCode::GEP
@ GEP
OpCode::INPUT
@ INPUT
OpGraphsForTesting.hpp
OpCode::SHL
@ SHL
finalFixup
OpGraph finalFixup(OpGraph opgraph)
Definition: OpGraphsForTesting.cpp:5
OpCode::TRUNC
@ TRUNC
end
auto end(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:138
makeDFG_Tree112
OpGraph makeDFG_Tree112()
Definition: OpGraphsForTesting.cpp:139
OpCode::OUTPUT
@ OUTPUT
makeDFG_2NodeLoop4Node
OpGraph makeDFG_2NodeLoop4Node()
Definition: OpGraphsForTesting.cpp:115
OpCode::AND
@ AND
OpGraph::insert
OpDescriptor insert(OpGraphOp op)
Definition: OpGraph.cpp:338
OpCode::SUB
@ SUB
OpCode::MUL
@ MUL
TEST_CASE
TEST_CASE("OpGraphOpCode to and from Strings")
Definition: OpGraph_tests.cpp:129
OpCode::ICMP
@ ICMP
opcode_from_string
OpGraphOpCode opcode_from_string(const std::string &str)
Definition: OpGraph.cpp:113
SCENARIO
SCENARIO("removing nodes and links from OpGraphs", "")
Definition: OpGraph_tests.cpp:22
Operands::BINARY_ANY
static constexpr auto & BINARY_ANY
Definition: OpGraph.h:91
OpGraph::erase
void erase(OpDescriptor op)
Definition: OpGraph.cpp:453
OpGraph
Definition: OpGraph.h:215