aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/hyperscan/src/nfa/goughcompile.cpp
diff options
context:
space:
mode:
authorbnagaev <bnagaev@yandex-team.ru>2022-02-10 16:47:04 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:47:04 +0300
commitc74559fb88da8adac0d9186cfa55a6b13c47695f (patch)
treeb83306b6e37edeea782e9eed673d89286c4fef35 /contrib/libs/hyperscan/src/nfa/goughcompile.cpp
parentd6449ba66291ff0c0d352c82e6eb3efb4c8a7e8d (diff)
downloadydb-c74559fb88da8adac0d9186cfa55a6b13c47695f.tar.gz
Restoring authorship annotation for <bnagaev@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/libs/hyperscan/src/nfa/goughcompile.cpp')
-rw-r--r--contrib/libs/hyperscan/src/nfa/goughcompile.cpp2564
1 files changed, 1282 insertions, 1282 deletions
diff --git a/contrib/libs/hyperscan/src/nfa/goughcompile.cpp b/contrib/libs/hyperscan/src/nfa/goughcompile.cpp
index 0fd64bf126..d41c6f4235 100644
--- a/contrib/libs/hyperscan/src/nfa/goughcompile.cpp
+++ b/contrib/libs/hyperscan/src/nfa/goughcompile.cpp
@@ -1,1170 +1,1170 @@
-/*
+/*
* Copyright (c) 2015-2018, Intel Corporation
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Intel Corporation nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "goughcompile.h"
-
-#include "accel.h"
-#include "goughcompile_dump.h"
-#include "goughcompile_internal.h"
-#include "gough_internal.h"
-#include "grey.h"
-#include "mcclellancompile.h"
-#include "nfa_internal.h"
-#include "util/compile_context.h"
-#include "util/container.h"
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "goughcompile.h"
+
+#include "accel.h"
+#include "goughcompile_dump.h"
+#include "goughcompile_internal.h"
+#include "gough_internal.h"
+#include "grey.h"
+#include "mcclellancompile.h"
+#include "nfa_internal.h"
+#include "util/compile_context.h"
+#include "util/container.h"
#include "util/flat_containers.h"
-#include "util/graph_range.h"
-#include "util/make_unique.h"
-#include "util/order_check.h"
+#include "util/graph_range.h"
+#include "util/make_unique.h"
+#include "util/order_check.h"
#include "util/report_manager.h"
-#include "util/verify_types.h"
-
-#include "ue2common.h"
-
-#include <algorithm>
-#include <boost/dynamic_bitset.hpp>
-#include <boost/range/adaptor/map.hpp>
-
-using namespace std;
-using boost::adaptors::map_keys;
-using boost::adaptors::map_values;
-using boost::vertex_index;
-
-namespace ue2 {
-
-void raw_som_dfa::stripExtraEodReports(void) {
- /* if a state generates a given report as a normal accept - then it does
- * not also need to generate an eod report for it */
- for (vector<dstate_som>::iterator it = state_som.begin();
- it != state_som.end(); ++it) {
- for (const som_report &sr : it->reports) {
- it->reports_eod.erase(sr);
- }
- dstate &norm = states[it - state_som.begin()];
- norm.reports_eod.clear();
- for (const som_report &sr : it->reports_eod) {
- norm.reports_eod.insert(sr.report);
- }
- }
-}
-
-namespace {
-
-class gough_build_strat : public mcclellan_build_strat {
-public:
+#include "util/verify_types.h"
+
+#include "ue2common.h"
+
+#include <algorithm>
+#include <boost/dynamic_bitset.hpp>
+#include <boost/range/adaptor/map.hpp>
+
+using namespace std;
+using boost::adaptors::map_keys;
+using boost::adaptors::map_values;
+using boost::vertex_index;
+
+namespace ue2 {
+
+void raw_som_dfa::stripExtraEodReports(void) {
+ /* if a state generates a given report as a normal accept - then it does
+ * not also need to generate an eod report for it */
+ for (vector<dstate_som>::iterator it = state_som.begin();
+ it != state_som.end(); ++it) {
+ for (const som_report &sr : it->reports) {
+ it->reports_eod.erase(sr);
+ }
+ dstate &norm = states[it - state_som.begin()];
+ norm.reports_eod.clear();
+ for (const som_report &sr : it->reports_eod) {
+ norm.reports_eod.insert(sr.report);
+ }
+ }
+}
+
+namespace {
+
+class gough_build_strat : public mcclellan_build_strat {
+public:
gough_build_strat(
raw_som_dfa &r, const GoughGraph &g, const ReportManager &rm_in,
const map<dstate_id_t, gough_accel_state_info> &accel_info)
: mcclellan_build_strat(r, rm_in, false), rdfa(r), gg(g),
- accel_gough_info(accel_info) {}
- unique_ptr<raw_report_info> gatherReports(vector<u32> &reports /* out */,
- vector<u32> &reports_eod /* out */,
- u8 *isSingleReport /* out */,
- ReportID *arbReport /* out */) const override;
+ accel_gough_info(accel_info) {}
+ unique_ptr<raw_report_info> gatherReports(vector<u32> &reports /* out */,
+ vector<u32> &reports_eod /* out */,
+ u8 *isSingleReport /* out */,
+ ReportID *arbReport /* out */) const override;
AccelScheme find_escape_strings(dstate_id_t this_idx) const override;
- size_t accelSize(void) const override { return sizeof(gough_accel); }
+ size_t accelSize(void) const override { return sizeof(gough_accel); }
void buildAccel(dstate_id_t this_idx, const AccelScheme &info,
void *accel_out) override;
u32 max_allowed_offset_accel() const override { return 0; }
DfaType getType() const override { return Gough; }
-
- raw_som_dfa &rdfa;
- const GoughGraph &gg;
- map<dstate_id_t, gough_accel_state_info> accel_gough_info;
- map<gough_accel *, dstate_id_t> built_accel;
-};
-
-}
-
-GoughSSAVar::~GoughSSAVar() {
-}
-
-void GoughSSAVar::clear_outputs() {
- for (GoughSSAVarWithInputs *var : outputs) {
- var->remove_input_raw(this);
- }
- outputs.clear();
-}
-
-void GoughSSAVarWithInputs::clear_all() {
- clear_inputs();
- clear_outputs();
-}
-
-void GoughSSAVarMin::clear_inputs() {
- for (GoughSSAVar *var : inputs) {
- assert(contains(var->outputs, this));
- var->outputs.erase(this);
- }
- inputs.clear();
-}
-
-void GoughSSAVarMin::replace_input(GoughSSAVar *old_v, GoughSSAVar *new_v) {
- assert(contains(inputs, old_v));
- inputs.erase(old_v);
- old_v->outputs.erase(this);
- inputs.insert(new_v);
- new_v->outputs.insert(this);
-}
-
-static
-void translateRawReports(UNUSED GoughGraph &cfg, UNUSED const raw_som_dfa &raw,
- const flat_map<u32, GoughSSAVarJoin *> &joins_at_s,
- UNUSED GoughVertex s,
- const set<som_report> &reports_in,
- vector<pair<ReportID, GoughSSAVar *> > *reports_out) {
- for (const som_report &sr : reports_in) {
- DEBUG_PRINTF("state %u: report %u slot %d\n", cfg[s].state_id,
- sr.report, sr.slot);
- GoughSSAVar *var = nullptr;
- if (sr.slot == CREATE_NEW_SOM) {
- assert(!generates_callbacks(raw.kind));
- } else {
- var = joins_at_s.at(sr.slot);
- }
- reports_out->push_back(make_pair(sr.report, var));
- }
-}
-
-static
-void makeCFG_reports(GoughGraph &cfg, const raw_som_dfa &raw,
- const vector<flat_map<u32, GoughSSAVarJoin *> > &joins,
- const vector<GoughVertex> &vertices) {
- for (u32 i = 1; i < raw.states.size(); ++i) {
- GoughVertex s = vertices[i];
- const flat_map<u32, GoughSSAVarJoin *> &joins_at_s
- = joins[get(vertex_index, cfg, s)];
- translateRawReports(cfg, raw, joins_at_s, s,
- raw.state_som[i].reports, &cfg[s].reports);
- translateRawReports(cfg, raw, joins_at_s, s,
- raw.state_som[i].reports_eod, &cfg[s].reports_eod);
- }
-}
-
-static never_inline
-void makeCFG_top_edge(GoughGraph &cfg, const vector<GoughVertex> &vertices,
- const vector<flat_map<u32, GoughSSAVarJoin *> > &joins,
- u32 trigger_slot, const som_tran_info &src_slots,
- const som_tran_info &dest_slot_pred,
- dstate_id_t i, dstate_id_t n, const GoughEdge &e) {
- GoughVertex s = vertices[i];
- GoughVertex t = vertices[n];
- const flat_map<u32, GoughSSAVarJoin *> &joins_at_s
- = joins[get(vertex_index, cfg, s)];
- const flat_map<u32, GoughSSAVarJoin *> &joins_at_t
- = joins[get(vertex_index, cfg, t)];
-
- DEBUG_PRINTF("top for %u -> %u\n", i, n);
-
- for (som_tran_info::const_iterator it = dest_slot_pred.begin();
- it != dest_slot_pred.end(); ++it) {
- /* for ordering, need to ensure that new values feeding directly
- * into mins come first */
- u32 slot_id = it->first;
-
- shared_ptr<GoughSSAVarNew> vnew;
- if (slot_id == trigger_slot) {
- vnew = make_shared<GoughSSAVarNew>(0U);
- cfg[e].vars.push_back(vnew);
- } else {
- assert(contains(src_slots, slot_id));
- }
-
- GoughSSAVar *final_var;
- if (vnew && !contains(src_slots, slot_id)) {
- final_var = vnew.get();
- DEBUG_PRINTF("bypassing min on join %u\n", slot_id);
- } else if (!vnew) {
- final_var = joins_at_s.at(slot_id);
- DEBUG_PRINTF("bypassing min on join %u\n", slot_id);
- } else {
- assert(vnew);
- assert(contains(src_slots, slot_id));
-
- shared_ptr<GoughSSAVarMin> vmin = make_shared<GoughSSAVarMin>();
- cfg[e].vars.push_back(vmin);
- final_var = vmin.get();
-
- DEBUG_PRINTF("slot %u gets a new value\n", slot_id);
- vmin->add_input(vnew.get());
-
- DEBUG_PRINTF("slot %u is constant\n", slot_id);
- vmin->add_input(joins_at_s.at(slot_id));
- }
-
- /* wire to destination target */
- GoughSSAVarJoin *vk = joins_at_t.at(slot_id);
- vk->add_input(final_var, e);
- }
-}
-
-static never_inline
-void makeCFG_edge(GoughGraph &cfg, const map<u32, u32> &som_creators,
- const vector<GoughVertex> &vertices,
- const vector<flat_map<u32, GoughSSAVarJoin *> > &joins,
- const som_tran_info &src_slots,
- const som_tran_info &dest_slot_pred, dstate_id_t i,
- dstate_id_t n, const GoughEdge &e) {
- GoughVertex s = vertices[i];
- GoughVertex t = vertices[n];
- const flat_map<u32, GoughSSAVarJoin *> &joins_at_s
- = joins[get(vertex_index, cfg, s)];
- const flat_map<u32, GoughSSAVarJoin *> &joins_at_t
- = joins[get(vertex_index, cfg, t)];
-
- map<u32, shared_ptr<GoughSSAVarNew> > vnew_by_adj;
- for (som_tran_info::const_iterator it = dest_slot_pred.begin();
- it != dest_slot_pred.end(); ++it) {
- /* for ordering, need to ensure that new values feeding directly
- * into mins come first */
- u32 slot_id = it->first;
-
- if (contains(som_creators, slot_id) && !som_creators.at(slot_id)) {
- continue;
- }
-
- shared_ptr<GoughSSAVarNew> vnew;
- const vector<u32> &inputs = it->second;
- u32 useful_input_count = 0;
- u32 first_useful_input = ~0U;
-
- for (const u32 &input_slot : inputs) {
- if (!contains(src_slots, input_slot)) {
- continue;
- }
- DEBUG_PRINTF("%u is useful\n", input_slot);
-
- if (!vnew || !contains(som_creators, input_slot)) {
- useful_input_count++;
- if (useful_input_count == 1) {
- first_useful_input = input_slot;
- }
- }
-
- if (contains(som_creators, input_slot)) {
- u32 adjust = som_creators.at(input_slot);
-
- if (vnew && vnew->adjust >= adjust) {
- DEBUG_PRINTF("skipping %u as domininated by adj%u\n",
- adjust, vnew->adjust);
- continue; /* deeper starts can be seen to statically
- dominate */
- }
-
- if (contains(vnew_by_adj, adjust)) {
- vnew = vnew_by_adj[adjust];
- } else {
- vnew = make_shared<GoughSSAVarNew>(adjust);
- cfg[e].vars.push_back(vnew);
- vnew_by_adj[adjust] = vnew;
- }
- assert(vnew);
- }
- }
-
- /* If we have a new start of match (with no offset or 1 byte offset) and
- * other variables coming in, the new will always be dominated by the
- * existing variables (as they must be at least one byte into the match)
- * -- and so can be dropped. */
- if (vnew && vnew->adjust < 2 && useful_input_count > 1) {
- useful_input_count--;
- vnew.reset();
-
- /* need to reestablish the first useful input */
- for (const u32 &input_slot : inputs) {
- if (!contains(src_slots, input_slot)) {
- continue;
- }
- if (!contains(som_creators, input_slot)) {
- first_useful_input = input_slot;
- }
- }
-
- }
-
- GoughSSAVar *final_var;
- if (useful_input_count == 1) {
- if (vnew) {
- final_var = vnew.get();
- } else {
- assert(first_useful_input != ~0U);
- final_var = joins_at_s.at(first_useful_input);
- }
- DEBUG_PRINTF("bypassing min on join %u\n", slot_id);
- } else {
- shared_ptr<GoughSSAVarMin> vmin = make_shared<GoughSSAVarMin>();
- cfg[e].vars.push_back(vmin);
- final_var = vmin.get();
-
- if (vnew) {
- vmin->add_input(vnew.get());
- }
-
- /* wire the normal inputs to the min */
- for (const u32 &input_slot : inputs) {
- if (!contains(src_slots, input_slot)) {
- continue;
- }
- if (!contains(som_creators, input_slot)) {
- vmin->add_input(joins_at_s.at(input_slot));
- }
- }
- assert(vmin->get_inputs().size() > 1);
- DEBUG_PRINTF("wire min to join %u\n", slot_id);
- }
-
- GoughSSAVarJoin *vk = joins_at_t.at(slot_id);
- assert(final_var);
- vk->add_input(final_var, e);
- }
-}
-
-static never_inline
-unique_ptr<GoughGraph> makeCFG(const raw_som_dfa &raw) {
- vector<GoughVertex> vertices;
- vertices.reserve(raw.states.size());
- unique_ptr<GoughGraph> cfg = ue2::make_unique<GoughGraph>();
- u32 min_state = !is_triggered(raw.kind);
-
- if (min_state) {
- vertices.push_back(GoughGraph::null_vertex()); /* skip dead state */
- }
-
- vector<flat_map<u32, GoughSSAVarJoin *> > joins(raw.states.size());
- for (u32 i = min_state; i < raw.states.size(); ++i) {
- GoughVertex v = add_vertex(GoughVertexProps(i), *cfg);
- vertices.push_back(v);
-
- /* create JOIN variables */
- for (som_tran_info::const_iterator it = raw.state_som[i].preds.begin();
- it != raw.state_som[i].preds.end(); ++it) {
- u32 slot_id = it->first;
- if (!contains(raw.new_som_nfa_states, slot_id)
- || raw.new_som_nfa_states.at(slot_id)) {
- (*cfg)[v].vars.push_back(make_shared<GoughSSAVarJoin>());
- joins[get(vertex_index, *cfg, v)][slot_id]
- = (*cfg)[v].vars.back().get();
- DEBUG_PRINTF("dfa %u:: slot %u\n", i, slot_id);
- }
- }
- }
-
- u16 top_sym = raw.alpha_remap[TOP];
+
+ raw_som_dfa &rdfa;
+ const GoughGraph &gg;
+ map<dstate_id_t, gough_accel_state_info> accel_gough_info;
+ map<gough_accel *, dstate_id_t> built_accel;
+};
+
+}
+
+GoughSSAVar::~GoughSSAVar() {
+}
+
+void GoughSSAVar::clear_outputs() {
+ for (GoughSSAVarWithInputs *var : outputs) {
+ var->remove_input_raw(this);
+ }
+ outputs.clear();
+}
+
+void GoughSSAVarWithInputs::clear_all() {
+ clear_inputs();
+ clear_outputs();
+}
+
+void GoughSSAVarMin::clear_inputs() {
+ for (GoughSSAVar *var : inputs) {
+ assert(contains(var->outputs, this));
+ var->outputs.erase(this);
+ }
+ inputs.clear();
+}
+
+void GoughSSAVarMin::replace_input(GoughSSAVar *old_v, GoughSSAVar *new_v) {
+ assert(contains(inputs, old_v));
+ inputs.erase(old_v);
+ old_v->outputs.erase(this);
+ inputs.insert(new_v);
+ new_v->outputs.insert(this);
+}
+
+static
+void translateRawReports(UNUSED GoughGraph &cfg, UNUSED const raw_som_dfa &raw,
+ const flat_map<u32, GoughSSAVarJoin *> &joins_at_s,
+ UNUSED GoughVertex s,
+ const set<som_report> &reports_in,
+ vector<pair<ReportID, GoughSSAVar *> > *reports_out) {
+ for (const som_report &sr : reports_in) {
+ DEBUG_PRINTF("state %u: report %u slot %d\n", cfg[s].state_id,
+ sr.report, sr.slot);
+ GoughSSAVar *var = nullptr;
+ if (sr.slot == CREATE_NEW_SOM) {
+ assert(!generates_callbacks(raw.kind));
+ } else {
+ var = joins_at_s.at(sr.slot);
+ }
+ reports_out->push_back(make_pair(sr.report, var));
+ }
+}
+
+static
+void makeCFG_reports(GoughGraph &cfg, const raw_som_dfa &raw,
+ const vector<flat_map<u32, GoughSSAVarJoin *> > &joins,
+ const vector<GoughVertex> &vertices) {
+ for (u32 i = 1; i < raw.states.size(); ++i) {
+ GoughVertex s = vertices[i];
+ const flat_map<u32, GoughSSAVarJoin *> &joins_at_s
+ = joins[get(vertex_index, cfg, s)];
+ translateRawReports(cfg, raw, joins_at_s, s,
+ raw.state_som[i].reports, &cfg[s].reports);
+ translateRawReports(cfg, raw, joins_at_s, s,
+ raw.state_som[i].reports_eod, &cfg[s].reports_eod);
+ }
+}
+
+static never_inline
+void makeCFG_top_edge(GoughGraph &cfg, const vector<GoughVertex> &vertices,
+ const vector<flat_map<u32, GoughSSAVarJoin *> > &joins,
+ u32 trigger_slot, const som_tran_info &src_slots,
+ const som_tran_info &dest_slot_pred,
+ dstate_id_t i, dstate_id_t n, const GoughEdge &e) {
+ GoughVertex s = vertices[i];
+ GoughVertex t = vertices[n];
+ const flat_map<u32, GoughSSAVarJoin *> &joins_at_s
+ = joins[get(vertex_index, cfg, s)];
+ const flat_map<u32, GoughSSAVarJoin *> &joins_at_t
+ = joins[get(vertex_index, cfg, t)];
+
+ DEBUG_PRINTF("top for %u -> %u\n", i, n);
+
+ for (som_tran_info::const_iterator it = dest_slot_pred.begin();
+ it != dest_slot_pred.end(); ++it) {
+ /* for ordering, need to ensure that new values feeding directly
+ * into mins come first */
+ u32 slot_id = it->first;
+
+ shared_ptr<GoughSSAVarNew> vnew;
+ if (slot_id == trigger_slot) {
+ vnew = make_shared<GoughSSAVarNew>(0U);
+ cfg[e].vars.push_back(vnew);
+ } else {
+ assert(contains(src_slots, slot_id));
+ }
+
+ GoughSSAVar *final_var;
+ if (vnew && !contains(src_slots, slot_id)) {
+ final_var = vnew.get();
+ DEBUG_PRINTF("bypassing min on join %u\n", slot_id);
+ } else if (!vnew) {
+ final_var = joins_at_s.at(slot_id);
+ DEBUG_PRINTF("bypassing min on join %u\n", slot_id);
+ } else {
+ assert(vnew);
+ assert(contains(src_slots, slot_id));
+
+ shared_ptr<GoughSSAVarMin> vmin = make_shared<GoughSSAVarMin>();
+ cfg[e].vars.push_back(vmin);
+ final_var = vmin.get();
+
+ DEBUG_PRINTF("slot %u gets a new value\n", slot_id);
+ vmin->add_input(vnew.get());
+
+ DEBUG_PRINTF("slot %u is constant\n", slot_id);
+ vmin->add_input(joins_at_s.at(slot_id));
+ }
+
+ /* wire to destination target */
+ GoughSSAVarJoin *vk = joins_at_t.at(slot_id);
+ vk->add_input(final_var, e);
+ }
+}
+
+static never_inline
+void makeCFG_edge(GoughGraph &cfg, const map<u32, u32> &som_creators,
+ const vector<GoughVertex> &vertices,
+ const vector<flat_map<u32, GoughSSAVarJoin *> > &joins,
+ const som_tran_info &src_slots,
+ const som_tran_info &dest_slot_pred, dstate_id_t i,
+ dstate_id_t n, const GoughEdge &e) {
+ GoughVertex s = vertices[i];
+ GoughVertex t = vertices[n];
+ const flat_map<u32, GoughSSAVarJoin *> &joins_at_s
+ = joins[get(vertex_index, cfg, s)];
+ const flat_map<u32, GoughSSAVarJoin *> &joins_at_t
+ = joins[get(vertex_index, cfg, t)];
+
+ map<u32, shared_ptr<GoughSSAVarNew> > vnew_by_adj;
+ for (som_tran_info::const_iterator it = dest_slot_pred.begin();
+ it != dest_slot_pred.end(); ++it) {
+ /* for ordering, need to ensure that new values feeding directly
+ * into mins come first */
+ u32 slot_id = it->first;
+
+ if (contains(som_creators, slot_id) && !som_creators.at(slot_id)) {
+ continue;
+ }
+
+ shared_ptr<GoughSSAVarNew> vnew;
+ const vector<u32> &inputs = it->second;
+ u32 useful_input_count = 0;
+ u32 first_useful_input = ~0U;
+
+ for (const u32 &input_slot : inputs) {
+ if (!contains(src_slots, input_slot)) {
+ continue;
+ }
+ DEBUG_PRINTF("%u is useful\n", input_slot);
+
+ if (!vnew || !contains(som_creators, input_slot)) {
+ useful_input_count++;
+ if (useful_input_count == 1) {
+ first_useful_input = input_slot;
+ }
+ }
+
+ if (contains(som_creators, input_slot)) {
+ u32 adjust = som_creators.at(input_slot);
+
+ if (vnew && vnew->adjust >= adjust) {
+ DEBUG_PRINTF("skipping %u as domininated by adj%u\n",
+ adjust, vnew->adjust);
+ continue; /* deeper starts can be seen to statically
+ dominate */
+ }
+
+ if (contains(vnew_by_adj, adjust)) {
+ vnew = vnew_by_adj[adjust];
+ } else {
+ vnew = make_shared<GoughSSAVarNew>(adjust);
+ cfg[e].vars.push_back(vnew);
+ vnew_by_adj[adjust] = vnew;
+ }
+ assert(vnew);
+ }
+ }
+
+ /* If we have a new start of match (with no offset or 1 byte offset) and
+ * other variables coming in, the new will always be dominated by the
+ * existing variables (as they must be at least one byte into the match)
+ * -- and so can be dropped. */
+ if (vnew && vnew->adjust < 2 && useful_input_count > 1) {
+ useful_input_count--;
+ vnew.reset();
+
+ /* need to reestablish the first useful input */
+ for (const u32 &input_slot : inputs) {
+ if (!contains(src_slots, input_slot)) {
+ continue;
+ }
+ if (!contains(som_creators, input_slot)) {
+ first_useful_input = input_slot;
+ }
+ }
+
+ }
+
+ GoughSSAVar *final_var;
+ if (useful_input_count == 1) {
+ if (vnew) {
+ final_var = vnew.get();
+ } else {
+ assert(first_useful_input != ~0U);
+ final_var = joins_at_s.at(first_useful_input);
+ }
+ DEBUG_PRINTF("bypassing min on join %u\n", slot_id);
+ } else {
+ shared_ptr<GoughSSAVarMin> vmin = make_shared<GoughSSAVarMin>();
+ cfg[e].vars.push_back(vmin);
+ final_var = vmin.get();
+
+ if (vnew) {
+ vmin->add_input(vnew.get());
+ }
+
+ /* wire the normal inputs to the min */
+ for (const u32 &input_slot : inputs) {
+ if (!contains(src_slots, input_slot)) {
+ continue;
+ }
+ if (!contains(som_creators, input_slot)) {
+ vmin->add_input(joins_at_s.at(input_slot));
+ }
+ }
+ assert(vmin->get_inputs().size() > 1);
+ DEBUG_PRINTF("wire min to join %u\n", slot_id);
+ }
+
+ GoughSSAVarJoin *vk = joins_at_t.at(slot_id);
+ assert(final_var);
+ vk->add_input(final_var, e);
+ }
+}
+
+static never_inline
+unique_ptr<GoughGraph> makeCFG(const raw_som_dfa &raw) {
+ vector<GoughVertex> vertices;
+ vertices.reserve(raw.states.size());
+ unique_ptr<GoughGraph> cfg = ue2::make_unique<GoughGraph>();
+ u32 min_state = !is_triggered(raw.kind);
+
+ if (min_state) {
+ vertices.push_back(GoughGraph::null_vertex()); /* skip dead state */
+ }
+
+ vector<flat_map<u32, GoughSSAVarJoin *> > joins(raw.states.size());
+ for (u32 i = min_state; i < raw.states.size(); ++i) {
+ GoughVertex v = add_vertex(GoughVertexProps(i), *cfg);
+ vertices.push_back(v);
+
+ /* create JOIN variables */
+ for (som_tran_info::const_iterator it = raw.state_som[i].preds.begin();
+ it != raw.state_som[i].preds.end(); ++it) {
+ u32 slot_id = it->first;
+ if (!contains(raw.new_som_nfa_states, slot_id)
+ || raw.new_som_nfa_states.at(slot_id)) {
+ (*cfg)[v].vars.push_back(make_shared<GoughSSAVarJoin>());
+ joins[get(vertex_index, *cfg, v)][slot_id]
+ = (*cfg)[v].vars.back().get();
+ DEBUG_PRINTF("dfa %u:: slot %u\n", i, slot_id);
+ }
+ }
+ }
+
+ u16 top_sym = raw.alpha_remap[TOP];
DEBUG_PRINTF("top: %hu, kind %s\n", top_sym, to_string(raw.kind).c_str());
-
- /* create edges, JOIN variables (on edge targets) */
- map<dstate_id_t, GoughEdge> seen;
- for (u32 i = min_state; i < raw.states.size(); ++i) {
- seen.clear(); /* seen is really local to each state */
-
- DEBUG_PRINTF("creating edges out of %u/%zu\n", i, raw.states.size());
- GoughVertex s = vertices[i];
- const vector<dstate_id_t> &next = raw.states[i].next;
- for (u32 j = 0; j < next.size(); ++j) {
- if (!is_triggered(raw.kind) && j == top_sym) {
- continue;
- }
-
- dstate_id_t n = next[j];
- DEBUG_PRINTF(" edge to %hu out on %u\n", n, j);
- assert(n < raw.states.size());
- GoughVertex t = vertices[n];
-
- if (j == top_sym) {
- GoughEdge e = add_edge(s, t, *cfg).first;
- (*cfg)[e].top = true;
- makeCFG_top_edge(*cfg, vertices, joins, raw.trigger_nfa_state,
- raw.state_som[i].preds, raw.state_som[n].preds,
- i, n, e);
- } else {
- if (contains(seen, n)) {
- const GoughEdge &e = seen[n];
- (*cfg)[e].reach.set(j);
- continue;
- }
-
- GoughEdge e = add_edge(s, t, *cfg).first;
- (*cfg)[e].reach.set(j);
-
- seen[n] = e;
-
- makeCFG_edge(*cfg, raw.new_som_nfa_states, vertices, joins,
- raw.state_som[i].preds, raw.state_som[n].preds,
- i, n, e);
- }
- }
- }
-
- /* populate reports */
- makeCFG_reports(*cfg, raw, joins, vertices);
-
- using boost::graph_bundle;
- if (is_triggered(raw.kind)) {
- (*cfg)[graph_bundle].initial_vertex = vertices[DEAD_STATE];
- } else {
- (*cfg)[graph_bundle].initial_vertex = vertices[raw.start_anchored];
- }
-
- return cfg;
-}
-
-static
-void copy_propagate_report_set(vector<pair<ReportID, GoughSSAVar *> > &rep) {
- vector<pair<ReportID, GoughSSAVar *> >::iterator it = rep.begin();
- while (it != rep.end()) {
- GoughSSAVar *var = it->second;
- if (!var) {
- ++it;
- continue;
- }
- const flat_set<GoughSSAVar *> &inputs = var->get_inputs();
- if (inputs.size() != 1) {
- ++it;
- continue;
- }
- it->second = *inputs.begin(); /* note may result in dupes,
- filter later */
- }
-}
-
-template<typename VarP>
-void copy_propagate_update_vars(vector<VarP> &vars, bool *changes) {
- for (u32 i = 0; i < vars.size(); i++) {
- GoughSSAVar *vp = vars[i].get();
- const flat_set<GoughSSAVar *> &inputs = vp->get_inputs();
-
- /* no need to worry about data coming from self; ignore self loops */
- GoughSSAVar *new_input = nullptr;
-
- if (inputs.size() == 1) {
- new_input = *inputs.begin();
- } else if (inputs.size() == 2) {
- flat_set<GoughSSAVar *>::const_iterator jt = inputs.begin();
- GoughSSAVar *i_0 = *jt;
- GoughSSAVar *i_1 = *++jt;
-
- if (i_0 == vp) {
- new_input = i_1;
- } else if (i_1 == vp) {
- new_input = i_0;
- }
- }
-
- if (!new_input) {
- continue;
- }
-
- assert(new_input != vp);
-
- /* copy set as it will be modified by iteration */
- const flat_set<GoughSSAVarWithInputs *> outputs = vp->get_outputs();
-
- for (GoughSSAVar *curr : outputs) {
- curr->replace_input(vp, new_input);
- *changes = true;
- }
- }
-}
-
-static
-void copy_propagation(GoughGraph &g, const Grey &grey) {
- if (!grey.goughCopyPropagate) {
- return;
- }
- /* TODO order visit of variables sensibly */
- bool changes = false;
- do {
- DEBUG_PRINTF("new iteration\n");
- changes = false;
- for (auto v : vertices_range(g)) {
- copy_propagate_update_vars(g[v].vars, &changes);
- }
- for (const auto &e : edges_range(g)) {
- copy_propagate_update_vars(g[e].vars, &changes);
- }
- } while(changes);
-
- /* see if any reports can also be moved along */
- for (auto v : vertices_range(g)) {
- copy_propagate_report_set(g[v].reports);
- copy_propagate_report_set(g[v].reports_eod);
- }
-}
-
-static
-void mark_live_reports(const vector<pair<ReportID, GoughSSAVar *> > &reps,
- vector<GoughSSAVar *> *queue) {
- for (const auto &r : reps) {
- GoughSSAVar *var = r.second;
- if (!var || var->seen) {
- continue;
- }
- var->seen = true;
- queue->push_back(var);
- }
-}
-
-static
-void remove_dead(GoughGraph &g) {
- vector<GoughSSAVar *> queue;
-
- for (auto v : vertices_range(g)) {
- mark_live_reports(g[v].reports, &queue);
- mark_live_reports(g[v].reports_eod, &queue);
- }
-
- while (!queue.empty()) {
- GoughSSAVar *v = queue.back();
- queue.pop_back();
- for (GoughSSAVar *var : v->get_inputs()) {
- if (var->seen) {
- continue;
- }
- var->seen = true;
- queue.push_back(var);
- }
- }
-
- /* remove unused variables */
- for (auto v : vertices_range(g)) {
- for (u32 i = 0; i < g[v].vars.size(); i++) {
- GoughSSAVar *var = g[v].vars[i].get();
- if (var->seen) {
- continue;
- }
- var->clear_all();
- g[v].vars.erase(g[v].vars.begin() + i);
- i--;
- }
- }
- for (const auto &e : edges_range(g)) {
- for (u32 i = 0; i < g[e].vars.size(); i++) {
- GoughSSAVar *var = g[e].vars[i].get();
- if (var->seen) {
- continue;
- }
- var->clear_all();
- g[e].vars.erase(g[e].vars.begin() + i);
- i--;
- }
- }
-}
-
-static
-gough_ins make_gough_ins(u8 op, u32 dest = INVALID_SLOT,
- u32 src = INVALID_SLOT) {
- assert(dest != INVALID_SLOT || op == GOUGH_INS_END);
- assert(src != INVALID_SLOT || op == GOUGH_INS_END || op == GOUGH_INS_NEW);
- gough_ins rv;
- rv.op = op;
- rv.dest = dest;
- rv.src = src;
- return rv;
-}
-
-void GoughSSAVarNew::generate(vector<gough_ins> *out) const {
- assert(slot != INVALID_SLOT);
- out->push_back(make_gough_ins(GOUGH_INS_NEW, slot, adjust));
-}
-
-#ifndef NDEBUG
-template<typename C, typename K>
-bool contains_loose(const C &container, const K &key) {
- for (const auto &elem : container) {
- if (elem == key) {
- return true;
- }
- }
- return false;
-}
-#endif
-
-void GoughSSAVarMin::generate(vector<gough_ins> *out) const {
- assert(slot != INVALID_SLOT);
- assert(!inputs.empty());
- // assert(inputs.size() > 1);
- vector<u32> input_slots; /* for determinism */
- bool first = true;
- for (const GoughSSAVar *var : inputs) {
- assert(contains_loose(var->outputs, this));
- if (var->slot == slot) {
- /* if the destination is one of the sources, no need to move it */
- first = false;
- } else {
- input_slots.push_back(var->slot);
- }
- }
-
- sort(input_slots.begin(), input_slots.end());
-
- for (const u32 &input_slot : input_slots) {
- if (first) {
- out->push_back(make_gough_ins(GOUGH_INS_MOV, slot, input_slot));
- first = false;
- } else {
- out->push_back(make_gough_ins(GOUGH_INS_MIN, slot, input_slot));
- }
- }
-}
-
-void GoughSSAVarMin::remove_input_raw(GoughSSAVar *v) {
- assert(contains(inputs, v));
- inputs.erase(v);
-}
-
-void GoughSSAVarJoin::generate(UNUSED vector<gough_ins> *out) const {
- assert(0);
-}
-
-GoughSSAVar *GoughSSAVarJoin::get_input(const GoughEdge &prev) const {
- for (const auto &var_edge : input_map) {
- if (contains(var_edge.second, prev)) {
- return var_edge.first;
- }
- }
- assert(0);
- return nullptr;
-}
-
-const flat_set<GoughEdge> &GoughSSAVarJoin::get_edges_for_input(
- GoughSSAVar *input) const {
- return input_map.at(input);
-}
-
-const map<GoughSSAVar *, flat_set<GoughEdge> > &GoughSSAVarJoin::get_input_map()
- const {
- return input_map;
-}
-
-void GoughSSAVarJoin::clear_inputs() {
- for (GoughSSAVar *var : input_map | map_keys) {
- assert(contains(var->outputs, this));
- var->outputs.erase(this);
- }
- input_map.clear();
- inputs.clear();
-}
-
-void GoughSSAVarJoin::replace_input(GoughSSAVar *old_v, GoughSSAVar *new_v) {
- assert(contains(input_map, old_v));
- assert(contains(inputs, old_v));
- if (old_v == new_v) {
- assert(0);
- return;
- }
- insert(&input_map[new_v], input_map[old_v]);
- input_map.erase(old_v);
- inputs.erase(old_v);
- inputs.insert(new_v);
- old_v->outputs.erase(this);
- new_v->outputs.insert(this);
-}
-
-void GoughSSAVarJoin::add_input(GoughSSAVar *v, GoughEdge prev) {
- input_map[v].insert(prev);
- inputs.insert(v);
- v->outputs.insert(this);
-}
-
-void GoughSSAVarJoin::remove_input_raw(GoughSSAVar *v) {
- assert(contains(inputs, v));
- assert(contains(input_map, v));
- input_map.erase(v);
- inputs.erase(v);
-}
-
-static
-u32 highest_slot_used(const vector<gough_ins> &program) {
- u32 rv = INVALID_SLOT;
- for (const gough_ins &ins : program) {
- if (rv == INVALID_SLOT) {
- rv = ins.dest;
- } else if (ins.dest != INVALID_SLOT) {
- ENSURE_AT_LEAST(&rv, ins.dest);
- }
- if (rv == INVALID_SLOT) {
- rv = ins.src;
- } else if (ins.src != INVALID_SLOT) {
- ENSURE_AT_LEAST(&rv, ins.src);
- }
- }
- assert(rv != INVALID_SLOT);
- return rv;
-}
-
-static
-u32 highest_slot_used(const map<gough_edge_id, vector<gough_ins> > &blocks) {
- u32 rv = INVALID_SLOT;
- for (const vector<gough_ins> &ins_list : blocks | map_values) {
- u32 used = highest_slot_used(ins_list);
- if (rv == INVALID_SLOT) {
- rv = used;
- } else if (used != INVALID_SLOT) {
- ENSURE_AT_LEAST(&rv, used);
- }
- }
- return rv;
-}
-
-static
-void add_to_block(const vector<shared_ptr<GoughSSAVar> > &vars,
- vector<gough_ins> *out) {
- for (const auto &var : vars) {
- var->generate(out);
- }
-}
-
-namespace {
-struct edge_join_info {
- bool empty() const { return dest_to_src.empty(); }
-
- void insert(u32 src, u32 dest) {
- assert(!contains(dest_to_src, dest));
- assert(src != dest);
- dest_to_src[dest] = src;
- src_to_dest[src].insert(dest);
- }
-
- void erase(u32 src, u32 dest) {
- assert(dest_to_src.at(dest) == src);
- dest_to_src.erase(dest);
- src_to_dest[src].erase(dest);
-
- if (src_to_dest[src].empty()) {
- src_to_dest.erase(src);
- }
- }
-
- bool is_src(u32 v) const {
- bool rv = contains(src_to_dest, v);
- assert(!rv || !src_to_dest.at(v).empty());
- return rv;
- }
-
- bool is_dest(u32 v) const {
- return contains(dest_to_src, v);
- }
-
- void remap_src(u32 old_src, u32 new_src) {
- assert(is_src(old_src));
- assert(!is_src(new_src));
-
- for (const u32 &e : src_to_dest[old_src]) {
- assert(e != new_src);
- dest_to_src[e] = new_src;
- }
- src_to_dest[new_src].swap(src_to_dest[old_src]);
- src_to_dest.erase(old_src);
-
- assert(!is_src(old_src));
- assert(is_src(new_src));
- }
-
- /* returns an arbitrary unresolved entry */
- void get_pending(u32 *src, u32 *dest) {
- assert(!empty());
- *dest = dest_to_src.begin()->first;
- *src = dest_to_src.begin()->second;
- }
-
- const map<u32, u32> &get_dest_mapping() const { return dest_to_src; }
-
-private:
- map<u32, set<u32> > src_to_dest;
- map<u32, u32> dest_to_src;
-};
-
-}
-
-static
-void prep_joins_for_generation(const GoughGraph &g, GoughVertex v,
- map<GoughEdge, edge_join_info> *edge_info) {
- DEBUG_PRINTF("writing out joins for %u\n", g[v].state_id);
- for (const auto &var : g[v].vars) {
- u32 dest_slot = var->slot;
- for (const auto &var_edges : var->get_input_map()) {
- u32 input = var_edges.first->slot;
- if (dest_slot == input) {
- continue;
- }
-
- for (const GoughEdge &incoming_edge : var_edges.second) {
- (*edge_info)[incoming_edge].insert(input, dest_slot);
- DEBUG_PRINTF("need %u<-%u\n", dest_slot, input);
- }
- }
- }
-}
-
-static
-void add_simple_joins(edge_join_info &eji, vector<gough_ins> *out) {
- /* any slot whose value we don't need can be written to immediately */
- const map<u32, u32> &dest_to_src = eji.get_dest_mapping();
-
- bool changed;
- do {
- changed = false;
- for (map<u32, u32>::const_iterator it = dest_to_src.begin();
- it != dest_to_src.end();) {
- u32 src = it->second;
- u32 dest = it->first;
- ++it; /* avoid iterator being invalidated */
-
- if (eji.is_src(dest)) {
- continue; /* conflict; not simple (yet) */
- }
-
- /* value of destination slot is not used by any remaining joins;
- * we can output this join immediately */
- DEBUG_PRINTF("out %u<-%u\n", dest, src);
- out->push_back(make_gough_ins(GOUGH_INS_MOV, dest, src));
-
- eji.erase(src, dest);
-
- if (eji.is_dest(src) && eji.is_src(src)) {
- /* we can unblock src being used as an output by shifting
- * across everybody using src as input to using dest (as == src
- * now) */
- eji.remap_src(src, dest);
- }
- changed = true;
- }
- } while (changed);
-}
-
-static
-void add_joins_to_block(edge_join_info &eji, vector<gough_ins> *out,
- u32 base_temp_slot) {
- /* joins happen concurrently: none of them should see the outputs of another
- * join happening due to the same entry of the vertex. If there are
- * conflicts we may have to handle things by using a temp output slot for
- * each join and then copying into the final slot.
- */
-
- add_simple_joins(eji, out);
- while (!eji.empty()) {
- u32 split;
- u32 input_for_split;
- eji.get_pending(&input_for_split, &split);
-
- assert(eji.is_src(split)); /* otherwise should be handled by simple */
-
- /* stash the initial value of the split register in a temp register */
- u32 temp = base_temp_slot++;
- DEBUG_PRINTF("out %u<-%u\n", temp, split);
- out->push_back(make_gough_ins(GOUGH_INS_MOV, temp, split));
- eji.remap_src(split, temp); /* update maps */
-
- /* split can now be safely written out to as all the uses of it as an
- * input now refer to temp instead */
-
- DEBUG_PRINTF("out %u<-%u\n", split, input_for_split);
- out->push_back(make_gough_ins(GOUGH_INS_MOV, split, input_for_split));
- eji.erase(input_for_split, split);
-
- /* handle any uncovered simple cases */
- add_simple_joins(eji, out);
- }
-}
-
-static
-void build_blocks(const GoughGraph &g,
- map<gough_edge_id, vector<gough_ins> > *blocks,
- u32 base_temp_slot) {
- for (const auto &e : edges_range(g)) {
- if (g[e].vars.empty()) {
- continue;
- }
-
- vector<gough_ins> &block = (*blocks)[gough_edge_id(g, e)];
- add_to_block(g[e].vars, &block);
- assert(!block.empty());
- }
-
- for (const auto t : vertices_range(g)) {
- if (g[t].vars.empty()) {
- continue;
- }
-
- map<GoughEdge, edge_join_info> eji;
- prep_joins_for_generation(g, t, &eji);
-
- for (auto &m : eji) {
- vector<gough_ins> &block = (*blocks)[gough_edge_id(g, m.first)];
- u32 cur_base = base_temp_slot;
- if (!block.empty()) {
- /* some temp slots may already be in use by short-lived vars */
- ENSURE_AT_LEAST(&cur_base, highest_slot_used(block) + 1);
- }
-
- add_joins_to_block(m.second, &block, cur_base);
- if (block.empty()) {
- blocks->erase(gough_edge_id(g, m.first));
- }
- }
- }
-
- for (vector<gough_ins> &ins_list : *blocks | map_values) {
- assert(!ins_list.empty());
- ins_list.push_back(make_gough_ins(GOUGH_INS_END));
- }
-}
-
-static
-void copy_in_blocks(raw_som_dfa &raw, u8 alphaShift, const GoughGraph &cfg,
- const map<gough_edge_id, vector<gough_ins> > &blocks,
- u32 *edge_blocks, u32 *top_blocks, u32 base_offset,
- map<vector<gough_ins>, u32> *prog_offsets,
- vector<gough_ins> *out) {
- u32 impl_alpha_size = 1U << alphaShift;
- UNUSED u32 top_sym = raw.alpha_remap[TOP];
- assert(top_sym == raw.alpha_size - 1U);
- map<vector<gough_ins>, u32> &processed = *prog_offsets;
-
- for (const auto &e : edges_range(cfg)) {
- if (!contains(blocks, gough_edge_id(cfg, e))) {
- continue;
- }
- const vector<gough_ins> &block = blocks.at(gough_edge_id(cfg, e));
- u32 prog_offset;
- if (!contains(processed, block)) {
- prog_offset = base_offset + byte_length(*out);
- insert(out, out->end(), block);
- processed[block] = prog_offset;
- } else {
- prog_offset = processed[block];
- }
-
- /* update edges */
- u32 s_id = cfg[source(e, cfg)].state_id;
- UNUSED u32 t_id = cfg[target(e, cfg)].state_id;
- u32 impl_src_id = raw.states[s_id].impl_id;
- DEBUG_PRINTF("%u: writing out block for edge_%u_%u at %u:\n",
- impl_src_id, s_id, t_id,prog_offset);
-
- for (u32 j = cfg[e].reach.find_first(); j != CharReach::npos;
- j = cfg[e].reach.find_next(j)) {
- assert(raw.states[s_id].next[j] == t_id);
- u32 edge_index = impl_src_id * impl_alpha_size + j;
- DEBUG_PRINTF("\tsetting on %u, %u\n", j, edge_index);
- edge_blocks[edge_index] = prog_offset;
- }
-
- if (cfg[e].top) {
- assert(raw.states[s_id].next[top_sym] == t_id);
- DEBUG_PRINTF("\tsetting top on %u to block at %u\n", impl_src_id,
- prog_offset);
- top_blocks[impl_src_id] = prog_offset;
- }
- }
-}
-
-bool find_normal_self_loop(GoughVertex v, const GoughGraph &g, GoughEdge *out) {
- for (const auto &e : out_edges_range(v, g)) {
- if (target(e, g) != v) {
- continue;
- }
- if (g[e].top) {
- assert(g[e].reach.find_first() == CharReach::npos);
- continue; /* corresponds to a top, not a normal transition */
- }
-
- *out = e;
- return true;
- }
-
- return false;
-}
-
-static never_inline
-void update_accel_prog_offset(const gough_build_strat &gbs,
- const map<gough_edge_id, vector<gough_ins> > &blocks,
- const map<vector<gough_ins>, u32> &prog_offsets) {
- map<dstate_id_t, GoughVertex> verts;
- for (auto v : vertices_range(gbs.gg)) {
- verts[gbs.gg[v].state_id] = v;
- }
-
- for (auto &m : gbs.built_accel) {
- gough_accel *ga = m.first;
- assert(!ga->prog_offset);
- GoughVertex v = verts[m.second];
- GoughEdge e;
- UNUSED bool rv = find_normal_self_loop(v, gbs.gg, &e);
- assert(rv);
-
- if (!rv) {
- continue;
- }
-
- DEBUG_PRINTF("updating state %u accel with margin %hhu\n",
- gbs.gg[v].state_id, ga->margin_dist);
- if (contains(blocks, gough_edge_id(gbs.gg, e))) {
- const vector<gough_ins> &block
- = blocks.at(gough_edge_id(gbs.gg, e));
- ga->prog_offset = prog_offsets.at(block);
- DEBUG_PRINTF("prog offset %u\n", ga->prog_offset);
- } else {
- ga->margin_dist = 0;
- DEBUG_PRINTF("removing margin as no som\n");
- }
- }
-}
-
+
+ /* create edges, JOIN variables (on edge targets) */
+ map<dstate_id_t, GoughEdge> seen;
+ for (u32 i = min_state; i < raw.states.size(); ++i) {
+ seen.clear(); /* seen is really local to each state */
+
+ DEBUG_PRINTF("creating edges out of %u/%zu\n", i, raw.states.size());
+ GoughVertex s = vertices[i];
+ const vector<dstate_id_t> &next = raw.states[i].next;
+ for (u32 j = 0; j < next.size(); ++j) {
+ if (!is_triggered(raw.kind) && j == top_sym) {
+ continue;
+ }
+
+ dstate_id_t n = next[j];
+ DEBUG_PRINTF(" edge to %hu out on %u\n", n, j);
+ assert(n < raw.states.size());
+ GoughVertex t = vertices[n];
+
+ if (j == top_sym) {
+ GoughEdge e = add_edge(s, t, *cfg).first;
+ (*cfg)[e].top = true;
+ makeCFG_top_edge(*cfg, vertices, joins, raw.trigger_nfa_state,
+ raw.state_som[i].preds, raw.state_som[n].preds,
+ i, n, e);
+ } else {
+ if (contains(seen, n)) {
+ const GoughEdge &e = seen[n];
+ (*cfg)[e].reach.set(j);
+ continue;
+ }
+
+ GoughEdge e = add_edge(s, t, *cfg).first;
+ (*cfg)[e].reach.set(j);
+
+ seen[n] = e;
+
+ makeCFG_edge(*cfg, raw.new_som_nfa_states, vertices, joins,
+ raw.state_som[i].preds, raw.state_som[n].preds,
+ i, n, e);
+ }
+ }
+ }
+
+ /* populate reports */
+ makeCFG_reports(*cfg, raw, joins, vertices);
+
+ using boost::graph_bundle;
+ if (is_triggered(raw.kind)) {
+ (*cfg)[graph_bundle].initial_vertex = vertices[DEAD_STATE];
+ } else {
+ (*cfg)[graph_bundle].initial_vertex = vertices[raw.start_anchored];
+ }
+
+ return cfg;
+}
+
+static
+void copy_propagate_report_set(vector<pair<ReportID, GoughSSAVar *> > &rep) {
+ vector<pair<ReportID, GoughSSAVar *> >::iterator it = rep.begin();
+ while (it != rep.end()) {
+ GoughSSAVar *var = it->second;
+ if (!var) {
+ ++it;
+ continue;
+ }
+ const flat_set<GoughSSAVar *> &inputs = var->get_inputs();
+ if (inputs.size() != 1) {
+ ++it;
+ continue;
+ }
+ it->second = *inputs.begin(); /* note may result in dupes,
+ filter later */
+ }
+}
+
+template<typename VarP>
+void copy_propagate_update_vars(vector<VarP> &vars, bool *changes) {
+ for (u32 i = 0; i < vars.size(); i++) {
+ GoughSSAVar *vp = vars[i].get();
+ const flat_set<GoughSSAVar *> &inputs = vp->get_inputs();
+
+ /* no need to worry about data coming from self; ignore self loops */
+ GoughSSAVar *new_input = nullptr;
+
+ if (inputs.size() == 1) {
+ new_input = *inputs.begin();
+ } else if (inputs.size() == 2) {
+ flat_set<GoughSSAVar *>::const_iterator jt = inputs.begin();
+ GoughSSAVar *i_0 = *jt;
+ GoughSSAVar *i_1 = *++jt;
+
+ if (i_0 == vp) {
+ new_input = i_1;
+ } else if (i_1 == vp) {
+ new_input = i_0;
+ }
+ }
+
+ if (!new_input) {
+ continue;
+ }
+
+ assert(new_input != vp);
+
+ /* copy set as it will be modified by iteration */
+ const flat_set<GoughSSAVarWithInputs *> outputs = vp->get_outputs();
+
+ for (GoughSSAVar *curr : outputs) {
+ curr->replace_input(vp, new_input);
+ *changes = true;
+ }
+ }
+}
+
+static
+void copy_propagation(GoughGraph &g, const Grey &grey) {
+ if (!grey.goughCopyPropagate) {
+ return;
+ }
+ /* TODO order visit of variables sensibly */
+ bool changes = false;
+ do {
+ DEBUG_PRINTF("new iteration\n");
+ changes = false;
+ for (auto v : vertices_range(g)) {
+ copy_propagate_update_vars(g[v].vars, &changes);
+ }
+ for (const auto &e : edges_range(g)) {
+ copy_propagate_update_vars(g[e].vars, &changes);
+ }
+ } while(changes);
+
+ /* see if any reports can also be moved along */
+ for (auto v : vertices_range(g)) {
+ copy_propagate_report_set(g[v].reports);
+ copy_propagate_report_set(g[v].reports_eod);
+ }
+}
+
+static
+void mark_live_reports(const vector<pair<ReportID, GoughSSAVar *> > &reps,
+ vector<GoughSSAVar *> *queue) {
+ for (const auto &r : reps) {
+ GoughSSAVar *var = r.second;
+ if (!var || var->seen) {
+ continue;
+ }
+ var->seen = true;
+ queue->push_back(var);
+ }
+}
+
+static
+void remove_dead(GoughGraph &g) {
+ vector<GoughSSAVar *> queue;
+
+ for (auto v : vertices_range(g)) {
+ mark_live_reports(g[v].reports, &queue);
+ mark_live_reports(g[v].reports_eod, &queue);
+ }
+
+ while (!queue.empty()) {
+ GoughSSAVar *v = queue.back();
+ queue.pop_back();
+ for (GoughSSAVar *var : v->get_inputs()) {
+ if (var->seen) {
+ continue;
+ }
+ var->seen = true;
+ queue.push_back(var);
+ }
+ }
+
+ /* remove unused variables */
+ for (auto v : vertices_range(g)) {
+ for (u32 i = 0; i < g[v].vars.size(); i++) {
+ GoughSSAVar *var = g[v].vars[i].get();
+ if (var->seen) {
+ continue;
+ }
+ var->clear_all();
+ g[v].vars.erase(g[v].vars.begin() + i);
+ i--;
+ }
+ }
+ for (const auto &e : edges_range(g)) {
+ for (u32 i = 0; i < g[e].vars.size(); i++) {
+ GoughSSAVar *var = g[e].vars[i].get();
+ if (var->seen) {
+ continue;
+ }
+ var->clear_all();
+ g[e].vars.erase(g[e].vars.begin() + i);
+ i--;
+ }
+ }
+}
+
+static
+gough_ins make_gough_ins(u8 op, u32 dest = INVALID_SLOT,
+ u32 src = INVALID_SLOT) {
+ assert(dest != INVALID_SLOT || op == GOUGH_INS_END);
+ assert(src != INVALID_SLOT || op == GOUGH_INS_END || op == GOUGH_INS_NEW);
+ gough_ins rv;
+ rv.op = op;
+ rv.dest = dest;
+ rv.src = src;
+ return rv;
+}
+
+void GoughSSAVarNew::generate(vector<gough_ins> *out) const {
+ assert(slot != INVALID_SLOT);
+ out->push_back(make_gough_ins(GOUGH_INS_NEW, slot, adjust));
+}
+
+#ifndef NDEBUG
+template<typename C, typename K>
+bool contains_loose(const C &container, const K &key) {
+ for (const auto &elem : container) {
+ if (elem == key) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+void GoughSSAVarMin::generate(vector<gough_ins> *out) const {
+ assert(slot != INVALID_SLOT);
+ assert(!inputs.empty());
+ // assert(inputs.size() > 1);
+ vector<u32> input_slots; /* for determinism */
+ bool first = true;
+ for (const GoughSSAVar *var : inputs) {
+ assert(contains_loose(var->outputs, this));
+ if (var->slot == slot) {
+ /* if the destination is one of the sources, no need to move it */
+ first = false;
+ } else {
+ input_slots.push_back(var->slot);
+ }
+ }
+
+ sort(input_slots.begin(), input_slots.end());
+
+ for (const u32 &input_slot : input_slots) {
+ if (first) {
+ out->push_back(make_gough_ins(GOUGH_INS_MOV, slot, input_slot));
+ first = false;
+ } else {
+ out->push_back(make_gough_ins(GOUGH_INS_MIN, slot, input_slot));
+ }
+ }
+}
+
+void GoughSSAVarMin::remove_input_raw(GoughSSAVar *v) {
+ assert(contains(inputs, v));
+ inputs.erase(v);
+}
+
+void GoughSSAVarJoin::generate(UNUSED vector<gough_ins> *out) const {
+ assert(0);
+}
+
+GoughSSAVar *GoughSSAVarJoin::get_input(const GoughEdge &prev) const {
+ for (const auto &var_edge : input_map) {
+ if (contains(var_edge.second, prev)) {
+ return var_edge.first;
+ }
+ }
+ assert(0);
+ return nullptr;
+}
+
+const flat_set<GoughEdge> &GoughSSAVarJoin::get_edges_for_input(
+ GoughSSAVar *input) const {
+ return input_map.at(input);
+}
+
+const map<GoughSSAVar *, flat_set<GoughEdge> > &GoughSSAVarJoin::get_input_map()
+ const {
+ return input_map;
+}
+
+void GoughSSAVarJoin::clear_inputs() {
+ for (GoughSSAVar *var : input_map | map_keys) {
+ assert(contains(var->outputs, this));
+ var->outputs.erase(this);
+ }
+ input_map.clear();
+ inputs.clear();
+}
+
+void GoughSSAVarJoin::replace_input(GoughSSAVar *old_v, GoughSSAVar *new_v) {
+ assert(contains(input_map, old_v));
+ assert(contains(inputs, old_v));
+ if (old_v == new_v) {
+ assert(0);
+ return;
+ }
+ insert(&input_map[new_v], input_map[old_v]);
+ input_map.erase(old_v);
+ inputs.erase(old_v);
+ inputs.insert(new_v);
+ old_v->outputs.erase(this);
+ new_v->outputs.insert(this);
+}
+
+void GoughSSAVarJoin::add_input(GoughSSAVar *v, GoughEdge prev) {
+ input_map[v].insert(prev);
+ inputs.insert(v);
+ v->outputs.insert(this);
+}
+
+void GoughSSAVarJoin::remove_input_raw(GoughSSAVar *v) {
+ assert(contains(inputs, v));
+ assert(contains(input_map, v));
+ input_map.erase(v);
+ inputs.erase(v);
+}
+
+static
+u32 highest_slot_used(const vector<gough_ins> &program) {
+ u32 rv = INVALID_SLOT;
+ for (const gough_ins &ins : program) {
+ if (rv == INVALID_SLOT) {
+ rv = ins.dest;
+ } else if (ins.dest != INVALID_SLOT) {
+ ENSURE_AT_LEAST(&rv, ins.dest);
+ }
+ if (rv == INVALID_SLOT) {
+ rv = ins.src;
+ } else if (ins.src != INVALID_SLOT) {
+ ENSURE_AT_LEAST(&rv, ins.src);
+ }
+ }
+ assert(rv != INVALID_SLOT);
+ return rv;
+}
+
+static
+u32 highest_slot_used(const map<gough_edge_id, vector<gough_ins> > &blocks) {
+ u32 rv = INVALID_SLOT;
+ for (const vector<gough_ins> &ins_list : blocks | map_values) {
+ u32 used = highest_slot_used(ins_list);
+ if (rv == INVALID_SLOT) {
+ rv = used;
+ } else if (used != INVALID_SLOT) {
+ ENSURE_AT_LEAST(&rv, used);
+ }
+ }
+ return rv;
+}
+
+static
+void add_to_block(const vector<shared_ptr<GoughSSAVar> > &vars,
+ vector<gough_ins> *out) {
+ for (const auto &var : vars) {
+ var->generate(out);
+ }
+}
+
+namespace {
+struct edge_join_info {
+ bool empty() const { return dest_to_src.empty(); }
+
+ void insert(u32 src, u32 dest) {
+ assert(!contains(dest_to_src, dest));
+ assert(src != dest);
+ dest_to_src[dest] = src;
+ src_to_dest[src].insert(dest);
+ }
+
+ void erase(u32 src, u32 dest) {
+ assert(dest_to_src.at(dest) == src);
+ dest_to_src.erase(dest);
+ src_to_dest[src].erase(dest);
+
+ if (src_to_dest[src].empty()) {
+ src_to_dest.erase(src);
+ }
+ }
+
+ bool is_src(u32 v) const {
+ bool rv = contains(src_to_dest, v);
+ assert(!rv || !src_to_dest.at(v).empty());
+ return rv;
+ }
+
+ bool is_dest(u32 v) const {
+ return contains(dest_to_src, v);
+ }
+
+ void remap_src(u32 old_src, u32 new_src) {
+ assert(is_src(old_src));
+ assert(!is_src(new_src));
+
+ for (const u32 &e : src_to_dest[old_src]) {
+ assert(e != new_src);
+ dest_to_src[e] = new_src;
+ }
+ src_to_dest[new_src].swap(src_to_dest[old_src]);
+ src_to_dest.erase(old_src);
+
+ assert(!is_src(old_src));
+ assert(is_src(new_src));
+ }
+
+ /* returns an arbitrary unresolved entry */
+ void get_pending(u32 *src, u32 *dest) {
+ assert(!empty());
+ *dest = dest_to_src.begin()->first;
+ *src = dest_to_src.begin()->second;
+ }
+
+ const map<u32, u32> &get_dest_mapping() const { return dest_to_src; }
+
+private:
+ map<u32, set<u32> > src_to_dest;
+ map<u32, u32> dest_to_src;
+};
+
+}
+
+static
+void prep_joins_for_generation(const GoughGraph &g, GoughVertex v,
+ map<GoughEdge, edge_join_info> *edge_info) {
+ DEBUG_PRINTF("writing out joins for %u\n", g[v].state_id);
+ for (const auto &var : g[v].vars) {
+ u32 dest_slot = var->slot;
+ for (const auto &var_edges : var->get_input_map()) {
+ u32 input = var_edges.first->slot;
+ if (dest_slot == input) {
+ continue;
+ }
+
+ for (const GoughEdge &incoming_edge : var_edges.second) {
+ (*edge_info)[incoming_edge].insert(input, dest_slot);
+ DEBUG_PRINTF("need %u<-%u\n", dest_slot, input);
+ }
+ }
+ }
+}
+
+static
+void add_simple_joins(edge_join_info &eji, vector<gough_ins> *out) {
+ /* any slot whose value we don't need can be written to immediately */
+ const map<u32, u32> &dest_to_src = eji.get_dest_mapping();
+
+ bool changed;
+ do {
+ changed = false;
+ for (map<u32, u32>::const_iterator it = dest_to_src.begin();
+ it != dest_to_src.end();) {
+ u32 src = it->second;
+ u32 dest = it->first;
+ ++it; /* avoid iterator being invalidated */
+
+ if (eji.is_src(dest)) {
+ continue; /* conflict; not simple (yet) */
+ }
+
+ /* value of destination slot is not used by any remaining joins;
+ * we can output this join immediately */
+ DEBUG_PRINTF("out %u<-%u\n", dest, src);
+ out->push_back(make_gough_ins(GOUGH_INS_MOV, dest, src));
+
+ eji.erase(src, dest);
+
+ if (eji.is_dest(src) && eji.is_src(src)) {
+ /* we can unblock src being used as an output by shifting
+ * across everybody using src as input to using dest (as == src
+ * now) */
+ eji.remap_src(src, dest);
+ }
+ changed = true;
+ }
+ } while (changed);
+}
+
+static
+void add_joins_to_block(edge_join_info &eji, vector<gough_ins> *out,
+ u32 base_temp_slot) {
+ /* joins happen concurrently: none of them should see the outputs of another
+ * join happening due to the same entry of the vertex. If there are
+ * conflicts we may have to handle things by using a temp output slot for
+ * each join and then copying into the final slot.
+ */
+
+ add_simple_joins(eji, out);
+ while (!eji.empty()) {
+ u32 split;
+ u32 input_for_split;
+ eji.get_pending(&input_for_split, &split);
+
+ assert(eji.is_src(split)); /* otherwise should be handled by simple */
+
+ /* stash the initial value of the split register in a temp register */
+ u32 temp = base_temp_slot++;
+ DEBUG_PRINTF("out %u<-%u\n", temp, split);
+ out->push_back(make_gough_ins(GOUGH_INS_MOV, temp, split));
+ eji.remap_src(split, temp); /* update maps */
+
+ /* split can now be safely written out to as all the uses of it as an
+ * input now refer to temp instead */
+
+ DEBUG_PRINTF("out %u<-%u\n", split, input_for_split);
+ out->push_back(make_gough_ins(GOUGH_INS_MOV, split, input_for_split));
+ eji.erase(input_for_split, split);
+
+ /* handle any uncovered simple cases */
+ add_simple_joins(eji, out);
+ }
+}
+
+static
+void build_blocks(const GoughGraph &g,
+ map<gough_edge_id, vector<gough_ins> > *blocks,
+ u32 base_temp_slot) {
+ for (const auto &e : edges_range(g)) {
+ if (g[e].vars.empty()) {
+ continue;
+ }
+
+ vector<gough_ins> &block = (*blocks)[gough_edge_id(g, e)];
+ add_to_block(g[e].vars, &block);
+ assert(!block.empty());
+ }
+
+ for (const auto t : vertices_range(g)) {
+ if (g[t].vars.empty()) {
+ continue;
+ }
+
+ map<GoughEdge, edge_join_info> eji;
+ prep_joins_for_generation(g, t, &eji);
+
+ for (auto &m : eji) {
+ vector<gough_ins> &block = (*blocks)[gough_edge_id(g, m.first)];
+ u32 cur_base = base_temp_slot;
+ if (!block.empty()) {
+ /* some temp slots may already be in use by short-lived vars */
+ ENSURE_AT_LEAST(&cur_base, highest_slot_used(block) + 1);
+ }
+
+ add_joins_to_block(m.second, &block, cur_base);
+ if (block.empty()) {
+ blocks->erase(gough_edge_id(g, m.first));
+ }
+ }
+ }
+
+ for (vector<gough_ins> &ins_list : *blocks | map_values) {
+ assert(!ins_list.empty());
+ ins_list.push_back(make_gough_ins(GOUGH_INS_END));
+ }
+}
+
+static
+void copy_in_blocks(raw_som_dfa &raw, u8 alphaShift, const GoughGraph &cfg,
+ const map<gough_edge_id, vector<gough_ins> > &blocks,
+ u32 *edge_blocks, u32 *top_blocks, u32 base_offset,
+ map<vector<gough_ins>, u32> *prog_offsets,
+ vector<gough_ins> *out) {
+ u32 impl_alpha_size = 1U << alphaShift;
+ UNUSED u32 top_sym = raw.alpha_remap[TOP];
+ assert(top_sym == raw.alpha_size - 1U);
+ map<vector<gough_ins>, u32> &processed = *prog_offsets;
+
+ for (const auto &e : edges_range(cfg)) {
+ if (!contains(blocks, gough_edge_id(cfg, e))) {
+ continue;
+ }
+ const vector<gough_ins> &block = blocks.at(gough_edge_id(cfg, e));
+ u32 prog_offset;
+ if (!contains(processed, block)) {
+ prog_offset = base_offset + byte_length(*out);
+ insert(out, out->end(), block);
+ processed[block] = prog_offset;
+ } else {
+ prog_offset = processed[block];
+ }
+
+ /* update edges */
+ u32 s_id = cfg[source(e, cfg)].state_id;
+ UNUSED u32 t_id = cfg[target(e, cfg)].state_id;
+ u32 impl_src_id = raw.states[s_id].impl_id;
+ DEBUG_PRINTF("%u: writing out block for edge_%u_%u at %u:\n",
+ impl_src_id, s_id, t_id,prog_offset);
+
+ for (u32 j = cfg[e].reach.find_first(); j != CharReach::npos;
+ j = cfg[e].reach.find_next(j)) {
+ assert(raw.states[s_id].next[j] == t_id);
+ u32 edge_index = impl_src_id * impl_alpha_size + j;
+ DEBUG_PRINTF("\tsetting on %u, %u\n", j, edge_index);
+ edge_blocks[edge_index] = prog_offset;
+ }
+
+ if (cfg[e].top) {
+ assert(raw.states[s_id].next[top_sym] == t_id);
+ DEBUG_PRINTF("\tsetting top on %u to block at %u\n", impl_src_id,
+ prog_offset);
+ top_blocks[impl_src_id] = prog_offset;
+ }
+ }
+}
+
+bool find_normal_self_loop(GoughVertex v, const GoughGraph &g, GoughEdge *out) {
+ for (const auto &e : out_edges_range(v, g)) {
+ if (target(e, g) != v) {
+ continue;
+ }
+ if (g[e].top) {
+ assert(g[e].reach.find_first() == CharReach::npos);
+ continue; /* corresponds to a top, not a normal transition */
+ }
+
+ *out = e;
+ return true;
+ }
+
+ return false;
+}
+
+static never_inline
+void update_accel_prog_offset(const gough_build_strat &gbs,
+ const map<gough_edge_id, vector<gough_ins> > &blocks,
+ const map<vector<gough_ins>, u32> &prog_offsets) {
+ map<dstate_id_t, GoughVertex> verts;
+ for (auto v : vertices_range(gbs.gg)) {
+ verts[gbs.gg[v].state_id] = v;
+ }
+
+ for (auto &m : gbs.built_accel) {
+ gough_accel *ga = m.first;
+ assert(!ga->prog_offset);
+ GoughVertex v = verts[m.second];
+ GoughEdge e;
+ UNUSED bool rv = find_normal_self_loop(v, gbs.gg, &e);
+ assert(rv);
+
+ if (!rv) {
+ continue;
+ }
+
+ DEBUG_PRINTF("updating state %u accel with margin %hhu\n",
+ gbs.gg[v].state_id, ga->margin_dist);
+ if (contains(blocks, gough_edge_id(gbs.gg, e))) {
+ const vector<gough_ins> &block
+ = blocks.at(gough_edge_id(gbs.gg, e));
+ ga->prog_offset = prog_offsets.at(block);
+ DEBUG_PRINTF("prog offset %u\n", ga->prog_offset);
+ } else {
+ ga->margin_dist = 0;
+ DEBUG_PRINTF("removing margin as no som\n");
+ }
+ }
+}
+
bytecode_ptr<NFA> goughCompile(raw_som_dfa &raw, u8 somPrecision,
const CompileContext &cc,
const ReportManager &rm) {
- assert(somPrecision == 2 || somPrecision == 4 || somPrecision == 8
- || !cc.streaming);
-
- if (!cc.grey.allowGough) {
- return nullptr;
- }
-
- DEBUG_PRINTF("hello world\n");
- unique_ptr<GoughGraph> cfg = makeCFG(raw);
- dump(*cfg, "init", cc.grey);
- copy_propagation(*cfg, cc.grey);
- remove_dead(*cfg);
- dump(*cfg, "prop", cc.grey);
- u32 slot_count = assign_slots(*cfg, cc.grey);
- dump(*cfg, "slots", cc.grey);
-
- map<gough_edge_id, vector<gough_ins> > blocks;
- build_blocks(*cfg, &blocks, slot_count);
- DEBUG_PRINTF("%u slots\n", highest_slot_used(blocks) + 1);
-
- u32 scratch_slot_count = highest_slot_used(blocks) + 1;
- assert(slot_count <= scratch_slot_count);
-
- dump(*cfg, "final", cc.grey);
- dump_blocks(blocks, "final", cc.grey);
-
- gough_info gi;
- memset(&gi, 0, sizeof(gi));
-
- map<dstate_id_t, gough_accel_state_info> accel_allowed;
- find_allowed_accel_states(*cfg, blocks, &accel_allowed);
+ assert(somPrecision == 2 || somPrecision == 4 || somPrecision == 8
+ || !cc.streaming);
+
+ if (!cc.grey.allowGough) {
+ return nullptr;
+ }
+
+ DEBUG_PRINTF("hello world\n");
+ unique_ptr<GoughGraph> cfg = makeCFG(raw);
+ dump(*cfg, "init", cc.grey);
+ copy_propagation(*cfg, cc.grey);
+ remove_dead(*cfg);
+ dump(*cfg, "prop", cc.grey);
+ u32 slot_count = assign_slots(*cfg, cc.grey);
+ dump(*cfg, "slots", cc.grey);
+
+ map<gough_edge_id, vector<gough_ins> > blocks;
+ build_blocks(*cfg, &blocks, slot_count);
+ DEBUG_PRINTF("%u slots\n", highest_slot_used(blocks) + 1);
+
+ u32 scratch_slot_count = highest_slot_used(blocks) + 1;
+ assert(slot_count <= scratch_slot_count);
+
+ dump(*cfg, "final", cc.grey);
+ dump_blocks(blocks, "final", cc.grey);
+
+ gough_info gi;
+ memset(&gi, 0, sizeof(gi));
+
+ map<dstate_id_t, gough_accel_state_info> accel_allowed;
+ find_allowed_accel_states(*cfg, blocks, &accel_allowed);
gough_build_strat gbs(raw, *cfg, rm, accel_allowed);
auto basic_dfa = mcclellanCompile_i(raw, gbs, cc);
- assert(basic_dfa);
- if (!basic_dfa) {
- return nullptr;
- }
-
- u8 alphaShift
- = ((const mcclellan *)getImplNfa(basic_dfa.get()))->alphaShift;
- u32 edge_count = (1U << alphaShift) * raw.states.size();
-
- u32 curr_offset = ROUNDUP_N(basic_dfa->length, 4);
-
- u32 haig_offset = curr_offset;
- curr_offset += sizeof(gi);
- /* reserve space for edge->program mapping */
- u32 edge_prog_offset = curr_offset;
- curr_offset += sizeof(u32) * edge_count;
- vector<u32> edge_blocks(edge_count);
-
- u32 top_prog_offset = 0;
- if (is_triggered(raw.kind)) {
- /* reserve space for edge->program mapping */
- top_prog_offset = curr_offset;
- curr_offset += sizeof(u32) * raw.states.size();
- }
- gi.top_prog_offset = top_prog_offset;
- vector<u32> top_blocks(raw.states.size());
-
- /* reserve space for blocks */
- u32 prog_base_offset = curr_offset;
- gi.prog_base_offset = prog_base_offset;
-
- vector<gough_ins> temp_blocks;
- map<vector<gough_ins>, u32> prog_offsets;
- copy_in_blocks(raw, alphaShift, *cfg, blocks, &edge_blocks[0],
- &top_blocks[0], prog_base_offset, &prog_offsets,
- &temp_blocks);
- update_accel_prog_offset(gbs, blocks, prog_offsets);
-
- u32 total_prog_size = byte_length(temp_blocks);
- curr_offset += total_prog_size;
-
- gi.stream_som_loc_count = slot_count;
- gi.stream_som_loc_width = somPrecision;
-
- u32 gough_size = ROUNDUP_N(curr_offset, 16);
+ assert(basic_dfa);
+ if (!basic_dfa) {
+ return nullptr;
+ }
+
+ u8 alphaShift
+ = ((const mcclellan *)getImplNfa(basic_dfa.get()))->alphaShift;
+ u32 edge_count = (1U << alphaShift) * raw.states.size();
+
+ u32 curr_offset = ROUNDUP_N(basic_dfa->length, 4);
+
+ u32 haig_offset = curr_offset;
+ curr_offset += sizeof(gi);
+ /* reserve space for edge->program mapping */
+ u32 edge_prog_offset = curr_offset;
+ curr_offset += sizeof(u32) * edge_count;
+ vector<u32> edge_blocks(edge_count);
+
+ u32 top_prog_offset = 0;
+ if (is_triggered(raw.kind)) {
+ /* reserve space for edge->program mapping */
+ top_prog_offset = curr_offset;
+ curr_offset += sizeof(u32) * raw.states.size();
+ }
+ gi.top_prog_offset = top_prog_offset;
+ vector<u32> top_blocks(raw.states.size());
+
+ /* reserve space for blocks */
+ u32 prog_base_offset = curr_offset;
+ gi.prog_base_offset = prog_base_offset;
+
+ vector<gough_ins> temp_blocks;
+ map<vector<gough_ins>, u32> prog_offsets;
+ copy_in_blocks(raw, alphaShift, *cfg, blocks, &edge_blocks[0],
+ &top_blocks[0], prog_base_offset, &prog_offsets,
+ &temp_blocks);
+ update_accel_prog_offset(gbs, blocks, prog_offsets);
+
+ u32 total_prog_size = byte_length(temp_blocks);
+ curr_offset += total_prog_size;
+
+ gi.stream_som_loc_count = slot_count;
+ gi.stream_som_loc_width = somPrecision;
+
+ u32 gough_size = ROUNDUP_N(curr_offset, 16);
auto gough_dfa = make_zeroed_bytecode_ptr<NFA>(gough_size);
-
- memcpy(gough_dfa.get(), basic_dfa.get(), basic_dfa->length);
- memcpy((char *)gough_dfa.get() + haig_offset, &gi, sizeof(gi));
- if (gough_dfa->type == MCCLELLAN_NFA_16) {
- gough_dfa->type = GOUGH_NFA_16;
- } else {
- assert(gough_dfa->type == MCCLELLAN_NFA_8);
- gough_dfa->type = GOUGH_NFA_8;
- }
-
- /* update stream state requirements */
- u32 base_state_size = gough_dfa->type == GOUGH_NFA_8 ? 1 : 2;
- gough_dfa->streamStateSize = base_state_size + slot_count * somPrecision;
- gough_dfa->scratchStateSize = (u32)(16 + scratch_slot_count * sizeof(u64a));
-
- mcclellan *m = (mcclellan *)getMutableImplNfa(gough_dfa.get());
- m->haig_offset = haig_offset;
-
- /* update nfa length, haig_info offset (leave mcclellan length alone) */
- gough_dfa->length = gough_size;
-
- /* copy in blocks */
- copy_bytes((u8 *)gough_dfa.get() + edge_prog_offset, edge_blocks);
- if (top_prog_offset) {
- copy_bytes((u8 *)gough_dfa.get() + top_prog_offset, top_blocks);
- }
- copy_bytes((u8 *)gough_dfa.get() + prog_base_offset, temp_blocks);
-
- return gough_dfa;
-}
-
+
+ memcpy(gough_dfa.get(), basic_dfa.get(), basic_dfa->length);
+ memcpy((char *)gough_dfa.get() + haig_offset, &gi, sizeof(gi));
+ if (gough_dfa->type == MCCLELLAN_NFA_16) {
+ gough_dfa->type = GOUGH_NFA_16;
+ } else {
+ assert(gough_dfa->type == MCCLELLAN_NFA_8);
+ gough_dfa->type = GOUGH_NFA_8;
+ }
+
+ /* update stream state requirements */
+ u32 base_state_size = gough_dfa->type == GOUGH_NFA_8 ? 1 : 2;
+ gough_dfa->streamStateSize = base_state_size + slot_count * somPrecision;
+ gough_dfa->scratchStateSize = (u32)(16 + scratch_slot_count * sizeof(u64a));
+
+ mcclellan *m = (mcclellan *)getMutableImplNfa(gough_dfa.get());
+ m->haig_offset = haig_offset;
+
+ /* update nfa length, haig_info offset (leave mcclellan length alone) */
+ gough_dfa->length = gough_size;
+
+ /* copy in blocks */
+ copy_bytes((u8 *)gough_dfa.get() + edge_prog_offset, edge_blocks);
+ if (top_prog_offset) {
+ copy_bytes((u8 *)gough_dfa.get() + top_prog_offset, top_blocks);
+ }
+ copy_bytes((u8 *)gough_dfa.get() + prog_base_offset, temp_blocks);
+
+ return gough_dfa;
+}
+
AccelScheme gough_build_strat::find_escape_strings(dstate_id_t this_idx) const {
AccelScheme rv;
- if (!contains(accel_gough_info, this_idx)) {
+ if (!contains(accel_gough_info, this_idx)) {
rv.cr = CharReach::dot();
rv.double_byte.clear();
return rv;
- }
-
+ }
+
rv = mcclellan_build_strat::find_escape_strings(this_idx);
-
+
assert(!rv.offset || rv.cr.all()); /* should have been limited by strat */
if (rv.offset) {
rv.cr = CharReach::dot();
rv.double_byte.clear();
return rv;
- }
+ }
if (rv.double_offset
|| !accel_gough_info.at(this_idx).two_byte) {
@@ -1172,163 +1172,163 @@ AccelScheme gough_build_strat::find_escape_strings(dstate_id_t this_idx) const {
}
return rv;
-}
-
+}
+
void gough_build_strat::buildAccel(dstate_id_t this_idx, const AccelScheme &info,
void *accel_out) {
- assert(mcclellan_build_strat::accelSize() == sizeof(AccelAux));
- gough_accel *accel = (gough_accel *)accel_out;
- /* build a plain accelaux so we can work out where we can get to */
+ assert(mcclellan_build_strat::accelSize() == sizeof(AccelAux));
+ gough_accel *accel = (gough_accel *)accel_out;
+ /* build a plain accelaux so we can work out where we can get to */
mcclellan_build_strat::buildAccel(this_idx, info, &accel->accel);
- DEBUG_PRINTF("state %hu is accel with type %hhu\n", this_idx,
- accel->accel.accel_type);
- if (accel->accel.accel_type == ACCEL_NONE) {
- return;
- }
-
+ DEBUG_PRINTF("state %hu is accel with type %hhu\n", this_idx,
+ accel->accel.accel_type);
+ if (accel->accel.accel_type == ACCEL_NONE) {
+ return;
+ }
+
assert(!accel->accel.generic.offset);
- assert(contains(accel_gough_info, this_idx));
- accel->margin_dist = verify_u8(accel_gough_info.at(this_idx).margin);
- built_accel[accel] = this_idx;
- DEBUG_PRINTF("state %hu is accel with margin %hhu\n", this_idx,
- accel->margin_dist);
-}
-
-namespace {
-struct raw_gough_report_list {
- set<som_report> reports;
-
+ assert(contains(accel_gough_info, this_idx));
+ accel->margin_dist = verify_u8(accel_gough_info.at(this_idx).margin);
+ built_accel[accel] = this_idx;
+ DEBUG_PRINTF("state %hu is accel with margin %hhu\n", this_idx,
+ accel->margin_dist);
+}
+
+namespace {
+struct raw_gough_report_list {
+ set<som_report> reports;
+
raw_gough_report_list(
const vector<pair<ReportID, GoughSSAVar *>> &raw_reports,
const ReportManager &rm, bool do_remap) {
- for (const auto &m : raw_reports) {
+ for (const auto &m : raw_reports) {
ReportID r = do_remap ? rm.getProgramOffset(m.first) : m.first;
- u32 impl_slot = INVALID_SLOT;
- if (m.second) {
- impl_slot = m.second->slot;
- assert(impl_slot != INVALID_SLOT);
- }
- reports.emplace(r, impl_slot);
- }
- }
-
- bool operator<(const raw_gough_report_list &b) const {
- return reports < b.reports;
- }
-};
-
-struct raw_gough_report_info_impl : public raw_report_info {
- vector<raw_gough_report_list> rl;
- u32 getReportListSize() const override;
- size_t size() const override;
- void fillReportLists(NFA *n, size_t base_offset,
- vector<u32> &ro /* out */) const override;
-};
-}
-
-unique_ptr<raw_report_info> gough_build_strat::gatherReports(
- vector<u32> &reports,
- vector<u32> &reports_eod,
- u8 *isSingleReport,
- ReportID *arbReport) const {
- DEBUG_PRINTF("gathering reports\n");
-
+ u32 impl_slot = INVALID_SLOT;
+ if (m.second) {
+ impl_slot = m.second->slot;
+ assert(impl_slot != INVALID_SLOT);
+ }
+ reports.emplace(r, impl_slot);
+ }
+ }
+
+ bool operator<(const raw_gough_report_list &b) const {
+ return reports < b.reports;
+ }
+};
+
+struct raw_gough_report_info_impl : public raw_report_info {
+ vector<raw_gough_report_list> rl;
+ u32 getReportListSize() const override;
+ size_t size() const override;
+ void fillReportLists(NFA *n, size_t base_offset,
+ vector<u32> &ro /* out */) const override;
+};
+}
+
+unique_ptr<raw_report_info> gough_build_strat::gatherReports(
+ vector<u32> &reports,
+ vector<u32> &reports_eod,
+ u8 *isSingleReport,
+ ReportID *arbReport) const {
+ DEBUG_PRINTF("gathering reports\n");
+
const bool remap_reports = has_managed_reports(rdfa.kind);
auto ri = ue2::make_unique<raw_gough_report_info_impl>();
map<raw_gough_report_list, u32> rev;
- assert(!rdfa.states.empty());
-
- vector<GoughVertex> verts(rdfa.states.size());
- for (auto v : vertices_range(gg)) {
- verts[gg[v].state_id] = v;
- }
-
- for (u32 state_id = 0; state_id < verts.size(); state_id++) {
- assert(state_id < rdfa.states.size());
- GoughVertex v = verts[state_id];
- assert(v != GoughGraph::null_vertex() || !state_id);
-
- DEBUG_PRINTF("i = %zu [%zu]\n", reports.size(), gg[v].reports.size());
- if (v == GoughGraph::null_vertex() || gg[v].reports.empty()) {
- reports.push_back(MO_INVALID_IDX);
- continue;
- }
-
+ assert(!rdfa.states.empty());
+
+ vector<GoughVertex> verts(rdfa.states.size());
+ for (auto v : vertices_range(gg)) {
+ verts[gg[v].state_id] = v;
+ }
+
+ for (u32 state_id = 0; state_id < verts.size(); state_id++) {
+ assert(state_id < rdfa.states.size());
+ GoughVertex v = verts[state_id];
+ assert(v != GoughGraph::null_vertex() || !state_id);
+
+ DEBUG_PRINTF("i = %zu [%zu]\n", reports.size(), gg[v].reports.size());
+ if (v == GoughGraph::null_vertex() || gg[v].reports.empty()) {
+ reports.push_back(MO_INVALID_IDX);
+ continue;
+ }
+
raw_gough_report_list rrl(gg[v].reports, rm, remap_reports);
- DEBUG_PRINTF("non empty r %zu\n", reports.size());
- if (rev.find(rrl) != rev.end()) {
- reports.push_back(rev[rrl]);
- } else {
- DEBUG_PRINTF("adding to rl\n");
- rev[rrl] = ri->size();
- reports.push_back(ri->size());
- ri->rl.push_back(rrl);
- }
- }
-
- for (auto v : verts) {
- if (v == GoughGraph::null_vertex() || gg[v].reports_eod.empty()) {
- reports_eod.push_back(MO_INVALID_IDX);
- continue;
- }
-
- DEBUG_PRINTF("non empty r eod\n");
+ DEBUG_PRINTF("non empty r %zu\n", reports.size());
+ if (rev.find(rrl) != rev.end()) {
+ reports.push_back(rev[rrl]);
+ } else {
+ DEBUG_PRINTF("adding to rl\n");
+ rev[rrl] = ri->size();
+ reports.push_back(ri->size());
+ ri->rl.push_back(rrl);
+ }
+ }
+
+ for (auto v : verts) {
+ if (v == GoughGraph::null_vertex() || gg[v].reports_eod.empty()) {
+ reports_eod.push_back(MO_INVALID_IDX);
+ continue;
+ }
+
+ DEBUG_PRINTF("non empty r eod\n");
raw_gough_report_list rrl(gg[v].reports_eod, rm, remap_reports);
- if (rev.find(rrl) != rev.end()) {
- reports_eod.push_back(rev[rrl]);
- continue;
- }
-
- DEBUG_PRINTF("adding to rl eod %zu\n", gg[v].reports_eod.size());
- rev[rrl] = ri->size();
- reports_eod.push_back(ri->size());
- ri->rl.push_back(rrl);
- }
-
- /* TODO: support single report in gough */
- *isSingleReport = 0;
- *arbReport = MO_INVALID_IDX;
- assert(!ri->rl.empty()); /* all components should be able to generate
- reports */
- return move(ri);
-}
-
-u32 raw_gough_report_info_impl::getReportListSize() const {
- u32 sz = 0;
-
- for (const raw_gough_report_list &r : rl) {
- sz += sizeof(gough_report_list);
- sz += sizeof(gough_report) * r.reports.size();
- }
-
- return sz;
-}
-
-size_t raw_gough_report_info_impl::size() const {
- return rl.size();
-}
-
-void raw_gough_report_info_impl::fillReportLists(NFA *n, size_t base_offset,
- vector<u32> &ro) const {
- for (const raw_gough_report_list &r : rl) {
- ro.push_back(base_offset);
-
- gough_report_list *p = (gough_report_list *)((char *)n + base_offset);
- u32 i = 0;
-
- for (const som_report &sr : r.reports) {
- p->report[i].r = sr.report;
- p->report[i].som = sr.slot;
- i++;
- }
-
- p->count = verify_u32(r.reports.size());
-
- base_offset += sizeof(gough_report_list);
- base_offset += sizeof(gough_report) * r.reports.size();
- }
-}
-
-} // namespace ue2
+ if (rev.find(rrl) != rev.end()) {
+ reports_eod.push_back(rev[rrl]);
+ continue;
+ }
+
+ DEBUG_PRINTF("adding to rl eod %zu\n", gg[v].reports_eod.size());
+ rev[rrl] = ri->size();
+ reports_eod.push_back(ri->size());
+ ri->rl.push_back(rrl);
+ }
+
+ /* TODO: support single report in gough */
+ *isSingleReport = 0;
+ *arbReport = MO_INVALID_IDX;
+ assert(!ri->rl.empty()); /* all components should be able to generate
+ reports */
+ return move(ri);
+}
+
+u32 raw_gough_report_info_impl::getReportListSize() const {
+ u32 sz = 0;
+
+ for (const raw_gough_report_list &r : rl) {
+ sz += sizeof(gough_report_list);
+ sz += sizeof(gough_report) * r.reports.size();
+ }
+
+ return sz;
+}
+
+size_t raw_gough_report_info_impl::size() const {
+ return rl.size();
+}
+
+void raw_gough_report_info_impl::fillReportLists(NFA *n, size_t base_offset,
+ vector<u32> &ro) const {
+ for (const raw_gough_report_list &r : rl) {
+ ro.push_back(base_offset);
+
+ gough_report_list *p = (gough_report_list *)((char *)n + base_offset);
+ u32 i = 0;
+
+ for (const som_report &sr : r.reports) {
+ p->report[i].r = sr.report;
+ p->report[i].som = sr.slot;
+ i++;
+ }
+
+ p->count = verify_u32(r.reports.size());
+
+ base_offset += sizeof(gough_report_list);
+ base_offset += sizeof(gough_report) * r.reports.size();
+ }
+}
+
+} // namespace ue2