[Mesa-dev] [PATCH v2] glsl: add ir_cache class for IR serialization (WIP)

Tapani Pälli tapani.palli at intel.com
Fri Sep 20 04:48:54 PDT 2013


Patch introduces ir_cache class that can serialize a gl_shader
to a given file and also unserialize it back from a memory map.
This can be used by the shader compiler to cache individual
shaders and skip lexing, parsing, type checking, AST->IR
generation and optimization rounds that happen during compilation.

v2: fix builtin function handling in read_ir_function(), now
generated IR is identical with original

Known issues / bugs / missing implementation:

- constant structures not supported
- interfaces not supported
- no support for geometry shader emit_vertex, emit_primitive
- write/read file i/o likely not portable to some architectures

TODO

- try to use tpl or other library for serialization
- think of ways to reorganize ir_* structure contents for
  faster data dumping/loading
- when writing, write to memory first, then data can be
  either dumped to disk or given to further usage (for
  shader cache extension)
- save also process name if possible, this would help when
  filing bugs and debugging

Signed-off-by: Tapani Pälli <tapani.palli at intel.com>
---
 src/glsl/Makefile.sources         |   2 +
 src/glsl/ir_cache.h               | 536 +++++++++++++++++++++
 src/glsl/ir_cache_serialize.cpp   | 603 +++++++++++++++++++++++
 src/glsl/ir_cache_unserialize.cpp | 973 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 2114 insertions(+)
 create mode 100644 src/glsl/ir_cache.h
 create mode 100644 src/glsl/ir_cache_serialize.cpp
 create mode 100644 src/glsl/ir_cache_unserialize.cpp

diff --git a/src/glsl/Makefile.sources b/src/glsl/Makefile.sources
index 2f7bfa1..1a3e72e 100644
--- a/src/glsl/Makefile.sources
+++ b/src/glsl/Makefile.sources
@@ -30,6 +30,8 @@ LIBGLSL_FILES = \
 	$(GLSL_SRCDIR)/hir_field_selection.cpp \
 	$(GLSL_SRCDIR)/ir_basic_block.cpp \
 	$(GLSL_SRCDIR)/ir_builder.cpp \
+	$(GLSL_SRCDIR)/ir_cache_serialize.cpp \
+	$(GLSL_SRCDIR)/ir_cache_unserialize.cpp \
 	$(GLSL_SRCDIR)/ir_clone.cpp \
 	$(GLSL_SRCDIR)/ir_constant_expression.cpp \
 	$(GLSL_SRCDIR)/ir.cpp \
diff --git a/src/glsl/ir_cache.h b/src/glsl/ir_cache.h
new file mode 100644
index 0000000..ad9c18a
--- /dev/null
+++ b/src/glsl/ir_cache.h
@@ -0,0 +1,536 @@
+/* -*- 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_H
+#define IR_CACHE_H
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ir.h"
+#include "main/imports.h"
+#include "glsl_parser_extras.h"
+#include "program/hash_table.h"
+
+extern "C" {
+#include "program/symbol_table.h"
+}
+
+//#define SHADER_CACHE_DEBUG
+#ifdef SHADER_CACHE_DEBUG
+#define CACHE_DEBUG(fmt, args...) printf(fmt, ## args)
+#else
+#define CACHE_DEBUG(fmt, args...) do {} while (0)
+#endif
+
+
+/**
+ * helpers for writing to file
+ */
+#define _WRITE_VAL(_a, _file) fwrite(&_a, sizeof(_a), 1, _file)
+
+#define _WRITE_STRING_DATA(_s, _file) {\
+   _WRITE_VAL(_s->len, _file);\
+   fwrite(_s->data, _s->len, 1, _file);\
+}
+
+#define WRITE_STRING(_s, _file) {\
+   int32_t _len = strlen(_s);\
+   _WRITE_VAL(_len, _file);\
+   fwrite(_s, _len * sizeof(char), 1, _file);\
+}
+
+
+/**
+ * helper functions for dumping package size information
+ */
+static long PROLOGUE(FILE *out)
+{
+   long value = 666;
+   long offset = ftell(out);
+   _WRITE_VAL(value, out);
+   return offset;
+}
+
+static void EPILOGUE(FILE *out, long start)
+{
+   long end = ftell(out);
+   fseek(out, start, SEEK_SET);
+   long size = end - start - sizeof(long);
+   _WRITE_VAL(size, out);
+   fseek(out, end, SEEK_SET);
+}
+
+
+/**
+ * structures used for instruction serialization/creation
+ */
+struct string_data
+{
+public:
+   string_data() : data(NULL) {}
+   string_data(const char *cstr) :
+      data(NULL)
+   {
+      if (cstr) {
+         len = strlen(cstr);
+         data = _mesa_strdup(cstr);
+      }
+   }
+
+   ~string_data() {
+      if (data) {
+         free(data);
+         data = NULL;
+      }
+   }
+
+   void set(const char *cstr)
+   {
+      if (data)
+         free(data);
+      data = _mesa_strdup(cstr);
+      len = strlen(cstr);
+   }
+
+   int32_t len;
+   char *data;
+
+};
+
+
+/**
+ * data required to serialize glsl_type
+ */
+struct glsl_type_data
+{
+public:
+   glsl_type_data() :
+      name(NULL),
+      element_type(NULL),
+      field_names(NULL),
+      field_types(NULL) {}
+
+   glsl_type_data(const glsl_type *t) :
+      base_type(t->base_type),
+      length(t->length),
+      vector_elms(t->vector_elements),
+      matrix_cols(t->matrix_columns),
+      sampler_dimensionality(t->sampler_dimensionality),
+      sampler_shadow(t->sampler_shadow),
+      sampler_array(t->sampler_array),
+      sampler_type(t->sampler_type),
+      interface_packing(t->interface_packing),
+      element_type(NULL),
+      field_names(NULL),
+      field_types(NULL)
+   {
+      name = new string_data(t->name);
+
+      /* for array, save element type information */
+      if (t->base_type == GLSL_TYPE_ARRAY)
+         element_type =
+            new glsl_type_data(t->element_type());
+
+      /* with structs, copy each struct field name + type */
+      else if (t->base_type == GLSL_TYPE_STRUCT) {
+
+         field_names = new string_data[t->length];
+         field_types = new glsl_type_data*[t->length];
+
+         glsl_struct_field *field = t->fields.structure;
+         glsl_type_data **field_t = field_types;
+         for (unsigned k = 0; k < t->length; k++, field++, field_t++) {
+            field_names[k].set(field->name);
+            *field_t = new glsl_type_data(field->type);
+         }
+      }
+
+      else if (t->base_type == GLSL_TYPE_SAMPLER) {
+         /* TODO - figure out if we need to dump more data than right now */
+      }
+
+   }
+
+   ~glsl_type_data() {
+      delete name;
+      delete element_type;
+      delete [] field_names;
+      if (field_types) {
+          struct glsl_type_data **data = field_types;
+          for (int k = 0; k < length; k++, data++)
+             delete *data;
+          delete [] field_types;
+      }
+   }
+
+   int serialize(FILE *out)
+   {
+      _WRITE_STRING_DATA(name, out);
+
+      long start = PROLOGUE(out);
+
+      fwrite(this, sizeof(*this), 1, out);
+
+      if (base_type == GLSL_TYPE_ARRAY)
+         element_type->serialize(out);
+      else if (base_type == GLSL_TYPE_STRUCT) {
+         struct string_data *data = field_names;
+         glsl_type_data **field_t = field_types;
+         for (int k = 0; k < length; k++, data++, field_t++) {
+            _WRITE_STRING_DATA(data, out);
+            (*field_t)->serialize(out);
+         }
+      }
+
+      EPILOGUE(out, start);
+      return 0;
+   }
+
+   int32_t base_type;
+   int32_t length;
+   int32_t vector_elms;
+   int32_t matrix_cols;
+
+   uint32_t sampler_dimensionality;
+   uint32_t sampler_shadow;
+   uint32_t sampler_array;
+   uint32_t sampler_type;
+
+   uint32_t interface_packing;
+
+   struct string_data *name;
+
+   /* array element type */
+   struct glsl_type_data *element_type;
+
+   /* structure fields */
+   struct string_data *field_names;
+   struct glsl_type_data **field_types;
+};
+
+
+/* helper to create a unique id from a ir_variable address */
+static uint32_t _unique_id(ir_variable *var)
+{
+   char buffer[256];
+   _mesa_snprintf(buffer, 256, "%s_%p", var->name, var);
+   return _mesa_str_checksum(buffer);
+}
+
+
+struct ir_variable_data
+{
+public:
+   ir_variable_data() :
+      type(NULL),
+      name(NULL),
+      unique_name(NULL) {}
+
+   ir_variable_data(ir_variable *ir) :
+      unique_id(_unique_id(ir)),
+      max_array_access(ir->max_array_access),
+      ir_type(ir->ir_type),
+      mode(ir->mode),
+      location(ir->location),
+      centroid(ir->centroid),
+      invariant(ir->invariant),
+      interpolation(ir->interpolation),
+      explicit_location(ir->explicit_location),
+      explicit_index(ir->explicit_index),
+      explicit_binding(ir->explicit_binding),
+      has_constant_value(ir->constant_value ? 1 : 0),
+      has_constant_initializer(ir->constant_initializer ? 1 : 0)
+   {
+      char uniq[256];
+      _mesa_snprintf(uniq, 256, "%s_%d", ir->name, unique_id);
+      unique_name = new string_data(uniq);
+      name = new string_data(ir->name);
+      type = new glsl_type_data(ir->type);
+   }
+
+   ~ir_variable_data()
+   {
+      delete name;
+      delete unique_name;
+      delete type;
+   }
+
+   int serialize(FILE *out)
+   {
+      type->serialize(out);
+      _WRITE_STRING_DATA(name, out);
+      _WRITE_STRING_DATA(unique_name, out);
+      fwrite(this, sizeof(*this), 1, out);
+
+      return 0;
+   }
+
+   struct glsl_type_data *type;
+   struct string_data *name;
+   struct string_data *unique_name;
+
+   uint32_t unique_id;
+
+   uint32_t max_array_access;
+   int32_t ir_type;
+   int32_t mode;
+   int32_t location;
+   uint32_t centroid;
+   uint32_t invariant;
+   uint32_t interpolation;
+
+   uint32_t explicit_location;
+   uint32_t explicit_index;
+   uint32_t explicit_binding;
+
+   uint32_t has_constant_value;
+   uint32_t has_constant_initializer;
+};
+
+
+/**
+ * helper class to read instructions
+ */
+struct memory_map
+{
+public:
+   memory_map() :
+      fd(0),
+      cache_size(0),
+      cache_mmap(NULL),
+      cache_mmap_p(NULL) { }
+
+   int map(const char *path)
+   {
+      struct stat stat_info;
+      if (stat(path, &stat_info) != 0)
+         return -1;
+
+      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;
+   }
+
+   ~memory_map() {
+      if (cache_mmap) {
+         munmap(cache_mmap, cache_size);
+         close(fd);
+      }
+   }
+
+   /* move read pointer forward */
+   inline void ffwd(int len)
+   {
+      cache_mmap_p += len;
+   }
+
+   /* have we reached the end of mapping? */
+   inline bool end()
+   {
+      return (!((cache_mmap_p - cache_mmap) < cache_size));
+   }
+
+   /* template avoids use of typeof() cast */
+   template <typename T>
+   inline void read_value(T *val)
+   {
+      *val = *(T *)cache_mmap_p;
+      ffwd(sizeof(T));
+   }
+
+   template <typename T>
+   inline void read_nval(T *val, uint32_t amount)
+   {
+      memcpy(val, cache_mmap_p, amount * sizeof(val[0]));
+      ffwd(amount * sizeof(val[0]));
+   }
+
+   inline void read_string(char *str)
+   {
+      uint32_t len;
+      read_value(&len);
+      memcpy(str, cache_mmap_p, len);
+      str[len] = '\0';
+      ffwd(len);
+   }
+
+   template <typename T>
+   inline void read_struct(T *s)
+   {
+      memcpy(s, cache_mmap_p, sizeof(*s));
+      ffwd(sizeof(*s));
+   }
+
+private:
+
+   int32_t fd;
+   int32_t cache_size;
+   char *cache_mmap;
+   char *cache_mmap_p;
+};
+
+
+struct ir_cache
+{
+public:
+   ir_cache(bool prototypes = false) :
+      prototypes_only(prototypes)
+   {
+      var_ht = hash_table_ctor(0, hash_table_string_hash,
+         hash_table_string_compare);
+   }
+
+   ~ir_cache()
+   {
+      hash_table_call_foreach(this->var_ht, delete_key, NULL);
+      hash_table_dtor(this->var_ht);
+   }
+
+   /* serialize gl_shader to a FILE */
+   int serialize(struct gl_shader *shader,
+      struct _mesa_glsl_parse_state *state, FILE *out,
+      const char *mesa_sha);
+
+   /* unserialize gl_shader from mapped memory */
+   struct gl_shader *unserialize(void *mem_ctx, memory_map &map,
+      struct _mesa_glsl_parse_state *state,
+      const char *mesa_sha,
+      int *error_code);
+
+   enum cache_error {
+      GENERAL_READ_ERROR = -1,
+      DIFFERENT_MESA_SHA = -2,
+   };
+
+private:
+
+   /* variables and methods required for serialization */
+
+   bool prototypes_only;
+
+   /**
+    * writes ir_type and instruction dump size as a 'header'
+    * for each instruction before calling save_ir
+    */
+   int save(ir_instruction *ir, FILE *out);
+
+   int save_ir(ir_variable *ir, FILE *out);
+   int save_ir(ir_assignment *ir, FILE *out);
+   int save_ir(ir_call *ir, FILE *out);
+   int save_ir(ir_constant *ir, FILE *out);
+   int save_ir(ir_dereference_array *ir, FILE *out);
+   int save_ir(ir_dereference_record *ir, FILE *out);
+   int save_ir(ir_dereference_variable *ir, FILE *out);
+   int save_ir(ir_discard *ir, FILE *out);
+   int save_ir(ir_expression *ir, FILE *out);
+   int save_ir(ir_function *ir, FILE *out);
+   int save_ir(ir_function_signature *ir, FILE *out);
+   int save_ir(ir_if *ir, FILE *out);
+   int save_ir(ir_loop *ir, FILE *out);
+   int save_ir(ir_loop_jump *ir, FILE *out);
+   int save_ir(ir_return *ir, FILE *out);
+   int save_ir(ir_swizzle *ir, FILE *out);
+   int save_ir(ir_emit_vertex *ir, FILE *out);
+   int save_ir(ir_end_primitive *ir, FILE *out);
+
+   /* variables and methods required for unserialization */
+
+   struct _mesa_glsl_parse_state *state;
+   void *mem_ctx;
+
+   struct exec_list *top_level;
+   struct exec_list *prototypes;
+   struct exec_list *current_function;
+
+   /* reads saved parser state variables and initializes builtins */
+   int read_state(struct _mesa_glsl_parse_state *state, memory_map &map);
+
+   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);
+
+   /* rvalue readers */
+   ir_rvalue *read_ir_rvalue(memory_map &map);
+   ir_constant *read_ir_constant(memory_map &map);
+   ir_swizzle *read_ir_swizzle(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);
+
+   const glsl_type *read_glsl_type(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-pasta 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 = 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);
+   }
+
+
+};
+
+#endif /* IR_CACHE_H */
diff --git a/src/glsl/ir_cache_serialize.cpp b/src/glsl/ir_cache_serialize.cpp
new file mode 100644
index 0000000..d7d3144
--- /dev/null
+++ b/src/glsl/ir_cache_serialize.cpp
@@ -0,0 +1,603 @@
+/* -*- 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.h"
+
+
+inline static int _write_string(const char *str, FILE *out)
+{
+   uint32_t len = strlen(str);
+
+   _WRITE_VAL(len, out);
+
+   if(fwrite(str, sizeof(char), len, out) == len)
+      return 0;
+
+   return -1;
+}
+
+
+int ir_cache::save_ir(ir_variable *ir, FILE *out)
+{
+   ir_variable_data *data = new ir_variable_data(ir);
+
+   data->serialize(out);
+
+   CACHE_DEBUG("save ir_variable [%s] id %d\n",
+      data->name->data, data->unique_id);
+
+   delete data;
+
+   if (ir->is_interface_instance()) {
+      CACHE_DEBUG("sorry, interfaces not supported\n");
+      return -1;
+   }
+
+   if (ir->constant_value)
+      save(ir->constant_value, out);
+
+   if (ir->constant_initializer)
+      save(ir->constant_initializer, out);
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_dereference_array *ir, FILE *out)
+{
+   _WRITE_VAL(ir->array->ir_type, out);
+
+   save(ir->array, out);
+
+   _WRITE_VAL(ir->array_index->ir_type, out);
+
+   return save(ir->array_index, out);
+}
+
+
+int ir_cache::save_ir(ir_dereference_record *ir, FILE *out)
+{
+   _write_string(ir->field, out);
+
+   _WRITE_VAL(ir->record->ir_type, out);
+
+   return save(ir->record, out);
+}
+
+
+int ir_cache::save_ir(ir_dereference_variable *ir, FILE *out)
+{
+   _write_string(ir->var->name, out);
+   uint32_t unique_id = _unique_id(ir->var);
+   _WRITE_VAL(unique_id, out);
+
+   CACHE_DEBUG("save ir_dereference_variable [%s] id %d\n",
+      ir->var->name, unique_id);
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_constant *ir, FILE *out)
+{
+   glsl_type_data *data = new glsl_type_data(ir->type);
+   data->serialize(out);
+
+   fwrite(&ir->value, sizeof(ir_constant_data), 1, out);
+
+   delete data;
+
+   if (ir->array_elements) {
+      for (unsigned i = 0; i < ir->type->length; i++)
+         if (save(ir->array_elements[i], out))
+            return -1;
+   }
+
+   /* FIXME, support missing for structs */
+   else if (!ir->components.is_empty()) {
+      CACHE_DEBUG("unsupported ir_constant type (struct)\n");
+      return -1;
+   }
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_expression *ir, FILE *out)
+{
+   glsl_type_data *data = new glsl_type_data(ir->type);
+   int32_t num_operands = ir->get_num_operands();
+
+   data->serialize(out);
+   delete data;
+
+   _WRITE_VAL(ir->operation, out);
+   _WRITE_VAL(num_operands, out);
+
+   /* operand ir_type below is written to make parsing easier */
+   for (unsigned i = 0; i < ir->get_num_operands(); i++) {
+      _WRITE_VAL(ir->operands[i]->ir_type, out);
+      if (save(ir->operands[i], out))
+         return -1;
+   }
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_function *ir, FILE *out)
+{
+   uint32_t sig_amount = 0;
+
+   foreach_iter(exec_list_iterator, iter, *ir)
+      sig_amount++;
+
+   _write_string(ir->name, out);
+   _WRITE_VAL(sig_amount, out);
+
+   CACHE_DEBUG("save ir_function [%s], %d sigs\n", ir->name, sig_amount);
+
+   foreach_iter(exec_list_iterator, iter, *ir) {
+      ir_function_signature *const sig = (ir_function_signature *) iter.get();
+      if (save(sig, out))
+         return -1;
+   }
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_function_signature *ir, FILE *out)
+{
+   int32_t par_count = 0;
+   int32_t body_size = 0;
+   uint32_t is_builtin = ir->is_builtin();
+
+   foreach_iter(exec_list_iterator, iter, ir->parameters)
+      par_count++;
+
+   foreach_iter(exec_list_iterator, iter, ir->body)
+      body_size++;
+
+   CACHE_DEBUG("signature (%s), returns %d, params %d size %d (builtin %d)\n",
+      ir->function_name(), ir->return_type->base_type, par_count, body_size,
+      is_builtin);
+
+   _WRITE_VAL(par_count, out);
+   _WRITE_VAL(body_size, out);
+   _WRITE_VAL(is_builtin, out);
+
+   /* dump the return type of function */
+   glsl_type_data *data = new glsl_type_data(ir->return_type);
+   data->serialize(out);
+   delete data;
+
+   /* function parameters */
+   foreach_iter(exec_list_iterator, iter, ir->parameters) {
+      ir_variable *const inst = (ir_variable *) iter.get();
+      CACHE_DEBUG("   parameter %s\n", inst->name);
+      if (save(inst, out))
+         return -1;
+   }
+
+   if (prototypes_only)
+      return 0;
+
+   /* function body */
+   foreach_iter(exec_list_iterator, iter, ir->body) {
+      ir_instruction *const inst = (ir_instruction *) iter.get();
+      CACHE_DEBUG("   body instruction node type %d\n", inst->ir_type);
+      if (save(inst, out))
+         return -1;
+   }
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_assignment *ir, FILE *out)
+{
+   uint32_t write_mask = ir->write_mask;
+
+   _WRITE_VAL(write_mask, out);
+
+   /* lhs (ir_deference_*) */
+   _WRITE_VAL(ir->lhs->ir_type, out);
+
+   _write_string(ir->lhs->variable_referenced()->name, out);
+
+   if (save(ir->lhs, out))
+      return -1;
+
+   if (ir->condition) {
+      CACHE_DEBUG("%s: assignment has condition, not supported", __func__);
+   }
+
+    /* rhs (constant, expression ...) */
+   _WRITE_VAL(ir->rhs->ir_type, out);
+
+   if (save(ir->rhs, out))
+      return -1;
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_return *ir, FILE *out)
+{
+   ir_rvalue *const value = ir->get_value();
+   uint32_t has_rvalue = value ? 1 : 0;
+
+   _WRITE_VAL(has_rvalue, out);
+   _WRITE_VAL(value->ir_type, out);
+
+   return save(value, out);
+}
+
+
+int ir_cache::save_ir(ir_swizzle *ir, FILE *out)
+{
+   uint32_t components = ir->mask.num_components;
+   const uint32_t mask[4] = {
+      ir->mask.x,
+      ir->mask.y,
+      ir->mask.z,
+      ir->mask.w
+   };
+
+   _WRITE_VAL(components, out);
+   fwrite(&mask, sizeof(mask[0]), 4, out);
+
+   _WRITE_VAL(ir->val->ir_type, out);
+
+   return save(ir->val, out);
+}
+
+
+int ir_cache::save_ir(ir_discard *ir, FILE *out)
+{
+   unsigned has_condition = ir->condition ? 1 : 0;
+   _WRITE_VAL(has_condition, out);
+
+   /* TODO - figure out what condition on discard means */
+   if (ir->condition != NULL) {
+      CACHE_DEBUG("%s: error, there is no cond support here yet...\n",
+         __func__);
+      if (save(ir->condition, out))
+         return -1;
+   }
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_call *ir, FILE *out)
+{
+   _write_string(ir->callee_name(), out);
+
+   uint32_t has_return_deref = ir->return_deref ? 1 : 0;
+   uint32_t list_len = 0;
+   uint32_t use_builtin = ir->use_builtin;
+
+   _WRITE_VAL(has_return_deref, out);
+
+   if (ir->return_deref)
+      if (save(ir->return_deref, out))
+         return -1;
+
+   /* call parameter list */
+   foreach_iter(exec_list_iterator, iter, *ir)
+      list_len++;
+
+   _WRITE_VAL(list_len, out);
+
+   foreach_iter(exec_list_iterator, iter, *ir) {
+      ir_instruction *const inst = (ir_instruction *) iter.get();
+
+      int32_t ir_type = inst->ir_type;
+      _WRITE_VAL(ir_type, out);
+
+      if (save(inst, out))
+         return -1;
+   }
+
+   _WRITE_VAL(use_builtin, out);
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_if *ir, FILE *out)
+{
+   uint32_t then_len = 0, else_len = 0;
+
+   /* then and else branch lengths */
+   foreach_iter(exec_list_iterator, iter, ir->then_instructions)
+      then_len++;
+   foreach_iter(exec_list_iterator, iter, ir->else_instructions)
+      else_len++;
+
+   _WRITE_VAL(then_len, out);
+   _WRITE_VAL(else_len, out);
+   _WRITE_VAL(ir->condition->ir_type, out);
+
+   CACHE_DEBUG("dump ir_if (then %d else %d), condition ir_type %d\n",
+      then_len, else_len, ir->condition->ir_type);
+
+   save(ir->condition, out);
+
+   /* dump branch instruction lists */
+   foreach_iter(exec_list_iterator, iter, ir->then_instructions) {
+      ir_instruction *const inst = (ir_instruction *) iter.get();
+      CACHE_DEBUG("   ir_if then instruction node type %d\n", inst->ir_type);
+      if (save(inst, out))
+         return -1;
+   }
+
+   foreach_iter(exec_list_iterator, iter, ir->else_instructions) {
+      ir_instruction *const inst = (ir_instruction *) iter.get();
+      CACHE_DEBUG("   ir_if else instruction node type %d\n", inst->ir_type);
+      if (save(inst, out))
+         return -1;
+   }
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_loop *ir, FILE *out)
+{
+   uint32_t has_counter = ir->counter ? 1 : 0;
+   uint32_t has_from = ir->from ? 1 : 0;
+   uint32_t has_to = ir->to ? 1 : 0;
+   uint32_t has_incr = ir->increment ? 1 : 0;
+   uint32_t body_size = 0;
+
+   foreach_iter(exec_list_iterator, iter, ir->body_instructions)
+      body_size++;
+
+   _WRITE_VAL(has_from, out);
+   _WRITE_VAL(has_to, out);
+   _WRITE_VAL(has_incr, out);
+   _WRITE_VAL(has_counter, out);
+   _WRITE_VAL(ir->cmp, out);
+   _WRITE_VAL(body_size, out);
+
+   if (has_from) {
+      _WRITE_VAL(ir->from->ir_type, out);
+      if (save(ir->from, out))
+         return -1;
+   }
+
+   if (has_to) {
+      _WRITE_VAL(ir->to->ir_type, out);
+      if (save(ir->to, out))
+         return -1;
+   }
+
+   if (has_incr) {
+      _WRITE_VAL(ir->increment->ir_type, out);
+      if (save(ir->increment, out))
+         return -1;
+   }
+
+   if (has_counter) {
+     _write_string(ir->counter->name, out);
+     if (save(ir->counter, out))
+        return -1;
+   }
+
+   foreach_iter(exec_list_iterator, iter, ir->body_instructions) {
+      ir_instruction *const inst = (ir_instruction *) iter.get();
+      CACHE_DEBUG("save loop instruction type %d\n", inst->ir_type);
+      if (save(inst, out))
+         return -1;
+   }
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_loop_jump *ir, FILE *out)
+{
+   _WRITE_VAL(ir->mode, out);
+
+   return 0;
+}
+
+
+int ir_cache::save_ir(ir_emit_vertex *ir, FILE *out)
+{
+   return -1;
+}
+
+
+int ir_cache::save_ir(ir_end_primitive *ir, FILE *out)
+{
+   return -1;
+}
+
+
+/**
+ * writes instruction type, packet size and calls
+ * save function for the instruction to save the data
+ */
+int ir_cache::save(ir_instruction *ir, FILE *out)
+{
+   _WRITE_VAL(ir->ir_type, out);
+
+   long start = PROLOGUE(out);
+
+#define SAVE_IR(type)\
+   if (save_ir(static_cast<type *>(ir), out)) goto write_errors;
+
+   switch(ir->ir_type) {
+
+   case ir_type_variable:
+      SAVE_IR(ir_variable);
+      break;
+   case ir_type_call:
+      SAVE_IR(ir_call);
+      break;
+   case ir_type_constant:
+      SAVE_IR(ir_constant);
+      break;
+   case ir_type_discard:
+      SAVE_IR(ir_discard);
+      break;
+   case ir_type_expression:
+      SAVE_IR(ir_expression);
+      break;
+   case ir_type_dereference_array:
+      SAVE_IR(ir_dereference_array);
+      break;
+   case ir_type_dereference_record:
+      SAVE_IR(ir_dereference_record);
+      break;
+   case ir_type_dereference_variable:
+      SAVE_IR(ir_dereference_variable);
+      break;
+   case ir_type_function:
+      SAVE_IR(ir_function);
+      break;
+   case ir_type_function_signature:
+      SAVE_IR(ir_function_signature);
+      break;
+   case ir_type_swizzle:
+      SAVE_IR(ir_swizzle);
+      break;
+   case ir_type_assignment:
+      SAVE_IR(ir_assignment);
+      break;
+   case ir_type_if:
+      SAVE_IR(ir_if);
+      break;
+   case ir_type_loop:
+      SAVE_IR(ir_loop);
+      break;
+   case ir_type_loop_jump:
+      SAVE_IR(ir_loop_jump);
+      break;
+   case ir_type_return:
+      SAVE_IR(ir_return);
+      break;
+   case ir_type_emit_vertex:
+      SAVE_IR(ir_emit_vertex);
+      break;
+   case ir_type_end_primitive:
+      SAVE_IR(ir_end_primitive);
+      break;
+
+   default:
+      CACHE_DEBUG("%s: error, type %d not implemented\n",
+         __func__, ir->ir_type);
+      return -1;
+   }
+
+   EPILOGUE(out, start);
+   return 0;
+
+write_errors:
+   CACHE_DEBUG("%s: write errors (ir type %d)\n", __func__, ir->ir_type);
+   return -1;
+}
+
+
+static void _write_state(struct _mesa_glsl_parse_state *state, FILE *out)
+{
+   uint32_t es = state->es_shader ? 1 : 0;
+
+   _WRITE_VAL(state->language_version, out);
+   _WRITE_VAL(state->target, out);
+   _WRITE_VAL(es, out);
+}
+
+
+static void _write_header(gl_shader *shader, FILE *out, const char *mesa_sha)
+{
+   _write_string(mesa_sha, out);
+   _WRITE_VAL(shader->Version, out);
+   _WRITE_VAL(shader->Type, out);
+   _WRITE_VAL(shader->IsES, out);
+
+}
+
+
+/**
+ * serializes a single gl_shader, writes state variables,
+ * shader header information and exec_list of instructions
+ */
+int ir_cache::serialize(struct gl_shader *shader,
+   struct _mesa_glsl_parse_state *state, FILE *out,
+   const char *mesa_sha)
+{
+   uint32_t total = 0;
+
+   prototypes_only = true;
+
+   _write_state(state, out);
+   _write_header(shader, out, mesa_sha);
+
+   /* count variables + functions and dump prototypes */
+   foreach_list_const(node, shader->ir) {
+      if (((ir_instruction *) node)->as_variable())
+         total++;
+      if (((ir_instruction *) node)->as_function())
+         total++;
+   }
+
+   _WRITE_VAL(total, out);
+
+   CACHE_DEBUG("write %d prototypes\n", total);
+
+   foreach_list_const(node, shader->ir) {
+      ir_instruction *const inst = (ir_instruction *) node;
+      if (inst->as_variable())
+         if (save(inst, out))
+            goto write_errors;
+   }
+
+   foreach_list_const(node, shader->ir) {
+      ir_instruction *const inst = (ir_instruction *) node;
+      if (inst->as_function())
+         if (save(inst, out))
+            goto write_errors;
+   }
+
+   /* all shader instructions */
+   prototypes_only = false;
+   foreach_list_const(node, shader->ir) {
+      ir_instruction *instruction = (ir_instruction *) node;
+      if (save(instruction, out))
+         goto write_errors;
+   }
+
+   CACHE_DEBUG("cached a shader\n");
+
+   return 0;
+
+write_errors:
+   return -1;
+}
+
diff --git a/src/glsl/ir_cache_unserialize.cpp b/src/glsl/ir_cache_unserialize.cpp
new file mode 100644
index 0000000..40eeeea
--- /dev/null
+++ b/src/glsl/ir_cache_unserialize.cpp
@@ -0,0 +1,973 @@
+/* -*- 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.h"
+
+
+static ir_variable *search_var(struct exec_list *list,
+   const char *name, bool debug_print = false)
+{
+   foreach_list_safe(node, list) {
+      ir_variable *var = ((ir_instruction *) node)->as_variable();
+      if (var && debug_print) printf("[%s vs %s]\n", name, var->name);
+      if (var && strstr(name, var->name))
+         return var;
+   }
+   return NULL;
+}
+
+
+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 && strstr(name, func->name) &&
+         func->matching_signature(state, parameters))
+         return func;
+   }
+   return NULL;
+}
+
+
+int ir_cache::read_state(struct _mesa_glsl_parse_state *state, memory_map &map)
+{
+   uint32_t es;
+
+   CACHE_DEBUG("%s\n", __func__);
+
+   map.read_value(&state->language_version);
+   map.read_value(&state->target);
+   map.read_value(&es);
+
+   state->es_shader = es ? 1 : 0;
+
+   /* initialize builtins, types */
+   state->num_builtins_to_link = 0;
+
+   _mesa_glsl_initialize_builtin_functions();
+   _mesa_glsl_initialize_types(state);
+
+   return 0;
+}
+
+
+/**
+ * main purpose of the header is that it validates that cached
+ * shader was produced with the same Mesa drivers
+ */
+int ir_cache::read_header(struct gl_shader *shader, memory_map &map,
+   const char *mesa_sha)
+{
+   char cache_mesa_sha[256];
+
+   map.read_string(cache_mesa_sha);
+
+   map.read_value(&shader->Version);
+   map.read_value(&shader->Type);
+   map.read_value(&shader->IsES);
+
+   CACHE_DEBUG("%s: version %d, type 0x%x, %s (mesa %s)\n",
+      __func__,  shader->Version, shader->Type,
+      (shader->IsES) ? "glsl es" : "desktop glsl",
+      cache_mesa_sha);
+
+   return memcmp(cache_mesa_sha, mesa_sha, strlen(mesa_sha));
+}
+
+
+const glsl_type *ir_cache::read_glsl_type(memory_map &map)
+{
+   char name[256];
+   long type_size;
+
+   map.read_string(name);
+   map.read_value(&type_size);
+
+   const glsl_type *type_exists = state->symbols->get_type(name);
+
+   /* if type exists, move read pointer forward and return type */
+   if (type_exists) {
+      map.ffwd(type_size);
+      return type_exists;
+   }
+
+   glsl_type_data data;
+
+   map.read_struct(&data);
+
+   data.name = NULL;
+   data.element_type = NULL;
+   data.field_names = NULL;
+   data.field_types = NULL;
+
+   if (data.base_type == GLSL_TYPE_SAMPLER) {
+
+      switch(data.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__, data.sampler_dimensionality);
+      }
+   }
+
+   /* array type has additional element_type information */
+   if (data.base_type == GLSL_TYPE_ARRAY) {
+      const glsl_type *element_type = read_glsl_type(map);
+      if (!element_type) {
+         CACHE_DEBUG("error reading array element type\n");
+         return NULL;
+      }
+      return glsl_type::get_array_instance(element_type, data.length);
+   }
+
+   /* structures have fields containing of names and types */
+   else if (data.base_type == GLSL_TYPE_STRUCT) {
+      glsl_struct_field *fields = ralloc_array(mem_ctx,
+         glsl_struct_field, data.length);
+      for (int k = 0; k < data.length; k++) {
+         char field_name[256];
+         map.read_string(field_name);
+         fields[k].name = _mesa_strdup(field_name);
+         fields[k].type = read_glsl_type(map);
+      }
+      return glsl_type::get_record_instance(fields, data.length, name);
+   }
+
+   return glsl_type::get_instance(data.base_type,
+      data.vector_elms, data.matrix_cols);
+}
+
+
+int ir_cache::read_ir_variable(struct exec_list *list, memory_map &map)
+{
+   char name[256];
+   char unique_name[256];
+   glsl_type_data type_data;
+   ir_variable_data data;
+
+   const glsl_type *type = read_glsl_type(map);
+
+   map.read_string(name);
+   map.read_string(unique_name);
+   map.read_struct(&data);
+   data.name = NULL;
+   data.unique_name = NULL;
+   data.type = NULL;
+
+   ir_variable *var = new(mem_ctx)ir_variable(type,
+      name, (ir_variable_mode) data.mode);
+
+   if (!var)
+      return -1;
+
+   var->explicit_location = data.explicit_location;
+   var->explicit_index = data.explicit_index;
+   var->explicit_binding = data.explicit_binding;
+
+   var->max_array_access = data.max_array_access;
+   var->location = data.location;
+   var->centroid = data.centroid;
+   var->invariant = data.invariant;
+   var->interpolation = data.interpolation;
+
+   if (data.has_constant_value)
+      var->constant_value = read_ir_constant(map);
+
+   if (data.has_constant_initializer)
+      var->constant_initializer = read_ir_constant(map);
+
+   /* store address to this variable */
+   hash_store(var, unique_name);
+
+   list->push_tail(var);
+
+   return 0;
+}
+
+
+int ir_cache::read_ir_function(struct exec_list *list, memory_map &map)
+{
+   char name[256];
+   int32_t par_count = 0;
+   int32_t body_count = 0;
+   uint32_t is_builtin = 0;
+   int ir_type;
+   long len;
+   uint32_t sig_amount;
+
+   map.read_string(name);
+   map.read_value(&sig_amount);
+
+   ir_function *f = new(mem_ctx) ir_function(name);
+   ir_function_signature *sig = NULL;
+   const ir_function *builtin = NULL;
+
+   /* add all signatures to the function */
+   for (unsigned j = 0; j < sig_amount; j++) {
+
+      /* ir_function_signature */
+      map.read_value(&ir_type);
+      map.read_value(&len);
+
+      if (ir_type != ir_type_function_signature) {
+         CACHE_DEBUG("cache format error with function %s\n", name);
+         return -1;
+      }
+
+      map.read_value(&par_count);
+      map.read_value(&body_count);
+      map.read_value(&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);
+
+      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));
+      }
+
+      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 user function [%s]\n", 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);
+   return -1;
+
+}
+
+
+ir_dereference_array *ir_cache::read_ir_dereference_array(memory_map &map)
+{
+   int ir_type;
+   long len;
+
+   CACHE_DEBUG("%s\n", __func__);
+
+   map.read_value(&ir_type);
+   map.read_value(&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;
+}
+
+
+ir_dereference_record *ir_cache::read_ir_dereference_record(memory_map &map)
+{
+   int ir_type;
+   long len;
+   char name[256];
+
+   CACHE_DEBUG("%s\n", __func__);
+
+   map.read_value(&ir_type);
+   map.read_value(&len);
+   map.read_string(name);
+
+   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_cache::read_ir_dereference_variable(memory_map &map)
+{
+   int ir_type;
+   long len;
+   char name[256];
+   char unique_name[256];
+   uint32_t unique_id;
+
+   map.read_value(&ir_type);
+   map.read_value(&len);
+   map.read_string(name);
+   map.read_value(&unique_id);
+
+   _mesa_snprintf(unique_name, 256, "%s_%d", name, unique_id);
+   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__, name);
+   return NULL;
+}
+
+
+ir_constant *ir_cache::read_ir_constant(memory_map &map)
+{
+   ir_constant *con = NULL;
+   int ir_type;
+   long size;
+
+   map.read_value(&ir_type);
+   map.read_value(&size);
+
+   const glsl_type *constant_type = read_glsl_type(map);
+
+   /* data structure */
+   ir_constant_data data;
+   map.read_struct(&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);
+
+      return con;
+   }
+
+   /* FIXME, not supported yet */
+   else if (constant_type->base_type == GLSL_TYPE_STRUCT)
+      return NULL;
+
+   return con;
+}
+
+
+ir_swizzle *ir_cache::read_ir_swizzle(memory_map &map)
+{
+   unsigned swiz[4] = { 0 };
+   unsigned count;
+   int ir_type;
+   long size;
+
+   CACHE_DEBUG("%s\n", __func__);
+
+   map.read_value(&ir_type);
+   map.read_value(&size);
+
+   /* num of components + swizzle mask, rvalue */
+   map.read_value(&count);
+   map.read_nval(swiz, 4);
+
+   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_expression *ir_cache::read_ir_expression(memory_map &map)
+{
+   ir_expression_operation operation;
+   ir_rvalue *ir_rvalue_table[4] = { NULL };
+   int operands;
+   int ir_type;
+   long r_size;
+
+   CACHE_DEBUG("%s\n", __func__);
+
+   map.read_value(&ir_type);
+   map.read_value(&r_size);
+
+   /* glsl_type resulted from operation */
+   const glsl_type *rval_type = read_glsl_type(map);
+
+   /* read operation type + all operands for creating ir_expression */
+   map.read_value(&operation);
+   map.read_value(&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_cache::read_ir_rvalue(memory_map &map)
+{
+   int32_t ir_type = ir_type_unset;
+
+   map.read_value(&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);
+   default:
+      CACHE_DEBUG("%s: error, unhandled type %d\n",
+      __func__, ir_type);
+      break;
+   }
+   return NULL;
+}
+
+
+/**
+ * read assignment instruction, mask + rvalue
+ */
+int ir_cache::read_ir_assignment(struct exec_list *list, memory_map &map)
+{
+   unsigned write_mask = 0;
+   int lhs_type = 0;
+   char lhs_name[256];
+
+   ir_assignment *assign = NULL;
+   ir_dereference *lhs_deref = NULL;
+
+   map.read_value(&write_mask);
+   map.read_value(&lhs_type);
+
+   CACHE_DEBUG("%s: mask %d lhs_type %d\n", __func__, write_mask, lhs_type);
+
+   map.read_string(lhs_name);
+
+   CACHE_DEBUG("%s : lhs name [%s]\n", __func__, lhs_name);
+
+   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;
+}
+
+
+/**
+ * if with condition + then and else branches
+ */
+int ir_cache::read_ir_if(struct exec_list *list, memory_map &map)
+{
+   unsigned then_len, else_len;
+
+   CACHE_DEBUG("%s\n", __func__);
+
+   map.read_value(&then_len);
+   map.read_value(&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_cache::read_ir_return(struct exec_list *list, memory_map &map)
+{
+   uint32_t has_rvalue = 0;
+   map.read_value(&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 a ir_function, finds the correct function
+ * signature from prototypes list and creates the call
+ */
+int ir_cache::read_ir_call(struct exec_list *list, memory_map &map)
+{
+   unsigned has_return_deref = 0;
+   unsigned list_len = 0;
+   unsigned use_builtin = 0;
+   struct exec_list parameters;
+   char name[256];
+   ir_dereference_variable *return_deref = NULL;
+
+   map.read_string(name);
+
+   map.read_value(&has_return_deref);
+
+   if (has_return_deref)
+      return_deref = read_ir_dereference_variable(map);
+
+   map.read_value(&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_value(&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);
+
+      /**
+       * FIXME - this is a workaround for a call to empty user defined function
+       * (happens with glb2.7), linking would fail if we would create a call,
+       * empty functions get removed only afer linking .. this is not a safe
+       * thing todo here though :/
+       */
+      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_cache::read_ir_discard(struct exec_list *list, memory_map &map)
+{
+   uint32_t has_condition;
+   map.read_value(&has_condition);
+
+   CACHE_DEBUG("%s\n", __func__);
+
+   list->push_tail(new(mem_ctx) ir_discard);
+   return 0;
+}
+
+
+/**
+ * read in ir_loop
+ */
+int ir_cache::read_ir_loop(struct exec_list *list, memory_map &map)
+{
+   unsigned has_counter, has_from, has_to, has_increment, body_size;
+   int cmp;
+   char counter_name[256];
+   ir_loop *loop = NULL;
+
+   loop = new(mem_ctx) ir_loop;
+
+   map.read_value(&has_from);
+   map.read_value(&has_to);
+   map.read_value(&has_increment);
+   map.read_value(&has_counter);
+   map.read_value(&cmp);
+   map.read_value(&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) {
+      map.read_string(counter_name);
+      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_cache::read_ir_loop_jump(struct exec_list *list, memory_map &map)
+{
+   int32_t mode;
+   map.read_value(&mode);
+   list->push_tail(new(mem_ctx) ir_loop_jump((ir_loop_jump::jump_mode)mode));
+   return 0;
+}
+
+
+int ir_cache::read_instruction(struct exec_list *list, memory_map &map,
+   bool ignore)
+{
+   int ir_type = ir_type_unset;
+   long inst_dumpsize = 0;
+
+   map.read_value(&ir_type);
+   map.read_value(&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_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);
+   default:
+      CACHE_DEBUG("%s cannot read type %d, todo...\n",
+         __func__, ir_type);
+   }
+
+   return -1;
+}
+
+
+/**
+ * reads prototypes section of the dump, consists
+ * of variables and functions
+ */
+int ir_cache::read_prototypes(memory_map &map)
+{
+   uint32_t total;
+   int ir_type;
+   long inst_dumpsize;
+
+   map.read_value(&total);
+
+   prototypes_only = true;
+
+   for (unsigned k = 0; k < total; k++) {
+
+      map.read_value(&ir_type);
+      map.read_value(&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;
+}
+
+
+struct gl_shader *ir_cache::unserialize(void *mem_ctx,
+   memory_map &map,
+   struct _mesa_glsl_parse_state *state,
+   const char *mesa_sha,
+   int *error_code)
+{
+   int error = 0;
+   *error_code = ir_cache::GENERAL_READ_ERROR;
+
+   struct gl_shader *shader =
+      (struct gl_shader *) ralloc (mem_ctx, struct gl_shader);
+
+   if (!shader)
+      return NULL;
+
+   shader->ir = new(shader) exec_list;
+
+   top_level = shader->ir;
+
+   if (read_state(state, map))
+      goto error_unserialize;
+
+   if (read_header(shader, map, mesa_sha)) {
+      *error_code = ir_cache::DIFFERENT_MESA_SHA;
+      goto error_unserialize;
+   }
+
+   /**
+    * 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);
+
+   /* top level exec_list read loop, constructs a new list */
+   while(!map.end() && error == 0)
+      error = read_instruction(shader->ir, map);
+
+   ralloc_free(prototypes);
+
+   if (error)
+      goto error_unserialize;
+
+   *error_code = 0;
+
+   CACHE_DEBUG("shader from cache\n");
+
+   return shader;
+
+error_unserialize:
+   ralloc_free(shader->ir);
+   ralloc_free(shader);
+   return NULL;
+}
+
-- 
1.8.1.4



More information about the mesa-dev mailing list