[Mesa-dev] [v2 4/6] glsl: ir_deserializer class for the shader cache

Paul Berry stereotype441 at gmail.com
Tue Nov 5 12:16:31 PST 2013


On 1 November 2013 02:16, Tapani Pälli <tapani.palli at intel.com> wrote:

> ir_deserializer can create a gl_shader structure out of binary
> data from ir_serializer, will be used by OES_get_program_binary
> implementation.
>
> Signed-off-by: Tapani Pälli <tapani.palli at intel.com>
> ---
>  src/glsl/Makefile.sources          |    1 +
>  src/glsl/ir_cache_deserializer.cpp | 1341
> ++++++++++++++++++++++++++++++++++++
>  src/glsl/ir_cache_deserializer.h   |  301 ++++++++
>  3 files changed, 1643 insertions(+)
>  create mode 100644 src/glsl/ir_cache_deserializer.cpp
>  create mode 100644 src/glsl/ir_cache_deserializer.h
>

General comment: my attitude about error handling is different when
reviewing this patch than it was when reviewing the last patch, because in
the case of deserialization, we're dealing with data passed in by the
client, so I think it's far more important to be robust in the case of
malformed input data.  We probably don't need to worry about malicious
clients, since the client is in process so if they wanted to be malicious
they could just walk all over memory.  But it will make life a lot easier
for library clients (and for us when we're debugging) if we can avoid
having Mesa die in a fire when it receives a corrupted shader binary.

Having said that, I still don't think integer return values are the way to
go, for the following reasons:
- it's confusing for 0 to mean success.
- we're going to forget to check return values.
- it complicates the signatures of a lot of functions that should be able
to just return the thing that they read.

What I'd recommend doing instead is adding a "failed" boolean to
memory_map.  That way rather than having to check every single return value
for failure, we just have to check the "failed" boolean at appropriate
critical times (such as inside loops and at the end of functions like
ir_read_variable()), and return early in the event of failure.  An
additional advantage of this is that it makes it easy to add bounds
checking to memory_map--the read() and ffwd() functions can simply check if
the position goes out of bounds, and if so, set "failed" flag to 0 and read
zeros.


>
> diff --git a/src/glsl/Makefile.sources b/src/glsl/Makefile.sources
> index 0014f6f..81d5753 100644
> --- a/src/glsl/Makefile.sources
> +++ b/src/glsl/Makefile.sources
> @@ -31,6 +31,7 @@ LIBGLSL_FILES = \
>         $(GLSL_SRCDIR)/ir_basic_block.cpp \
>         $(GLSL_SRCDIR)/ir_builder.cpp \
>         $(GLSL_SRCDIR)/ir_cache_serializer.cpp \
> +       $(GLSL_SRCDIR)/ir_cache_deserializer.cpp \
>         $(GLSL_SRCDIR)/ir_clone.cpp \
>         $(GLSL_SRCDIR)/ir_constant_expression.cpp \
>         $(GLSL_SRCDIR)/ir.cpp \
> diff --git a/src/glsl/ir_cache_deserializer.cpp
> b/src/glsl/ir_cache_deserializer.cpp
> new file mode 100644
> index 0000000..a671df8
> --- /dev/null
> +++ b/src/glsl/ir_cache_deserializer.cpp
> @@ -0,0 +1,1341 @@
> +/* -*- c++ -*- */
> +/*
> + * Copyright © 2013 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> "Software"),
> + * to deal in the Software without restriction, including without
> limitation
> + * the rights to use, copy, modify, merge, publish, distribute,
> sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> next
> + * paragraph) shall be included in all copies or substantial portions of
> the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT
> SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include "ir_cache_deserializer.h"
> +
> +/**
> + * Searches for ir_variable from given exec_list
> + */
> +static ir_variable *
> +search_var(struct exec_list *list, const char *name)
> +{
> +   foreach_list_safe(node, list) {
> +      ir_variable *var = ((ir_instruction *) node)->as_variable();
> +      if (var && strcmp(name, var->name) == 0)
> +         return var;
> +   }
> +   return NULL;
> +}
> +
> +/**
> + * Searches for ir_function with matching signature from exec_list
> + */
> +static ir_function *
> +search_func(struct _mesa_glsl_parse_state *state, struct exec_list *list,
> +   const char *name, struct exec_list *parameters)
> +{
> +   foreach_list_safe(node, list) {
> +      ir_function *func = ((ir_instruction *) node)->as_function();
> +      if (func && strcmp(name, func->name) == 0 &&
> +         func->matching_signature(state, parameters))
> +         return func;
> +   }
> +   return NULL;
> +}
> +
> +
> +/**
> + * Reads header part of the binary blob. Main purpose of this header is to
> + * validate that cached shader was produced with same Mesa driver version.
> + */
> +int
> +ir_deserializer::read_header(struct gl_shader *shader, memory_map &map,
> +   const char *mesa_sha)
>

Rather than pass the memory_map to all of the ir_deserializer functions,
I'd prefer to see it just be a member of the ir_deserializer class.  It's a
closer parallel to what you do with memory_writer in ir_serializer, and it
makes the code easier to read by making all of the function signatures and
invocations smaller.


> +{
> +   char *cache_mesa_sha = map.read_string();
> +   char *driver_vendor = map.read_string();
> +   char *driver_renderer = map.read_string();
> +
> +   /* only used or debug output, silence compiler warning */
> +   (void) driver_vendor;
> +   (void) driver_renderer;
> +
> +   map.read(&shader->Version);
> +   map.read(&shader->Type);
> +   map.read(&shader->IsES);
>

As with the memory_writer::write*() functions, I think the
memory_map::read() functions should include the type in their name, and
should use return by value rather than a pointer argument.  E.g.:

int32_t read_int32();
bool read_bool();

and so on.

(Note: I think it's still ok to have a read() function that operates on a
pointer for reading larger structs).

>
> +
> +   CACHE_DEBUG("%s: version %d, type 0x%x, %s (mesa %s)\n[%s %s]\n",
> +               __func__,  shader->Version, shader->Type,
> +               (shader->IsES) ? "glsl es" : "desktop glsl",
> +               cache_mesa_sha, driver_vendor, driver_renderer);
> +
> +   int error = memcmp(cache_mesa_sha, mesa_sha, strlen(mesa_sha));
> +   if (error)
> +      return error;
> +
> +   /* post-link data */
> +   map.read(&shader->num_samplers);
> +   map.read(&shader->active_samplers);
> +   map.read(&shader->shadow_samplers);
> +   map.read(&shader->num_uniform_components);
> +   map.read(&shader->num_combined_uniform_components);
> +
> +   for (unsigned i = 0; i < MAX_SAMPLERS; i++)
> +      map.read(&shader->SamplerUnits[i]);
> +
> +   for (unsigned i = 0; i < MAX_SAMPLERS; i++)
> +      map.read(&shader->SamplerTargets[i]);
> +
> +   return 0;
> +}
> +
> +
> +const glsl_type *
> +ir_deserializer::read_glsl_type(memory_map &map,
> +   struct _mesa_glsl_parse_state *_state)
>

Although technically the C standard allows locals whose name begin with an
underscore (provided that what follows is a lower case letter), it seems
strange--names beginning with underscores typically refer to globals that
belong to libraries.

Also, it looks like all the callers of read_glsl_type pass this->state for
this parameter, so I don't understand why the parameter is needed at all.


> +{
> +   uint32_t type_size;
> +
> +   char *name = map.read_string();
> +   map.read(&type_size);
> +
> +   const glsl_type *type_exists = NULL;
>

"type_exists" sounds like the name of a boolean.  Can we rename this to
"existing_type"?


> +
> +   if (_state && _state->symbols)
> +      type_exists = _state->symbols->get_type(name);
>

I don't see any circumstances where _state or _state->symbols would be
NULL.  Am I missing something?


> +
> +   /* if type exists, move read pointer forward and return type */
> +   if (type_exists) {
> +      map.ffwd(type_size);
> +      return type_exists;
> +   }
>

A single shader is allowed to have multiple distinct interface types with
the same name (e.g. an input and an output with the block name, but
different interface members), so this won't work.  It will cause the
distinct interface types to alias to each other.


> +
> +   uint32_t base_type;
> +   uint32_t length;
> +   uint8_t vector_elms;
> +   uint8_t matrix_cols;
> +   uint8_t sampler_dimensionality;
> +   uint8_t sampler_shadow;
> +   uint8_t sampler_array;
> +   uint8_t sampler_type;
> +   uint8_t interface_packing;
> +
> +   map.read(&base_type);
> +   map.read(&length);
> +   map.read(&vector_elms);
> +   map.read(&matrix_cols);
> +   map.read(&sampler_dimensionality);
> +   map.read(&sampler_shadow);
> +   map.read(&sampler_array);
> +   map.read(&sampler_type);
> +   map.read(&interface_packing);
> +
> +   if (base_type == GLSL_TYPE_SAMPLER) {
> +
> +      switch(sampler_dimensionality) {
> +      case 0:
> +         return glsl_type::sampler1D_type;
> +      case 1:
> +         return glsl_type::sampler2D_type;
> +      case 2:
> +         return glsl_type::sampler3D_type;
> +      case 3:
> +         return glsl_type::samplerCube_type;
> +      default:
> +         CACHE_DEBUG("%s: unknown sampler type (dim %d)\n",
> +                     __func__, sampler_dimensionality);
>
+      }
> +   }
>

I would have thought that the logic above for looking up existing types
would take care of samplers, since they are built-in types (and hence
populated in the symbol table before deserialization).  Am I missing
something here?

If we do need to keep this switch statement, I recommend adding some error
handling in the default case to make sure the "failed" flag is set and then
return glsl_type::error_type.  That will reduce the risk of Mesa crashing
in the event of a corrupted shader binary.


> +
> +   /* array type has additional element_type information */
> +   else if (base_type == GLSL_TYPE_ARRAY) {
> +      const glsl_type *element_type = read_glsl_type(map, state);
> +      if (!element_type) {
> +         CACHE_DEBUG("error reading array element type\n");
> +         return NULL;
> +      }
> +      return glsl_type::get_array_instance(element_type, length);
> +   }
> +
> +   /* structures have fields containing of names and types */
> +   else if (base_type == GLSL_TYPE_STRUCT ||
> +      base_type == GLSL_TYPE_INTERFACE) {
> +      glsl_struct_field *fields = ralloc_array(mem_ctx,
> +         glsl_struct_field, length);
>

We should check for a memory allocation failure here and do appropriate
error handling; that way a corrupted binary that has an outrageously large
length stored in it won't lead to a crash.


> +      for (unsigned k = 0; k < length; k++) {
> +         uint8_t row_major, interpolation, centroid;
> +         int32_t location;
> +         char *field_name = map.read_string();
> +         fields[k].name = _mesa_strdup(field_name);
> +         fields[k].type = read_glsl_type(map, state);
> +         map.read(&row_major);
> +         map.read(&location);
> +         map.read(&interpolation);
> +         map.read(&centroid);
> +         fields[k].row_major = row_major;
> +         fields[k].location = location;
> +         fields[k].interpolation = interpolation;
> +         fields[k].centroid = centroid;
> +      }
> +
> +      const glsl_type *ret_type = NULL;
> +
> +      if (base_type == GLSL_TYPE_STRUCT)
> +         ret_type = glsl_type::get_record_instance(fields, length, name);
> +      else if (base_type == GLSL_TYPE_INTERFACE)
> +         ret_type = glsl_type::get_interface_instance(fields,
> +            length, (glsl_interface_packing) interface_packing, name);
> +
> +      /* free allocated memory */
> +      for (unsigned k = 0; k < length; k++)
> +         free((void *)fields[k].name);
> +      ralloc_free(fields);
> +
> +      return ret_type;
> +   }
> +
> +   return glsl_type::get_instance(base_type, vector_elms, matrix_cols);
>

Again, I would have thought this case is taken care of by the logic above
for looking up existing types.


> +}
> +
> +
> +int
> +ir_deserializer::read_ir_variable(struct exec_list *list, memory_map &map)
>

General comment on code organization:  I'd prefer to see deserialization
functions like these written as constructors (i.e. this function would be
an ir_variable constructor, which takes an ir_deserializer as a single
parameter).  The advantages of this approach are (a) the deserialization
code is closer to the other ir_variable-related code, so there's less
danger of us forgetting to update it when we change the structure of
ir_variable, and (b) if we change ir_variable and forget to update the
deserialization code, static analysis tools like coverity will alert us to
the bug.


> +{
> +   const glsl_type *type = read_glsl_type(map, state);
> +
> +   char *name = map.read_string();
> +   char *unique_name = map.read_string();
> +
> +   uint8_t mode;
> +   map.read(&mode);
> +
> +   ir_variable *var = new(mem_ctx)ir_variable(type,
> +      name, (ir_variable_mode) mode);
> +
> +   if (!var)
> +      return -1;
> +
> +   uint8_t read_only;
> +   uint8_t centroid;
> +   uint8_t invariant;
> +   uint8_t interpolation;
> +   uint8_t origin_upper_left;
> +   uint8_t pixel_center_integer;
> +   uint8_t explicit_location;
> +   uint8_t explicit_index;
> +   uint8_t explicit_binding;
> +   uint8_t has_initializer;
> +   int32_t depth_layout;
> +   uint8_t location_frac;
> +   uint8_t has_constant_value;
> +   uint8_t has_constant_initializer;
> +
> +   map.read(&var->max_array_access);
> +   map.read(&var->location);
> +   map.read(&read_only);
> +   map.read(&centroid);
> +   map.read(&invariant);
> +   map.read(&interpolation);
> +   map.read(&origin_upper_left);
> +   map.read(&pixel_center_integer);
> +   map.read(&explicit_location);
> +   map.read(&explicit_index);
> +   map.read(&explicit_binding);
> +   map.read(&has_initializer);
> +   map.read(&depth_layout);
> +   map.read(&location_frac);
> +   map.read(&var->num_state_slots);
> +   map.read(&has_constant_value);
> +   map.read(&has_constant_initializer);
> +
> +   var->read_only = read_only;
> +   var->centroid = centroid;
> +   var->invariant = invariant;
> +   var->interpolation = interpolation;
> +   var->origin_upper_left = origin_upper_left;
> +   var->pixel_center_integer = pixel_center_integer;
> +   var->depth_layout = (ir_depth_layout) depth_layout;
> +   var->has_initializer = has_initializer;
> +   var->location_frac = location_frac;
>

The code above is an extreme example of why the read() functions should be
non-overloaded and should return the value they read rather than operate on
a pointer.  That way instead of e.g.:

uint8_t read_only;
map.read(&read_only);
var->read_only = read_only;

we simply would do:

var->read_only = map.read_uint8();

Of course, my idea from the last patch of serializing a "bits" data
structure would make this example moot.


> +
> +
> +   var->state_slots = NULL;
> +
> +   if (var->num_state_slots > 0) {
> +      var->state_slots = ralloc_array(var, ir_state_slot,
> +         var->num_state_slots);
> +
> +      for (unsigned i = 0; i < var->num_state_slots; i++) {
> +         map.read(&var->state_slots[i].swizzle);
> +         for (int j = 0; j < 5; j++) {
> +            map.read(&var->state_slots[i].tokens[j]);
> +         }
> +      }
> +   }
> +
> +   if (has_constant_value)
> +      var->constant_value = read_ir_constant(map);
> +
> +   if (has_constant_initializer)
> +      var->constant_initializer = read_ir_constant(map);
> +
> +   uint8_t has_interface_type;
> +   map.read(&has_interface_type);
> +
> +   if (has_interface_type)
> +      var->init_interface_type(read_glsl_type(map, state));
> +
> +   /**
> +    * Store address to this variable so that variable
> +    * dereference readers can find it later.
> +    */
> +   hash_store(var, unique_name);
> +
> +   list->push_tail(var);
> +
> +   return 0;
> +}
>

Rather than having this function read the variable and add it to a list,
I'd prefer for it to read the variable and return a pointer to it.  Then
the caller can decide whether to add it to a list or do something else to
it.  The key advantage IMHO is more flexibility for the future.  For
example, we have talked about the idea of changing the storage of function
parameter ir_variables so that they use an array of ir_variable pointers
rather than an exec_list.  As the deserialization code is written right now
we have extra obstacles to making that future change.

A similar comment applies to most of the other read...() functions below.


> +
> +
> +int
> +ir_deserializer::read_ir_function(struct exec_list *list, memory_map &map)
> +{
> +   int32_t par_count = 0;
> +   int32_t body_count = 0;
> +   uint8_t is_builtin = 0;
> +   uint32_t ir_type;
> +   uint32_t len;
> +   uint32_t sig_amount;
>

As in the previous patch, I'd recommend renaming "sig_amount" to
"num_signatures" and "par_count" to "param_count".


> +
> +   char *name = map.read_string();
> +   map.read(&sig_amount);
> +
> +   ir_function *f = new(mem_ctx) ir_function(name);
> +   ir_function_signature *sig = NULL;
> +
> +   /* add all signatures to the function */
> +   for (unsigned j = 0; j < sig_amount; j++) {
> +
> +      /* ir_function_signature */
> +      map.read(&ir_type);
>
+      map.read(&len);
> +
> +      if (ir_type != ir_type_function_signature) {
> +         CACHE_DEBUG("cache format error with function %s\n", name);
> +         return -1;
> +      }
>

It seems silly to store ir_type in the instruction stream and then generate
an error if it doesn't match.  If we want to be robust in the face of a
corrupted shader binary, we should be checking for things that don't make
sense (like is_builtin being a number other than 0 or 1) rather than
storing redundant information like ir_type in the instruction stream and
then checking that it equals the only value that it could possibly be.


> +
> +      map.read(&par_count);
> +      map.read(&body_count);
> +      map.read(&is_builtin);
> +
> +      CACHE_DEBUG("%s: [%s] %d parameters, body size %d (is_builtin
> %d)\n",
> +                  __func__, name, par_count, body_count, is_builtin);
> +
> +      const glsl_type *return_type = read_glsl_type(map, state);
> +
> +      if (!return_type) {
> +         CACHE_DEBUG("no return type found for [%s]\n", name);
> +         return -1;
> +      }
> +
> +      sig = new(mem_ctx) ir_function_signature(return_type);
> +
> +      /* fill parameters for function signature */
> +      for (int k = 0; k < par_count; k++)
> +         if (read_instruction(&sig->parameters, map))
> +            goto read_errors;
> +
> +      /* insert function parameter variables to prototypes list ... */
> +      foreach_list_const(node, &sig->parameters) {
> +         ir_variable *var = ((ir_instruction *) node)->as_variable();
> +         if (var)
> +            prototypes->push_tail(var->clone(mem_ctx, NULL));
> +      }
>

Can you explain what's going on with the variables in the prototypes list?
I don't understand what purpose this serves.


> +
> +      current_function = &sig->body;
> +
> +      /* fill instructions for the function body */
> +      if (!prototypes_only)
> +         for (int k = 0; k < body_count; k++)
> +            if (read_instruction(&sig->body, map, is_builtin ? true :
> false))
> +               goto read_errors;
> +
> +      sig->is_defined = body_count ? 1 : 0;
> +
> +      if (!is_builtin) {
> +         f->add_signature(sig);
> +      } else {
> +         ir_function_signature *builtin_sig =
> +            _mesa_glsl_find_builtin_function(state, name,
> &sig->parameters);
> +
> +         if (builtin_sig) {
> +            CACHE_DEBUG("found builtin signature for [%s]\n", name);
> +            f->add_signature(sig);
> +         } else {
> +            CACHE_DEBUG("cannot find builtin, function [%s]\n", name);
> +            return -1;
> +         }
> +      }
> +
> +   } /* for each function signature */
> +
> +   CACHE_DEBUG("added %s function [%s]\n",
> +               is_builtin ? "builtin" : "user", name);
> +
> +   /* push ready function to the IR exec_list */
> +   list->push_tail(f);
> +
> +   return 0;
> +
> +read_errors:
> +   CACHE_DEBUG("%s: read errors with [%s]\n", __func__, name);
> +   if (sig)
> +      ralloc_free(sig);
>

Part of the point of ralloc is that we shouldn't have to do this kind of
clean up.  Instead, in the event of failure, we should just free the
toplevel memory context, and all the memory we allocated during the failed
deserialization attempt will be automatically reclaimed.

If we let ralloc take care of reclaiming the memory at the end of
deserialization, then we can replace a lot of these goto labels for error
conditions with early returns, which I think will make the code easier to
read.


> +   return -1;
> +
> +}
> +
> +
> +ir_dereference_array *
> +ir_deserializer::read_ir_dereference_array(memory_map &map)
> +{
> +   uint32_t ir_type;
> +   uint32_t len;
> +
> +   CACHE_DEBUG("%s\n", __func__);
> +
> +   map.read(&ir_type);
> +   map.read(&len);
> +
> +   ir_rvalue *array_rval = read_ir_rvalue(map);
> +   ir_rvalue *index_rval = read_ir_rvalue(map);
> +
> +   if (array_rval && index_rval)
> +      return new(mem_ctx) ir_dereference_array(array_rval, index_rval);
> +
> +   CACHE_DEBUG("%s: could not get rvalues", __func__);
> +   return NULL;
>

If we adopt the idea of the "failed" boolean that I talked about above,
then that gives us the opportunity to return a dummy ir_dereference_array
rather than NULL.  The advantage of doing that is that there's less risk of
the caller segfaulting before it figures out that we are in an error
condition.

Of course, if we adopt my suggestion of turning these reader functions into
IR constructors, that will happen automatically.


> +}
> +
> +
> +ir_dereference_record *
> +ir_deserializer::read_ir_dereference_record(memory_map &map)
> +{
> +   uint32_t ir_type;
> +   uint32_t len;
> +
> +   CACHE_DEBUG("%s\n", __func__);
> +
> +   map.read(&ir_type);
> +   map.read(&len);
> +   char *name = map.read_string();
> +
> +   ir_rvalue *rval = read_ir_rvalue(map);
> +
> +   if (rval)
> +      return new(mem_ctx) ir_dereference_record(rval, name);
> +
> +   CACHE_DEBUG("%s: could not get rvalue", __func__);
> +   return NULL;
> +}
> +
> +
> +/**
> + * Reads in a variable deref, seeks variable address
> + * from a map with it's unique_name
> + */
> +ir_dereference_variable *
> +ir_deserializer::read_ir_dereference_variable(memory_map &map)
> +{
> +   uint32_t ir_type;
> +   uint32_t len;
> +
> +   map.read(&ir_type);
> +   map.read(&len);
> +   char *unique_name = map.read_string();
> +
> +   const void *addr = hash_table_find(var_ht, (const void *) unique_name);
> +
> +   CACHE_DEBUG("found addr %p with name %s\n", addr, unique_name);
> +
> +   if (addr != 0) {
> +      ir_variable *var = (ir_variable*) addr;
> +      return new(mem_ctx) ir_dereference_variable(var);
> +   }
> +
> +   CACHE_DEBUG("%s: could not find [%s]\n", __func__, unique_name);
> +   return NULL;
> +}
> +
> +
> +ir_constant *
> +ir_deserializer::read_ir_constant(memory_map &map, struct exec_list *list)
> +{
> +   ir_constant *con = NULL;
> +   uint32_t ir_type;
> +   uint32_t len;
> +
> +   map.read(&ir_type);
> +   map.read(&len);
> +
> +   const glsl_type *constant_type = read_glsl_type(map, state);
> +
> +   /* data structure */
> +   ir_constant_data data;
> +   map.read(&data, sizeof(data));
> +
> +   con = new(mem_ctx) ir_constant(constant_type, &data);
> +
> +   /* constant with array of constants */
> +   if (constant_type->base_type == GLSL_TYPE_ARRAY) {
> +      con->array_elements = ralloc_array(mem_ctx, ir_constant *,
> +         constant_type->length);
> +
> +      for (unsigned i = 0; i < constant_type->length; i++)
> +         con->array_elements[i] = read_ir_constant(map);
> +
> +      goto read_return;
> +   }
> +
> +   else if (constant_type->base_type == GLSL_TYPE_STRUCT) {
> +      uint32_t components;
> +      map.read(&components);
> +      for (unsigned i = 0; i < components; i++)
> +         if (read_instruction(&con->components, map))
> +            goto read_errors;
> +   }
> +
> +read_return:
> +   if (list)
> +      list->push_tail(con);
>

This is a great example of why it should be the caller's responsibility to
add the deserialized IR to the appropriate list.  That way callers that
need to put the IR in a list can do so, and callers that don't need to put
it in a list won't.


> +
> +   return con;
> +
> +read_errors:
> +   ralloc_free(con);
> +   return NULL;
> +}
> +
> +
> +ir_swizzle *
> +ir_deserializer::read_ir_swizzle(memory_map &map)
> +{
> +   uint32_t swiz[4] = { 0 };
> +   uint32_t count;
> +   uint32_t ir_type;
> +   uint32_t len;
> +
> +   CACHE_DEBUG("%s\n", __func__);
> +
> +   map.read(&ir_type);
> +   map.read(&len);
> +
> +   /* num of components + swizzle mask, rvalue */
> +   map.read(&count);
> +   map.read(swiz, 4 * sizeof(uint32_t));
> +
> +   ir_rvalue *rval = read_ir_rvalue(map);
> +
> +   if (rval)
> +      return new(mem_ctx) ir_swizzle(rval, swiz, count);
> +
> +   CACHE_DEBUG("error, could not handle rvalue for swizzle\n");
> +   return NULL;
> +}
> +
> +
> +ir_texture *
> +ir_deserializer::read_ir_texture(memory_map &map)
> +{
> +   uint32_t ir_type;
> +   uint32_t len;
> +   int32_t op;
> +   uint8_t has_coordinate;
> +   uint8_t has_projector;
> +   uint8_t has_shadow_comp;
> +   uint8_t has_offset;
> +   const glsl_type *type = NULL;
> +   ir_dereference *sampler = NULL;
> +
> +   map.read(&ir_type);
> +   map.read(&len);
> +
> +   map.read(&op);
> +   map.read(&has_coordinate);
> +   map.read(&has_projector);
> +   map.read(&has_shadow_comp);
> +   map.read(&has_offset);
> +
> +   CACHE_DEBUG("%s: op %d coord %d proj %d shadow %d offset %d\n",
> __func__,
> +               op, has_coordinate, has_projector,
> +               has_shadow_comp, has_offset);
> +
> +   ir_texture *new_tex = new(mem_ctx) ir_texture((ir_texture_opcode)op);
> +
> +   if (!new_tex)
> +      goto errors;
> +
> +   type = read_glsl_type(map, state);
> +
> +   /* sampler type */
> +   map.read(&ir_type);
> +
> +   switch (ir_type) {
> +   case ir_type_dereference_variable:
> +      sampler = read_ir_dereference_variable(map);
> +      break;
> +   case ir_type_dereference_record:
> +      sampler = read_ir_dereference_record(map);
> +      break;
> +   case ir_type_dereference_array:
> +      sampler = read_ir_dereference_array(map);
> +      break;
> +   default:
> +      CACHE_DEBUG("%s: error, unhandled sampler type %d\n",
> +                  __func__, ir_type);
> +   }
> +
> +   if (!sampler)
> +      goto errors;
> +
> +   new_tex->set_sampler(sampler, type);
> +
> +   if (has_coordinate)
> +      new_tex->coordinate = read_ir_rvalue(map);
> +
> +   if (has_projector)
> +      new_tex->projector = read_ir_rvalue(map);
> +
> +   if (has_shadow_comp)
> +      new_tex->shadow_comparitor = read_ir_rvalue(map);
> +
> +   if (has_offset)
> +      new_tex->offset = read_ir_rvalue(map);
> +
> +   /* lod_info structure */
> +   uint8_t has_lod;
> +   uint8_t has_bias;
> +   uint8_t has_sample_index;
> +   uint8_t has_component;
> +   uint8_t has_dpdx;
> +   uint8_t has_dpdy;
> +
> +   map.read(&has_lod);
> +   map.read(&has_bias);
> +   map.read(&has_sample_index);
> +   map.read(&has_component);
> +   map.read(&has_dpdx);
> +   map.read(&has_dpdy);
> +
> +   memset(&new_tex->lod_info, 0, sizeof(ir_texture::lod_info));
> +
> +   if (has_lod)
> +      new_tex->lod_info.lod = read_ir_rvalue(map);
> +   if (has_bias)
> +      new_tex->lod_info.bias = read_ir_rvalue(map);
> +   if (has_sample_index)
> +      new_tex->lod_info.sample_index = read_ir_rvalue(map);
> +   if (has_component)
> +      new_tex->lod_info.component = read_ir_rvalue(map);
> +   if (has_dpdx)
> +      new_tex->lod_info.grad.dPdx = read_ir_rvalue(map);
> +   if (has_dpdy)
> +      new_tex->lod_info.grad.dPdy = read_ir_rvalue(map);
> +
> +   return new_tex;
> +
> +errors:
> +   CACHE_DEBUG("error, could not read ir_texture\n");
> +   return NULL;
> +}
> +
> +
> +ir_expression *
> +ir_deserializer::read_ir_expression(memory_map &map)
> +{
> +   uint32_t operation;
> +   ir_rvalue *ir_rvalue_table[4] = { NULL };
> +   int32_t operands;
> +   uint32_t ir_type;
> +   uint32_t len;
> +
> +   CACHE_DEBUG("%s\n", __func__);
> +
> +   map.read(&ir_type);
> +   map.read(&len);
> +
> +   /* glsl_type resulted from operation */
> +   const glsl_type *rval_type = read_glsl_type(map, state);
> +
> +   /* read operation type + all operands for creating ir_expression */
> +   map.read(&operation);
> +   map.read(&operands);
> +
> +   CACHE_DEBUG("%s : operation %d, operands %d\n",
> +               __func__, operation, operands);
> +
> +   for (int k = 0; k < operands; k++) {
> +      ir_rvalue *val = read_ir_rvalue(map);
> +
> +      if (!val)
> +         return NULL;
> +
> +      ir_rvalue_table[k] = val;
> +   }
> +
> +   return new(mem_ctx) ir_expression(operation,
> +      rval_type,
> +      ir_rvalue_table[0],
> +      ir_rvalue_table[1],
> +      ir_rvalue_table[2],
> +      ir_rvalue_table[3]);
> +}
> +
> +
> +ir_rvalue *
> +ir_deserializer::read_ir_rvalue(memory_map &map)
> +{
> +   uint32_t ir_type = ir_type_unset;
> +
> +   map.read(&ir_type);
> +
> +   CACHE_DEBUG("%s: ir_value %d\n", __func__, ir_type);
> +
> +   switch(ir_type) {
> +   case ir_type_constant:
> +      return read_ir_constant(map);
> +   case ir_type_dereference_variable:
> +      return read_ir_dereference_variable(map);
> +   case ir_type_dereference_record:
> +      return read_ir_dereference_record(map);
> +   case ir_type_dereference_array:
> +      return read_ir_dereference_array(map);
> +   case ir_type_expression:
> +      return read_ir_expression(map);
> +   case ir_type_swizzle:
> +      return read_ir_swizzle(map);
> +   case ir_type_texture:
> +      return read_ir_texture(map);
> +   default:
> +      CACHE_DEBUG("%s: error, unhandled type %d\n",
> +                  __func__, ir_type);
> +      break;
> +   }
> +   return NULL;
> +}
> +
> +
> +int
> +ir_deserializer::read_ir_assignment(struct exec_list *list, memory_map
> &map)
> +{
> +   uint32_t write_mask = 0;
> +   uint32_t lhs_type = 0;
> +
> +   ir_assignment *assign = NULL;
> +   ir_dereference *lhs_deref = NULL;
> +
> +   map.read(&write_mask);
> +   map.read(&lhs_type);
> +
> +   CACHE_DEBUG("%s: mask %d lhs_type %d\n", __func__, write_mask,
> lhs_type);
> +
> +   switch (lhs_type) {
> +   case ir_type_dereference_variable:
> +      lhs_deref = read_ir_dereference_variable(map);
> +      break;
> +   case ir_type_dereference_record:
> +      lhs_deref = read_ir_dereference_record(map);
> +      break;
> +   case ir_type_dereference_array:
> +      lhs_deref = read_ir_dereference_array(map);
> +      break;
> +   default:
> +      CACHE_DEBUG("%s: error, unhandled lhs_type %d\n",
> +                  __func__, lhs_type);
> +   }
> +
> +   if (!lhs_deref) {
> +      CACHE_DEBUG("could not find lhs variable, bailing out\n");
> +      return -1;
> +   }
> +
> +   /* rvalue for assignment */
> +   ir_rvalue *rval = read_ir_rvalue(map);
> +
> +   /* if we managed to parse rvalue, then we can construct assignment */
> +   if (rval) {
> +
> +      CACHE_DEBUG("%s: lhs type %d\n", __func__, lhs_type);
> +
> +      assign = new(mem_ctx) ir_assignment(lhs_deref, rval, NULL,
> write_mask);
> +      list->push_tail(assign);
> +      return 0;
> +   }
> +
> +   CACHE_DEBUG("error reading assignment rhs\n");
> +   return -1;
> +}
> +
> +
> +int
> +ir_deserializer::read_ir_if(struct exec_list *list, memory_map &map)
> +{
> +   uint32_t then_len, else_len;
> +
> +   CACHE_DEBUG("%s\n", __func__);
> +
> +   map.read(&then_len);
> +   map.read(&else_len);
> +
> +   ir_rvalue *cond = read_ir_rvalue(map);
> +
> +   if (!cond) {
> +      CACHE_DEBUG("%s: error reading condition\n", __func__);
> +      return -1;
> +   }
> +
> +   ir_if *irif = new(mem_ctx) ir_if(cond);
> +
> +   for (unsigned k = 0; k < then_len; k++)
> +      if (read_instruction(&irif->then_instructions, map))
> +         goto read_errors;
> +
> +   for (unsigned k = 0; k < else_len; k++)
> +      if (read_instruction(&irif->else_instructions, map))
> +         goto read_errors;
> +
> +   list->push_tail(irif);
> +   return 0;
> +
> +read_errors:
> +   CACHE_DEBUG("%s: read errors(then %d else %d)\n",
> +               __func__, then_len, else_len);
> +   ralloc_free(irif);
> +   return -1;
> +}
> +
> +
> +int
> +ir_deserializer::read_ir_return(struct exec_list *list, memory_map &map)
> +{
> +   uint8_t has_rvalue = 0;
> +   map.read(&has_rvalue);
> +
> +   CACHE_DEBUG("%s\n", __func__);
> +
> +   ir_rvalue *rval = NULL;
> +   if (has_rvalue)
> +      rval = read_ir_rvalue(map);
> +
> +   ir_return *ret = new(mem_ctx) ir_return(rval);
> +   list->push_tail(ret);
> +
> +   return 0;
> +}
> +
> +
> +/**
> + * Read a call to ir_function, finds the correct function
> + * signature from prototypes list and creates the call
> + */
> +int
> +ir_deserializer::read_ir_call(struct exec_list *list, memory_map &map)
> +{
> +   uint8_t has_return_deref = 0;
> +   uint8_t list_len = 0;
> +   uint8_t use_builtin = 0;
> +   struct exec_list parameters;
> +   ir_dereference_variable *return_deref = NULL;
> +
> +   char *name = map.read_string();
> +
> +   map.read(&has_return_deref);
> +
> +   if (has_return_deref)
> +      return_deref = read_ir_dereference_variable(map);
> +
> +   map.read(&list_len);
> +
> +   CACHE_DEBUG("call to function %s, %d parameters (ret deref %p)\n",
> +               name, list_len, return_deref);
> +
> +   /* read call parameters */
> +   for(unsigned k = 0; k < list_len; k++) {
> +
> +      ir_rvalue *rval = read_ir_rvalue(map);
> +      if (rval) {
> +         parameters.push_tail(rval);
> +      } else {
> +         CACHE_DEBUG("%s: error reading rvalue\n", __func__);
> +         return -1;
> +      }
> +   }
> +
> +   map.read(&use_builtin);
> +
> +   if (use_builtin) {
> +      ir_function_signature *builtin_sig =
> +         _mesa_glsl_find_builtin_function(state, name, &parameters);
> +
> +      if (builtin_sig) {
> +         CACHE_DEBUG("%s: found function %s from builtins\n", __func__,
> name);
> +
> +         ir_function_signature *callee = builtin_sig;
> +
> +         CACHE_DEBUG("function signature for builtin %s : %p\n", name,
> callee);
> +         if (!callee) {
> +            CACHE_DEBUG("sorry, cannot find signature for builtin ..\n");
> +            return -1;
> +         }
> +
> +         ir_call *call = new(mem_ctx) ir_call(callee, return_deref,
> +            &parameters);
> +
> +         call->use_builtin = true;
> +
> +         list->push_tail(call);
> +         return 0;
> +      }
> +   }
> +
> +   /* find the function from the prototypes */
> +   ir_function *func = search_func(state, prototypes, name, &parameters);
> +
> +   if (func) {
> +      CACHE_DEBUG("found function with name %s (has user sig %d)\n",
> +                  name, func->has_user_signature());
> +
> +      ir_function_signature *callee = func->matching_signature(state,
> +         &parameters);
> +
> +      /**
> +       * This is a workaround for a call to empty user defined function,
> that
> +       * happens with glb2.7 if dumping unlinked shaders, linking would
> fail
> +       * if we would create a call, empty functions get removed only afer
> linking
> +       * .. this may look a bit strange thing todo here but we ignore
> call here
> +       */
> +      if (!callee->is_defined)
> +         return 0;
> +
> +      ir_call *call = new(mem_ctx) ir_call(callee, return_deref,
> &parameters);
> +      list->push_tail(call);
> +      return 0;
> +   }
> +
> +   CACHE_DEBUG("%s:function %s not found for ir_call ...\n",
> +               __func__, name);
> +   return -1;
> +}
> +
> +
> +int
> +ir_deserializer::read_ir_discard(struct exec_list *list, memory_map &map)
> +{
> +   uint8_t has_condition;
> +   map.read(&has_condition);
> +   ir_rvalue *condition = NULL;
> +
> +   CACHE_DEBUG("%s\n", __func__);
> +
> +   if (has_condition)
> +      condition = read_ir_rvalue(map);
> +      if (!condition)
> +         return -1;
> +
> +   list->push_tail(new(mem_ctx) ir_discard(condition));
> +   return 0;
> +}
> +
> +
> +int
> +ir_deserializer::read_ir_loop(struct exec_list *list, memory_map &map)
> +{
> +   uint8_t has_counter, has_from, has_to, has_increment;
> +   uint32_t body_size;
> +   int32_t cmp;
> +   ir_loop *loop = NULL;
> +
> +   loop = new(mem_ctx) ir_loop;
> +
> +   map.read(&has_from);
> +   map.read(&has_to);
> +   map.read(&has_increment);
> +   map.read(&has_counter);
> +   map.read(&cmp);
> +   map.read(&body_size);
> +
> +   /* comparison operation for loop termination */
> +   loop->cmp = cmp;
> +
> +   /* ir_rvalues: from, to, increment + one ir_variable counter */
> +   if (has_from) {
> +      loop->from = read_ir_rvalue(map);
> +      if (!loop->from)
> +         return -1;
> +   }
> +
> +   if (has_to) {
> +      loop->to = read_ir_rvalue(map);
> +      if (!loop->to)
> +         return -1;
> +   }
> +
> +   if (has_increment) {
> +      loop->increment = read_ir_rvalue(map);
> +      if (!loop->increment)
> +         return -1;
> +   }
> +
> +  /* read ir_variable to prototypes list and search from there */
> +  if (has_counter) {
> +      char *counter_name = map.read_string();
> +      if (read_instruction(prototypes, map))
> +         return -1;
> +      loop->counter = search_var(prototypes, counter_name);
> +      if (!loop->counter)
> +         return -1;
> +  }
> +
> +   CACHE_DEBUG("%s: from %p to %p increment %p counter %p size %d\n",
> __func__,
> +               loop->from, loop->to, loop->increment, loop->counter,
> body_size);
> +
> +   for (unsigned k = 0; k < body_size; k++) {
> +      if (read_instruction(&loop->body_instructions, map))
> +         goto read_errors;
> +   }
> +
> +   list->push_tail(loop);
> +   return 0;
> +
> +read_errors:
> +   CACHE_DEBUG("%s: read errors\n", __func__);
> +   if (loop)
> +      ralloc_free(loop);
> +   return -1;
> +}
> +
> +
> +int
> +ir_deserializer::read_ir_loop_jump(struct exec_list *list, memory_map
> &map)
> +{
> +   uint32_t mode;
> +   map.read(&mode);
> +   list->push_tail(new(mem_ctx)
> ir_loop_jump((ir_loop_jump::jump_mode)mode));
> +   return 0;
> +}
> +
> +
> +int
> +ir_deserializer::read_emit_vertex(struct exec_list *list, memory_map &map)
> +{
> +   list->push_tail(new(mem_ctx) ir_emit_vertex);
> +   return 0;
> +}
> +
> +
> +int
> +ir_deserializer::read_end_primitive(struct exec_list *list, memory_map
> &map)
> +{
> +   list->push_tail(new(mem_ctx) ir_end_primitive);
> +   return 0;
> +}
> +
> +
> +int
> +ir_deserializer::read_instruction(struct exec_list *list, memory_map &map,
> +                              bool ignore)
> +{
> +   uint32_t ir_type = ir_type_unset;
> +   uint32_t inst_dumpsize = 0;
> +
> +   map.read(&ir_type);
> +   map.read(&inst_dumpsize);
> +
> +   /* reader wants to jump over this instruction */
> +   if (ignore) {
> +      map.ffwd(inst_dumpsize);
> +      return 0;
> +   }
> +
> +   switch(ir_type) {
> +   case ir_type_variable:
> +      return read_ir_variable(list, map);
> +   case ir_type_assignment:
> +      return read_ir_assignment(list, map);
> +   case ir_type_constant:
> +      return (read_ir_constant(map, list)) ? 0 : -1;
> +   case ir_type_function:
> +      return read_ir_function(list, map);
> +   case ir_type_if:
> +      return read_ir_if(list, map);
> +   case ir_type_return:
> +      return read_ir_return(list, map);
> +   case ir_type_call:
> +      return read_ir_call(list, map);
> +   case ir_type_discard:
> +      return read_ir_discard(list, map);
> +   case ir_type_loop:
> +      return read_ir_loop(list, map);
> +   case ir_type_loop_jump:
> +      return read_ir_loop_jump(list, map);
> +   case ir_type_emit_vertex:
> +      return read_emit_vertex(list, map);
> +   case ir_type_end_primitive:
> +      return read_end_primitive(list, map);
> +   default:
> +      CACHE_DEBUG("%s cannot read type %d, todo...\n",
> +                  __func__, ir_type);
> +   }
> +
> +   return -1;
> +}
> +
> +
> +/**
> + * Reads prototypes section of the dump that consists
> + * only of variables and functions
> + */
> +int
> +ir_deserializer::read_prototypes(memory_map &map)
> +{
> +   uint32_t total;
> +   uint32_t ir_type;
> +   uint32_t inst_dumpsize;
> +
> +   map.read(&total);
> +
> +   prototypes_only = true;
> +
> +   for (unsigned k = 0; k < total; k++) {
> +
> +      map.read(&ir_type);
> +      map.read(&inst_dumpsize);
> +
> +      switch (ir_type) {
> +      case ir_type_variable:
> +         if (read_ir_variable(prototypes, map))
> +            return -1;
> +         break;
> +      case ir_type_function:
> +         if (read_ir_function(prototypes, map))
> +            return -1;
> +         break;
> +      default:
> +         CACHE_DEBUG("%s: error in cache data (ir %d)\n",
> +                     __func__, ir_type);
> +         return -1;
> +      }
> +   }
> +
> +   prototypes_only = false;
> +
> +   CACHE_DEBUG("%s: done\n", __func__);
> +   return 0;
> +}
> +
> +
> +static uint8_t
> +read_bool(memory_map &map)
> +{
> +   uint8_t value = 0;
> +   map.read(&value);
> +   return value;
> +}
>

As in the previous patch, this should just be a function in memory_map.


> +
> +
> +static int
> +_read_state(struct _mesa_glsl_parse_state *state, memory_map &map)
> +{
> +   uint32_t language_version;
> +   map.read(&language_version);
> +
> +   /* if cache was produced with different glsl version */
> +   if (language_version != state->language_version)
> +      return -1;
> +
> +   /* would be cool to have these in a structure of it own */
> +   state->ARB_draw_buffers_enable = read_bool(map);
> +   state->ARB_draw_buffers_warn = read_bool(map);
> +   state->ARB_draw_instanced_enable = read_bool(map);
> +   state->ARB_draw_instanced_warn = read_bool(map);
> +   state->ARB_explicit_attrib_location_enable = read_bool(map);
> +   state->ARB_explicit_attrib_location_warn = read_bool(map);
> +   state->ARB_fragment_coord_conventions_enable = read_bool(map);
> +   state->ARB_fragment_coord_conventions_warn = read_bool(map);
> +   state->ARB_texture_rectangle_enable = read_bool(map);
> +   state->ARB_texture_rectangle_warn = read_bool(map);
> +   state->EXT_texture_array_enable = read_bool(map);
> +   state->EXT_texture_array_warn = read_bool(map);
> +   state->ARB_shader_texture_lod_enable = read_bool(map);
> +   state->ARB_shader_texture_lod_warn = read_bool(map);
> +   state->ARB_shader_stencil_export_enable = read_bool(map);
> +   state->ARB_shader_stencil_export_warn = read_bool(map);
> +   state->AMD_conservative_depth_enable = read_bool(map);
> +   state->AMD_conservative_depth_warn = read_bool(map);
> +   state->ARB_conservative_depth_enable = read_bool(map);
> +   state->ARB_conservative_depth_warn = read_bool(map);
> +   state->AMD_shader_stencil_export_enable = read_bool(map);
> +   state->AMD_shader_stencil_export_warn = read_bool(map);
> +   state->OES_texture_3D_enable = read_bool(map);
> +   state->OES_texture_3D_warn = read_bool(map);
> +   state->OES_EGL_image_external_enable = read_bool(map);
> +   state->OES_EGL_image_external_warn = read_bool(map);
> +   state->ARB_shader_bit_encoding_enable = read_bool(map);
> +   state->ARB_shader_bit_encoding_warn = read_bool(map);
> +   state->ARB_uniform_buffer_object_enable = read_bool(map);
> +   state->ARB_uniform_buffer_object_warn = read_bool(map);
> +   state->OES_standard_derivatives_enable = read_bool(map);
> +   state->OES_standard_derivatives_warn = read_bool(map);
> +   state->ARB_texture_cube_map_array_enable = read_bool(map);
> +   state->ARB_texture_cube_map_array_warn = read_bool(map);
> +   state->ARB_shading_language_packing_enable = read_bool(map);
> +   state->ARB_shading_language_packing_warn = read_bool(map);
> +   state->ARB_texture_multisample_enable = read_bool(map);
> +   state->ARB_texture_multisample_warn = read_bool(map);
> +   state->ARB_texture_query_lod_enable = read_bool(map);
> +   state->ARB_texture_query_lod_warn = read_bool(map);
> +   state->ARB_gpu_shader5_enable = read_bool(map);
> +   state->ARB_gpu_shader5_warn = read_bool(map);
> +   state->AMD_vertex_shader_layer_enable = read_bool(map);
> +   state->AMD_vertex_shader_layer_warn = read_bool(map);
> +   state->ARB_shading_language_420pack_enable = read_bool(map);
> +   state->ARB_shading_language_420pack_warn = read_bool(map);
> +   state->EXT_shader_integer_mix_enable = read_bool(map);
> +   state->EXT_shader_integer_mix_warn = read_bool(map);
> +
> +   return 0;
> +}
> +
> +
> +struct gl_shader *
> +ir_deserializer::deserialize(void *mem_ctx, memory_map &map, uint32_t
> shader_size,
> +   struct _mesa_glsl_parse_state *state, const char *mesa_sha,
> +   int *error_code)
> +{
> +   int error = 0;
> +   uint32_t is_es = 0;
> +
> +   *error_code = ir_deserializer::GENERAL_READ_ERROR;
> +
> +   struct _mesa_glsl_parse_state *_state = state;
> +
> +   uint32_t type = 0;
> +   map.read(&type);
> +
> +   GET_CURRENT_CONTEXT(ctx);
> +   struct gl_shader *shader = ctx->Driver.NewShader(NULL, 0, type);
> +
> +   if (!shader)
> +      return NULL;
> +
> +   shader->Source = NULL;
> +   shader->Label = NULL;
> +   shader->InfoLog = ralloc_strdup(mem_ctx, "");
> +   shader->ir = NULL;
> +
> +   if (read_header(shader, map, mesa_sha)) {
> +      *error_code = ir_deserializer::DIFFERENT_MESA_SHA;
> +      goto error_deserialize;
> +   }
> +
> +   is_es = shader->IsES ? 1 : 0;
> +
> +   /* check if cache produced using different language version */
> +   if (state) {
> +      if (state->language_version != shader->Version ||
> +         state->es_shader != is_es) {
> +         *error_code = ir_deserializer::DIFFERENT_LANG_VER;
> +         goto error_deserialize;
> +      }
> +   }
> +
> +   if (state) {
> +      if (_read_state(state, map))
> +         goto error_deserialize;
> +      _state = state;
> +   } else {
> +      /* no existing parse state, we need to create one */
> +      GET_CURRENT_CONTEXT(ctx);
> +      _state = new(mem_ctx) _mesa_glsl_parse_state(ctx,
> +         shader->Type, shader);
> +   }
> +   this->state = _state;
> +
> +   /* fill parse state from shader header information */
> +   switch (shader->Type) {
> +   case GL_VERTEX_SHADER:
> +      _state->target = vertex_shader;
> +      break;
> +   case GL_FRAGMENT_SHADER:
> +      _state->target = fragment_shader;
> +      break;
> +   case GL_GEOMETRY_SHADER_ARB:
> +      _state->target = geometry_shader;
> +      break;
> +   }
> +
> +   _state->num_builtins_to_link = 0;
> +
> +   _mesa_glsl_initialize_builtin_functions();
> +   _mesa_glsl_initialize_types(_state);
> +
> +   /**
> +    * parser state is used to find builtin functions and
> +    * existing types during reading
> +    */
> +   this->state = _state;
> +
> +   /* allocations during reading */
> +   this->mem_ctx = mem_ctx;
> +
> +   prototypes = new(mem_ctx) exec_list;
> +
> +   error = read_prototypes(map);
> +
> +   shader->ir = new(shader) exec_list;
> +   top_level = shader->ir;
> +
> +   /* top level exec_list read loop, constructs a new list */
> +   while(map.position() < shader_size && error == 0)
> +      error = read_instruction(shader->ir, map);
> +
> +   ralloc_free(prototypes);
> +
> +   if (error)
> +      goto error_deserialize;
> +
> +   *error_code = 0;
> +
> +   shader->CompileStatus = GL_TRUE;
> +
> +   /* allocates glsl_symbol_table internally */
> +   populate_symbol_table(shader);
> +
> +   memcpy(shader->builtins_to_link, _state->builtins_to_link,
> +      sizeof(shader->builtins_to_link[0]) * _state->num_builtins_to_link);
> +      shader->num_builtins_to_link = _state->num_builtins_to_link;
> +
> +   validate_ir_tree(shader->ir);
> +
> +   CACHE_DEBUG("shader from cache\n");
> +
> +   return shader;
> +
> +error_deserialize:
> +   if (shader->ir)
> +      ralloc_free(shader->ir);
> +   ralloc_free(shader);
> +   return NULL;
> +}
> diff --git a/src/glsl/ir_cache_deserializer.h
> b/src/glsl/ir_cache_deserializer.h
> new file mode 100644
> index 0000000..91d2c28
> --- /dev/null
> +++ b/src/glsl/ir_cache_deserializer.h
> @@ -0,0 +1,301 @@
> +/* -*- c++ -*- */
> +/*
> + * Copyright © 2013 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> "Software"),
> + * to deal in the Software without restriction, including without
> limitation
> + * the rights to use, copy, modify, merge, publish, distribute,
> sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> next
> + * paragraph) shall be included in all copies or substantial portions of
> the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT
> SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + */
> +
> +#pragma once
> +#ifndef IR_CACHE_DESERIALIZER_H
> +#define IR_CACHE_DESERIALIZER_H
> +
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +
> +#include "glsl_parser_extras.h"
> +#include "program/hash_table.h"
> +#include "main/imports.h"
> +#include "linker.h"
> +
> +#ifdef SHADER_CACHE_DEBUG
> +#define CACHE_DEBUG(fmt, ...) printf(fmt, ## __VA_ARGS__)
> +#else
> +#define CACHE_DEBUG(fmt, ...) do {} while (0)
> +#endif
> +
> +#ifdef __cplusplus
> +
> +class memory_map;
> +
> +/**
> + * Class to deserialize gl_shader from a binary data blob
> + *
> + * Deserialization is done with a help of memory_map class that takes care
> + * of actual data reading. Class reads the blob header and checks that
> this
> + * blob was created with the same Mesa version we are running on, if this
> is
> + * true it continues and creates a gl_shader structure and fills it with
> all
> + * IR instructions from the binary blob.
> + */
> +class ir_deserializer
> +{
> +public:
> +   ir_deserializer(bool prototypes = false) :
> +      prototypes_only(prototypes)
> +   {
> +      var_ht = hash_table_ctor(0, hash_table_string_hash,
> +         hash_table_string_compare);
> +   }
> +
> +   ~ir_deserializer()
> +   {
> +      hash_table_call_foreach(this->var_ht, delete_key, NULL);
> +      hash_table_dtor(this->var_ht);
> +   }
> +
> +   /* unserialize gl_shader from mapped memory */
> +   struct gl_shader *deserialize(void *mem_ctx, memory_map &map,
> +                             uint32_t shader_size,
> +                             struct _mesa_glsl_parse_state *state,
> +                             const char *mesa_sha,
> +                             int *error_code);
> +
> +   enum cache_error {
> +      GENERAL_READ_ERROR = -1, /* read failed, possible bug or data
> corrupt */
> +      DIFFERENT_MESA_SHA = -2, /* blob created with different mesa
> version  */
> +      DIFFERENT_LANG_VER = -3, /* blob created using different GLSL
> version */
> +   };
> +
> +   /**
> +    * this method is public so that gl_shader_program
> +    * unserialization can use it when reading the
> +    * uniform storage
> +    */
> +   const glsl_type *read_glsl_type(memory_map &map,
> +      struct _mesa_glsl_parse_state *state);
> +
> +private:
> +
> +   /* variables and methods required for unserialization */
> +   bool prototypes_only;
> +
> +   struct _mesa_glsl_parse_state *state;
> +   void *mem_ctx;
> +
> +   /* pointer to shader->ir list */
> +   struct exec_list *top_level;
> +
> +   /* pointer to list which contains prototypes of functions */
> +   struct exec_list *prototypes;
> +
> +   /* pointer to current functions body */
> +   struct exec_list *current_function;
> +
> +   int read_header(struct gl_shader *shader, memory_map &map,
> +               const char *mesa_sha);
> +
> +   int read_prototypes(memory_map &map);
> +
> +   int read_instruction(struct exec_list *list, memory_map &map,
> +                    bool ignore = false);
> +
> +   int read_ir_variable(struct exec_list *list, memory_map &map);
> +   int read_ir_assignment(struct exec_list *list, memory_map &map);
> +   int read_ir_function(struct exec_list *list, memory_map &map);
> +   int read_ir_if(struct exec_list *list, memory_map &map);
> +   int read_ir_return(struct exec_list *list, memory_map &map);
> +   int read_ir_call(struct exec_list *list, memory_map &map);
> +   int read_ir_discard(struct exec_list *list, memory_map &map);
> +   int read_ir_loop(struct exec_list *list, memory_map &map);
> +   int read_ir_loop_jump(struct exec_list *list, memory_map &map);
> +   int read_emit_vertex(struct exec_list *list, memory_map &map);
> +   int read_end_primitive(struct exec_list *list, memory_map &map);
> +
> +   /* rvalue readers */
> +   ir_rvalue *read_ir_rvalue(memory_map &map);
> +   ir_constant *read_ir_constant(memory_map &map,
> +      struct exec_list *list = NULL);
> +   ir_swizzle *read_ir_swizzle(memory_map &map);
> +   ir_texture *read_ir_texture(memory_map &map);
> +   ir_expression *read_ir_expression(memory_map &map);
> +   ir_dereference_array *read_ir_dereference_array(memory_map &map);
> +   ir_dereference_record *read_ir_dereference_record(memory_map &map);
> +   ir_dereference_variable *read_ir_dereference_variable(memory_map &map);
> +
> +   /**
> +    * var_ht is used to store created ir_variables with a unique_key for
> +    * each so that ir_dereference_variable creation can find the variable
> +    */
> +   struct hash_table *var_ht;
> +
> +   /**
> +    * these 2 functions are copy pasted from string_to_uint_map,
> +    * we need a hash here with a string key and pointer value
> +    */
> +   void hash_store(void * value, const char *key)
> +   {
> +      char *dup_key = _mesa_strdup(key);
> +      bool result = hash_table_replace(this->var_ht, value, dup_key);
> +      if (result)
> +         free(dup_key);
> +   }
> +
> +   static void delete_key(const void *key, void *data, void *closure)
> +   {
> +      (void) data;
> +      (void) closure;
> +      free((char *)key);
> +   }
> +
> +};
> +
> +
> +/**
> + * Helper class to read instructions
> + *
> + * Class can read either from user given memory or from a file. On Linux
> + * file reading wraps around the Posix functions for mapping a file into
> + * the process's address space. Other OS may need different
> implementation.
> + */
> +class memory_map
> +{
> +public:
> +   memory_map() :
> +      mode(memory_map::READ_MEM),
> +      fd(0),
> +      cache_size(0),
> +      cache_mmap(NULL),
> +      cache_mmap_p(NULL)
> +   {
> +      /* only used by read_string() */
> +      mem_ctx = ralloc_context(NULL);
> +   }
> +
> +   /* read from disk */
> +   int map(const char *path)
> +   {
> +      struct stat stat_info;
> +      if (stat(path, &stat_info) != 0)
> +         return -1;
> +
> +      mode = memory_map::READ_MAP;
> +      cache_size = stat_info.st_size;
> +
> +      fd = open(path, O_RDONLY);
> +      if (fd) {
> +         cache_mmap_p = cache_mmap = (char *)
> +            mmap(NULL, cache_size, PROT_READ, MAP_PRIVATE, fd, 0);
> +         return (cache_mmap == MAP_FAILED) ? -1 : 0;
> +      }
> +      return -1;
> +   }
> +
> +   /* read from memory */
> +   int map(const void *memory, size_t size)
> +   {
> +      cache_mmap_p = cache_mmap = (char *) memory;
> +      cache_size = size;
> +      return 0;
> +   }
> +
> +   ~memory_map() {
> +      if (cache_mmap && mode == READ_MAP) {
> +         munmap(cache_mmap, cache_size);
> +         close(fd);
> +      }
> +      ralloc_free(mem_ctx);
> +   }
> +
> +   /* move read pointer forward */
> +   inline void ffwd(int len)
> +   {
> +      cache_mmap_p += len;
> +   }
> +
> +   /* position of read pointer */
> +   inline uint32_t position()
> +   {
> +      return cache_mmap_p - cache_mmap;
> +   }
> +
> +   inline char *read_string()
> +   {
> +      char *str = ralloc_strdup(mem_ctx, cache_mmap_p);
> +      ffwd(strlen(str)+1);
> +      return str;
> +   }
> +
> +   /* read functions for different types */
> +#define _READ_TYPE(type) inline void read(type *val) {\
> +   *val = *(type *) cache_mmap_p;\
> +   ffwd(sizeof(type));\
> +}
> +   _READ_TYPE(int32_t)
> +   _READ_TYPE(uint32_t)
> +   _READ_TYPE(bool)
> +   _READ_TYPE(GLboolean)
> +   _READ_TYPE(gl_texture_index)
> +   _READ_TYPE(ir_expression_operation)
> +
> +   inline void read(void *dst, size_t size)
> +   {
> +      memcpy(dst, cache_mmap_p, size);
> +      ffwd(size);
> +   }
> +
> +   /* read and serialize a gl_shader */
> +   inline struct gl_shader *read_shader(void *mem_ctx,
> +                                    const char *mesa_sha)
> +   {
> +      uint32_t shader_size;
> +      read(&shader_size);
> +
> +      ir_deserializer s;
> +      memory_map map;
> +      int error;
> +
> +      map.map(cache_mmap_p, shader_size);
> +      struct gl_shader *sha = s.deserialize(mem_ctx,
> +         map, shader_size, NULL, mesa_sha, &error);
> +
> +      ffwd(shader_size);
> +      return sha;
> +   }
> +
> +private:
> +
> +   void *mem_ctx;
> +
> +   /* specifies if we are reading mapped memory or user passed mem */
> +   enum read_mode {
> +      READ_MEM = 0,
> +      READ_MAP
> +   };
> +
> +   int32_t mode;
> +   int32_t fd;
> +   int32_t cache_size;
> +   char *cache_mmap;
> +   char *cache_mmap_p;
> +};
> +#endif /* ifdef __cplusplus */
> +
> +#endif /* IR_CACHE_DESERIALIZER_H */
> --
> 1.8.1.4
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/mesa-dev/attachments/20131105/dc907e0c/attachment-0001.html>


More information about the mesa-dev mailing list