<p dir="ltr">I can't help but think that we keep solving this problem... We have a low-collision hash table for vkGetProcAddress, something for glxGetProcAddress and eglGetProcAddress (hopefully the same?) and now this.  Can we pick a method, make it a little Python helper, and use it everywhere?</p>
<p dir="ltr">Not being critical; this is probably a fine solution for compile-time string -> int mappings and possibly the best to date.  It just seems like kind of a big hammer especially if glsl_to_nir is its only user.</p>
<div class="gmail_quote">On Jul 5, 2016 5:46 PM, "Ian Romanick" <<a href="mailto:idr@freedesktop.org">idr@freedesktop.org</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">From: Ian Romanick <<a href="mailto:ian.d.romanick@intel.com">ian.d.romanick@intel.com</a>><br>
<br>
If there is a way to do this cleanly in mako, I'm very interested to<br>
hear about it.<br>
<br>
   text    data     bss     dec     hex filename<br>
7529003  273096   28584 7830683  777c9b /tmp/i965_dri-64bit-before.so<br>
7528883  273096   28584 7830563  777c23 /tmp/i965_dri-64bit-after.so<br>
<br>
Signed-off-by: Ian Romanick <<a href="mailto:ian.d.romanick@intel.com">ian.d.romanick@intel.com</a>><br>
---<br>
 src/compiler/glsl/nir_intrinsic_map.py | 131 ++++++++++++++++++++++++++++++---<br>
 1 file changed, 119 insertions(+), 12 deletions(-)<br>
<br>
diff --git a/src/compiler/glsl/nir_intrinsic_map.py b/src/compiler/glsl/nir_intrinsic_map.py<br>
index 7f13c6c..5962d4b 100644<br>
--- a/src/compiler/glsl/nir_intrinsic_map.py<br>
+++ b/src/compiler/glsl/nir_intrinsic_map.py<br>
@@ -66,6 +66,123 @@ intrinsics = [("__intrinsic_atomic_read", ("nir_intrinsic_atomic_counter_read_va<br>
               ("__intrinsic_atomic_exchange_shared", ("nir_intrinsic_shared_atomic_exchange", None)),<br>
               ("__intrinsic_atomic_comp_swap_shared", ("nir_intrinsic_shared_atomic_comp_swap", None))]<br>
<br>
+def remove_prefix(table, prefix_length):<br>
+    """Strip prefix_length characters off the name of each entry in table."""<br>
+<br>
+    return [(s[prefix_length:], d) for (s, d) in table]<br>
+<br>
+<br>
+def generate_trie(table):<br>
+    """table is a list of (string, data) tuples.  It is assumed to be sorted by<br>
+    string.<br>
+<br>
+    A radix trie (or compact prefix trie) is recursively generated from the<br>
+    list of names.  Names are paritioned into groups that have at least<br>
+    prefix_thresh (tunable parameter) common prefix characters.  Each of these<br>
+    groups becomes the branches at the current level of the tree.  The<br>
+    matching prefix characters from each group is removed, and the group is<br>
+    recursively operated on in the same fashion.<br>
+<br>
+    The recursion terminates when no groups can be formed with at least<br>
+    prefix_thresh matching characters.<br>
+<br>
+    Each node in the trie is a 3-element tuple:<br>
+<br>
+        (prefix_string, [child_nodes], client_data)<br>
+<br>
+    One of [child_nodes] or client_data will be None.<br>
+<br>
+    See <a href="https://en.wikipedia.org/wiki/Radix_tree" rel="noreferrer" target="_blank">https://en.wikipedia.org/wiki/Radix_tree</a> for more background details<br>
+    on the data structure.<br>
+<br>
+    """<br>
+<br>
+    # Threshold for considering two strings to have the same prefix.<br>
+    prefix_thresh = 1<br>
+<br>
+    if len(table) == 1 and table[0][0] == "":<br>
+        return [("", None, table[0][1])]<br>
+<br>
+    trie_level = []<br>
+<br>
+    (s, d) = table[0]<br>
+    candidates = [(s, d)]<br>
+    base = s<br>
+    prefix_length = len(s)<br>
+<br>
+    for (s, d) in table[1:]:<br>
+        if s[:prefix_thresh] == base[:prefix_thresh]:<br>
+            candidates.append((s, d))<br>
+<br>
+            l = len(s[:([x[0]==x[1] for x in zip(s, base)]+[0]).index(0)])<br>
+            if l < prefix_length:<br>
+                prefix_length = l<br>
+        else:<br>
+            trie_level.append((base[:prefix_length], generate_trie(remove_prefix(candidates, prefix_length)), None))<br>
+<br>
+            candidates = [(s, d)]<br>
+            base = s<br>
+            prefix_length = len(s)<br>
+<br>
+    trie_level.append((base[:prefix_length], generate_trie(remove_prefix(candidates, prefix_length)), None))<br>
+<br>
+    return trie_level<br>
+<br>
+<br>
+def emit_trie_leaf(indent, d):<br>
+    if d[1] is None:<br>
+        return "{}return {};\n".format(indent, d[0])<br>
+    else:<br>
+        c_code = "{}int_op = {};\n".format(indent, d[0])<br>
+        c_code += "{}uint_op = {};\n".format(indent, d[1])<br>
+        return c_code<br>
+<br>
+<br>
+def trie_as_C_code(trie, indent="   ", prefix_string="__intrinsic_"):<br>
+    conditional = "if"<br>
+<br>
+    c_code = ""<br>
+    for (s, t, d) in trie:<br>
+        if d is not None:<br>
+            c_code +=  "{}{} (name[0] == '\\0') {{\n".format(indent, conditional)<br>
+            c_code += "{}   /* {} */\n".format(indent, prefix_string)<br>
+            c_code += emit_trie_leaf(indent + "   ", d);<br>
+<br>
+        else:<br>
+            # Before emitting the string comparison, check to see of the<br>
+            # subtree has a single element with an empty string.  In that<br>
+            # case, use strcmp() instead of strncmp() and don't advance the<br>
+            # name pointer.<br>
+<br>
+            if len(t) == 1 and t[0][2] is not None:<br>
+                if s == "":<br>
+                    c_code += "{}{} (name[0] == '\\0') {{\n".format(indent, conditional, s)<br>
+                else:<br>
+                    c_code += "{}{} (strcmp(name, \"{}\") == 0) {{\n".format(indent, conditional, s)<br>
+<br>
+                c_code += "{}   /* {} */\n".format(indent, prefix_string + s)<br>
+                c_code += emit_trie_leaf(indent + "   ", t[0][2]);<br>
+            else:<br>
+                c_code += "{}{} (strncmp(name, \"{}\", {}) == 0) {{\n".format(indent, conditional, s, len(s))<br>
+                c_code += "{}   name += {};\n\n".format(indent, len(s))<br>
+<br>
+                c_code += trie_as_C_code(t, indent + "   ", prefix_string + s)<br>
+<br>
+        conditional = "} else if"<br>
+<br>
+    c_code += "{}}} else\n".format(indent)<br>
+    c_code += "{}   unreachable(\"Invalid intrinsic name\");\n\n".format(indent)<br>
+    return c_code<br>
+<br>
+<br>
+intrinsics.sort(key=lambda tup: tup[0])<br>
+<br>
+trimmed_name_intrinsics = []<br>
+for (s, d) in intrinsics:<br>
+    trimmed_name_intrinsics.append((s[12:], d))<br>
+<br>
+trie_search = trie_as_C_code(generate_trie(trimmed_name_intrinsics))<br>
+<br>
 template = """<br>
 namespace _glsl_to_nir {<br>
<br>
@@ -80,17 +197,7 @@ get_intrinsic_opcode(const char *name, const ir_dereference *return_deref)<br>
    nir_intrinsic_op int_op;<br>
    nir_intrinsic_op uint_op;<br>
<br>
-    % for (name, ops) in intrinsics:<br>
-   if (strcmp(name, "${name[12:]}") == 0) {<br>
-        % if ops[1] is None:<br>
-      return ${ops[0]};<br>
-        % else:<br>
-      int_op = ${ops[0]};<br>
-      uint_op = ${ops[1]};<br>
-        % endif<br>
-   } else<br>
-    % endfor<br>
-      unreachable("Unknown intrinsic name");<br>
+${trie_search}<br>
<br>
    assert(return_deref);<br>
    if (return_deref->type == glsl_type::int_type)<br>
@@ -103,4 +210,4 @@ get_intrinsic_opcode(const char *name, const ir_dereference *return_deref)<br>
 }<br>
 """<br>
<br>
-print(Template(template).render(intrinsics=intrinsics))<br>
+print(Template(template).render(trie_search=trie_search))<br>
--<br>
2.5.5<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev" rel="noreferrer" target="_blank">https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</blockquote></div>