- When building domination information, we need to some array data
    structure, and need to index into them using basic block index.
    Calculate the basic block index during adding edges.

  - The Step-1 in Tarjan algorithm is to assign dfs order number for
    each bb in the cfg. We need to iterate on all bbs in reverse dfs order.

  - Step-2 and Step-3 in Tarjan algorithm to calculate semi-dominators and
    immediate-dominators implicitly and explicitly.

  - Step-4, we add a new bitmap field inside subprog_info to keep the
    dominator tree there.

  - Post-domination information is built but not tested.

Signed-off-by: Jiong Wang <jiong.w...@netronome.com>
---
 include/linux/bpf_verifier.h |   3 +
 kernel/bpf/cfg.c             | 386 ++++++++++++++++++++++++++++++++++++++++++-
 kernel/bpf/cfg.h             |   3 +-
 kernel/bpf/verifier.c        |   5 +-
 4 files changed, 393 insertions(+), 4 deletions(-)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 404a5d8..9c0a808 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -177,6 +177,9 @@ struct bpf_subprog_info {
        u32 start; /* insn idx of function entry point */
        u16 stack_depth; /* max. stack depth used by this function */
        struct list_head bbs; /* basic blocks list. */
+       u32 bb_num; /* total basic block num. */
+       unsigned long *dtree;
+       bool dtree_avail;
 };
 
 /* single container for all structs
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index 208aac7..b50937a 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -17,11 +17,33 @@ struct edge_node {
        struct bb_node *dst;
 };
 
+struct edge_iter {
+       struct list_head *list_head;
+       struct edge_node *edge;
+};
+
+#define first_edge(e_list)     list_first_entry(e_list, struct edge_node, l)
+#define last_edge(e_list)      list_last_entry(e_list, struct edge_node, l)
+#define next_edge(e)           list_next_entry(e, l)
+
+static bool ei_end_p(struct edge_iter *ei)
+{
+       return &ei->edge->l == ei->list_head;
+}
+
+static void ei_next(struct edge_iter *ei)
+{
+       struct edge_node *e = ei->edge;
+
+       ei->edge = next_edge(e);
+}
+
 struct bb_node {
        struct list_head l;
        struct list_head e_prevs;
        struct list_head e_succs;
        u16 head;
+       u16 idx;
 };
 
 #define bb_prev(bb)            list_prev_entry(bb, l)
@@ -31,6 +53,27 @@ struct bb_node {
 #define entry_bb(bb_list)      bb_first(bb_list)
 #define exit_bb(bb_list)       bb_last(bb_list)
 
+struct dom_info {
+       u16 *dfs_parent;
+       u16 *dfs_order;
+       struct bb_node **dfs_to_bb;
+       /* immediate-dominator */
+       u16 *idom;
+       /* semi-dominator */
+       u16 *sdom;
+       u16 *bucket;
+       u16 *next_bucket;
+       /* best node during path compression. */
+       u16 *best;
+       /* ancestor along tree edge. */
+       u16 *ancestor;
+       /* size and child are used for tree balancing. */
+       u16 *size;
+       u16 *child;
+
+       u16 dfsnum;
+};
+
 int subprog_append_bb(struct list_head *bb_list, int head)
 {
        struct bb_node *new_bb, *bb;
@@ -107,6 +150,7 @@ int subprog_add_bb_edges(struct bpf_insn *insns, struct 
list_head *bb_list)
 {
        struct bb_node *bb, *exit_bb;
        struct edge_node *edge;
+       int bb_num;
 
        bb = entry_bb(bb_list);
        edge = kcalloc(2, sizeof(*edge), GFP_KERNEL);
@@ -118,9 +162,12 @@ int subprog_add_bb_edges(struct bpf_insn *insns, struct 
list_head *bb_list)
        edge[1].src = edge->src;
        edge[1].dst = edge->dst;
        list_add_tail(&edge[1].l, &edge[1].dst->e_prevs);
+       bb->idx = -1;
 
        exit_bb = exit_bb(bb_list);
+       exit_bb->idx = -2;
        bb = bb_next(bb);
+       bb_num = 0;
        list_for_each_entry_from(bb, &exit_bb->l, l) {
                bool has_fallthrough, only_has_fallthrough;
                bool has_branch, only_has_branch;
@@ -129,6 +176,8 @@ int subprog_add_bb_edges(struct bpf_insn *insns, struct 
list_head *bb_list)
                struct bpf_insn insn;
                u8 code;
 
+               bb->idx = bb_num++;
+
                edge = kcalloc(2, sizeof(*edge), GFP_KERNEL);
                if (!edge)
                        return -ENOMEM;
@@ -184,9 +233,341 @@ int subprog_add_bb_edges(struct bpf_insn *insns, struct 
list_head *bb_list)
                }
        }
 
+       return bb_num + 2;
+}
+
+static int init_dom_info(struct bpf_subprog_info *subprog, struct dom_info *di)
+{
+       u16 *p, bb_num, i;
+
+       di->dfs_parent = NULL;
+       di->dfs_to_bb = NULL;
+
+       bb_num = subprog->bb_num;
+       p = kcalloc(10 * bb_num, sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       di->dfs_parent = p;
+       di->dfs_order = di->dfs_parent + bb_num;
+       di->idom = di->dfs_order + bb_num;
+       di->sdom = di->idom + bb_num;
+       di->bucket = di->sdom + bb_num;
+       di->next_bucket = di->bucket + bb_num;
+       di->best = di->next_bucket + bb_num;
+       di->ancestor = di->best + bb_num;
+       di->size = di->ancestor + bb_num;
+       di->child = di->size + bb_num;
+       di->dfs_to_bb = kcalloc(bb_num, sizeof(struct bb_node *), GFP_KERNEL);
+       di->dfsnum = 1;
+
+       for (i = 0; i < bb_num; i++) {
+               di->size[i] = 1;
+               di->best[i] = i;
+               di->sdom[i] = i;
+       }
+
        return 0;
 }
 
+static void compress_path(struct dom_info *di, unsigned int v)
+{
+       unsigned int parent = di->ancestor[v];
+
+       if (di->ancestor[parent]) {
+               compress_path(di, parent);
+
+               if (di->sdom[di->best[parent]] < di->sdom[di->best[v]])
+                       di->best[v] = di->best[parent];
+
+               di->ancestor[v] = di->ancestor[parent];
+       }
+}
+
+static unsigned int eval(struct dom_info *di, unsigned int v)
+{
+       unsigned int ancestor = di->ancestor[v];
+
+       /* v is root. */
+       if (!ancestor)
+               return di->best[v];
+
+       /* compress path */
+       compress_path(di, v);
+       ancestor = di->ancestor[v];
+
+       if (di->sdom[di->best[ancestor]] >= di->sdom[di->best[v]])
+               return di->best[v];
+       else
+               return di->best[ancestor];
+}
+
+/* Re-balancing the tree before linking. */
+static void link(struct dom_info *di, unsigned int v, unsigned int w)
+{
+       unsigned int s = w;
+
+       while (di->sdom[di->best[w]] < di->sdom[di->best[di->child[s]]]) {
+               if (di->size[s] + di->size[di->child[di->child[s]]] >=
+                       2 * di->size[di->child[s]]) {
+                       di->ancestor[di->child[s]] = s;
+                       di->child[s] = di->child[di->child[s]];
+               } else {
+                       di->size[di->child[s]] = di->size[s];
+                       di->ancestor[s] = di->child[s];
+                       s = di->child[s];
+               }
+       }
+
+       di->best[s] = di->best[w];
+       di->size[v] += di->size[w];
+       if (di->size[v] < 2 * di->size[w]) {
+               unsigned int t = s;
+
+               s = di->child[v];
+               di->child[v] = t;
+       }
+
+       while (s) {
+               di->ancestor[s] = v;
+               s = di->child[s];
+       }
+}
+
+static void calc_idoms(struct bpf_subprog_info *subprog, struct dom_info *di,
+                      bool reverse)
+{
+       u16 entry_bb_fake_idx = subprog->bb_num - 2, idx, w, k, par;
+       struct list_head *bb_list = &subprog->bbs;
+       struct bb_node *entry_bb;
+       struct edge_iter ei;
+
+       if (reverse)
+               entry_bb = exit_bb(bb_list);
+       else
+               entry_bb = entry_bb(bb_list);
+       idx = di->dfsnum - 1;
+
+       while (idx > 1) {
+               struct bb_node *bb = di->dfs_to_bb[idx];
+               struct edge_node *e;
+
+               par = di->dfs_parent[idx];
+               k = idx;
+
+               if (reverse) {
+                       ei.edge = first_edge(&bb->e_succs);
+                       ei.list_head = &bb->e_succs;
+               } else {
+                       ei.edge = first_edge(&bb->e_prevs);
+                       ei.list_head = &bb->e_prevs;
+               }
+
+               while (!ei_end_p(&ei)) {
+                       struct bb_node *b;
+                       u16 k1;
+
+                       e = ei.edge;
+                       if (reverse)
+                               b = e->dst;
+                       else
+                               b = e->src;
+                       ei_next(&ei);
+
+                       if (b == entry_bb)
+                               k1 = di->dfs_order[entry_bb_fake_idx];
+                       else
+                               k1 = di->dfs_order[b->idx];
+
+                       if (k1 > idx)
+                               k1 = di->sdom[eval(di, k1)];
+                       if (k1 < k)
+                               k = k1;
+               }
+
+               di->sdom[idx] = k;
+               link(di, par, idx);
+               di->next_bucket[idx] = di->bucket[k];
+               di->bucket[k] = idx;
+
+               for (w = di->bucket[par]; w; w = di->next_bucket[w]) {
+                       k = eval(di, w);
+                       if (di->sdom[k] < di->sdom[w])
+                               di->idom[w] = k;
+                       else
+                               di->idom[w] = par;
+               }
+               di->bucket[par] = 0;
+               idx--;
+       }
+
+       di->idom[1] = 0;
+       for (idx = 2; idx <= di->dfsnum - 1; idx++)
+               if (di->idom[idx] != di->sdom[idx])
+                       di->idom[idx] = di->idom[di->idom[idx]];
+}
+
+static int calc_dfs_tree(struct bpf_subprog_info *subprog, struct dom_info *di,
+                        bool reverse)
+{
+       u16 bb_num = subprog->bb_num, sp = 0, idx, parent_idx;
+       struct list_head *bb_list = &subprog->bbs;
+       u16 entry_bb_fake_idx = bb_num - 2;
+       struct bb_node *entry_bb, *exit_bb;
+       struct edge_iter ei, *stack;
+       struct edge_node *e;
+
+       di->dfs_order[entry_bb_fake_idx] = di->dfsnum;
+
+       stack = kmalloc_array(bb_num - 1, sizeof(struct edge_iter), GFP_KERNEL);
+       if (!stack)
+               return -ENOMEM;
+
+       if (reverse) {
+               entry_bb = exit_bb(bb_list);
+               exit_bb = entry_bb(bb_list);
+               di->dfs_to_bb[di->dfsnum++] = exit_bb;
+               ei.edge = first_edge(&entry_bb->e_prevs);
+               ei.list_head = &entry_bb->e_prevs;
+       } else {
+               entry_bb = entry_bb(bb_list);
+               exit_bb = exit_bb(bb_list);
+               di->dfs_to_bb[di->dfsnum++] = entry_bb;
+               ei.edge = first_edge(&entry_bb->e_succs);
+               ei.list_head = &entry_bb->e_succs;
+       }
+
+       while (1) {
+               struct bb_node *bb_dst, *bb_src;
+
+               while (!ei_end_p(&ei)) {
+                       e = ei.edge;
+
+                       if (reverse) {
+                               bb_dst = e->src;
+                               if (bb_dst == exit_bb ||
+                                   di->dfs_order[bb_dst->idx]) {
+                                       ei_next(&ei);
+                                       continue;
+                               }
+                               bb_src = e->dst;
+                       } else {
+                               bb_dst = e->dst;
+                               if (bb_dst == exit_bb ||
+                                   di->dfs_order[bb_dst->idx]) {
+                                       ei_next(&ei);
+                                       continue;
+                               }
+                               bb_src = e->src;
+                       }
+
+                       if (bb_src != entry_bb)
+                               parent_idx = di->dfs_order[bb_src->idx];
+                       else
+                               parent_idx = di->dfs_order[entry_bb_fake_idx];
+
+                       idx = di->dfsnum++;
+                       di->dfs_order[bb_dst->idx] = idx;
+                       di->dfs_to_bb[idx] = bb_dst;
+                       di->dfs_parent[idx] = parent_idx;
+
+                       stack[sp++] = ei;
+                       if (reverse) {
+                               ei.edge = first_edge(&bb_dst->e_prevs);
+                               ei.list_head = &bb_dst->e_prevs;
+                       } else {
+                               ei.edge = first_edge(&bb_dst->e_succs);
+                               ei.list_head = &bb_dst->e_succs;
+                       }
+               }
+
+               if (!sp)
+                       break;
+
+               ei = stack[--sp];
+               ei_next(&ei);
+       }
+
+       kfree(stack);
+
+       return 0;
+}
+
+static int idoms_to_doms(struct bpf_subprog_info *subprog, struct dom_info *di)
+{
+       int bb_num, i, end_index, bb, bb_idom, lane_len;
+       unsigned long *bitmap;
+
+       bb_num = subprog->bb_num - 2;
+       lane_len = BITS_TO_LONGS(bb_num);
+       bitmap = kcalloc(bb_num, lane_len * sizeof(long), GFP_KERNEL);
+       subprog->dtree = bitmap;
+       if (!subprog->dtree)
+               return -ENOMEM;
+       subprog->dtree_avail = true;
+       end_index = di->dfs_order[bb_num];
+
+       for (i = 1; i <= di->dfsnum - 1; i++) {
+               if (i == end_index)
+                       continue;
+
+               bb = di->dfs_to_bb[i]->idx;
+               if (di->idom[i] && di->idom[i] != end_index) {
+                       bb_idom = di->dfs_to_bb[di->idom[i]]->idx;
+                       bitmap_copy(bitmap + bb * lane_len,
+                                   bitmap + bb_idom * lane_len, bb_num);
+               }
+
+               bitmap_set(bitmap + bb * lane_len, bb, 1);
+       }
+
+       return 0;
+}
+
+/* Build domination information using Lengauer and Tarjan algorithm.
+ *
+ *   1. dfs on cfg to assign dfs-num for each node(bb).
+ *   2. calculate semi-dominator and calculate immediate dominator when
+ *      possible.
+ *   3. calculate immediate dominator not finished during step 2.
+ *   4. build domination bitmap using immediate dominator information.
+ *
+ * See:
+ *   A fast algorithm for finding dominators in a flowgraph.
+ *     - 1979, by T Lengauer and R Tarjan
+ *
+ *   Especially, Appendix B: The Complete Dominators Algorithms.
+ *
+ * The implementation also referenced GNU GCC 3.0.
+ */
+
+int subprog_build_dom_info(struct bpf_subprog_info *subprog)
+{
+       struct dom_info di;
+       int ret;
+
+       ret = init_dom_info(subprog, &di);
+       if (ret < 0)
+               goto free_dominfo;
+
+       ret = calc_dfs_tree(subprog, &di, false);
+       if (ret < 0)
+               goto free_dominfo;
+
+       calc_idoms(subprog, &di, false);
+       ret = idoms_to_doms(subprog, &di);
+       if (ret < 0)
+               goto free_dominfo;
+
+       ret = 0;
+
+free_dominfo:
+       if (di.dfs_parent)
+               kfree(di.dfs_parent);
+
+       return ret;
+}
+
 static void subprog_free_edge(struct bb_node *bb)
 {
        struct list_head *succs = &bb->e_succs;
@@ -199,7 +580,7 @@ static void subprog_free_edge(struct bb_node *bb)
        }
 }
 
-void subprog_free_bb(struct bpf_subprog_info *subprog, int end_idx)
+void subprog_free(struct bpf_subprog_info *subprog, int end_idx)
 {
        int i = 0;
 
@@ -214,5 +595,8 @@ void subprog_free_bb(struct bpf_subprog_info *subprog, int 
end_idx)
                        list_del(&bb->l);
                        kfree(bb);
                }
+
+               if (subprog[i].dtree_avail)
+                       kfree(subprog[i].dtree);
        }
 }
diff --git a/kernel/bpf/cfg.h b/kernel/bpf/cfg.h
index 7a9d5dd..cbb44f2 100644
--- a/kernel/bpf/cfg.h
+++ b/kernel/bpf/cfg.h
@@ -10,8 +10,9 @@
 
 int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list);
 int subprog_append_bb(struct list_head *bb_list, int head);
+int subprog_build_dom_info(struct bpf_subprog_info *subprog);
 int subprog_fini_bb(struct list_head *bb_list, int subprog_end);
 int subprog_init_bb(struct list_head *bb_list, int subprog_start);
-void subprog_free_bb(struct bpf_subprog_info *subprog, int end_idx);
+void subprog_free(struct bpf_subprog_info *subprog, int end_idx);
 
 #endif
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 585e0a0..6543470 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -878,6 +878,7 @@ static int check_subprogs(struct bpf_verifier_env *env)
                        ret = subprog_add_bb_edges(insn, cur_bb_list);
                        if (ret < 0)
                                goto free_nodes;
+                       subprog[cur_subprog].bb_num = ret;
                        subprog_start = subprog_end;
                        cur_subprog++;
                        if (cur_subprog < env->subprog_cnt) {
@@ -894,8 +895,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
        ret = 0;
 
 free_nodes:
-       subprog_free_bb(subprog, cur_subprog == env->subprog_cnt ?
-                                       cur_subprog - 1 : cur_subprog);
+       subprog_free(subprog, cur_subprog == env->subprog_cnt ?
+                               cur_subprog - 1 : cur_subprog);
        return ret;
 }
 
-- 
2.7.4

Reply via email to