[Mesa-dev] [PATCH] anv: Split dispatch tables into device and instance

Lionel Landwerlin lionel.g.landwerlin at intel.com
Sun Oct 14 13:56:06 UTC 2018


Nice!

Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin at intel.com>

On 13/10/2018 19:35, Jason Ekstrand wrote:
> There's no reason why we need generate trampoline functions for instance
> functions or carry N copies of the instance dispatch table around for
> every hardware generation.  Splitting the tables and being more
> conservative shaves about 34K off .text and about 4K off .data when
> built with clang.
>
> Before splitting dispatch tables:
>
>     text	   data	    bss	    dec	    hex	filename
> 3224305	 286216	   8960	3519481	 35b3f9	_install/lib64/libvulkan_intel.so
>
> After splitting dispatch tables:
>
>     text	   data	    bss	    dec	    hex	filename
> 3190325	 282232	   8960	3481517	 351fad	_install/lib64/libvulkan_intel.so
> ---
>   src/intel/vulkan/anv_device.c           |  61 ++++--
>   src/intel/vulkan/anv_entrypoints_gen.py | 242 +++++++++++++++++-------
>   src/intel/vulkan/anv_private.h          |  18 +-
>   3 files changed, 230 insertions(+), 91 deletions(-)
>
> diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c
> index 6a24d1086d8..a2551452eb1 100644
> --- a/src/intel/vulkan/anv_device.c
> +++ b/src/intel/vulkan/anv_device.c
> @@ -637,14 +637,28 @@ VkResult anv_CreateInstance(
>         /* Vulkan requires that entrypoints for extensions which have not been
>          * enabled must not be advertised.
>          */
> -      if (!anv_entrypoint_is_enabled(i, instance->app_info.api_version,
> -                                     &instance->enabled_extensions, NULL)) {
> +      if (!anv_instance_entrypoint_is_enabled(i, instance->app_info.api_version,
> +                                              &instance->enabled_extensions)) {
>            instance->dispatch.entrypoints[i] = NULL;
> -      } else if (anv_dispatch_table.entrypoints[i] != NULL) {
> -         instance->dispatch.entrypoints[i] = anv_dispatch_table.entrypoints[i];
>         } else {
>            instance->dispatch.entrypoints[i] =
> -            anv_tramp_dispatch_table.entrypoints[i];
> +            anv_instance_dispatch_table.entrypoints[i];
> +      }
> +   }
> +
> +   for (unsigned i = 0; i < ARRAY_SIZE(instance->device_dispatch.entrypoints); i++) {
> +      /* Vulkan requires that entrypoints for extensions which have not been
> +       * enabled must not be advertised.
> +       */
> +      if (!anv_device_entrypoint_is_enabled(i, instance->app_info.api_version,
> +                                            &instance->enabled_extensions, NULL)) {
> +         instance->device_dispatch.entrypoints[i] = NULL;
> +      } else if (anv_device_dispatch_table.entrypoints[i] != NULL) {
> +         instance->device_dispatch.entrypoints[i] =
> +            anv_device_dispatch_table.entrypoints[i];
> +      } else {
> +         instance->device_dispatch.entrypoints[i] =
> +            anv_tramp_device_dispatch_table.entrypoints[i];
>         }
>      }
>   
> @@ -1349,11 +1363,15 @@ PFN_vkVoidFunction anv_GetInstanceProcAddr(
>      if (instance == NULL)
>         return NULL;
>   
> -   int idx = anv_get_entrypoint_index(pName);
> -   if (idx < 0)
> -      return NULL;
> +   int idx = anv_get_instance_entrypoint_index(pName);
> +   if (idx >= 0)
> +      return instance->dispatch.entrypoints[idx];
> +
> +   idx = anv_get_device_entrypoint_index(pName);
> +   if (idx >= 0)
> +      return instance->device_dispatch.entrypoints[idx];
>   
> -   return instance->dispatch.entrypoints[idx];
> +   return NULL;
>   }
>   
>   /* With version 1+ of the loader interface the ICD should expose
> @@ -1381,7 +1399,7 @@ PFN_vkVoidFunction anv_GetDeviceProcAddr(
>      if (!device || !pName)
>         return NULL;
>   
> -   int idx = anv_get_entrypoint_index(pName);
> +   int idx = anv_get_device_entrypoint_index(pName);
>      if (idx < 0)
>         return NULL;
>   
> @@ -1531,25 +1549,25 @@ VkResult anv_EnumerateDeviceExtensionProperties(
>   static void
>   anv_device_init_dispatch(struct anv_device *device)
>   {
> -   const struct anv_dispatch_table *genX_table;
> +   const struct anv_device_dispatch_table *genX_table;
>      switch (device->info.gen) {
>      case 11:
> -      genX_table = &gen11_dispatch_table;
> +      genX_table = &gen11_device_dispatch_table;
>         break;
>      case 10:
> -      genX_table = &gen10_dispatch_table;
> +      genX_table = &gen10_device_dispatch_table;
>         break;
>      case 9:
> -      genX_table = &gen9_dispatch_table;
> +      genX_table = &gen9_device_dispatch_table;
>         break;
>      case 8:
> -      genX_table = &gen8_dispatch_table;
> +      genX_table = &gen8_device_dispatch_table;
>         break;
>      case 7:
>         if (device->info.is_haswell)
> -         genX_table = &gen75_dispatch_table;
> +         genX_table = &gen75_device_dispatch_table;
>         else
> -         genX_table = &gen7_dispatch_table;
> +         genX_table = &gen7_device_dispatch_table;
>         break;
>      default:
>         unreachable("unsupported gen\n");
> @@ -1559,14 +1577,15 @@ anv_device_init_dispatch(struct anv_device *device)
>         /* Vulkan requires that entrypoints for extensions which have not been
>          * enabled must not be advertised.
>          */
> -      if (!anv_entrypoint_is_enabled(i, device->instance->app_info.api_version,
> -                                     &device->instance->enabled_extensions,
> -                                     &device->enabled_extensions)) {
> +      if (!anv_device_entrypoint_is_enabled(i, device->instance->app_info.api_version,
> +                                            &device->instance->enabled_extensions,
> +                                            &device->enabled_extensions)) {
>            device->dispatch.entrypoints[i] = NULL;
>         } else if (genX_table->entrypoints[i]) {
>            device->dispatch.entrypoints[i] = genX_table->entrypoints[i];
>         } else {
> -         device->dispatch.entrypoints[i] = anv_dispatch_table.entrypoints[i];
> +         device->dispatch.entrypoints[i] =
> +            anv_device_dispatch_table.entrypoints[i];
>         }
>      }
>   }
> diff --git a/src/intel/vulkan/anv_entrypoints_gen.py b/src/intel/vulkan/anv_entrypoints_gen.py
> index e6cd9bbf0a9..beb658b8660 100644
> --- a/src/intel/vulkan/anv_entrypoints_gen.py
> +++ b/src/intel/vulkan/anv_entrypoints_gen.py
> @@ -50,11 +50,11 @@ LAYERS = [
>   TEMPLATE_H = Template("""\
>   /* This file generated from ${filename}, don't edit directly. */
>   
> -struct anv_dispatch_table {
> +struct anv_instance_dispatch_table {
>      union {
> -      void *entrypoints[${len(entrypoints)}];
> +      void *entrypoints[${len(instance_entrypoints)}];
>         struct {
> -      % for e in entrypoints:
> +      % for e in instance_entrypoints:
>           % if e.guard is not None:
>   #ifdef ${e.guard}
>             PFN_${e.name} ${e.name};
> @@ -69,12 +69,45 @@ struct anv_dispatch_table {
>      };
>   };
>   
> +struct anv_device_dispatch_table {
> +   union {
> +      void *entrypoints[${len(device_entrypoints)}];
> +      struct {
> +      % for e in device_entrypoints:
> +        % if e.guard is not None:
> +#ifdef ${e.guard}
> +          PFN_${e.name} ${e.name};
> +#else
> +          void *${e.name};
> +# endif
> +        % else:
> +          PFN_${e.name} ${e.name};
> +        % endif
> +      % endfor
> +      };
> +   };
> +};
> +
> +extern const struct anv_instance_dispatch_table anv_instance_dispatch_table;
>   %for layer in LAYERS:
> -extern const struct anv_dispatch_table ${layer}_dispatch_table;
> +extern const struct anv_device_dispatch_table ${layer}_device_dispatch_table;
>   %endfor
> -extern const struct anv_dispatch_table anv_tramp_dispatch_table;
> +extern const struct anv_device_dispatch_table anv_tramp_device_dispatch_table;
>   
> -% for e in entrypoints:
> +% for e in instance_entrypoints:
> +  % if e.alias:
> +    <% continue %>
> +  % endif
> +  % if e.guard is not None:
> +#ifdef ${e.guard}
> +  % endif
> +  ${e.return_type} ${e.prefixed_name('anv')}(${e.decl_params()});
> +  % if e.guard is not None:
> +#endif // ${e.guard}
> +  % endif
> +% endfor
> +
> +% for e in device_entrypoints:
>     % if e.alias:
>       <% continue %>
>     % endif
> @@ -129,13 +162,14 @@ struct string_map_entry {
>    * store the index into this big string.
>    */
>   
> -static const char strings[] =
> +<%def name="strmap(strmap, prefix)">
> +static const char ${prefix}_strings[] =
>   % for s in strmap.sorted_strings:
>       "${s.string}\\0"
>   % endfor
>   ;
>   
> -static const struct string_map_entry string_map_entries[] = {
> +static const struct string_map_entry ${prefix}_string_map_entries[] = {
>   % for s in strmap.sorted_strings:
>       { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */
>   % endfor
> @@ -150,14 +184,14 @@ static const struct string_map_entry string_map_entries[] = {
>    */
>   
>   #define none 0xffff
> -static const uint16_t string_map[${strmap.hash_size}] = {
> +static const uint16_t ${prefix}_string_map[${strmap.hash_size}] = {
>   % for e in strmap.mapping:
>       ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' },
>   % endfor
>   };
>   
>   static int
> -string_map_lookup(const char *str)
> +${prefix}_string_map_lookup(const char *str)
>   {
>       static const uint32_t prime_factor = ${strmap.prime_factor};
>       static const uint32_t prime_step = ${strmap.prime_step};
> @@ -172,25 +206,54 @@ string_map_lookup(const char *str)
>   
>       h = hash;
>       while (1) {
> -        i = string_map[h & ${strmap.hash_mask}];
> +        i = ${prefix}_string_map[h & ${strmap.hash_mask}];
>           if (i == none)
>              return -1;
> -        e = &string_map_entries[i];
> -        if (e->hash == hash && strcmp(str, strings + e->name) == 0)
> +        e = &${prefix}_string_map_entries[i];
> +        if (e->hash == hash && strcmp(str, ${prefix}_strings + e->name) == 0)
>               return e->num;
>           h += prime_step;
>       }
>   
>       return -1;
>   }
> +</%def>
> +
> +${strmap(instance_strmap, 'instance')}
> +${strmap(device_strmap, 'device')}
>   
>   /* Weak aliases for all potential implementations. These will resolve to
>    * NULL if they're not defined, which lets the resolve_entrypoint() function
>    * either pick the correct entry point.
>    */
>   
> +% for e in instance_entrypoints:
> +  % if e.alias:
> +    <% continue %>
> +  % endif
> +  % if e.guard is not None:
> +#ifdef ${e.guard}
> +  % endif
> +  ${e.return_type} ${e.prefixed_name('anv')}(${e.decl_params()}) __attribute__ ((weak));
> +  % if e.guard is not None:
> +#endif // ${e.guard}
> +  % endif
> +% endfor
> +
> +const struct anv_instance_dispatch_table anv_instance_dispatch_table = {
> +% for e in instance_entrypoints:
> +  % if e.guard is not None:
> +#ifdef ${e.guard}
> +  % endif
> +  .${e.name} = ${e.prefixed_name('anv')},
> +  % if e.guard is not None:
> +#endif // ${e.guard}
> +  % endif
> +% endfor
> +};
> +
>   % for layer in LAYERS:
> -  % for e in entrypoints:
> +  % for e in device_entrypoints:
>       % if e.alias:
>         <% continue %>
>       % endif
> @@ -203,8 +266,8 @@ string_map_lookup(const char *str)
>       % endif
>     % endfor
>   
> -  const struct anv_dispatch_table ${layer}_dispatch_table = {
> -  % for e in entrypoints:
> +  const struct anv_device_dispatch_table ${layer}_device_dispatch_table = {
> +  % for e in device_entrypoints:
>       % if e.guard is not None:
>   #ifdef ${e.guard}
>       % endif
> @@ -219,8 +282,8 @@ string_map_lookup(const char *str)
>   
>   /** Trampoline entrypoints for all device functions */
>   
> -% for e in entrypoints:
> -  % if e.alias or not e.is_device_entrypoint():
> +% for e in device_entrypoints:
> +  % if e.alias:
>       <% continue %>
>     % endif
>     % if e.guard is not None:
> @@ -247,11 +310,8 @@ string_map_lookup(const char *str)
>     % endif
>   % endfor
>   
> -const struct anv_dispatch_table anv_tramp_dispatch_table = {
> -% for e in entrypoints:
> -  % if not e.is_device_entrypoint():
> -    <% continue %>
> -  % endif
> +const struct anv_device_dispatch_table anv_tramp_device_dispatch_table = {
> +% for e in device_entrypoints:
>     % if e.guard is not None:
>   #ifdef ${e.guard}
>     % endif
> @@ -269,26 +329,22 @@ const struct anv_dispatch_table anv_tramp_dispatch_table = {
>    * If device is NULL, all device extensions are considered enabled.
>    */
>   bool
> -anv_entrypoint_is_enabled(int index, uint32_t core_version,
> -                          const struct anv_instance_extension_table *instance,
> -                          const struct anv_device_extension_table *device)
> +anv_instance_entrypoint_is_enabled(int index, uint32_t core_version,
> +                                   const struct anv_instance_extension_table *instance)
>   {
>      switch (index) {
> -% for e in entrypoints:
> +% for e in instance_entrypoints:
>      case ${e.num}:
>         /* ${e.name} */
>      % if e.core_version:
> -      % if e.is_device_entrypoint():
> -         return ${e.core_version.c_vk_version()} <= core_version;
> -      % else:
> -         return !device && ${e.core_version.c_vk_version()} <= core_version;
> -      % endif
> +      return ${e.core_version.c_vk_version()} <= core_version;
>      % elif e.extensions:
>        % for ext in e.extensions:
> -       % if ext.type == 'instance':
> -      if (!device && instance->${ext.name[3:]}) return true;
> +        % if ext.type == 'instance':
> +      if (instance->${ext.name[3:]}) return true;
>           % else:
> -      if (!device || device->${ext.name[3:]}) return true;
> +      /* All device extensions are considered enabled at the instance level */
> +      return true;
>           % endif
>        % endfor
>         return false;
> @@ -301,32 +357,74 @@ anv_entrypoint_is_enabled(int index, uint32_t core_version,
>      }
>   }
>   
> -static void * __attribute__ ((noinline))
> -anv_resolve_entrypoint(const struct gen_device_info *devinfo, uint32_t index)
> +/** Return true if the core version or extension in which the given entrypoint
> + * is defined is enabled.
> + *
> + * If device is NULL, all device extensions are considered enabled.
> + */
> +bool
> +anv_device_entrypoint_is_enabled(int index, uint32_t core_version,
> +                                 const struct anv_instance_extension_table *instance,
> +                                 const struct anv_device_extension_table *device)
>   {
> -   if (devinfo == NULL) {
> -      return anv_dispatch_table.entrypoints[index];
> +   switch (index) {
> +% for e in device_entrypoints:
> +   case ${e.num}:
> +      /* ${e.name} */
> +   % if e.core_version:
> +      return ${e.core_version.c_vk_version()} <= core_version;
> +   % elif e.extensions:
> +     % for ext in e.extensions:
> +        % if ext.type == 'instance':
> +           <% assert False %>
> +        % else:
> +      if (!device || device->${ext.name[3:]}) return true;
> +        % endif
> +     % endfor
> +      return false;
> +   % else:
> +      return true;
> +   % endif
> +% endfor
> +   default:
> +      return false;
>      }
> +}
> +
> +int
> +anv_get_instance_entrypoint_index(const char *name)
> +{
> +   return instance_string_map_lookup(name);
> +}
> +
> +int
> +anv_get_device_entrypoint_index(const char *name)
> +{
> +   return device_string_map_lookup(name);
> +}
>   
> -   const struct anv_dispatch_table *genX_table;
> +static void * __attribute__ ((noinline))
> +anv_resolve_device_entrypoint(const struct gen_device_info *devinfo, uint32_t index)
> +{
> +   const struct anv_device_dispatch_table *genX_table;
>      switch (devinfo->gen) {
>      case 11:
> -      genX_table = &gen11_dispatch_table;
> +      genX_table = &gen11_device_dispatch_table;
>         break;
>      case 10:
> -      genX_table = &gen10_dispatch_table;
> +      genX_table = &gen10_device_dispatch_table;
>         break;
>      case 9:
> -      genX_table = &gen9_dispatch_table;
> +      genX_table = &gen9_device_dispatch_table;
>         break;
>      case 8:
> -      genX_table = &gen8_dispatch_table;
> +      genX_table = &gen8_device_dispatch_table;
>         break;
>      case 7:
>         if (devinfo->is_haswell)
> -         genX_table = &gen75_dispatch_table;
> +         genX_table = &gen75_device_dispatch_table;
>         else
> -         genX_table = &gen7_dispatch_table;
> +         genX_table = &gen7_device_dispatch_table;
>         break;
>      default:
>         unreachable("unsupported gen\\n");
> @@ -335,22 +433,21 @@ anv_resolve_entrypoint(const struct gen_device_info *devinfo, uint32_t index)
>      if (genX_table->entrypoints[index])
>         return genX_table->entrypoints[index];
>      else
> -      return anv_dispatch_table.entrypoints[index];
> -}
> -
> -int
> -anv_get_entrypoint_index(const char *name)
> -{
> -   return string_map_lookup(name);
> +      return anv_device_dispatch_table.entrypoints[index];
>   }
>   
>   void *
>   anv_lookup_entrypoint(const struct gen_device_info *devinfo, const char *name)
>   {
> -   int idx = anv_get_entrypoint_index(name);
> -   if (idx < 0)
> -      return NULL;
> -   return anv_resolve_entrypoint(devinfo, idx);
> +   int idx = anv_get_instance_entrypoint_index(name);
> +   if (idx >= 0)
> +      return anv_instance_dispatch_table.entrypoints[idx];
> +
> +   idx = anv_get_device_entrypoint_index(name);
> +   if (idx >= 0)
> +      return anv_resolve_device_entrypoint(devinfo, idx);
> +
> +   return NULL;
>   }""", output_encoding='utf-8')
>   
>   U32_MASK = 2**32 - 1
> @@ -554,23 +651,40 @@ def main():
>           EntrypointParam('VkImage', 'pImage', 'VkImage* pImage')
>       ]))
>   
> -    strmap = StringIntMap()
> -    for num, e in enumerate(entrypoints):
> -        strmap.add_string(e.name, num)
> +    device_entrypoints = []
> +    instance_entrypoints = []
> +    for e in entrypoints:
> +        if e.is_device_entrypoint():
> +            device_entrypoints.append(e)
> +        else:
> +            instance_entrypoints.append(e)
> +
> +    device_strmap = StringIntMap()
> +    for num, e in enumerate(device_entrypoints):
> +        device_strmap.add_string(e.name, num)
> +        e.num = num
> +    device_strmap.bake()
> +
> +    instance_strmap = StringIntMap()
> +    for num, e in enumerate(instance_entrypoints):
> +        instance_strmap.add_string(e.name, num)
>           e.num = num
> -    strmap.bake()
> +    instance_strmap.bake()
>   
>       # For outputting entrypoints.h we generate a anv_EntryPoint() prototype
>       # per entry point.
>       try:
>           with open(os.path.join(args.outdir, 'anv_entrypoints.h'), 'wb') as f:
> -            f.write(TEMPLATE_H.render(entrypoints=entrypoints,
> +            f.write(TEMPLATE_H.render(instance_entrypoints=instance_entrypoints,
> +                                      device_entrypoints=device_entrypoints,
>                                         LAYERS=LAYERS,
>                                         filename=os.path.basename(__file__)))
>           with open(os.path.join(args.outdir, 'anv_entrypoints.c'), 'wb') as f:
> -            f.write(TEMPLATE_C.render(entrypoints=entrypoints,
> +            f.write(TEMPLATE_C.render(instance_entrypoints=instance_entrypoints,
> +                                      device_entrypoints=device_entrypoints,
>                                         LAYERS=LAYERS,
> -                                      strmap=strmap,
> +                                      instance_strmap=instance_strmap,
> +                                      device_strmap=device_strmap,
>                                         filename=os.path.basename(__file__)))
>       except Exception:
>           # In the even there's an error this imports some helpers from mako
> diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
> index 5b4c286bf38..599b903f25c 100644
> --- a/src/intel/vulkan/anv_private.h
> +++ b/src/intel/vulkan/anv_private.h
> @@ -903,7 +903,8 @@ struct anv_instance {
>       struct anv_app_info                         app_info;
>   
>       struct anv_instance_extension_table         enabled_extensions;
> -    struct anv_dispatch_table                   dispatch;
> +    struct anv_instance_dispatch_table          dispatch;
> +    struct anv_device_dispatch_table            device_dispatch;
>   
>       int                                         physicalDeviceCount;
>       struct anv_physical_device                  physicalDevice;
> @@ -986,7 +987,7 @@ struct anv_device {
>       bool                                        can_chain_batches;
>       bool                                        robust_buffer_access;
>       struct anv_device_extension_table           enabled_extensions;
> -    struct anv_dispatch_table                   dispatch;
> +    struct anv_device_dispatch_table            dispatch;
>   
>       pthread_mutex_t                             vma_mutex;
>       struct util_vma_heap                        vma_lo;
> @@ -3242,12 +3243,17 @@ struct anv_query_pool {
>      struct anv_bo                                bo;
>   };
>   
> -int anv_get_entrypoint_index(const char *name);
> +int anv_get_instance_entrypoint_index(const char *name);
> +int anv_get_device_entrypoint_index(const char *name);
>   
>   bool
> -anv_entrypoint_is_enabled(int index, uint32_t core_version,
> -                          const struct anv_instance_extension_table *instance,
> -                          const struct anv_device_extension_table *device);
> +anv_instance_entrypoint_is_enabled(int index, uint32_t core_version,
> +                                   const struct anv_instance_extension_table *instance);
> +
> +bool
> +anv_device_entrypoint_is_enabled(int index, uint32_t core_version,
> +                                 const struct anv_instance_extension_table *instance,
> +                                 const struct anv_device_extension_table *device);
>   
>   void *anv_lookup_entrypoint(const struct gen_device_info *devinfo,
>                               const char *name);




More information about the mesa-dev mailing list