<div dir="ltr">You're 4.5 hours too late, I'm afraid.  I'd be happy to take some patches though. :-)<br></div><div class="gmail_extra"><br><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?<br>
<span class=""><br>
> +<br>
> +        self.offset = None<br>
> +<br>
<br>
</span>This needs a __hash__ method,<br>
<br>
        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?<br>
<span class=""><br>
> +<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"><br>
> +<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>