Add pmgen finish statement, return number of matches
authorClifford Wolf <clifford@clifford.at>
Fri, 16 Aug 2019 12:16:35 +0000 (14:16 +0200)
committerClifford Wolf <clifford@clifford.at>
Fri, 16 Aug 2019 12:16:35 +0000 (14:16 +0200)
Signed-off-by: Clifford Wolf <clifford@clifford.at>
passes/pmgen/README.md
passes/pmgen/pmgen.py
passes/pmgen/test_pmgen.cc
passes/pmgen/test_pmgen.pmg

index f92445a864b161b81e1f76ec94e9e20b9bbb79b0..be908ef0b9e43ea21adc17a547f21cb9f4651325 100644 (file)
@@ -45,9 +45,9 @@ of type `foobar_pm::state_<pattern_name>_t`.)
 Similarly the `.pmg` file declares user data variables that become members of
 `.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`.
 
-There are four versions of the `run_<pattern_name>()` method: Without callback,
-callback without arguments, callback with reference to `pm`, and callback with
-reference to `pm.st_<pattern_name>`.
+There are three versions of the `run_<pattern_name>()` method: Without callback,
+callback without arguments, and callback with reference to `pm`. All versions
+of the `run_<pattern_name>()` method return the number of found matches.
 
 
 The .pmg File Format
@@ -118,8 +118,8 @@ write matchers:
   connected to any of the given signal bits, plus one if any of the signal
   bits is also a primary input or primary output.
 
-- In `code..endcode` blocks there exist `accept`, `reject`, `branch`, and
-  `subpattern` statements.
+- In `code..endcode` blocks there exist `accept`, `reject`, `branch`,
+  `finish`, and `subpattern` statements.
 
 - In `index` statements there is a special `===` operator for the index
   lookup.
@@ -246,13 +246,13 @@ debug messages. For example:
 
     code
         stack.push_back(addAB);
-       ...
+        ...
     finally
         stack.pop_back();
     endcode
 
-`accept` statements can be used inside the `finally` section, but not
-`reject`, `branch`, or `subpattern`.
+`accept` and `finish` statements can be used inside the `finally` section,
+but not `reject`, `branch`, or `subpattern`.
 
 Declaring a subpattern
 ----------------------
@@ -265,52 +265,75 @@ Arguments may be passed to subpattern via state variables. The `subpattern`
 line must be followed by a `arg <arg1> <arg2> ...` line that lists the
 state variables used to pass arguments.
 
-       state <IdString> foobar_type
-       state <bool> foobar_state
-
-       code foobar_type foobar_state
-               foobar_state = false;
-               foobar_type = $add;
-               subpattern(foo);
-               foobar_type = $sub;
-               subpattern(bar);
-       endcode
-
-       subpattern foo
-       arg foobar_type foobar_state
-
-       match addsub
-               index <IdString> addsub->type === foobar_type
-               ...
-       endmatch
-
-       code
-               if (foobar_state) {
-                       subpattern(tail);
-               } else {
-                       foobar_state = true;
-                       subpattern(bar);
-               }
-       endcode
-
-       subpattern bar
-       arg foobar_type foobar_state
-
-       match addsub
-               index <IdString> addsub->type === foobar_type
-               ...
-       endmatch
-
-       code
-               if (foobar_state) {
-                       subpattern(tail);
-               } else {
-                       foobar_state = true;
-                       subpattern(foo);
-               }
-       endcode
-
-       subpattern tail
-       ...
+    state <IdString> foobar_type
+    state <bool> foobar_state
+
+    code foobar_type foobar_state
+        foobar_state = false;
+        foobar_type = $add;
+        subpattern(foo);
+        foobar_type = $sub;
+        subpattern(bar);
+    endcode
+
+    subpattern foo
+    arg foobar_type foobar_state
+
+    match addsub
+        index <IdString> addsub->type === foobar_type
+        ...
+    endmatch
+
+    code
+        if (foobar_state) {
+            subpattern(tail);
+        } else {
+            foobar_state = true;
+            subpattern(bar);
+        }
+    endcode
+
+    subpattern bar
+    arg foobar_type foobar_state
+
+    match addsub
+        index <IdString> addsub->type === foobar_type
+        ...
+    endmatch
+
+    code
+        if (foobar_state) {
+            subpattern(tail);
+        } else {
+            foobar_state = true;
+            subpattern(foo);
+        }
+    endcode
+
+    subpattern tail
+    ...
 
 Subpatterns cann be called recursively.
+
+Generate Blocks
+---------------
+
+Match blocks may contain an optional `generate` section that is used for automatic
+test-case generation. For example:
+
+    match mul
+        ...
+    generate 10
+        SigSpec Y = port(ff, \D);
+        SigSpec A = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
+        SigSpec B = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
+        module->addMul(NEW_ID, A, B, Y, rng(2));
+    endmatch
+
+The expression `rng(n)` returns a non-negative integer less than `n`.
+
+The argument to `generate` is the chance of this generate block being executed
+when the match block did not match anything, in percent.
+
+The special statement `finish` can be used within generate blocks to terminate
+the current pattern matcher run.
index 95fcd254064cac97a521dcfce93361bd27c251cc..6950a99c0aa7874927dae4862358ec60ae0debac 100644 (file)
@@ -328,6 +328,7 @@ with open(outfile, "w") as f:
     print("  SigMap sigmap;", file=f)
     print("  std::function<void()> on_accept;", file=f)
     print("  bool generate_mode;", file=f)
+    print("  int accept_cnt;", file=f)
     print("", file=f)
 
     print("  uint32_t rngseed;", file=f)
@@ -476,7 +477,8 @@ with open(outfile, "w") as f:
     print("", file=f)
 
     for current_pattern in sorted(patterns.keys()):
-        print("  void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
+        print("  int run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
+        print("    accept_cnt = 0;", file=f)
         print("    on_accept = on_accept_f;", file=f)
         print("    rollback = 0;", file=f)
         print("    blacklist_dirty = false;", file=f)
@@ -487,14 +489,15 @@ with open(outfile, "w") as f:
                 print("    st_{}.{} = {}();".format(current_pattern, s, t), file=f)
         print("    block_{}(1);".format(patterns[current_pattern]), file=f)
         print("    log_assert(rollback_stack.empty());", file=f)
+        print("    return accept_cnt;", file=f)
         print("  }", file=f)
         print("", file=f)
-        print("  void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
-        print("    run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
+        print("  int run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
+        print("    return run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
         print("  }", file=f)
         print("", file=f)
-        print("  void run_{}() {{".format(current_pattern), file=f)
-        print("    run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
+        print("  int run_{}() {{".format(current_pattern), file=f)
+        print("    return run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
         print("  }", file=f)
         print("", file=f)
 
@@ -574,7 +577,8 @@ with open(outfile, "w") as f:
         if block["type"] == "code":
             print("", file=f)
             print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
-            print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
+            print("#define accept do { accept_cnt++; on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
+            print("#define finish do { rollback = -1; rollback_stack.clean(); goto rollback_label; } while(0)", file=f)
             print("#define branch do {{ block_{}(recursion+1); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
             print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
 
@@ -586,6 +590,7 @@ with open(outfile, "w") as f:
 
             print("#undef reject", file=f)
             print("#undef accept", file=f)
+            print("#undef finish", file=f)
             print("#undef branch", file=f)
             print("#undef subpattern", file=f)
 
@@ -594,10 +599,12 @@ with open(outfile, "w") as f:
             print("    YS_ATTRIBUTE(unused);", file=f)
 
             if len(block["fcode"]):
-                print("#define accept do { on_accept(); check_blacklist(); } while(0)", file=f)
+                print("#define accept do { accept_cnt++; on_accept(); check_blacklist(); } while(0)", file=f)
+                print("#define finish do { rollback = -1; rollback_stack.clean(); return; } while(0)", file=f)
                 for line in block["fcode"]:
                     print("  " + line, file=f)
                 print("#undef accept", file=f)
+                print("#undef finish", file=f)
 
             if len(restore_st) or len(nonconst_st):
                 print("", file=f)
@@ -631,29 +638,32 @@ with open(outfile, "w") as f:
             print("    index_{}_key_type key;".format(index), file=f)
             for field, entry in enumerate(block["index"]):
                 print("    std::get<{}>(key) = {};".format(field, entry[2]), file=f)
-            print("    const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
+            print("    auto cells_ptr = index_{}.find(key);".format(index), file=f)
 
             if block["semioptional"] or block["genargs"] is not None:
                 print("    bool found_any_match = false;", file=f)
 
             print("", file=f)
-            print("    for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
-            print("      {} = cells[idx];".format(block["cell"]), file=f)
-            print("      if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
+            print("    if (cells_ptr != index_{}.end()) {{".format(index), file=f)
+            print("      const vector<Cell*> &cells = cells_ptr->second;".format(index), file=f)
+            print("      for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
+            print("        {} = cells[idx];".format(block["cell"]), file=f)
+            print("        if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
             for expr in block["filter"]:
-                print("      if (!({})) continue;".format(expr), file=f)
+                print("        if (!({})) continue;".format(expr), file=f)
             if block["semioptional"] or block["genargs"] is not None:
-                print("      found_any_match = true;", file=f)
-            print("      rollback_stack.push_back(make_pair(cells[idx], recursion));", file=f)
-            print("      block_{}(recursion+1);".format(index+1), file=f)
-            print("      if (rollback == 0) {", file=f)
-            print("        rollback_stack.pop_back();", file=f)
-            print("      } else {", file=f)
-            print("        if (rollback != recursion) {{".format(index+1), file=f)
-            print("          {} = backup_{};".format(block["cell"], block["cell"]), file=f)
-            print("          return;", file=f)
+                print("        found_any_match = true;", file=f)
+            print("        rollback_stack.push_back(make_pair(cells[idx], recursion));", file=f)
+            print("        block_{}(recursion+1);".format(index+1), file=f)
+            print("        if (rollback == 0) {", file=f)
+            print("          rollback_stack.pop_back();", file=f)
+            print("        } else {", file=f)
+            print("          if (rollback != recursion) {{".format(index+1), file=f)
+            print("            {} = backup_{};".format(block["cell"], block["cell"]), file=f)
+            print("            return;", file=f)
+            print("          }", file=f)
+            print("          rollback = 0;", file=f)
             print("        }", file=f)
-            print("        rollback = 0;", file=f)
             print("      }", file=f)
             print("    }", file=f)
 
@@ -669,14 +679,14 @@ with open(outfile, "w") as f:
             print("    {} = backup_{};".format(block["cell"], block["cell"]), file=f)
 
             if block["genargs"] is not None:
+                print("#define finish do { rollback = -1; rollback_stack.clean(); return; } while(0)", file=f)
                 print("    if (generate_mode && !found_any_match) {", file=f)
                 if len(block["genargs"]) == 1:
                     print("    if (rng(100) >= {}) return;".format(block["genargs"][0]), file=f)
                 for line in block["gencode"]:
                     print("      " + line, file=f)
-                print("      rollback_stack.clear();", file=f)
-                print("      rollback = -1;", file=f)
                 print("    }", file=f)
+                print("#undef finish", file=f)
         else:
             assert False
 
index 053dbe021805f948a1574b23c915361d2be68950..d787d49bee6f48710aca662e1e36c47a0a6f79ec 100644 (file)
@@ -235,7 +235,7 @@ struct TestPmgenPass : public Pass {
                extra_args(args, argidx, design);
 
                for (auto module : design->selected_modules())
-                       test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain);
+                       while (test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain)) {}
        }
 
        void execute_reduce_tree(std::vector<std::string> args, RTLIL::Design *design)
index 077d337d6b3a6699c5baf9c8444ef8047470cae0..211477a62325180767a41954f8dc92d7064da75f 100644 (file)
@@ -41,7 +41,8 @@ code
 finally
        chain.pop_back();
        log_assert(chain.empty());
-       accept;
+       if (GetSize(longest_chain) > 1)
+               accept;
 endcode
 
 // ------------------------------------------------------------------
@@ -80,7 +81,7 @@ match next
        select next->type.in($_AND_, $_OR_, $_XOR_)
        index <IdString> next->type === chain.back().first->type
        index <SigSpec> port(next, \Y) === port(chain.back().first, chain.back().second)
-generate 50
+generate 10
        SigSpec A = module->addWire(NEW_ID);
        SigSpec B = module->addWire(NEW_ID);
        SigSpec Y = port(chain.back().first, chain.back().second);