<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Wed, Mar 7, 2018 at 4:42 PM, Dylan Baker <span dir="ltr"><<a href="mailto:dylan@pnwbakers.com" target="_blank">dylan@pnwbakers.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Quoting Jason Ekstrand (2018-03-07 06:34:52)<br>
<div><div class="h5">> The original string map assumed that the mapping from strings to<br>
> entrypoints was a bijection.  This will not be true the moment we<br>
> add entrypoint aliasing.  This reworks things to be an arbitrary map<br>
> from strings to non-negative signed integers.  The old one also had a<br>
> potential bug if we ever had a hash collision because it didn't do the<br>
> strcmp inside the lookup loop.  While we're at it, we break things out<br>
> into a helpful class.<br>
><br>
> Reviewed-by: Lionel Landwerlin <<a href="mailto:lionel.g.landwerlin@intel.com">lionel.g.landwerlin@intel.com</a><wbr>><br>
> Reviewed-by: Samuel Iglesias Gonsálvez <<a href="mailto:siglesias@igalia.com">siglesias@igalia.com</a>><br>
> ---<br>
>  src/intel/vulkan/anv_<wbr>entrypoints_gen.py | 188 +++++++++++++++++-------------<wbr>--<br>
>  1 file changed, 103 insertions(+), 85 deletions(-)<br>
><br>
> diff --git a/src/intel/vulkan/anv_<wbr>entrypoints_gen.py b/src/intel/vulkan/anv_<wbr>entrypoints_gen.py<br>
> index 34ffedb..dc0f0e9 100644<br>
> --- a/src/intel/vulkan/anv_<wbr>entrypoints_gen.py<br>
> +++ b/src/intel/vulkan/anv_<wbr>entrypoints_gen.py<br>
> @@ -115,9 +115,10 @@ TEMPLATE_C = Template(u"""\<br>
><br>
>  #include "anv_private.h"<br>
><br>
> -struct anv_entrypoint {<br>
> +struct string_map_entry {<br>
>     uint32_t name;<br>
>     uint32_t hash;<br>
> +   uint32_t num;<br>
>  };<br>
><br>
>  /* We use a big string constant to avoid lots of reloctions from the entry<br>
> @@ -126,17 +127,60 @@ struct anv_entrypoint {<br>
>   */<br>
><br>
>  static const char strings[] =<br>
> -% for e in entrypoints:<br>
> -    "${<a href="http://e.name" rel="noreferrer" target="_blank">e.name</a>}\\0"<br>
> +% for s in strmap.sorted_strings:<br>
> +    "${s.string}\\0"<br>
>  % endfor<br>
>  ;<br>
><br>
> -static const struct anv_entrypoint entrypoints[] = {<br>
> -% for e in entrypoints:<br>
> -    [${e.num}] = { ${offsets[e.num]}, ${'{:0=#8x}'.format(e.get_c_<wbr>hash())} }, /* ${<a href="http://e.name" rel="noreferrer" target="_blank">e.name</a>} */<br>
> +static const struct string_map_entry string_map_entries[] = {<br>
> +% for s in strmap.sorted_strings:<br>
> +    { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */<br>
>  % endfor<br>
>  };<br>
><br>
> +/* Hash table stats:<br>
> + * size ${len(strmap.sorted_strings)} entries<br>
> + * collisions entries:<br>
> +% for i in xrange(10):<br>
> + *     ${i}${'+' if i == 9 else ' '}     ${strmap.collisions[i]}<br>
> +% endfor<br>
> + */<br>
> +<br>
> +#define none 0xffff<br>
> +static const uint16_t string_map[${strmap.hash_size}<wbr>] = {<br>
> +% for e in strmap.mapping:<br>
> +    ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' },<br>
> +% endfor<br>
> +};<br>
> +<br>
> +static int<br>
> +string_map_lookup(const char *str)<br>
> +{<br>
> +    static const uint32_t prime_factor = ${strmap.prime_factor};<br>
> +    static const uint32_t prime_step = ${strmap.prime_step};<br>
> +    const struct string_map_entry *e;<br>
> +    uint32_t hash, h;<br>
> +    uint16_t i;<br>
> +    const char *p;<br>
> +<br>
> +    hash = 0;<br>
> +    for (p = str; *p; p++)<br>
> +        hash = hash * prime_factor + *p;<br>
> +<br>
> +    h = hash;<br>
> +    while (1) {<br>
> +        i = string_map[h & ${strmap.hash_mask}];<br>
> +        if (i == none)<br>
> +           return -1;<br>
> +        e = &string_map_entries[i];<br>
> +        if (e->hash == hash && strcmp(str, strings + e->name) == 0)<br>
> +            return e->num;<br>
> +        h += prime_step;<br>
> +    }<br>
> +<br>
> +    return -1;<br>
> +}<br>
> +<br>
>  /* Weak aliases for all potential implementations. These will resolve to<br>
>   * NULL if they're not defined, which lets the resolve_entrypoint() function<br>
>   * either pick the correct entry point.<br>
> @@ -275,54 +319,10 @@ anv_resolve_entrypoint(const struct gen_device_info *devinfo, uint32_t index)<br>
>        return anv_dispatch_table.<wbr>entrypoints[index];<br>
>  }<br>
><br>
> -/* Hash table stats:<br>
> - * size ${hash_size} entries<br>
> - * collisions entries:<br>
> -% for i in xrange(10):<br>
> - *     ${i}${'+' if i == 9 else ''}     ${collisions[i]}<br>
> -% endfor<br>
> - */<br>
> -<br>
> -#define none ${'{:#x}'.format(none)}<br>
> -static const uint16_t map[] = {<br>
> -% for i in xrange(0, hash_size, 8):<br>
> -  % for j in xrange(i, i + 8):<br>
> -    ## This is 6 because the 0x is counted in the length<br>
> -    % if mapping[j] & 0xffff == 0xffff:<br>
> -      none,<br>
> -    % else:<br>
> -      ${'{:0=#6x}'.format(mapping[j] & 0xffff)},<br>
> -    % endif<br>
> -  % endfor<br>
> -% endfor<br>
> -};<br>
> -<br>
>  int<br>
>  anv_get_entrypoint_index(const char *name)<br>
>  {<br>
> -   static const uint32_t prime_factor = ${prime_factor};<br>
> -   static const uint32_t prime_step = ${prime_step};<br>
> -   const struct anv_entrypoint *e;<br>
> -   uint32_t hash, h, i;<br>
> -   const char *p;<br>
> -<br>
> -   hash = 0;<br>
> -   for (p = name; *p; p++)<br>
> -      hash = hash * prime_factor + *p;<br>
> -<br>
> -   h = hash;<br>
> -   do {<br>
> -      i = map[h & ${hash_mask}];<br>
> -      if (i == none)<br>
> -         return -1;<br>
> -      e = &entrypoints[i];<br>
> -      h += prime_step;<br>
> -   } while (e->hash != hash);<br>
> -<br>
> -   if (strcmp(name, strings + e->name) != 0)<br>
> -      return -1;<br>
> -<br>
> -   return i;<br>
> +   return string_map_lookup(name);<br>
>  }<br>
><br>
>  void *<br>
> @@ -334,7 +334,6 @@ anv_lookup_entrypoint(const struct gen_device_info *devinfo, const char *name)<br>
>     return anv_resolve_entrypoint(<wbr>devinfo, idx);<br>
>  }""", output_encoding='utf-8')<br>
><br>
> -NONE = 0xffff<br>
>  HASH_SIZE = 256<br>
>  U32_MASK = 2**32 - 1<br>
>  HASH_MASK = HASH_SIZE - 1<br>
> @@ -342,11 +341,54 @@ HASH_MASK = HASH_SIZE - 1<br>
>  PRIME_FACTOR = 5024183<br>
>  PRIME_STEP = 19<br>
><br>
> -<br>
> -def cal_hash(name):<br>
> -    """Calculate the same hash value that Mesa will calculate in C."""<br>
> -    return functools.reduce(<br>
> -        lambda h, c: (h * PRIME_FACTOR + ord(c)) & U32_MASK, name, 0)<br>
> +class StringIntMapEntry(object):<br>
> +    def __init__(self, string, num):<br>
> +        self.string = string<br>
> +        self.num = num<br>
> +<br>
> +        # Calculate the same hash value that we will calculate in C.<br>
> +        h = 0<br>
> +        for c in string:<br>
> +            h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK<br>
> +        self.hash = h<br>
<br>
</div></div>why no love for my use of reduce?<span class=""><br></span></blockquote><div><br></div><div>This seems easier to read to me.  We could always put the reduce back in, it's equivalent.  I just tend to prefer that if python and C are going to compute a value which must be identical between the two that the calculations look identical so it's easier to verify.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> +<br>
> +        self.offset = None<br>
> +<br>
<br>
</span>This needs a __hash__ method,<br></blockquote><div><br></div><div>Does it?  It's never used as the key in a python hash, just the value.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
        def __hash__(self):<br>
            return self.hash<br>
<br>
is probably sufficient<br>
<span class=""><br>
> +class StringIntMap(object):<br>
> +    def __init__(self):<br>
> +        self.baked = False<br>
> +        self.strings = dict()<br>
<br>
</span>can we add all of the instance attributes in the constructor please? just set<br>
them to None or something?<span class=""><br></span></blockquote><div><br></div><div>Why?  I have a feeling you're going to say something about ensuring you don't get value errors.  However, that was somewhat intentional as you're supposed to build up the string map, then call bake() and then write to the template.  If you use any of those before baking, it's an error and getting that exception is pretty much what we want.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> +<br>
> +    def add_string(self, string, num):<br>
> +        assert not self.baked<br>
> +        assert string not in self.strings<br>
> +        assert num >= 0 and num < 2**31<br>
> +        self.strings[string] = StringIntMapEntry(string, num)<br>
> +<br>
> +    def bake(self):<br>
> +        self.sorted_strings = \<br>
> +            sorted(self.strings.values(), key=lambda x: x.string)<br>
> +        offset = 0<br>
> +        for entry in self.sorted_strings:<br>
> +            entry.offset = offset<br>
> +            offset += len(entry.string) + 1<br>
<br>
</span>how about:<br>
           self.offset = sum(len(e.string) + 1 for e in self.sorted_strings)<br><div><div class="h5"></div></div></blockquote><div><br></div>We're not trying to compute a self.offset here, we're computing the offset of each of the entries.<br><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">
> +<br>
> +        # Save off some values that we'll need in C<br>
> +        self.hash_size = HASH_SIZE<br>
> +        self.hash_mask = HASH_SIZE - 1<br>
> +        self.prime_factor = PRIME_FACTOR<br>
> +        self.prime_step = PRIME_STEP<br>
> +<br>
> +        self.mapping = [-1] * self.hash_size<br>
> +        self.collisions = [0] * 10<br>
> +        for idx, s in enumerate(self.sorted_strings)<wbr>:<br>
> +            level = 0<br>
> +            h = s.hash<br>
> +            while self.mapping[h & self.hash_mask] >= 0:<br>
> +                h = h + PRIME_STEP<br>
> +                level = level + 1<br>
> +            self.collisions[min(level, 9)] += 1<br>
> +            self.mapping[h & self.hash_mask] = idx<br>
><br>
>  EntrypointParam = namedtuple('EntrypointParam', 'type name decl')<br>
><br>
> @@ -372,9 +414,6 @@ class Entrypoint(object):<br>
>      def call_params(self):<br>
>          return ', '.join(<a href="http://p.name" rel="noreferrer" target="_blank">p.name</a> for p in self.params)<br>
><br>
> -    def get_c_hash(self):<br>
> -        return cal_hash(<a href="http://self.name" rel="noreferrer" target="_blank">self.name</a>)<br>
> -<br>
>  def get_entrypoints(doc, entrypoints_to_defines, start_index):<br>
>      """Extract the entry points from the registry."""<br>
>      entrypoints = OrderedDict()<br>
> @@ -443,36 +482,15 @@ def get_entrypoints_defines(doc):<br>
><br>
>  def gen_code(entrypoints):<br>
>      """Generate the C code."""<br>
> -    i = 0<br>
> -    offsets = []<br>
> -    for e in entrypoints:<br>
> -        offsets.append(i)<br>
> -        i += len(<a href="http://e.name" rel="noreferrer" target="_blank">e.name</a>) + 1<br>
><br>
> -    mapping = [NONE] * HASH_SIZE<br>
> -    collisions = [0] * 10<br>
> +    strmap = StringIntMap()<br>
>      for e in entrypoints:<br>
> -        level = 0<br>
> -        h = e.get_c_hash()<br>
> -        while mapping[h & HASH_MASK] != NONE:<br>
> -            h = h + PRIME_STEP<br>
> -            level = level + 1<br>
> -        if level > 9:<br>
> -            collisions[9] += 1<br>
> -        else:<br>
> -            collisions[level] += 1<br>
> -        mapping[h & HASH_MASK] = e.num<br>
> +        strmap.add_string(<a href="http://e.name" rel="noreferrer" target="_blank">e.name</a>, e.num)<br>
> +    strmap.bake()<br>
><br>
>      return TEMPLATE_C.render(entrypoints=<wbr>entrypoints,<br>
>                               LAYERS=LAYERS,<br>
> -                             offsets=offsets,<br>
> -                             collisions=collisions,<br>
> -                             mapping=mapping,<br>
> -                             hash_mask=HASH_MASK,<br>
> -                             prime_step=PRIME_STEP,<br>
> -                             prime_factor=PRIME_FACTOR,<br>
> -                             none=NONE,<br>
> -                             hash_size=HASH_SIZE,<br>
> +                             strmap=strmap,<br>
>                               filename=os.path.basename(__<wbr>file__))<br>
><br>
><br>
> --<br>
> 2.5.0.400.gff86faf<br>
><br>
</div></div>> ______________________________<wbr>_________________<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/<wbr>mailman/listinfo/mesa-dev</a><br>
</blockquote></div><br></div></div>