zink: do not lower io
[mesa.git] / src / gallium / drivers / zink / nir_to_spirv / nir_to_spirv.c
index 166bdf2994913ff92d3b299d075cca97b4b43b10..8534ed1300a4e180035fbb8250bf69bf4201f78e 100644 (file)
@@ -27,6 +27,7 @@
 #include "nir.h"
 #include "pipe/p_state.h"
 #include "util/u_memory.h"
+#include "util/hash_table.h"
 
 struct ntv_context {
    struct spirv_builder builder;
@@ -34,8 +35,7 @@ struct ntv_context {
    SpvId GLSL_std_450;
 
    gl_shader_stage stage;
-   SpvId inputs[PIPE_MAX_SHADER_INPUTS][4];
-   SpvId outputs[PIPE_MAX_SHADER_OUTPUTS][4];
+   int var_location;
 
    SpvId ubos[128];
    size_t num_ubos;
@@ -46,8 +46,37 @@ struct ntv_context {
 
    SpvId *defs;
    size_t num_defs;
+
+   SpvId *regs;
+   size_t num_regs;
+
+   struct hash_table *vars; /* nir_variable -> SpvId */
+
+   const SpvId *block_ids;
+   size_t num_blocks;
+   bool block_started;
+   SpvId loop_break, loop_cont;
 };
 
+static SpvId
+get_fvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
+                  const float values[]);
+
+static SpvId
+get_uvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
+                  const uint32_t values[]);
+
+static SpvId
+emit_unop(struct ntv_context *ctx, SpvOp op, SpvId type, SpvId src);
+
+static SpvId
+emit_binop(struct ntv_context *ctx, SpvOp op, SpvId type,
+           SpvId src0, SpvId src1);
+
+static SpvId
+emit_triop(struct ntv_context *ctx, SpvOp op, SpvId type,
+           SpvId src0, SpvId src1, SpvId src2);
+
 static SpvId
 get_bvec_type(struct ntv_context *ctx, int num_components)
 {
@@ -60,6 +89,13 @@ get_bvec_type(struct ntv_context *ctx, int num_components)
    return bool_type;
 }
 
+static SpvId
+block_label(struct ntv_context *ctx, nir_block *block)
+{
+   assert(block->index < ctx->num_blocks);
+   return ctx->block_ids[block->index];
+}
+
 static SpvId
 get_fvec_type(struct ntv_context *ctx, unsigned bit_size, unsigned num_components)
 {
@@ -75,9 +111,37 @@ get_fvec_type(struct ntv_context *ctx, unsigned bit_size, unsigned num_component
 }
 
 static SpvId
-get_dest_type(struct ntv_context *ctx, nir_dest *dest)
+get_ivec_type(struct ntv_context *ctx, unsigned bit_size, unsigned num_components)
 {
-   return get_fvec_type(ctx, nir_dest_bit_size(*dest),
+   assert(bit_size == 32); // only 32-bit ints supported so far
+
+   SpvId int_type = spirv_builder_type_int(&ctx->builder, bit_size);
+   if (num_components > 1)
+      return spirv_builder_type_vector(&ctx->builder, int_type,
+                                       num_components);
+
+   assert(num_components == 1);
+   return int_type;
+}
+
+static SpvId
+get_uvec_type(struct ntv_context *ctx, unsigned bit_size, unsigned num_components)
+{
+   assert(bit_size == 32); // only 32-bit uints supported so far
+
+   SpvId uint_type = spirv_builder_type_uint(&ctx->builder, bit_size);
+   if (num_components > 1)
+      return spirv_builder_type_vector(&ctx->builder, uint_type,
+                                       num_components);
+
+   assert(num_components == 1);
+   return uint_type;
+}
+
+static SpvId
+get_dest_uvec_type(struct ntv_context *ctx, nir_dest *dest)
+{
+   return get_uvec_type(ctx, nir_dest_bit_size(*dest),
                              nir_dest_num_components(*dest));
 }
 
@@ -118,10 +182,10 @@ get_glsl_type(struct ntv_context *ctx, const struct glsl_type *type)
 static void
 emit_input(struct ntv_context *ctx, struct nir_variable *var)
 {
-   SpvId vec_type = get_glsl_type(ctx, var->type);
+   SpvId var_type = get_glsl_type(ctx, var->type);
    SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
                                                    SpvStorageClassInput,
-                                                   vec_type);
+                                                   var_type);
    SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
                                          SpvStorageClassInput);
 
@@ -129,19 +193,24 @@ emit_input(struct ntv_context *ctx, struct nir_variable *var)
       spirv_builder_emit_name(&ctx->builder, var_id, var->name);
 
    if (ctx->stage == MESA_SHADER_FRAGMENT) {
-      switch (var->data.location) {
-      case VARYING_SLOT_POS:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInFragCoord);
-         break;
-
-      case VARYING_SLOT_PNTC:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPointCoord);
-         break;
-
-      default:
+      if (var->data.location >= VARYING_SLOT_VAR0 ||
+          (var->data.location >= VARYING_SLOT_COL0 &&
+           var->data.location <= VARYING_SLOT_TEX7)) {
          spirv_builder_emit_location(&ctx->builder, var_id,
-                                     var->data.driver_location);
-         break;
+                                     ctx->var_location++);
+      } else {
+         switch (var->data.location) {
+         case VARYING_SLOT_POS:
+            spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInFragCoord);
+            break;
+
+         case VARYING_SLOT_PNTC:
+            spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPointCoord);
+            break;
+
+         default:
+            unreachable("unknown varying slot");
+         }
       }
    } else {
       spirv_builder_emit_location(&ctx->builder, var_id,
@@ -155,10 +224,7 @@ emit_input(struct ntv_context *ctx, struct nir_variable *var)
    if (var->data.interpolation == INTERP_MODE_FLAT)
       spirv_builder_emit_decoration(&ctx->builder, var_id, SpvDecorationFlat);
 
-   assert(var->data.driver_location < PIPE_MAX_SHADER_INPUTS);
-   assert(var->data.location_frac < 4);
-   assert(ctx->inputs[var->data.driver_location][var->data.location_frac] == 0);
-   ctx->inputs[var->data.driver_location][var->data.location_frac] = var_id;
+   _mesa_hash_table_insert(ctx->vars, var, (void *)(intptr_t)var_id);
 
    assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
    ctx->entry_ifaces[ctx->num_entry_ifaces++] = var_id;
@@ -167,10 +233,10 @@ emit_input(struct ntv_context *ctx, struct nir_variable *var)
 static void
 emit_output(struct ntv_context *ctx, struct nir_variable *var)
 {
-   SpvId vec_type = get_glsl_type(ctx, var->type);
+   SpvId var_type = get_glsl_type(ctx, var->type);
    SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
                                                    SpvStorageClassOutput,
-                                                   vec_type);
+                                                   var_type);
    SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
                                          SpvStorageClassOutput);
    if (var->name)
@@ -178,18 +244,24 @@ emit_output(struct ntv_context *ctx, struct nir_variable *var)
 
 
    if (ctx->stage == MESA_SHADER_VERTEX) {
-      switch (var->data.location) {
-      case VARYING_SLOT_POS:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPosition);
-         break;
-
-      case VARYING_SLOT_PSIZ:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPointSize);
-         break;
-
-      default:
+      if (var->data.location >= VARYING_SLOT_VAR0 ||
+          (var->data.location >= VARYING_SLOT_COL0 &&
+           var->data.location <= VARYING_SLOT_TEX7)) {
          spirv_builder_emit_location(&ctx->builder, var_id,
-                                     var->data.driver_location - 1);
+                                     ctx->var_location++);
+      } else {
+         switch (var->data.location) {
+         case VARYING_SLOT_POS:
+            spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPosition);
+            break;
+
+         case VARYING_SLOT_PSIZ:
+            spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPointSize);
+            break;
+
+         default:
+            unreachable("unknown varying slot");
+         }
       }
    } else if (ctx->stage == MESA_SHADER_FRAGMENT) {
       switch (var->data.location) {
@@ -207,10 +279,7 @@ emit_output(struct ntv_context *ctx, struct nir_variable *var)
       spirv_builder_emit_component(&ctx->builder, var_id,
                                    var->data.location_frac);
 
-   assert(var->data.driver_location < PIPE_MAX_SHADER_INPUTS);
-   assert(var->data.location_frac < 4);
-   assert(ctx->outputs[var->data.driver_location][var->data.location_frac] == 0);
-   ctx->outputs[var->data.driver_location][var->data.location_frac] = var_id;
+   _mesa_hash_table_insert(ctx->vars, var, (void *)(intptr_t)var_id);
 
    assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
    ctx->entry_ifaces[ctx->num_entry_ifaces++] = var_id;
@@ -274,7 +343,7 @@ static void
 emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
 {
    uint32_t size = glsl_count_attribute_slots(var->type, false);
-   SpvId vec4_type = get_fvec_type(ctx, 32, 4);
+   SpvId vec4_type = get_uvec_type(ctx, 32, 4);
    SpvId array_length = spirv_builder_const_uint(&ctx->builder, 32, size);
    SpvId array_type = spirv_builder_type_array(&ctx->builder, vec4_type,
                                                array_length);
@@ -320,21 +389,49 @@ emit_uniform(struct ntv_context *ctx, struct nir_variable *var)
 }
 
 static SpvId
-get_src(struct ntv_context *ctx, nir_src *src)
+get_src_uint_ssa(struct ntv_context *ctx, const nir_ssa_def *ssa)
 {
-   assert(src->is_ssa);
-   assert(src->ssa->index < ctx->num_defs);
-   assert(ctx->defs[src->ssa->index] != 0);
-   return ctx->defs[src->ssa->index];
+   assert(ssa->index < ctx->num_defs);
+   assert(ctx->defs[ssa->index] != 0);
+   return ctx->defs[ssa->index];
 }
 
 static SpvId
-get_alu_src(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
+get_var_from_reg(struct ntv_context *ctx, nir_register *reg)
+{
+   assert(reg->index < ctx->num_regs);
+   assert(ctx->regs[reg->index] != 0);
+   return ctx->regs[reg->index];
+}
+
+static SpvId
+get_src_uint_reg(struct ntv_context *ctx, const nir_reg_src *reg)
+{
+   assert(reg->reg);
+   assert(!reg->indirect);
+   assert(!reg->base_offset);
+
+   SpvId var = get_var_from_reg(ctx, reg->reg);
+   SpvId type = get_uvec_type(ctx, reg->reg->bit_size, reg->reg->num_components);
+   return spirv_builder_emit_load(&ctx->builder, type, var);
+}
+
+static SpvId
+get_src_uint(struct ntv_context *ctx, nir_src *src)
+{
+   if (src->is_ssa)
+      return get_src_uint_ssa(ctx, src->ssa);
+   else
+      return get_src_uint_reg(ctx, &src->reg);
+}
+
+static SpvId
+get_alu_src_uint(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
 {
    assert(!alu->src[src].negate);
    assert(!alu->src[src].abs);
 
-   SpvId def = get_src(ctx, &alu->src[src].src);
+   SpvId def = get_src_uint(ctx, &alu->src[src].src);
 
    unsigned used_channels = 0;
    bool need_swizzle = false;
@@ -358,23 +455,27 @@ get_alu_src(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
 
    int bit_size = nir_src_bit_size(alu->src[src].src);
 
+   SpvId uint_type = spirv_builder_type_uint(&ctx->builder, bit_size);
    if (used_channels == 1) {
-      SpvId result_type = spirv_builder_type_float(&ctx->builder, bit_size);
       uint32_t indices[] =  { alu->src[src].swizzle[0] };
-      return spirv_builder_emit_composite_extract(&ctx->builder, result_type,
+      return spirv_builder_emit_composite_extract(&ctx->builder, uint_type,
                                                   def, indices,
                                                   ARRAY_SIZE(indices));
    } else if (live_channels == 1) {
-      SpvId type = get_fvec_type(ctx, bit_size, used_channels);
+      SpvId uvec_type = spirv_builder_type_vector(&ctx->builder, uint_type,
+                                                  used_channels);
 
       SpvId constituents[NIR_MAX_VEC_COMPONENTS];
       for (unsigned i = 0; i < used_channels; ++i)
         constituents[i] = def;
 
-      return spirv_builder_emit_composite_construct(&ctx->builder, type,
+      return spirv_builder_emit_composite_construct(&ctx->builder, uvec_type,
                                                     constituents,
                                                     used_channels);
    } else {
+      SpvId uvec_type = spirv_builder_type_vector(&ctx->builder, uint_type,
+                                                  used_channels);
+
       uint32_t components[NIR_MAX_VEC_COMPONENTS];
       size_t num_components = 0;
       for (unsigned i = 0; i < NIR_MAX_VEC_COMPONENTS; i++) {
@@ -384,32 +485,107 @@ get_alu_src(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
          components[num_components++] = alu->src[src].swizzle[i];
       }
 
-      SpvId vecType = get_fvec_type(ctx, bit_size, used_channels);
-      return spirv_builder_emit_vector_shuffle(&ctx->builder, vecType,
+      return spirv_builder_emit_vector_shuffle(&ctx->builder, uvec_type,
                                         def, def, components, num_components);
    }
 }
 
 static void
-store_ssa_def(struct ntv_context *ctx, nir_ssa_def *ssa, SpvId result)
+store_ssa_def_uint(struct ntv_context *ctx, nir_ssa_def *ssa, SpvId result)
 {
    assert(result != 0);
    assert(ssa->index < ctx->num_defs);
    ctx->defs[ssa->index] = result;
 }
 
+static SpvId
+bvec_to_uvec(struct ntv_context *ctx, SpvId value, unsigned num_components)
+{
+   SpvId otype = get_uvec_type(ctx, 32, num_components);
+   uint32_t zeros[4] = { 0, 0, 0, 0 };
+   uint32_t ones[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
+   SpvId zero = get_uvec_constant(ctx, 32, num_components, zeros);
+   SpvId one = get_uvec_constant(ctx, 32, num_components, ones);
+   return emit_triop(ctx, SpvOpSelect, otype, value, one, zero);
+}
+
+static SpvId
+uvec_to_bvec(struct ntv_context *ctx, SpvId value, unsigned num_components)
+{
+   SpvId type = get_bvec_type(ctx, num_components);
+
+   uint32_t zeros[NIR_MAX_VEC_COMPONENTS] = { 0 };
+   SpvId zero = get_uvec_constant(ctx, 32, num_components, zeros);
+
+   return emit_binop(ctx, SpvOpINotEqual, type, value, zero);
+}
+
+static SpvId
+bitcast_to_uvec(struct ntv_context *ctx, SpvId value, unsigned bit_size,
+                unsigned num_components)
+{
+   SpvId type = get_uvec_type(ctx, bit_size, num_components);
+   return emit_unop(ctx, SpvOpBitcast, type, value);
+}
+
+static SpvId
+bitcast_to_ivec(struct ntv_context *ctx, SpvId value, unsigned bit_size,
+                unsigned num_components)
+{
+   SpvId type = get_ivec_type(ctx, bit_size, num_components);
+   return emit_unop(ctx, SpvOpBitcast, type, value);
+}
+
+static SpvId
+bitcast_to_fvec(struct ntv_context *ctx, SpvId value, unsigned bit_size,
+               unsigned num_components)
+{
+   SpvId type = get_fvec_type(ctx, bit_size, num_components);
+   return emit_unop(ctx, SpvOpBitcast, type, value);
+}
+
 static void
-store_dest(struct ntv_context *ctx, nir_dest *dest, SpvId result)
+store_reg_def(struct ntv_context *ctx, nir_reg_dest *reg, SpvId result)
 {
-   assert(dest->is_ssa);
-   store_ssa_def(ctx, &dest->ssa, result);
+   SpvId var = get_var_from_reg(ctx, reg->reg);
+   assert(var);
+   spirv_builder_emit_store(&ctx->builder, var, result);
 }
 
 static void
-store_alu_result(struct ntv_context *ctx, nir_alu_dest *dest, SpvId result)
+store_dest_uint(struct ntv_context *ctx, nir_dest *dest, SpvId result)
 {
-   assert(!dest->saturate);
-   return store_dest(ctx, &dest->dest, result);
+   if (dest->is_ssa)
+      store_ssa_def_uint(ctx, &dest->ssa, result);
+   else
+      store_reg_def(ctx, &dest->reg, result);
+}
+
+static void
+store_dest(struct ntv_context *ctx, nir_dest *dest, SpvId result, nir_alu_type type)
+{
+   unsigned num_components = nir_dest_num_components(*dest);
+   unsigned bit_size = nir_dest_bit_size(*dest);
+
+   switch (nir_alu_type_get_base_type(type)) {
+   case nir_type_bool:
+      assert(bit_size == 1);
+      result = bvec_to_uvec(ctx, result, num_components);
+      break;
+
+   case nir_type_uint:
+      break; /* nothing to do! */
+
+   case nir_type_int:
+   case nir_type_float:
+      result = bitcast_to_uvec(ctx, result, bit_size, num_components);
+      break;
+
+   default:
+      unreachable("unsupported nir_alu_type");
+   }
+
+   store_dest_uint(ctx, dest, result);
 }
 
 static SpvId
@@ -452,7 +628,7 @@ emit_builtin_binop(struct ntv_context *ctx, enum GLSLstd450 op, SpvId type,
 
 static SpvId
 get_fvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
-                 float values[])
+                  const float values[])
 {
    assert(bit_size == 32);
 
@@ -471,6 +647,98 @@ get_fvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
    return spirv_builder_const_float(&ctx->builder, bit_size, values[0]);
 }
 
+static SpvId
+get_uvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
+                  const uint32_t values[])
+{
+   assert(bit_size == 32);
+
+   if (num_components > 1) {
+      SpvId components[num_components];
+      for (int i = 0; i < num_components; i++)
+         components[i] = spirv_builder_const_uint(&ctx->builder, bit_size,
+                                                  values[i]);
+
+      SpvId type = get_uvec_type(ctx, bit_size, num_components);
+      return spirv_builder_const_composite(&ctx->builder, type, components,
+                                           num_components);
+   }
+
+   assert(num_components == 1);
+   return spirv_builder_const_uint(&ctx->builder, bit_size, values[0]);
+}
+
+static inline unsigned
+alu_instr_src_components(const nir_alu_instr *instr, unsigned src)
+{
+   if (nir_op_infos[instr->op].input_sizes[src] > 0)
+      return nir_op_infos[instr->op].input_sizes[src];
+
+   if (instr->dest.dest.is_ssa)
+      return instr->dest.dest.ssa.num_components;
+   else
+      return instr->dest.dest.reg.reg->num_components;
+}
+
+static SpvId
+get_alu_src(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
+{
+   SpvId uint_value = get_alu_src_uint(ctx, alu, src);
+
+   unsigned num_components = alu_instr_src_components(alu, src);
+   unsigned bit_size = nir_src_bit_size(alu->src[src].src);
+   nir_alu_type type = nir_op_infos[alu->op].input_types[src];
+
+   switch (nir_alu_type_get_base_type(type)) {
+   case nir_type_bool:
+      assert(bit_size == 1);
+      return uvec_to_bvec(ctx, uint_value, num_components);
+
+   case nir_type_int:
+      return bitcast_to_ivec(ctx, uint_value, bit_size, num_components);
+
+   case nir_type_uint:
+      return uint_value;
+
+   case nir_type_float:
+      return bitcast_to_fvec(ctx, uint_value, bit_size, num_components);
+
+   default:
+      unreachable("unknown nir_alu_type");
+   }
+}
+
+static void
+store_alu_result(struct ntv_context *ctx, nir_alu_instr *alu, SpvId result)
+{
+   assert(!alu->dest.saturate);
+   return store_dest(ctx, &alu->dest.dest, result, nir_op_infos[alu->op].output_type);
+}
+
+static SpvId
+get_dest_type(struct ntv_context *ctx, nir_dest *dest, nir_alu_type type)
+{
+   unsigned num_components = nir_dest_num_components(*dest);
+   unsigned bit_size = nir_dest_bit_size(*dest);
+
+   switch (nir_alu_type_get_base_type(type)) {
+   case nir_type_bool:
+      return get_bvec_type(ctx, num_components);
+
+   case nir_type_int:
+      return get_ivec_type(ctx, bit_size, num_components);
+
+   case nir_type_uint:
+      return get_uvec_type(ctx, bit_size, num_components);
+
+   case nir_type_float:
+      return get_fvec_type(ctx, bit_size, num_components);
+
+   default:
+      unreachable("unsupported nir_alu_type");
+   }
+}
+
 static void
 emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
 {
@@ -478,7 +746,10 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
    for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++)
       src[i] = get_alu_src(ctx, alu, i);
 
-   SpvId dest_type = get_dest_type(ctx, &alu->dest.dest);
+   SpvId dest_type = get_dest_type(ctx, &alu->dest.dest,
+                                   nir_op_infos[alu->op].output_type);
+   unsigned bit_size = nir_dest_bit_size(alu->dest.dest);
+   unsigned num_components = nir_dest_num_components(alu->dest.dest);
 
    SpvId result = 0;
    switch (alu->op) {
@@ -521,9 +792,7 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       assert(nir_op_infos[alu->op].num_inputs == 1);
       float one[4] = { 1, 1, 1, 1 };
       src[1] = src[0];
-      src[0] = get_fvec_constant(ctx, nir_dest_bit_size(alu->dest.dest),
-                                     nir_dest_num_components(alu->dest.dest),
-                                     one);
+      src[0] = get_fvec_constant(ctx, bit_size, num_components, one);
       result = emit_binop(ctx, SpvOpFDiv, dest_type, src[0], src[1]);
       }
       break;
@@ -543,9 +812,13 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       result = emit_builtin_binop(ctx, spirv_op, dest_type, src[0], src[1]); \
       break;
 
+   BINOP(nir_op_iadd, SpvOpIAdd)
+   BINOP(nir_op_isub, SpvOpISub)
+   BINOP(nir_op_imul, SpvOpIMul)
    BINOP(nir_op_fadd, SpvOpFAdd)
    BINOP(nir_op_fsub, SpvOpFSub)
    BINOP(nir_op_fmul, SpvOpFMul)
+   BINOP(nir_op_fmod, SpvOpFMod)
    BINOP(nir_op_flt, SpvOpFUnordLessThan)
    BINOP(nir_op_fge, SpvOpFUnordGreaterThanEqual)
 
@@ -631,40 +904,20 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       return;
    }
 
-   store_alu_result(ctx, &alu->dest, result);
+   store_alu_result(ctx, alu, result);
 }
 
 static void
 emit_load_const(struct ntv_context *ctx, nir_load_const_instr *load_const)
 {
-   float values[NIR_MAX_VEC_COMPONENTS];
+   uint32_t values[NIR_MAX_VEC_COMPONENTS];
    for (int i = 0; i < load_const->def.num_components; ++i)
-      values[i] = load_const->value[i].f32;
+      values[i] = load_const->value[i].u32;
 
-   SpvId constant = get_fvec_constant(ctx, load_const->def.bit_size,
-                                            load_const->def.num_components,
-                                            values);
-   store_ssa_def(ctx, &load_const->def, constant);
-}
-
-static void
-emit_load_input(struct ntv_context *ctx, nir_intrinsic_instr *intr)
-{
-   nir_const_value *const_offset = nir_src_as_const_value(intr->src[0]);
-   if (const_offset) {
-      SpvId type = get_dest_type(ctx, &intr->dest);
-
-      int driver_location = (int)nir_intrinsic_base(intr) + const_offset->u32;
-      assert(driver_location < PIPE_MAX_SHADER_INPUTS);
-      int location_frac = nir_intrinsic_component(intr);
-      assert(location_frac < 4);
-
-      SpvId ptr = ctx->inputs[driver_location][location_frac];
-      assert(ptr > 0);
-
-      store_dest(ctx, &intr->dest, spirv_builder_emit_load(&ctx->builder, type, ptr));
-   } else
-      unreachable("input-addressing not yet supported");
+   SpvId constant = get_uvec_constant(ctx, load_const->def.bit_size,
+                                           load_const->def.num_components,
+                                           values);
+   store_ssa_def_uint(ctx, &load_const->def, constant);
 }
 
 static void
@@ -676,10 +929,10 @@ emit_load_ubo(struct ntv_context *ctx, nir_intrinsic_instr *intr)
 
    nir_const_value *const_offset = nir_src_as_const_value(intr->src[1]);
    if (const_offset) {
-      SpvId vec4_type = get_fvec_type(ctx, 32, 4);
+      SpvId uvec4_type = get_uvec_type(ctx, 32, 4);
       SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
                                                       SpvStorageClassUniform,
-                                                      vec4_type);
+                                                      uvec4_type);
 
       unsigned idx = const_offset->u32;
       SpvId member = spirv_builder_const_uint(&ctx->builder, 32, 0);
@@ -688,9 +941,9 @@ emit_load_ubo(struct ntv_context *ctx, nir_intrinsic_instr *intr)
       SpvId ptr = spirv_builder_emit_access_chain(&ctx->builder, pointer_type,
                                                   ctx->ubos[0], offsets,
                                                   ARRAY_SIZE(offsets));
-      SpvId result = spirv_builder_emit_load(&ctx->builder, vec4_type, ptr);
+      SpvId result = spirv_builder_emit_load(&ctx->builder, uvec4_type, ptr);
 
-      SpvId type = get_dest_type(ctx, &intr->dest);
+      SpvId type = get_dest_uvec_type(ctx, &intr->dest);
       unsigned num_components = nir_dest_num_components(intr->dest);
       if (num_components == 1) {
          uint32_t components[] = { 0 };
@@ -700,10 +953,10 @@ emit_load_ubo(struct ntv_context *ctx, nir_intrinsic_instr *intr)
                                                        1);
       } else if (num_components < 4) {
          SpvId constituents[num_components];
-         SpvId float_type = spirv_builder_type_float(&ctx->builder, 32);
+         SpvId uint_type = spirv_builder_type_uint(&ctx->builder, 32);
          for (uint32_t i = 0; i < num_components; ++i)
             constituents[i] = spirv_builder_emit_composite_extract(&ctx->builder,
-                                                                   float_type,
+                                                                   uint_type,
                                                                    result, &i,
                                                                    1);
 
@@ -713,44 +966,72 @@ emit_load_ubo(struct ntv_context *ctx, nir_intrinsic_instr *intr)
                                                          num_components);
       }
 
-      store_dest(ctx, &intr->dest, result);
+      store_dest_uint(ctx, &intr->dest, result);
    } else
       unreachable("uniform-addressing not yet supported");
 }
 
 static void
-emit_store_output(struct ntv_context *ctx, nir_intrinsic_instr *intr)
+emit_discard(struct ntv_context *ctx, nir_intrinsic_instr *intr)
 {
-   nir_const_value *const_offset = nir_src_as_const_value(intr->src[1]);
-   if (const_offset) {
-      int driver_location = (int)nir_intrinsic_base(intr) + const_offset->u32;
-      assert(driver_location < PIPE_MAX_SHADER_OUTPUTS);
-      int location_frac = nir_intrinsic_component(intr);
-      assert(location_frac < 4);
+   assert(ctx->block_started);
+   spirv_builder_emit_kill(&ctx->builder);
+   /* discard is weird in NIR, so let's just create an unreachable block after
+      it and hope that the vulkan driver will DCE any instructinos in it. */
+   spirv_builder_label(&ctx->builder, spirv_builder_new_id(&ctx->builder));
+}
 
-      SpvId ptr = ctx->outputs[driver_location][location_frac];
-      assert(ptr > 0);
+static void
+emit_load_deref(struct ntv_context *ctx, nir_intrinsic_instr *intr)
+{
+   nir_variable *var = nir_intrinsic_get_var(intr, 0);
+   struct hash_entry *he = _mesa_hash_table_search(ctx->vars, var);
+   assert(he);
+   SpvId ptr = (SpvId)(intptr_t)he->data;
+
+   // SpvId ptr = get_src_uint(ctx, intr->src); /* uint is a bit of a lie here; it's really just a pointer */
+   SpvId result = spirv_builder_emit_load(&ctx->builder,
+                                          get_glsl_type(ctx, var->type),
+                                          ptr);
+   unsigned num_components = nir_dest_num_components(intr->dest);
+   unsigned bit_size = nir_dest_bit_size(intr->dest);
+   result = bitcast_to_uvec(ctx, result, bit_size, num_components);
+   store_dest_uint(ctx, &intr->dest, result);
+}
 
-      SpvId src = get_src(ctx, &intr->src[0]);
-      spirv_builder_emit_store(&ctx->builder, ptr, src);
-   } else
-      unreachable("output-addressing not yet supported");
+static void
+emit_store_deref(struct ntv_context *ctx, nir_intrinsic_instr *intr)
+{
+   nir_variable *var = nir_intrinsic_get_var(intr, 0);
+   struct hash_entry *he = _mesa_hash_table_search(ctx->vars, var);
+   assert(he);
+   SpvId ptr = (SpvId)(intptr_t)he->data;
+   // SpvId ptr = get_src_uint(ctx, &intr->src[0]); /* uint is a bit of a lie here; it's really just a pointer */
+
+   SpvId src = get_src_uint(ctx, &intr->src[1]);
+   SpvId result = emit_unop(ctx, SpvOpBitcast, get_glsl_type(ctx, var->type),
+                            src);
+   spirv_builder_emit_store(&ctx->builder, ptr, result);
 }
 
 static void
 emit_intrinsic(struct ntv_context *ctx, nir_intrinsic_instr *intr)
 {
    switch (intr->intrinsic) {
-   case nir_intrinsic_load_input:
-      emit_load_input(ctx, intr);
-      break;
-
    case nir_intrinsic_load_ubo:
       emit_load_ubo(ctx, intr);
       break;
 
-   case nir_intrinsic_store_output:
-      emit_store_output(ctx, intr);
+   case nir_intrinsic_discard:
+      emit_discard(ctx, intr);
+      break;
+
+   case nir_intrinsic_load_deref:
+      emit_load_deref(ctx, intr);
+      break;
+
+   case nir_intrinsic_store_deref:
+      emit_store_deref(ctx, intr);
       break;
 
    default:
@@ -763,11 +1044,20 @@ emit_intrinsic(struct ntv_context *ctx, nir_intrinsic_instr *intr)
 static void
 emit_undef(struct ntv_context *ctx, nir_ssa_undef_instr *undef)
 {
-   SpvId type = get_fvec_type(ctx, undef->def.bit_size,
+   SpvId type = get_uvec_type(ctx, undef->def.bit_size,
                               undef->def.num_components);
 
-   store_ssa_def(ctx, &undef->def,
-                 spirv_builder_emit_undef(&ctx->builder, type));
+   store_ssa_def_uint(ctx, &undef->def,
+                      spirv_builder_emit_undef(&ctx->builder, type));
+}
+
+static SpvId
+get_src_float(struct ntv_context *ctx, nir_src *src)
+{
+   SpvId def = get_src_uint(ctx, src);
+   unsigned num_components = nir_src_num_components(*src);
+   unsigned bit_size = nir_src_bit_size(*src);
+   return bitcast_to_fvec(ctx, def, bit_size, num_components);
 }
 
 static void
@@ -777,19 +1067,26 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
    assert(nir_alu_type_get_base_type(tex->dest_type) == nir_type_float);
    assert(tex->texture_index == tex->sampler_index);
 
-   bool has_proj = false;
-   SpvId coord = 0, proj;
-   unsigned coord_size;
+   bool has_proj = false, has_lod = false;
+   SpvId coord = 0, proj, lod;
+   unsigned coord_components;
    for (unsigned i = 0; i < tex->num_srcs; i++) {
       switch (tex->src[i].src_type) {
       case nir_tex_src_coord:
-         coord = get_src(ctx, &tex->src[i].src);
-         coord_size = nir_src_num_components(tex->src[i].src);
+         coord = get_src_float(ctx, &tex->src[i].src);
+         coord_components = nir_src_num_components(tex->src[i].src);
          break;
 
       case nir_tex_src_projector:
          has_proj = true;
-         proj = get_src(ctx, &tex->src[i].src);
+         proj = get_src_float(ctx, &tex->src[i].src);
+         assert(nir_src_num_components(tex->src[i].src) == 1);
+         break;
+
+      case nir_tex_src_lod:
+         has_lod = true;
+         lod = get_src_float(ctx, &tex->src[i].src);
+         assert(nir_src_num_components(tex->src[i].src) == 1);
          break;
 
       default:
@@ -798,6 +1095,11 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
       }
    }
 
+   if (!has_lod && ctx->stage != MESA_SHADER_FRAGMENT) {
+      has_lod = true;
+      lod = spirv_builder_const_float(&ctx->builder, 32, 0);
+   }
+
    bool is_ms;
    SpvDim dimension = type_to_dim(tex->sampler_dim, &is_ms);
    SpvId float_type = spirv_builder_type_float(&ctx->builder, 32);
@@ -811,43 +1113,142 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
    SpvId load = spirv_builder_emit_load(&ctx->builder, sampled_type,
                                         ctx->samplers[tex->texture_index]);
 
-   SpvId dest_type = get_dest_type(ctx, &tex->dest);
+   SpvId dest_type = get_dest_type(ctx, &tex->dest, tex->dest_type);
 
    SpvId result;
    if (has_proj) {
-      SpvId constituents[coord_size + 1];
+      SpvId constituents[coord_components + 1];
       SpvId float_type = spirv_builder_type_float(&ctx->builder, 32);
-      for (uint32_t i = 0; i < coord_size; ++i)
+      for (uint32_t i = 0; i < coord_components; ++i)
          constituents[i] = spirv_builder_emit_composite_extract(&ctx->builder,
                                               float_type,
                                               coord,
                                               &i, 1);
 
-      constituents[coord_size++] = proj;
+      constituents[coord_components++] = proj;
 
-      SpvId vec_type = get_fvec_type(ctx, 32, coord_size);
+      SpvId vec_type = get_fvec_type(ctx, 32, coord_components);
       SpvId merged = spirv_builder_emit_composite_construct(&ctx->builder,
                                                             vec_type,
                                                             constituents,
-                                                            coord_size);
-
-      result = spirv_builder_emit_image_sample_proj_implicit_lod(&ctx->builder,
-                                                                 dest_type,
-                                                                 load,
-                                                                 merged);
-   } else
-      result = spirv_builder_emit_image_sample_implicit_lod(&ctx->builder,
-                                                            dest_type, load,
-                                                            coord);
+                                                            coord_components);
+
+      if (has_lod)
+         result = spirv_builder_emit_image_sample_proj_explicit_lod(&ctx->builder,
+                                                                    dest_type,
+                                                                    load,
+                                                                    merged,
+                                                                    lod);
+      else
+         result = spirv_builder_emit_image_sample_proj_implicit_lod(&ctx->builder,
+                                                                    dest_type,
+                                                                    load,
+                                                                    merged);
+   } else {
+      if (has_lod)
+         result = spirv_builder_emit_image_sample_explicit_lod(&ctx->builder,
+                                                               dest_type,
+                                                               load,
+                                                               coord, lod);
+      else
+         result = spirv_builder_emit_image_sample_implicit_lod(&ctx->builder,
+                                                               dest_type,
+                                                               load,
+                                                               coord);
+   }
    spirv_builder_emit_decoration(&ctx->builder, result,
                                  SpvDecorationRelaxedPrecision);
 
-   store_dest(ctx, &tex->dest, result);
+   store_dest(ctx, &tex->dest, result, tex->dest_type);
+}
+
+static void
+start_block(struct ntv_context *ctx, SpvId label)
+{
+   /* terminate previous block if needed */
+   if (ctx->block_started)
+      spirv_builder_emit_branch(&ctx->builder, label);
+
+   /* start new block */
+   spirv_builder_label(&ctx->builder, label);
+   ctx->block_started = true;
+}
+
+static void
+branch(struct ntv_context *ctx, SpvId label)
+{
+   assert(ctx->block_started);
+   spirv_builder_emit_branch(&ctx->builder, label);
+   ctx->block_started = false;
+}
+
+static void
+branch_conditional(struct ntv_context *ctx, SpvId condition, SpvId then_id,
+                   SpvId else_id)
+{
+   assert(ctx->block_started);
+   spirv_builder_emit_branch_conditional(&ctx->builder, condition,
+                                         then_id, else_id);
+   ctx->block_started = false;
+}
+
+static void
+emit_jump(struct ntv_context *ctx, nir_jump_instr *jump)
+{
+   switch (jump->type) {
+   case nir_jump_break:
+      assert(ctx->loop_break);
+      branch(ctx, ctx->loop_break);
+      break;
+
+   case nir_jump_continue:
+      assert(ctx->loop_cont);
+      branch(ctx, ctx->loop_cont);
+      break;
+
+   default:
+      unreachable("Unsupported jump type\n");
+   }
+}
+
+static void
+emit_deref(struct ntv_context *ctx, nir_deref_instr *deref)
+{
+   assert(deref->deref_type == nir_deref_type_var);
+
+   SpvStorageClass storage_class;
+   switch (deref->var->data.mode) {
+   case nir_var_shader_in:
+      storage_class = SpvStorageClassInput;
+      break;
+
+   case nir_var_shader_out:
+      storage_class = SpvStorageClassOutput;
+      break;
+
+   default:
+      unreachable("Unsupported nir_variable_mode\n");
+   }
+
+   struct hash_entry *he = _mesa_hash_table_search(ctx->vars, deref->var);
+   assert(he);
+
+   SpvId ptr_type = spirv_builder_type_pointer(&ctx->builder,
+                                               storage_class,
+                                               get_glsl_type(ctx, deref->type));
+
+   SpvId result = spirv_builder_emit_access_chain(&ctx->builder,
+                                                  ptr_type,
+                                                  (SpvId)(intptr_t)he->data,
+                                                  NULL, 0);
+   /* uint is a bit of a lie here, it's really just an opaque type */
+   store_dest_uint(ctx, &deref->dest, result);
 }
 
 static void
 emit_block(struct ntv_context *ctx, struct nir_block *block)
 {
+   start_block(ctx, block_label(ctx, block));
    nir_foreach_instr(instr, block) {
       switch (instr->type) {
       case nir_instr_type_alu:
@@ -869,7 +1270,7 @@ emit_block(struct ntv_context *ctx, struct nir_block *block)
          unreachable("nir_instr_type_phi not supported");
          break;
       case nir_instr_type_jump:
-         unreachable("nir_instr_type_jump not supported");
+         emit_jump(ctx, nir_instr_as_jump(instr));
          break;
       case nir_instr_type_call:
          unreachable("nir_instr_type_call not supported");
@@ -878,12 +1279,89 @@ emit_block(struct ntv_context *ctx, struct nir_block *block)
          unreachable("nir_instr_type_parallel_copy not supported");
          break;
       case nir_instr_type_deref:
-         unreachable("nir_instr_type_deref not supported");
+         /* these are handled in emit_{load,store}_deref */
+         /* emit_deref(ctx, nir_instr_as_deref(instr)); */
          break;
       }
    }
 }
 
+static void
+emit_cf_list(struct ntv_context *ctx, struct exec_list *list);
+
+static SpvId
+get_src_bool(struct ntv_context *ctx, nir_src *src)
+{
+   SpvId def = get_src_uint(ctx, src);
+   assert(nir_src_bit_size(*src) == 32);
+   unsigned num_components = nir_src_num_components(*src);
+   return uvec_to_bvec(ctx, def, num_components);
+}
+
+static void
+emit_if(struct ntv_context *ctx, nir_if *if_stmt)
+{
+   SpvId condition = get_src_bool(ctx, &if_stmt->condition);
+
+   SpvId header_id = spirv_builder_new_id(&ctx->builder);
+   SpvId then_id = block_label(ctx, nir_if_first_then_block(if_stmt));
+   SpvId endif_id = spirv_builder_new_id(&ctx->builder);
+   SpvId else_id = endif_id;
+
+   bool has_else = !exec_list_is_empty(&if_stmt->else_list);
+   if (has_else) {
+      assert(nir_if_first_else_block(if_stmt)->index < ctx->num_blocks);
+      else_id = block_label(ctx, nir_if_first_else_block(if_stmt));
+   }
+
+   /* create a header-block */
+   start_block(ctx, header_id);
+   spirv_builder_emit_selection_merge(&ctx->builder, endif_id,
+                                      SpvSelectionControlMaskNone);
+   branch_conditional(ctx, condition, then_id, else_id);
+
+   emit_cf_list(ctx, &if_stmt->then_list);
+
+   if (has_else) {
+      if (ctx->block_started)
+         branch(ctx, endif_id);
+
+      emit_cf_list(ctx, &if_stmt->else_list);
+   }
+
+   start_block(ctx, endif_id);
+}
+
+static void
+emit_loop(struct ntv_context *ctx, nir_loop *loop)
+{
+   SpvId header_id = spirv_builder_new_id(&ctx->builder);
+   SpvId begin_id = block_label(ctx, nir_loop_first_block(loop));
+   SpvId break_id = spirv_builder_new_id(&ctx->builder);
+   SpvId cont_id = spirv_builder_new_id(&ctx->builder);
+
+   /* create a header-block */
+   start_block(ctx, header_id);
+   spirv_builder_loop_merge(&ctx->builder, break_id, cont_id, SpvLoopControlMaskNone);
+   branch(ctx, begin_id);
+
+   SpvId save_break = ctx->loop_break;
+   SpvId save_cont = ctx->loop_cont;
+   ctx->loop_break = break_id;
+   ctx->loop_cont = cont_id;
+
+   emit_cf_list(ctx, &loop->body);
+
+   ctx->loop_break = save_break;
+   ctx->loop_cont = save_cont;
+
+   branch(ctx, cont_id);
+   start_block(ctx, cont_id);
+   branch(ctx, header_id);
+
+   start_block(ctx, break_id);
+}
+
 static void
 emit_cf_list(struct ntv_context *ctx, struct exec_list *list)
 {
@@ -894,11 +1372,11 @@ emit_cf_list(struct ntv_context *ctx, struct exec_list *list)
          break;
 
       case nir_cf_node_if:
-         unreachable("nir_cf_node_if not supported");
+         emit_if(ctx, nir_cf_node_as_if(node));
          break;
 
       case nir_cf_node_loop:
-         unreachable("nir_cf_node_loop not supported");
+         emit_loop(ctx, nir_cf_node_as_loop(node));
          break;
 
       case nir_cf_node_function:
@@ -970,9 +1448,11 @@ nir_to_spirv(struct nir_shader *s)
    SpvId type_main = spirv_builder_type_function(&ctx.builder, type_void,
                                                  NULL, 0);
    SpvId entry_point = spirv_builder_new_id(&ctx.builder);
-   SpvId label = spirv_builder_new_id(&ctx.builder);
    spirv_builder_emit_name(&ctx.builder, entry_point, "main");
 
+   ctx.vars = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
+                                      _mesa_key_pointer_equal);
+
    nir_foreach_variable(var, &s->inputs)
       emit_input(&ctx, var);
 
@@ -993,16 +1473,46 @@ nir_to_spirv(struct nir_shader *s)
    spirv_builder_function(&ctx.builder, entry_point, type_void,
                                             SpvFunctionControlMaskNone,
                                             type_main);
-   spirv_builder_label(&ctx.builder, label);
 
    nir_function_impl *entry = nir_shader_get_entrypoint(s);
+   nir_metadata_require(entry, nir_metadata_block_index);
 
    ctx.defs = (SpvId *)malloc(sizeof(SpvId) * entry->ssa_alloc);
    if (!ctx.defs)
       goto fail;
    ctx.num_defs = entry->ssa_alloc;
 
+   nir_index_local_regs(entry);
+   ctx.regs = malloc(sizeof(SpvId) * entry->reg_alloc);
+   if (!ctx.regs)
+      goto fail;
+   ctx.num_regs = entry->reg_alloc;
+
+   SpvId *block_ids = (SpvId *)malloc(sizeof(SpvId) * entry->num_blocks);
+   if (!block_ids)
+      goto fail;
+
+   for (int i = 0; i < entry->num_blocks; ++i)
+      block_ids[i] = spirv_builder_new_id(&ctx.builder);
+
+   ctx.block_ids = block_ids;
+   ctx.num_blocks = entry->num_blocks;
+
+   /* emit a block only for the variable declarations */
+   start_block(&ctx, spirv_builder_new_id(&ctx.builder));
+   foreach_list_typed(nir_register, reg, node, &entry->registers) {
+      SpvId type = get_uvec_type(&ctx, reg->bit_size, reg->num_components);
+      SpvId pointer_type = spirv_builder_type_pointer(&ctx.builder,
+                                                      SpvStorageClassFunction,
+                                                      type);
+      SpvId var = spirv_builder_emit_var(&ctx.builder, pointer_type,
+                                         SpvStorageClassFunction);
+
+      ctx.regs[reg->index] = var;
+   }
+
    emit_cf_list(&ctx, &entry->body);
+
    free(ctx.defs);
 
    spirv_builder_return(&ctx.builder); // doesn't belong here, but whatevz
@@ -1028,6 +1538,9 @@ fail:
    if (ret)
       spirv_shader_delete(ret);
 
+   if (ctx.vars)
+      _mesa_hash_table_destroy(ctx.vars, NULL);
+
    return NULL;
 }