[RFC,08/17] eir: add nir compiler and all its infrastructure

Submitted by Christian Gmeiner on May 10, 2019, 9:09 a.m.

Details

Message ID 20190510090915.2739-9-christian.gmeiner@gmail.com
State New
Headers show
Series "An other look at nir" ( rev: 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Christian Gmeiner May 10, 2019, 9:09 a.m.
Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
---
 src/etnaviv/compiler/eir_compiler.c     |   61 ++
 src/etnaviv/compiler/eir_compiler.h     |   29 +
 src/etnaviv/compiler/eir_compiler_nir.c | 1035 +++++++++++++++++++++++
 src/etnaviv/compiler/eir_shader.c       |  312 +++++++
 src/etnaviv/compiler/eir_shader.h       |  203 +++++
 src/etnaviv/compiler/meson.build        |    4 +
 6 files changed, 1644 insertions(+)
 create mode 100644 src/etnaviv/compiler/eir_compiler.c
 create mode 100644 src/etnaviv/compiler/eir_compiler_nir.c
 create mode 100644 src/etnaviv/compiler/eir_shader.c
 create mode 100644 src/etnaviv/compiler/eir_shader.h

Patch hide | download patch | download mbox

diff --git a/src/etnaviv/compiler/eir_compiler.c b/src/etnaviv/compiler/eir_compiler.c
new file mode 100644
index 00000000000..386dcd0b0bc
--- /dev/null
+++ b/src/etnaviv/compiler/eir_compiler.c
@@ -0,0 +1,61 @@ 
+/*
+ * Copyright (c) 2018 Etnaviv Project
+ * Copyright (C) 2018 Zodiac Inflight Innovations
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "eir.h"
+#include "eir_compiler.h"
+#include "util/ralloc.h"
+#include "util/u_debug.h"
+
+static const struct debug_named_value shader_debug_options[] = {
+   {"disasm",  EIR_DBG_DISASM, "Dump NIR and etnaviv shader disassembly"},
+   {"optmsgs", EIR_DBG_OPTMSGS,"Enable optimizer debug messages"},
+   DEBUG_NAMED_VALUE_END
+};
+
+DEBUG_GET_ONCE_FLAGS_OPTION(eir_compiler_debug, "EIR_COMPILER_DEBUG", shader_debug_options, 0)
+
+enum eir_compiler_debug eir_compiler_debug = 0;
+
+struct eir_compiler *
+eir_compiler_create(void)
+{
+   struct eir_compiler *compiler = rzalloc(NULL, struct eir_compiler);
+
+   if (!compiler)
+      return NULL;
+
+   eir_compiler_debug = debug_get_option_eir_compiler_debug();
+   compiler->set = eir_ra_alloc_reg_set(compiler);
+
+   return compiler;
+}
+
+void
+eir_compiler_free(const struct eir_compiler *compiler)
+{
+   ralloc_free((void *)compiler);
+}
diff --git a/src/etnaviv/compiler/eir_compiler.h b/src/etnaviv/compiler/eir_compiler.h
index 5c5412e4773..645ee6a0db2 100644
--- a/src/etnaviv/compiler/eir_compiler.h
+++ b/src/etnaviv/compiler/eir_compiler.h
@@ -28,7 +28,21 @@ 
 #ifndef H_EIR_COMPILER
 #define H_EIR_COMPILER
 
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
 struct eir_ra_reg_set;
+struct eir_shader_variant;
+
+enum eir_compiler_debug {
+   EIR_DBG_DISASM   = (1 << 0),
+   EIR_DBG_OPTMSGS  = (1 << 1),
+};
+
+extern enum eir_compiler_debug eir_compiler_debug;
 
 /**
  * Compiler state saved across compiler invocations, for any expensive global
@@ -36,6 +50,21 @@  struct eir_ra_reg_set;
  */
 struct eir_compiler {
    struct eir_ra_reg_set *set;
+   uint32_t shader_count;
 };
 
+struct eir_compiler *
+eir_compiler_create(void);
+
+void
+eir_compiler_free(const struct eir_compiler *compiler);
+
+int
+eir_compile_shader_nir(struct eir_compiler *compiler,
+                       struct eir_shader_variant *v);
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif // H_EIR_COMPILER
diff --git a/src/etnaviv/compiler/eir_compiler_nir.c b/src/etnaviv/compiler/eir_compiler_nir.c
new file mode 100644
index 00000000000..862f34390e0
--- /dev/null
+++ b/src/etnaviv/compiler/eir_compiler_nir.c
@@ -0,0 +1,1035 @@ 
+/*
+ * Copyright (c) 2018 Etnaviv Project
+ * Copyright (C) 2018 Zodiac Inflight Innovations
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "compiler/nir/nir.h"
+#include "eir.h"
+#include "eir_compiler.h"
+#include "eir_nir.h"
+#include "eir_shader.h"
+#include "gc/gc_instr.h"
+#include "util/u_debug.h"
+#include "util/u_math.h"
+#include "util/u_memory.h"
+#include "util/ralloc.h"
+
+struct eir_context
+{
+   struct eir_compiler *compiler;
+   struct nir_shader *s;
+   struct eir *ir;
+   struct eir_shader_variant *variant;
+   struct eir_block *block;
+   bool error;
+
+   unsigned uniforms;
+   struct eir_register *nir_locals;
+   struct eir_register *nir_ssa_values;
+};
+
+#define DBG(compiler, fmt, ...)                                   \
+   do {                                                           \
+         debug_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, \
+                      ##__VA_ARGS__);                             \
+   } while (0)
+
+static struct eir_context *
+compile_init(struct eir_compiler *compiler,
+             struct eir_shader_variant *v)
+{
+   struct eir_context *ctx = rzalloc(NULL, struct eir_context);
+
+   ctx->compiler = compiler;
+
+   nir_shader *s = nir_shader_clone(ctx, v->shader->nir);
+   ctx->ir = eir_create();
+   ctx->ir->uniform_offset = s->num_uniforms * 4;
+   ctx->s = s;
+   ctx->variant = v;
+
+   /* reset to sane values */
+   v->fs_color_out_reg = -1;
+   v->fs_depth_out_reg = -1;
+   v->vs_pointsize_out_reg = -1;
+   v->vs_pos_out_reg = -1;
+
+   ctx->block = eir_block_create(ctx->ir);
+
+   return ctx;
+}
+
+static void
+compile_free(struct eir_context *ctx)
+{
+   ralloc_free(ctx);
+}
+
+static void
+compile_error(struct eir_context *ctx, const char *format, ...)
+{
+   va_list ap;
+
+   va_start(ap, format);
+   _debug_vprintf(format, ap);
+   va_end(ap);
+
+   nir_print_shader(ctx->s, stdout);
+
+   ctx->error = true;
+   debug_assert(0);
+}
+
+#define compile_assert(ctx, cond) do { \
+      if (!(cond)) compile_error((ctx), "failed assert: "#cond"\n"); \
+   } while (0)
+
+static struct eir_register
+get_nir_dest(struct eir_context *ctx, const nir_dest *dest)
+{
+   if (dest->is_ssa) {
+      struct eir_register dst = eir_temp_register(ctx->ir, dest->ssa.num_components);
+
+      ctx->nir_ssa_values[dest->ssa.index] = dst;
+      return dst;
+   } else {
+      return ctx->nir_locals[dest->reg.reg->index];
+   }
+}
+
+/**
+ * Construct an identity swizzle for the set of enabled channels given by \p
+ * mask. The result will only reference channels enabled in the provided \p
+ * mask, assuming that \p mask is non-zero.
+ */
+static inline unsigned
+eir_swizzle_for_mask(unsigned mask)
+{
+   unsigned last = (mask ? ffs(mask) - 1 : 0);
+   unsigned swz[4];
+
+   for (unsigned i = 0; i < 4; i++)
+      last = swz[i] = (mask & (1 << i) ? i : last);
+
+   return INST_SWIZ(swz[0], swz[1], swz[2], swz[3]);
+}
+
+/**
+ * Construct an identity swizzle for the first \p n components of a vector.
+ */
+static inline unsigned
+eir_swizzle_for_size(unsigned n)
+{
+   return eir_swizzle_for_mask((1 << n) - 1);
+}
+
+static struct eir_register
+get_nir_src(const struct eir_context *ctx, const nir_src *src,
+            unsigned num_components)
+{
+   nir_const_value *const_value = nir_src_as_const_value(*src);
+   struct eir_register reg;
+
+   if (const_value) {
+      assert(src->is_ssa);
+      uint32_t c[src->ssa->num_components];
+
+      nir_const_value_to_array(c, const_value, src->ssa->num_components, u32);
+
+      return eir_uniform_register_vec4_ui(ctx->ir, src->ssa->num_components, c);
+   }
+
+   if (src->is_ssa)
+      reg = ctx->nir_ssa_values[src->ssa->index];
+   else
+      reg = ctx->nir_locals[src->reg.reg->index];
+
+   reg.swizzle = eir_swizzle_for_size(num_components);
+
+   return reg;
+}
+
+static inline unsigned
+eir_writemask_for_size(unsigned n)
+{
+   return (1 << n) - 1;
+}
+
+static inline unsigned
+eir_swizzle_for_nir_swizzle(const uint8_t swizzle[4])
+{
+   return INST_SWIZ(swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
+}
+
+static inline void
+nir_alu_src_to_eir(const struct eir_context *ctx, const nir_alu_src *src, struct eir_register *reg)
+{
+   *reg = get_nir_src(ctx, &src->src, 4);
+   reg->swizzle = eir_swizzle_for_nir_swizzle(src->swizzle);
+   reg->abs = src->abs;
+   reg->neg = src->negate;
+}
+
+static void
+emit_alu(struct eir_context *ctx, nir_alu_instr *alu)
+{
+   const nir_op_info *info = &nir_op_infos[alu->op];
+
+   struct eir_instruction *instr = NULL;
+   struct eir_register src[3] = { };
+
+   struct eir_register dst;
+   dst = get_nir_dest(ctx, &alu->dest.dest);
+   dst.writemask = alu->dest.write_mask;
+
+   for (unsigned i = 0; i < info->num_inputs; i++)
+      nir_alu_src_to_eir(ctx, &alu->src[i], &src[i]);
+
+   switch (alu->op) {
+   case nir_op_f2i32:
+      /* nothing to do - first seen when TGSI uses ARL opc */
+      break;
+
+   case nir_op_fmov:
+      instr = eir_MOV(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_fadd:
+      instr = eir_ADD(ctx->block, &dst, &src[0], &src[1]);
+      break;
+
+   case nir_op_fceil:
+      instr = eir_CEIL(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_fddx:
+      instr = eir_DSX(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_fddy:
+      instr = eir_DSY(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_fdot2:
+      /* fall-through */
+   case nir_op_fdot3:
+      /* fall-through */
+   case nir_op_fdot4:
+      unreachable("Should be lowered by fdot_replicates = true");
+      break;
+
+   case nir_op_fdot_replicated2:
+      instr = eir_DP2(ctx->block, &dst, &src[0], &src[1]);
+      break;
+
+   case nir_op_fdot_replicated3:
+      instr = eir_DP3(ctx->block, &dst, &src[0], &src[1]);
+      break;
+
+   case nir_op_fdot_replicated4:
+      instr = eir_DP4(ctx->block, &dst, &src[0], &src[1]);
+      break;
+
+   case nir_op_fexp2:
+      instr = eir_EXP(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_ffloor:
+      instr = eir_FLOOR(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_ffma:
+      instr = eir_MAD(ctx->block, &dst, &src[0], &src[1], &src[2]);
+      break;
+
+   case nir_op_ffract:
+      instr = eir_FRC(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_flog2:
+      /* TODO: new vs. old hw */
+      instr = eir_LOG(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_flrp:
+      unreachable("not reached: should be lowered by lower_flrp32 = true");
+      break;
+
+   case nir_op_fmax:
+      instr = eir_SELECT(ctx->block, &dst, &src[0], &src[1], &src[0]);
+      instr->gc.condition = GC_COND_LT;
+      break;
+
+   case nir_op_fmin:
+      instr = eir_SELECT(ctx->block, &dst, &src[0], &src[1], &src[2]);
+      instr->gc.condition = GC_COND_GT;
+      break;
+
+   case nir_op_fmul:
+      instr = eir_MUL(ctx->block, &dst, &src[0], &src[1]);
+      break;
+
+   case nir_op_fabs:
+      /* fall-through */
+   case nir_op_fneg:
+      /* fall-through */
+   case nir_op_fsat:
+      unreachable("not reached: should be lowered by lower_source mods");
+      break;
+
+   case nir_op_fpow:
+      unreachable("not reached: should be lowered by lower_fpow = true");
+      break;
+
+   case nir_op_frcp:
+      instr = eir_RCP(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_frsq:
+      instr = eir_RSQ(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_fcsel:
+      instr = eir_SELECT(ctx->block, &dst, &src[0], &src[1], &src[2]);
+      instr->gc.condition = GC_COND_NZ;
+      break;
+
+   case nir_op_fcos:
+   case nir_op_fsin: {
+      struct eir_register tmp = eir_temp_register(ctx->ir, 4);
+      struct eir_register m_2_pi = eir_uniform_register_f(ctx->ir, M_2_PI);
+
+      eir_MUL(ctx->block, &tmp, &src[0], &m_2_pi);
+
+      if (alu->op == nir_op_fsin)
+         instr = eir_SIN(ctx->block, &dst, &tmp);
+      else
+         instr = eir_COS(ctx->block, &dst, &tmp);
+      }
+      break;
+
+   case nir_op_fsqrt:
+      instr = eir_SQRT(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_ftrunc:
+      unreachable("not reached: should be lowered by lower_ftrunc = true");
+      break;
+
+   case nir_op_i2f32:
+   case nir_op_u2f32:
+      instr = eir_MOV(ctx->block, &dst, &src[0]);
+      break;
+
+   case nir_op_seq:
+      instr = eir_SET(ctx->block, &dst, &src[0], GC_COND_EQ, &src[1]);
+      break;
+
+   case nir_op_sge:
+      instr = eir_SET(ctx->block, &dst, &src[0], GC_COND_GE, &src[1]);
+      break;
+
+   case nir_op_slt:
+      instr = eir_SET(ctx->block, &dst, &src[0], GC_COND_LT, &src[1]);
+      break;
+
+   case nir_op_sne:
+      instr = eir_SET(ctx->block, &dst, &src[0], GC_COND_NE, &src[1]);
+      break;
+
+   case nir_op_b2f32:
+      /* fall-through */
+   case nir_op_b2i32:
+      /* fall-through */
+   case nir_op_f2b1:
+      /* fall-through */
+   case nir_op_i2b1:
+      /* fall-through */
+   case nir_op_flt:
+      /* fall-through */
+   case nir_op_fge:
+      /* fall-through */
+   case nir_op_feq:
+      /* fall-through */
+   case nir_op_fne:
+      /* fall-through */
+   case nir_op_ilt:
+      /* fall-through */
+   case nir_op_ige:
+      /* fall-through */
+   case nir_op_ieq:
+      /* fall-through */
+   case nir_op_ine:
+      /* fall-through */
+   case nir_op_ult:
+      /* fall-through */
+   case nir_op_uge:
+      /* fall-through */
+   case nir_op_ball_fequal2:
+      /* fall-through */
+   case nir_op_ball_fequal3:
+      /* fall-through */
+   case nir_op_ball_fequal4:
+      /* fall-through */
+   case nir_op_bany_fnequal2:
+      /* fall-through */
+   case nir_op_bany_fnequal3:
+      /* fall-through */
+   case nir_op_bany_fnequal4:
+      /* fall-through */
+   case nir_op_ball_iequal2:
+      /* fall-through */
+   case nir_op_ball_iequal3:
+      /* fall-through */
+   case nir_op_ball_iequal4:
+      /* fall-through */
+   case nir_op_bany_inequal2:
+      /* fall-through */
+   case nir_op_bany_inequal3:
+      /* fall-through */
+   case nir_op_bany_inequal4:
+      /* fall-through */
+   case nir_op_bcsel:
+      /* fall-through */
+   case nir_op_imov:
+      /* fall-through */
+   case nir_op_iand:
+      /* fall-through */
+   case nir_op_ixor:
+      /* fall-through */
+   case nir_op_ior:
+      /* fall-through */
+   case nir_op_inot:
+      unreachable("not reached: should be lowered by nir_lower_bool_to_float");
+      break;
+
+   default:
+      compile_error(ctx, "Unhandled ALU op: %s\n", info->name);
+      break;
+   }
+
+   if (instr)
+      instr->gc.saturate = alu->dest.saturate;
+}
+
+static void
+emit_intrinsic(struct eir_context *ctx, nir_intrinsic_instr *instr)
+{
+   const nir_intrinsic_info *info = &nir_intrinsic_infos[instr->intrinsic];
+
+   switch (instr->intrinsic) {
+   case nir_intrinsic_discard:
+      eir_TEXKILL(ctx->block);
+      break;
+
+   case nir_intrinsic_load_input: {
+      nir_const_value *const_offset = nir_src_as_const_value(instr->src[0]);
+      assert(const_offset);
+
+      struct eir_register src = {
+         .index = instr->const_index[0] + const_offset->u32,
+         .type = EIR_REG_TEMP,
+      };
+
+      ctx->nir_ssa_values[instr->dest.ssa.index] = src;
+      break;
+   }
+
+   case nir_intrinsic_load_uniform: {
+      if (nir_src_is_const(instr->src[0])) {
+         const unsigned load_offset = nir_src_as_uint(instr->src[0]);
+
+         /* Offsets are in bytes but they should always be multiples of 4 */
+         assert(load_offset % 4 == 0);
+
+         struct eir_register src = {
+            .index = instr->const_index[0],
+            .type = EIR_REG_UNIFORM,
+         };
+
+         ctx->nir_ssa_values[instr->dest.ssa.index] = src;
+      } else {
+         compile_error(ctx, "indirect load_uniform not supported yet\n");
+      }
+
+      break;
+   }
+
+   case nir_intrinsic_store_output: {
+      /* no support for indirect outputs */
+      nir_const_value *const_offset = nir_src_as_const_value(instr->src[1]);
+      assert(const_offset);
+
+      const int idx = nir_intrinsic_base(instr);
+      struct eir_register src = get_nir_src(ctx, &instr->src[0], instr->num_components);
+
+      struct eir_register dst = {
+         .index = idx,
+         .type = EIR_REG_TEMP,
+         .writemask = eir_writemask_for_size(instr->num_components),
+      };
+
+      eir_MOV(ctx->block, &dst, &src);
+
+      break;
+   }
+
+   case nir_intrinsic_nop:
+      eir_NOP(ctx->block);
+      break;
+
+   default:
+      compile_error(ctx, "Unhandled intrinsic type: %s\n", info->name);
+      break;
+   }
+}
+
+static void
+emit_tex(struct eir_context *ctx, nir_tex_instr *tex)
+{
+   const nir_op_info *info = &nir_op_infos[tex->op];
+   struct eir_register sampler;
+   struct eir_register coordinate;
+   struct eir_register bias;
+   MAYBE_UNUSED struct eir_register lod;
+
+   struct eir_register dst;
+   dst = get_nir_dest(ctx, &tex->dest);
+   dst.writemask = INST_COMPS_X | INST_COMPS_Y | INST_COMPS_Z | INST_COMPS_W;
+
+   sampler.type = EIR_REG_SAMPLER;
+   sampler.index = tex->texture_index;
+   sampler.swizzle = INST_SWIZ_IDENTITY;
+   /* TODO: amode */
+
+   for (unsigned i = 0; i < tex->num_srcs; i++) {
+      const unsigned src_size = nir_tex_instr_src_size(tex, i);
+
+      switch (tex->src[i].src_type) {
+      case nir_tex_src_coord:
+         coordinate = get_nir_src(ctx, &tex->src[i].src, src_size);
+         coordinate.swizzle = INST_SWIZ_IDENTITY;
+         break;
+
+      case nir_tex_src_projector:
+         unreachable("Should be lowered by nir_lower_tex");
+         break;
+
+      case nir_tex_src_bias:
+         bias = get_nir_src(ctx, &tex->src[i].src, src_size);
+         break;
+
+      case nir_tex_src_lod:
+         lod = get_nir_src(ctx, &tex->src[i].src, src_size);
+         break;
+
+      default:
+         compile_error(ctx, "Unhandled NIR tex src type: %d\n",
+                       tex->src[i].src_type);
+         return;
+      }
+   }
+
+   switch (tex->op) {
+   case nir_texop_tex:
+      eir_TEXLD(ctx->block, &dst, &sampler, &coordinate);
+      break;
+
+   case nir_texop_txb:
+      eir_TEXLDB(ctx->block, &dst, &sampler, &bias);
+      break;
+
+   case nir_texop_txl:
+      eir_TEXLDL(ctx->block, &dst, &sampler, &coordinate);
+      break;
+
+   case nir_texop_txs: {
+         const int dest_size = nir_tex_instr_dest_size(tex);
+         const unsigned unit = tex->texture_index;
+
+         assert(dest_size < 3);
+
+         const uint32_t v[] = {
+            unit,
+            unit,
+            unit,
+         };
+
+         const enum eir_uniform_contents c[] = {
+            EIR_UNIFORM_IMAGE_WIDTH,
+            EIR_UNIFORM_IMAGE_HEIGHT,
+            EIR_UNIFORM_IMAGE_DEPTH,
+         };
+
+         /* we do not support lod yet */
+         assert(nir_tex_instr_src_index(tex, nir_tex_src_lod) == 0);
+
+         struct eir_register size = eir_uniform_register_vec4(ctx->ir, 3, c, v);
+
+         /* TODO: get rid of mov and use uniform directly */
+         eir_MOV(ctx->block, &dst, &size);
+      }
+      break;
+
+   default:
+      compile_error(ctx, "Unhandled NIR tex op: %d\n", info->name);
+      break;
+   }
+}
+
+static void
+emit_jump(struct eir_context *ctx, nir_jump_instr *jump)
+{
+   compile_error(ctx, "Unhandled NIR jump type: %d\n", jump->type);
+}
+
+static void
+emit_undef(struct eir_context *ctx, nir_ssa_undef_instr *undef)
+{
+   ctx->nir_ssa_values[undef->def.index] = eir_temp_register(ctx->ir, undef->def.num_components);
+}
+
+static void
+emit_instr(struct eir_context *ctx, nir_instr *instr)
+{
+   switch (instr->type) {
+   case nir_instr_type_alu:
+      emit_alu(ctx, nir_instr_as_alu(instr));
+      break;
+   case nir_instr_type_intrinsic:
+      emit_intrinsic(ctx, nir_instr_as_intrinsic(instr));
+      break;
+   case nir_instr_type_load_const:
+      /* dealt with when using nir_src */
+      break;
+   case nir_instr_type_tex:
+      emit_tex(ctx, nir_instr_as_tex(instr));
+      break;
+   case nir_instr_type_jump:
+      emit_jump(ctx, nir_instr_as_jump(instr));
+      break;
+   case nir_instr_type_ssa_undef:
+      emit_undef(ctx, nir_instr_as_ssa_undef(instr));
+      break;
+   case nir_instr_type_phi:
+      /* we have converted phi webs to regs in NIR by now */
+      compile_error(ctx, "Unexpected NIR instruction type: %d\n", instr->type);
+      break;
+   case nir_instr_type_deref:
+   case nir_instr_type_call:
+   case nir_instr_type_parallel_copy:
+      compile_error(ctx, "Unhandled NIR instruction type: %d\n", instr->type);
+      break;
+   }
+}
+
+static void emit_cf_list(struct eir_context *ctx, struct exec_list *list);
+
+static void
+emit_block(struct eir_context *ctx, nir_block *nblock)
+{
+   nir_foreach_instr(instr, nblock) {
+      emit_instr(ctx, instr);
+      if (ctx->error)
+         return;
+   }
+}
+
+static void
+emit_if(struct eir_context *ctx, nir_if *nif)
+{
+   nir_block *nir_else_block = nir_if_first_else_block(nif);
+   bool empty_else_block = (nir_else_block == nir_if_last_else_block(nif) &&
+                           exec_list_is_empty(&nir_else_block->instr_list));
+
+   struct eir_block *then_block = eir_block_create(ctx->ir);
+   struct eir_block *after_block = eir_block_create(ctx->ir);
+   struct eir_block *else_block = NULL;
+
+   if (empty_else_block)
+      else_block = after_block;
+   else
+      else_block = eir_block_create(ctx->ir);
+
+   eir_link_blocks(ctx->block, else_block);
+   eir_link_blocks(ctx->block, then_block);
+
+   assert(nif->condition.is_ssa);
+   assert(nif->condition.ssa->parent_instr->type == nir_instr_type_alu);
+
+   nir_alu_instr *parent_alu = nir_instr_as_alu(nif->condition.ssa->parent_instr);
+
+   struct eir_register src0, src1;
+   nir_alu_src_to_eir(ctx, &parent_alu->src[0], &src0);
+   nir_alu_src_to_eir(ctx, &parent_alu->src[1], &src1);
+
+   struct eir_instruction *instr = eir_BRANCH(ctx->block, &src0, &src1);
+
+   switch (parent_alu->op) {
+   case nir_op_feq:
+   case nir_op_seq:
+      instr->gc.condition = GC_COND_NE;
+      break;
+
+   case nir_op_fne:
+   case nir_op_sne:
+      instr->gc.condition = GC_COND_EQ;
+      break;
+
+   case nir_op_flt:
+   case nir_op_slt:
+      instr->gc.condition = GC_COND_GT;
+      break;
+
+   case nir_op_fge:
+   case nir_op_sge:
+      instr->gc.condition = GC_COND_LE;
+      break;
+
+   default:
+      unreachable("not supported opc");
+   }
+
+   ctx->block = then_block;
+   eir_link_blocks(ctx->block, after_block);
+   emit_cf_list(ctx, &nif->then_list);
+
+   if (!empty_else_block) {
+      ctx->block = else_block;
+      emit_cf_list(ctx, &nif->else_list);
+
+      eir_link_blocks(ctx->block, after_block);
+      eir_link_blocks(ctx->block, else_block);
+   }
+
+   ctx->block = after_block;
+}
+
+static void
+emit_loop(struct eir_context *ctx, nir_loop *nloop)
+{
+   emit_cf_list(ctx, &nloop->body);
+
+   ctx->variant->num_loops++;
+}
+
+static void
+emit_cf_list(struct eir_context *ctx, struct exec_list *list)
+{
+   foreach_list_typed(nir_cf_node, node, node, list) {
+      switch (node->type) {
+      case nir_cf_node_block:
+         emit_block(ctx, nir_cf_node_as_block(node));
+         break;
+      case nir_cf_node_if:
+         emit_if(ctx, nir_cf_node_as_if(node));
+         break;
+      case nir_cf_node_loop:
+         emit_loop(ctx, nir_cf_node_as_loop(node));
+         break;
+      case nir_cf_node_function:
+         compile_error(ctx, "TODO\n");
+         break;
+      }
+   }
+}
+
+static void
+emit_function(struct eir_context *ctx, nir_function_impl *impl)
+{
+   emit_cf_list(ctx, &impl->body);
+}
+
+static void
+setup_input(struct eir_context *ctx, nir_variable *in)
+{
+   unsigned array_len = MAX2(glsl_get_length(in->type), 1);
+   unsigned ncomp = glsl_get_components(in->type);
+   unsigned n = in->data.driver_location;
+   unsigned slot = in->data.location;
+
+   DBG(ctx->compiler, "; in: slot=%u, len=%ux%u, drvloc=%u",
+         slot, array_len, ncomp, n);
+
+   compile_assert(ctx, n < ARRAY_SIZE(ctx->ir->inputs));
+
+   if (ctx->s->info.stage == MESA_SHADER_FRAGMENT) {
+      eir_assign_input(ctx->ir, n, slot, ncomp);
+   } else if (ctx->s->info.stage == MESA_SHADER_VERTEX) {
+      eir_assign_input(ctx->ir, n, slot, ncomp);
+   } else {
+      compile_error(ctx, "unknown shader type: %d\n", ctx->s->info.stage);
+   }
+}
+
+static void
+setup_output(struct eir_context *ctx, nir_variable *out)
+{
+   unsigned array_len = MAX2(glsl_get_length(out->type), 1);
+   unsigned ncomp = glsl_get_components(out->type);
+   unsigned n = out->data.driver_location;
+   unsigned slot = out->data.location;
+
+   DBG(ctx->compiler, "; out: slot=%u, len=%ux%u, drvloc=%u",
+         slot, array_len, ncomp, n);
+
+   compile_assert(ctx, n < ARRAY_SIZE(ctx->ir->outputs));
+
+   if (ctx->s->info.stage == MESA_SHADER_FRAGMENT) {
+      switch (slot) {
+      case FRAG_RESULT_DEPTH:
+         eir_assign_output(ctx->ir, n, slot, ncomp);
+         break;
+
+      case FRAG_RESULT_COLOR:
+         eir_assign_output(ctx->ir, n, slot, ncomp);
+         break;
+
+      default:
+         if (slot >= FRAG_RESULT_DATA0) {
+            eir_assign_output(ctx->ir, n, slot, ncomp);
+            break;
+         }
+
+         compile_error(ctx, "unknown FS output name: %s\n",
+                       gl_frag_result_name(slot));
+      }
+   } else if (ctx->s->info.stage == MESA_SHADER_VERTEX) {
+      switch (slot) {
+      case VARYING_SLOT_POS:
+         eir_assign_output(ctx->ir, n, slot, ncomp);
+         break;
+
+      case VARYING_SLOT_PSIZ:
+         eir_assign_output(ctx->ir, n, slot, ncomp);
+         break;
+
+      case VARYING_SLOT_COL0:
+         eir_assign_output(ctx->ir, n, slot, ncomp);
+         break;
+
+      default:
+         if (slot >= VARYING_SLOT_VAR0) {
+            eir_assign_output(ctx->ir, n, slot, ncomp);
+            break;
+         }
+
+         if ((VARYING_SLOT_TEX0 <= slot) && (slot <= VARYING_SLOT_TEX7)) {
+            eir_assign_output(ctx->ir, n, slot, ncomp);
+            break;
+         }
+
+         compile_error(ctx, "unknown VS output name: %s\n",
+               gl_varying_slot_name(slot));
+      }
+   } else {
+      compile_error(ctx, "unknown shader type: %d\n", ctx->s->info.stage);
+   }
+}
+
+static int
+max_drvloc(struct exec_list *vars)
+{
+   int drvloc = -1;
+
+   nir_foreach_variable(var, vars)
+      drvloc = MAX2(drvloc, (int)var->data.driver_location);
+
+   return drvloc;
+}
+
+static void
+emit_instructions(struct eir_context *ctx)
+{
+   const unsigned ninputs  = max_drvloc(&ctx->s->inputs) + 1;
+   const unsigned noutputs = max_drvloc(&ctx->s->outputs) + 1;
+
+   // keep t0..tn registers reserved for inputs and outputs
+   const unsigned reserved = MAX2(ninputs, noutputs);
+   for (unsigned i = 0; i < reserved; i++)
+      eir_temp_register(ctx->ir, 4);
+
+   nir_function_impl *fxn = nir_shader_get_entrypoint(ctx->s);
+
+   ctx->nir_locals = ralloc_array(ctx, struct eir_register, fxn->reg_alloc);
+   ctx->nir_ssa_values = ralloc_array(ctx, struct eir_register, fxn->ssa_alloc);
+
+   foreach_list_typed(nir_register, reg, node, &fxn->registers) {
+      assert(reg->num_array_elems == 0);
+      assert(reg->bit_size == 32);
+
+      ctx->nir_locals[reg->index] = eir_temp_register(ctx->ir, 4);
+   }
+
+   if (ctx->s->num_uniforms > 0)
+      ctx->uniforms = ctx->s->num_uniforms / 16;
+
+   nir_foreach_variable(var, &ctx->s->inputs)
+      setup_input(ctx, var);
+
+   nir_foreach_variable(var, &ctx->s->outputs)
+      setup_output(ctx, var);
+
+   emit_function(ctx, fxn);
+}
+
+static void
+setup_special_vert_register(const struct eir *ir, unsigned i, struct eir_shader_variant *v)
+{
+   const int reg = ir->outputs[i].reg;
+
+   switch (ir->outputs[i].slot) {
+   case VARYING_SLOT_POS:
+      v->vs_pos_out_reg = reg;
+      break;
+   default:
+      /* nothing to do */
+      break;
+   }
+}
+
+static void
+setup_special_frag_register(const struct eir *ir, unsigned i, struct eir_shader_variant *v)
+{
+   const int reg = ir->outputs[i].reg;
+
+   switch (ir->outputs[i].slot) {
+   case FRAG_RESULT_DEPTH:
+      v->fs_depth_out_reg = reg;
+      break;
+   case FRAG_RESULT_COLOR:
+      v->fs_color_out_reg = reg;
+      break;
+   default:
+      /* nothing to do */
+      break;
+   }
+}
+
+static void
+setup_shader_io(struct eir_context *ctx, struct eir_shader_variant *v)
+{
+   const struct eir *ir = ctx->ir;
+
+   for (unsigned i = 0; i < ir->num_inputs; i++) {
+      v->inputs[i].reg = ir->inputs[i].reg;
+      v->inputs[i].slot = ir->inputs[i].slot;
+      v->inputs[i].ncomp = ir->inputs[i].ncomp;
+
+      /* TODO:
+      v->inputs[n].interpolate = in->data.interpolation;
+      */
+   }
+
+   for (unsigned i = 0; i < ir->num_outputs; i++) {
+      v->outputs[i].reg = ir->outputs[i].reg;
+      v->outputs[i].slot = ir->outputs[i].slot;
+      v->outputs[i].ncomp = ir->outputs[i].ncomp;
+
+      if (v->shader->type == MESA_SHADER_VERTEX)
+         setup_special_vert_register(ir, i, v);
+      else if (v->shader->type == MESA_SHADER_FRAGMENT)
+         setup_special_frag_register(ir, i, v);
+   }
+
+   v->num_inputs = ir->num_inputs;
+   v->num_outputs = ir->num_outputs;
+}
+
+int
+eir_compile_shader_nir(struct eir_compiler *compiler,
+                       struct eir_shader_variant *v)
+{
+   int ret = 0;
+   struct eir_context *ctx;
+   bool success;
+
+   assert(!v->ir);
+   assert(v->shader->mem_ctx);
+
+   ctx = compile_init(compiler, v);
+   if (!ctx) {
+      DBG(compiler, "INIT failed!");
+      ret = -1;
+      goto out;
+   }
+
+   v->ir = ctx->ir;
+
+   emit_instructions(ctx);
+
+   if (ctx->error) {
+      DBG(compiler, "EMIT failed!");
+      ret = -1;
+      goto out;
+   }
+
+   if (eir_compiler_debug & EIR_DBG_OPTMSGS) {
+      printf("AFTER emitting:\n");
+      eir_print(ctx->ir);
+   }
+
+   eir_legalize(ctx->ir);
+   if (eir_compiler_debug & EIR_DBG_OPTMSGS) {
+      printf("AFTER legalization:\n");
+      eir_print(ctx->ir);
+   }
+
+   eir_calculate_live_intervals(ctx->ir);
+   if (eir_compiler_debug & EIR_DBG_OPTMSGS) {
+      printf("AFTER live-ranges:\n");
+      eir_print(ctx->ir);
+   }
+
+   success = eir_register_allocate(ctx->ir, v->shader->type, ctx->compiler);
+   if (!success) {
+      DBG(compiler, "RA failed!");
+      ret = -1;
+      goto out;
+   }
+
+   if (eir_compiler_debug & EIR_DBG_OPTMSGS) {
+      printf("AFTER RA:\n");
+      eir_print(ctx->ir);
+   }
+
+   setup_shader_io(ctx, v);
+
+   v->num_temps = ctx->ir->num_temps;
+   v->const_size = ctx->ir->uniform_offset;
+
+   util_dynarray_clone(&v->uniforms, v->shader->mem_ctx, &ctx->ir->uniform_alloc);
+
+out:
+   if (ret) {
+      if (v->ir)
+         eir_destroy(v->ir);
+      v->ir = NULL;
+   }
+
+   compile_free(ctx);
+
+   return ret;
+}
diff --git a/src/etnaviv/compiler/eir_shader.c b/src/etnaviv/compiler/eir_shader.c
new file mode 100644
index 00000000000..7ad40be5cc6
--- /dev/null
+++ b/src/etnaviv/compiler/eir_shader.c
@@ -0,0 +1,312 @@ 
+/*
+ * Copyright (c) 2018 Etnaviv Project
+ * Copyright (C) 2018 Zodiac Inflight Innovations
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "compiler/nir/nir.h"
+#include "eir_compiler.h"
+#include "eir_nir.h"
+#include "eir_shader.h"
+#include "gc/gc_disasm.h"
+
+#include "util/u_memory.h"
+#include "util/ralloc.h"
+
+static inline bool
+eir_shader_key_equal(struct eir_shader_key *a, struct eir_shader_key *b)
+{
+   STATIC_ASSERT(sizeof(struct eir_shader_key) <= sizeof(a->global));
+
+   return a->global == b->global;
+}
+
+static void
+delete_variant(struct eir_shader_variant *v)
+{
+   if (v->ir)
+      eir_destroy(v->ir);
+
+   if (v->code)
+      free(v->code);
+
+   FREE(v);
+}
+
+static inline void
+assemble_variant(struct eir_shader_variant *v)
+{
+   v->code = eir_assemble(v->ir, &v->info);
+
+	/* no need to keep the ir around beyond this point */
+	eir_destroy(v->ir);
+	v->ir = NULL;
+}
+
+static struct eir_shader_variant *
+create_variant(struct eir_shader *shader, struct eir_shader_key key)
+{
+   struct eir_shader_variant *v = CALLOC_STRUCT(eir_shader_variant);
+   int ret;
+
+   nir_print_shader(shader->nir, stdout);
+
+   if (!v)
+      return NULL;
+
+   v->id = shader->variant_count++;
+   v->shader = shader;
+   v->key = key;
+
+   ret = eir_compile_shader_nir(shader->compiler, v);
+   if (ret) {
+      debug_error("compile failed!");
+      goto fail;
+   }
+
+   assemble_variant(v);
+   if (!v->code) {
+      debug_error("assemble failed!");
+      goto fail;
+   }
+
+   return v;
+
+fail:
+   delete_variant(v);
+   return NULL;
+}
+
+/**
+ * Returns the minimum number of vec4 elements needed to pack a type.
+ *
+ * For simple types, it will return 1 (a single vec4); for matrices, the
+ * number of columns; for array and struct, the sum of the vec4_size of
+ * each of its elements; and for sampler and atomic, zero.
+ *
+ * This method is useful to calculate how much register space is needed to
+ * store a particular type.
+ */
+static int
+eir_type_size_vec4(const struct glsl_type *type, bool bindless)
+{
+   switch (glsl_get_base_type(type)) {
+   case GLSL_TYPE_UINT:
+   case GLSL_TYPE_INT:
+   case GLSL_TYPE_FLOAT:
+   case GLSL_TYPE_FLOAT16:
+   case GLSL_TYPE_BOOL:
+   case GLSL_TYPE_DOUBLE:
+   case GLSL_TYPE_UINT16:
+   case GLSL_TYPE_INT16:
+   case GLSL_TYPE_UINT8:
+   case GLSL_TYPE_INT8:
+   case GLSL_TYPE_UINT64:
+   case GLSL_TYPE_INT64:
+      if (glsl_type_is_matrix(type)) {
+         const struct glsl_type *col_type = glsl_get_column_type(type);
+         unsigned col_slots = glsl_type_is_dual_slot(col_type) ? 2 : 1;
+         return glsl_get_matrix_columns(type) * col_slots;
+      } else {
+         /* Regardless of size of vector, it gets a vec4. This is bad
+          * packing for things like floats, but otherwise arrays become a
+          * mess. Hopefully a later pass over the code can pack scalars
+          * down if appropriate.
+          */
+         return glsl_type_is_dual_slot(type) ? 2 : 1;
+      }
+   case GLSL_TYPE_ARRAY:
+      assert(glsl_get_length(type) > 0);
+      return eir_type_size_vec4(glsl_get_array_element(type), bindless) * glsl_get_length(type);
+   case GLSL_TYPE_STRUCT:
+   case GLSL_TYPE_SUBROUTINE:
+   case GLSL_TYPE_SAMPLER:
+   case GLSL_TYPE_ATOMIC_UINT:
+   case GLSL_TYPE_IMAGE:
+   case GLSL_TYPE_VOID:
+   case GLSL_TYPE_ERROR:
+   case GLSL_TYPE_INTERFACE:
+   case GLSL_TYPE_FUNCTION:
+   default:
+      unreachable("todo");
+   }
+
+   return 0;
+}
+
+struct eir_shader *
+eir_shader_from_nir(struct eir_compiler *compiler, struct nir_shader *nir)
+{
+   struct eir_shader *shader = CALLOC_STRUCT(eir_shader);
+
+   assert(compiler);
+
+   shader->mem_ctx = ralloc_context(NULL);
+   shader->compiler = compiler;
+   shader->id = shader->compiler->shader_count++;
+   shader->type = nir->info.stage;
+
+   NIR_PASS_V(nir, nir_lower_io, nir_var_all, eir_type_size_vec4,
+         (nir_lower_io_options)0);
+
+   shader->nir = eir_optimize_nir(nir);
+
+   return shader;
+}
+
+void
+eir_shader_destroy(struct eir_shader *shader)
+{
+   struct eir_shader_variant *v, *t;
+
+   assert(shader);
+
+   v = shader->variants;
+   while (v) {
+      t = v;
+      v = v->next;
+      delete_variant(t);
+   }
+
+   ralloc_free(shader->nir);
+   ralloc_free(shader->mem_ctx);
+   FREE(shader);
+}
+
+struct eir_shader_variant *
+eir_shader_get_variant(struct eir_shader *shader, struct eir_shader_key key, bool *created)
+{
+   struct eir_shader_variant *v;
+
+   *created = false;
+
+   for (v = shader->variants; v; v = v->next)
+      if (eir_shader_key_equal(&key, &v->key))
+         return v;
+
+   /* compile new variant if it doesn't exist already */
+   v = create_variant(shader, key);
+   if (v) {
+      v->next = shader->variants;
+      shader->variants = v;
+      *created = true;
+   }
+
+   return v;
+}
+
+static const char *
+swizzle_names[4] =
+{
+   "x",
+   "y",
+   "z",
+   "w"
+};
+
+void
+eir_dump_shader(struct eir_shader_variant *v)
+{
+   assert(v);
+   assert(v->shader);
+   assert((v->info.sizedwords % 2) == 0);
+   unsigned i;
+
+   printf("%s\n", _mesa_shader_stage_to_string(v->shader->type));
+
+   struct gc_string decoded = {
+      .string = (char *)rzalloc_size(NULL, 1),
+      .offset = 0,
+   };
+
+   for (i = 0; i < v->info.sizedwords; i += 4) {
+      const uint32_t *raw = &v->code[i];
+      gc_disasm(&decoded, raw);
+
+      printf("%04d: %08x %08x %08x %08x  %s\n",
+            i / 4,
+            raw[0], raw[1], raw[2], raw[3],
+            decoded.string);
+   }
+
+   ralloc_free(decoded.string);
+
+   printf("num loops: 0\n");
+   printf("num temps: %i\n", v->num_temps);
+   printf("num const: %i\n", v->const_size);
+
+   printf("immediates:\n");
+   i = 0;
+   util_dynarray_foreach(&v->uniforms, struct eir_uniform_data, uniform) {
+      printf(" u%i.%s = 0x%08x [%s]\n",
+             (v->const_size + i) / 4,
+             swizzle_names[i % 4],
+             uniform->data,
+             eir_uniform_content(uniform->content));
+
+      i++;
+   }
+
+   switch (v->shader->type) {
+   case MESA_SHADER_VERTEX:
+      printf("inputs:\n");
+      for (unsigned i = 0; i < v->num_inputs; i++)
+         printf(" [%i] %s comps: %u interp: %s\n",
+               i, gl_varying_slot_name(v->inputs[i].slot), v->inputs[i].ncomp,
+               glsl_interp_mode_name(v->inputs[i].interpolate));
+
+      printf("outputs:\n");
+      for (unsigned i = 0; i < v->num_outputs; i++)
+         printf(" [%i] %s comps: %u\n",
+               i, gl_varying_slot_name(v->outputs[i].slot), v->outputs[i].ncomp);
+
+      break;
+   case MESA_SHADER_FRAGMENT:
+      printf("inputs:\n");
+      for (unsigned i = 0; i < v->num_inputs; i++)
+         printf(" [%i] %s comps: %u interp: %s\n",
+               i, gl_varying_slot_name(v->inputs[i].slot), v->inputs[i].ncomp,
+               glsl_interp_mode_name(v->inputs[i].interpolate));
+
+      printf("outputs:\n");
+      for (unsigned i = 0; i < v->num_outputs; i++)
+         printf(" [%i] %s comps: %u\n",
+               i, gl_frag_result_name(v->outputs[i].slot), v->outputs[i].ncomp);
+
+      break;
+
+   default:
+      /* TODO */
+      break;
+   }
+
+   printf("special:\n");
+   if (v->shader->type == MESA_SHADER_VERTEX) {
+      printf("  vs_pos_out_reg=%i\n", v->vs_pos_out_reg);
+      printf("  vs_pointsize_out_reg=%i\n", v->vs_pointsize_out_reg);
+   } else {
+      printf("  ps_color_out_reg=%i\n", v->fs_color_out_reg);
+      printf("  ps_depth_out_reg=%i\n", v->fs_depth_out_reg);
+   }
+}
diff --git a/src/etnaviv/compiler/eir_shader.h b/src/etnaviv/compiler/eir_shader.h
new file mode 100644
index 00000000000..60c446f2943
--- /dev/null
+++ b/src/etnaviv/compiler/eir_shader.h
@@ -0,0 +1,203 @@ 
+/*
+ * Copyright (c) 2018 Etnaviv Project
+ * Copyright (C) 2018 Zodiac Inflight Innovations
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifndef H_EIR_SHADER
+#define H_EIR_SHADER
+
+#include "compiler/glsl_types.h"
+#include "compiler/shader_enums.h"
+#include "eir.h"
+#include <stdbool.h>
+#include <util/u_debug.h>
+
+struct nir_shader;
+
+struct eir_shader_key
+{
+   union {
+      struct {
+         /*
+          * Combined Vertex/Fragment shader parameters:
+          */
+
+         /* do we need to swap rb in frag color? */
+         unsigned frag_rb_swap : 1;
+      };
+      uint32_t global;
+   };
+};
+
+struct eir_shader;
+
+struct eir_shader_variant
+{
+   /* variant id (for debug) */
+   uint32_t id;
+
+   struct eir_shader *shader;
+   struct eir_shader_key key;
+   struct eir_info info;
+   struct eir *ir;
+
+   uint32_t *code;
+
+   unsigned num_temps;
+   unsigned num_loops;
+   unsigned const_size;
+
+   /* keep track of uniforms */
+   struct util_dynarray uniforms;
+
+   /* attributes/varyings: */
+   unsigned num_inputs;
+   struct {
+      uint8_t reg;
+      uint8_t slot;
+      uint8_t ncomp;
+
+      enum glsl_interp_mode interpolate;
+   } inputs[16];
+
+   /* varyings/outputs: */
+   unsigned num_outputs;
+   struct {
+      uint8_t reg;
+      uint8_t slot;
+      uint8_t ncomp;
+   } outputs[16];
+
+   /* special outputs (vs only) */
+   int vs_pos_out_reg; /* VS position output */
+   int vs_pointsize_out_reg; /* VS point size output */
+
+   /* special outputs (fs only) */
+   int fs_color_out_reg; /* color output register */
+   int fs_depth_out_reg; /* depth output register */
+
+   /* shader variants form a linked list */
+   struct eir_shader_variant *next;
+};
+
+struct eir_shader
+{
+   void *mem_ctx;
+   gl_shader_stage type;
+
+   /* shader id (for debug): */
+   uint32_t id;
+   uint32_t variant_count;
+
+   struct eir_compiler *compiler;
+   struct nir_shader *nir;
+   struct eir_shader_variant *variants;
+};
+
+struct eir_shader *
+eir_shader_from_nir(struct eir_compiler *compiler, struct nir_shader *nir);
+
+void
+eir_shader_destroy(struct eir_shader *shader);
+
+struct eir_shader_variant *
+eir_shader_get_variant(struct eir_shader *shader, struct eir_shader_key key, bool *created);
+
+void
+eir_dump_shader(struct eir_shader_variant *variant);
+
+struct eir_shader_linkage {
+   uint8_t num_varyings;
+   struct {
+      uint32_t pa_attributes;
+      uint8_t ncomp;
+      uint8_t use[4];
+      uint8_t reg;
+   } varyings[16];
+};
+
+static inline int
+eir_find_output(const struct eir_shader_variant *v, gl_varying_slot slot)
+{
+	for (unsigned i = 0; i < v->num_outputs; i++)
+		if (v->outputs[i].slot == slot)
+			return i;
+
+   debug_assert(0);
+
+   return 0;
+}
+
+/* TODO: */
+#define VARYING_COMPONENT_USE_UNUSED				0x00000000
+#define VARYING_COMPONENT_USE_USED				0x00000001
+#define VARYING_COMPONENT_USE_POINTCOORD_X			0x00000002
+#define VARYING_COMPONENT_USE_POINTCOORD_Y			0x00000003
+
+static inline void
+eir_link_add(struct eir_shader_linkage *l, uint8_t reg, uint8_t ncomp)
+{
+   bool interpolate_always = false; /* TODO: */
+   uint32_t pa_attributes;
+   int i = l->num_varyings++;
+
+   debug_assert(i < ARRAY_SIZE(l->varyings));
+
+   if (!interpolate_always) /* colors affected by flat shading */
+      pa_attributes = 0x200;
+   else /* texture coord or other bypasses flat shading */
+      pa_attributes = 0x2f1;
+
+   l->varyings[i].reg = reg;
+   l->varyings[i].ncomp = ncomp;
+   l->varyings[i].pa_attributes = pa_attributes;
+
+   l->varyings[i].use[0] = interpolate_always ? VARYING_COMPONENT_USE_POINTCOORD_X : VARYING_COMPONENT_USE_USED;
+   l->varyings[i].use[1] = interpolate_always ? VARYING_COMPONENT_USE_POINTCOORD_Y : VARYING_COMPONENT_USE_USED;
+   l->varyings[i].use[2] = VARYING_COMPONENT_USE_USED;
+   l->varyings[i].use[3] = VARYING_COMPONENT_USE_USED;
+}
+
+static inline void
+eir_link_shader(struct eir_shader_linkage *l,
+   const struct eir_shader_variant *vs,
+   const struct eir_shader_variant *fs)
+{
+   int i = 0;
+
+   while (l->num_varyings < ARRAY_SIZE(l->varyings)) {
+
+		if (i >= fs->num_inputs)
+			break;
+
+      int reg = eir_find_output(vs, fs->inputs[i].slot);
+
+      eir_link_add(l, reg, fs->inputs[i].ncomp);
+
+      i++;
+   }
+}
+
+#endif // H_EIR_SHADER
diff --git a/src/etnaviv/compiler/meson.build b/src/etnaviv/compiler/meson.build
index 747b9536bb7..f8af6c8b35d 100644
--- a/src/etnaviv/compiler/meson.build
+++ b/src/etnaviv/compiler/meson.build
@@ -23,13 +23,17 @@ 
 libetnaviv_compiler_files = files(
   'eir.c',
   'eir.h',
+  'eir_compiler.c',
   'eir_compiler.h',
+  'eir_compiler_nir.c',
   'eir_legalize.c',
   'eir_live_variables.c',
   'eir_nir.c',
   'eir_nir.h',
   'eir_print.c',
   'eir_register_allocate.c',
+  'eir_shader.c',
+  'eir_shader.h',
   'eir_uniform.c',
 )