nouveau: switch to libdrm_nouveau-2.0
[mesa.git] / src / gallium / drivers / nvc0 / nvc0_program.c
index 57a0874e679c003d3fcd1b136acdfb6ed0ee49c9..b403e2ee9f24801f2a34989e02f878938eec492d 100644 (file)
  * SOFTWARE.
  */
 
-#include "pipe/p_shader_tokens.h"
 #include "pipe/p_defines.h"
 
-#define NOUVEAU_DEBUG
-
-#include "tgsi/tgsi_parse.h"
-#include "tgsi/tgsi_util.h"
-#include "tgsi/tgsi_dump.h"
-
 #include "nvc0_context.h"
-#include "nvc0_pc.h"
-
-static unsigned
-nvc0_tgsi_src_mask(const struct tgsi_full_instruction *inst, int c)
-{
-   unsigned mask = inst->Dst[0].Register.WriteMask;
-
-   switch (inst->Instruction.Opcode) {
-   case TGSI_OPCODE_COS:
-   case TGSI_OPCODE_SIN:
-      return (mask & 0x8) | ((mask & 0x7) ? 0x1 : 0x0);
-   case TGSI_OPCODE_DP3:
-      return 0x7;
-   case TGSI_OPCODE_DP4:
-   case TGSI_OPCODE_DPH:
-   case TGSI_OPCODE_KIL: /* WriteMask ignored */
-      return 0xf;
-   case TGSI_OPCODE_DST:
-      return mask & (c ? 0xa : 0x6);
-   case TGSI_OPCODE_EX2:
-   case TGSI_OPCODE_EXP:
-   case TGSI_OPCODE_LG2:
-   case TGSI_OPCODE_LOG:
-   case TGSI_OPCODE_POW:
-   case TGSI_OPCODE_RCP:
-   case TGSI_OPCODE_RSQ:
-   case TGSI_OPCODE_SCS:
-      return 0x1;
-   case TGSI_OPCODE_IF:
-      return 0x1;
-   case TGSI_OPCODE_LIT:
-      return 0xb;
-   case TGSI_OPCODE_TEX:
-   case TGSI_OPCODE_TXB:
-   case TGSI_OPCODE_TXL:
-   case TGSI_OPCODE_TXP:
-   {
-      const struct tgsi_instruction_texture *tex;
-
-      assert(inst->Instruction.Texture);
-      tex = &inst->Texture;
-
-      mask = 0x7;
-      if (inst->Instruction.Opcode != TGSI_OPCODE_TEX &&
-          inst->Instruction.Opcode != TGSI_OPCODE_TXD)
-         mask |= 0x8; /* bias, lod or proj */
-
-      switch (tex->Texture) {
-      case TGSI_TEXTURE_1D:
-         mask &= 0x9;
-         break;
-      case TGSI_TEXTURE_SHADOW1D:
-         mask &= 0x5;
-         break;
-      case TGSI_TEXTURE_2D:
-         mask &= 0xb;
-         break;
-      default:
-         break;
-      }
-   }
-          return mask;
-   case TGSI_OPCODE_XPD:
-   {
-      unsigned x = 0;
-      if (mask & 1) x |= 0x6;
-      if (mask & 2) x |= 0x5;
-      if (mask & 4) x |= 0x3;
-      return x;
-   }
-   default:
-      break;
-   }
 
-   return mask;
-}
+#include "nv50/codegen/nv50_ir_driver.h"
 
+/* If only they told use the actual semantic instead of just GENERIC ... */
 static void
-nvc0_indirect_inputs(struct nvc0_translation_info *ti, int id)
+nvc0_mesa_varying_hack(struct nv50_ir_varying *var)
 {
-   int i, c;
-
-   for (i = 0; i < PIPE_MAX_SHADER_INPUTS; ++i)
-      for (c = 0; c < 4; ++c)
-         ti->input_access[i][c] = id;
-
-   ti->indirect_inputs = TRUE;
-}
+   unsigned c;
 
-static void
-nvc0_indirect_outputs(struct nvc0_translation_info *ti, int id)
-{
-   int i, c;
+   if (var->sn != TGSI_SEMANTIC_GENERIC)
+      return;
 
-   for (i = 0; i < PIPE_MAX_SHADER_OUTPUTS; ++i)
+   if (var->si <= 7) /* gl_TexCoord */
       for (c = 0; c < 4; ++c)
-         ti->output_access[i][c] = id;
-
-   ti->indirect_outputs = TRUE;
+         var->slot[c] = (0x300 + var->si * 0x10 + c * 0x4) / 4;
+   else
+   if (var->si == 9) /* gl_PointCoord */
+      for (c = 0; c < 4; ++c)
+         var->slot[c] = (0x2e0 + c * 0x4) / 4;
+   else
+      for (c = 0; c < 4; ++c) /* move down user varyings (first has index 8) */
+         var->slot[c] -= 0x80 / 4;
 }
 
-static INLINE unsigned
-nvc0_system_value_location(unsigned sn, unsigned si)
+static uint32_t
+nvc0_shader_input_address(unsigned sn, unsigned si, unsigned ubase)
 {
-   /* NOTE: locations 0xfxx indicate special regs */
    switch (sn) {
-      /*
-   case TGSI_SEMANTIC_VERTEXID:
-      return 0x2fc;
-      */
-   case TGSI_SEMANTIC_PRIMID:
-      return 0x60;
-      /*
-   case TGSI_SEMANTIC_LAYER_INDEX:
-      return 0x64;
-   case TGSI_SEMANTIC_VIEWPORT_INDEX:
-      return 0x68;
-      */
-   case TGSI_SEMANTIC_INSTANCEID:
-      return 0x2f8;
-   case TGSI_SEMANTIC_FACE:
-      return 0x3fc;
-      /*
-   case TGSI_SEMANTIC_INVOCATIONID:
-      return 0xf11;
-      */
+   case NV50_SEMANTIC_TESSFACTOR:   return 0x000 + si * 0x4;
+   case TGSI_SEMANTIC_PRIMID:       return 0x060;
+   case TGSI_SEMANTIC_PSIZE:        return 0x06c;
+   case TGSI_SEMANTIC_POSITION:     return 0x070;
+   case TGSI_SEMANTIC_GENERIC:      return ubase + si * 0x10;
+   case TGSI_SEMANTIC_FOG:          return 0x270;
+   case TGSI_SEMANTIC_COLOR:        return 0x280 + si * 0x10;
+   case TGSI_SEMANTIC_BCOLOR:       return 0x2a0 + si * 0x10;
+   case NV50_SEMANTIC_CLIPDISTANCE: return 0x2c0 + si * 0x4;
+   case TGSI_SEMANTIC_CLIPDIST:     return 0x2c0 + si * 0x10;
+   case TGSI_SEMANTIC_CLIPVERTEX:   return 0x260;
+   case NV50_SEMANTIC_POINTCOORD:   return 0x2e0;
+   case NV50_SEMANTIC_TESSCOORD:    return 0x2f0;
+   case TGSI_SEMANTIC_INSTANCEID:   return 0x2f8;
+   case TGSI_SEMANTIC_VERTEXID:     return 0x2fc;
+   case NV50_SEMANTIC_TEXCOORD:     return 0x300 + si * 0x10;
+   case TGSI_SEMANTIC_FACE:         return 0x3fc;
+   case NV50_SEMANTIC_INVOCATIONID: return ~0;
    default:
-      assert(0);
-      return 0x000;
+      assert(!"invalid TGSI input semantic");
+      return ~0;
    }
 }
 
-static INLINE unsigned
-nvc0_varying_location(unsigned sn, unsigned si)
+static uint32_t
+nvc0_shader_output_address(unsigned sn, unsigned si, unsigned ubase)
 {
    switch (sn) {
-   case TGSI_SEMANTIC_POSITION:
-      return 0x70;
-   case TGSI_SEMANTIC_COLOR:
-      return 0x280 + (si * 16); /* are these hard-wired ? */
-   case TGSI_SEMANTIC_BCOLOR:
-      return 0x2a0 + (si * 16);
-   case TGSI_SEMANTIC_FOG:
-      return 0x270;
-   case TGSI_SEMANTIC_PSIZE:
-      return 0x6c;
-      /*
-   case TGSI_SEMANTIC_PNTC:
-      return 0x2e0;
-      */
-   case TGSI_SEMANTIC_GENERIC:
-      assert(si < 31);
-      return 0x80 + (si * 16);
-   case TGSI_SEMANTIC_NORMAL:
-      return 0x360;
-   case TGSI_SEMANTIC_PRIMID:
-      return 0x40;
-   case TGSI_SEMANTIC_FACE:
-      return 0x3fc;
-      /*
-   case TGSI_SEMANTIC_CLIP_DISTANCE:
-      return 0x2c0 + (si * 4);
-      */
+   case NV50_SEMANTIC_TESSFACTOR:    return 0x000 + si * 0x4;
+   case TGSI_SEMANTIC_PRIMID:        return 0x060;
+   case NV50_SEMANTIC_LAYER:         return 0x064;
+   case NV50_SEMANTIC_VIEWPORTINDEX: return 0x068;
+   case TGSI_SEMANTIC_PSIZE:         return 0x06c;
+   case TGSI_SEMANTIC_POSITION:      return 0x070;
+   case TGSI_SEMANTIC_GENERIC:       return ubase + si * 0x10;
+   case TGSI_SEMANTIC_FOG:           return 0x270;
+   case TGSI_SEMANTIC_COLOR:         return 0x280 + si * 0x10;
+   case TGSI_SEMANTIC_BCOLOR:        return 0x2a0 + si * 0x10;
+   case NV50_SEMANTIC_CLIPDISTANCE:  return 0x2c0 + si * 0x4;
+   case TGSI_SEMANTIC_CLIPDIST:      return 0x2c0 + si * 0x10;
+   case TGSI_SEMANTIC_CLIPVERTEX:    return 0x260;
+   case NV50_SEMANTIC_TEXCOORD:      return 0x300 + si * 0x10;
+   case TGSI_SEMANTIC_EDGEFLAG:      return ~0;
    default:
-      assert(0);
-      return 0x000;
+      assert(!"invalid TGSI output semantic");
+      return ~0;
+   }
+}
+
+static int
+nvc0_vp_assign_input_slots(struct nv50_ir_prog_info *info)
+{
+   unsigned i, c, n;
+
+   for (n = 0, i = 0; i < info->numInputs; ++i) {
+      switch (info->in[i].sn) {
+      case TGSI_SEMANTIC_INSTANCEID: /* for SM4 only, in TGSI they're SVs */
+      case TGSI_SEMANTIC_VERTEXID:
+         info->in[i].mask = 0x1;
+         info->in[i].slot[0] =
+            nvc0_shader_input_address(info->in[i].sn, 0, 0) / 4;
+         continue;
+      default:
+         break;
+      }
+      for (c = 0; c < 4; ++c)
+         info->in[i].slot[c] = (0x80 + n * 0x10 + c * 0x4) / 4;
+      ++n;
    }
+
+   return 0;
 }
 
-static INLINE unsigned
-nvc0_interp_mode(const struct tgsi_full_declaration *decl)
+static int
+nvc0_sp_assign_input_slots(struct nv50_ir_prog_info *info)
 {
-   unsigned mode;
+   unsigned ubase = MAX2(0x80, 0x20 + info->numPatchConstants * 0x10);
+   unsigned offset;
+   unsigned i, c;
 
-   if (decl->Declaration.Interpolate == TGSI_INTERPOLATE_CONSTANT)
-      mode = NVC0_INTERP_FLAT;
-   else
-   if (decl->Declaration.Interpolate == TGSI_INTERPOLATE_PERSPECTIVE)
-      mode = NVC0_INTERP_PERSPECTIVE;
-   else
-      mode = NVC0_INTERP_LINEAR;
+   for (i = 0; i < info->numInputs; ++i) {
+      offset = nvc0_shader_input_address(info->in[i].sn,
+                                         info->in[i].si, ubase);
+      if (info->in[i].patch && offset >= 0x20)
+         offset = 0x20 + info->in[i].si * 0x10;
+
+      if (info->in[i].sn == NV50_SEMANTIC_TESSCOORD)
+         info->in[i].mask &= 3;
 
-   if (decl->Declaration.Centroid)
-      mode |= NVC0_INTERP_CENTROID;
+      for (c = 0; c < 4; ++c)
+         info->in[i].slot[c] = (offset + c * 0x4) / 4;
+
+      nvc0_mesa_varying_hack(&info->in[i]);
+   }
 
-   return mode;
+   return 0;
 }
 
-static void
-prog_immediate(struct nvc0_translation_info *ti,
-               const struct tgsi_full_immediate *imm)
+static int
+nvc0_fp_assign_output_slots(struct nv50_ir_prog_info *info)
 {
-   int c;
-   unsigned n = ti->immd32_nr++;
+   unsigned last = info->prop.fp.numColourResults * 4;
+   unsigned i, c;
 
-   assert(ti->immd32_nr <= ti->scan.immediate_count);
+   for (i = 0; i < info->numOutputs; ++i)
+      if (info->out[i].sn == TGSI_SEMANTIC_COLOR)
+         for (c = 0; c < 4; ++c)
+            info->out[i].slot[c] = info->out[i].si * 4 + c;
 
-   for (c = 0; c < 4; ++c)
-      ti->immd32[n * 4 + c] = imm->u[c].Uint;
+   if (info->io.sampleMask < PIPE_MAX_SHADER_OUTPUTS)
+      info->out[info->io.sampleMask].slot[0] = last++;
 
-   ti->immd32_ty[n] = imm->Immediate.DataType;
+   if (info->io.fragDepth < PIPE_MAX_SHADER_OUTPUTS)
+      info->out[info->io.fragDepth].slot[2] = last;
+
+   return 0;
 }
 
-static boolean
-prog_decl(struct nvc0_translation_info *ti,
-          const struct tgsi_full_declaration *decl)
+static int
+nvc0_sp_assign_output_slots(struct nv50_ir_prog_info *info)
 {
+   unsigned ubase = MAX2(0x80, 0x20 + info->numPatchConstants * 0x10);
+   unsigned offset;
    unsigned i, c;
-   unsigned sn = TGSI_SEMANTIC_GENERIC;
-   unsigned si = 0;
-   const unsigned first = decl->Range.First;
-   const unsigned last = decl->Range.Last;
-
-   if (decl->Declaration.Semantic) {
-      sn = decl->Semantic.Name;
-      si = decl->Semantic.Index;
-   }
-   
-   switch (decl->Declaration.File) {
-   case TGSI_FILE_INPUT:
-      for (i = first; i <= last; ++i) {
-         if (ti->prog->type == PIPE_SHADER_VERTEX) {
-            sn = TGSI_SEMANTIC_GENERIC;
-            si = i;
-         }
-         for (c = 0; c < 4; ++c)
-            ti->input_loc[i][c] = nvc0_varying_location(sn, si) + c * 4;
 
-         if (ti->prog->type == PIPE_SHADER_FRAGMENT)
-            ti->interp_mode[i] = nvc0_interp_mode(decl);
-      }
-      break;
-   case TGSI_FILE_OUTPUT:
-      for (i = first; i <= last; ++i, ++si) {
-         if (ti->prog->type == PIPE_SHADER_FRAGMENT) {
-            si = i;
-            if (i == ti->fp_depth_output) {
-               ti->output_loc[i][2] = (ti->scan.num_outputs - 1) * 4;
-            } else {
-               if (i > ti->fp_depth_output)
-                  si -= 1;
-               for (c = 0; c < 4; ++c)
-                  ti->output_loc[i][c] = si * 4 + c;
-            }
-         } else {
-            for (c = 0; c < 4; ++c)
-               ti->output_loc[i][c] = nvc0_varying_location(sn, si) + c * 4;
-         }
-      }
-      break;
-   case TGSI_FILE_SYSTEM_VALUE:
-      ti->sysval_loc[i] = nvc0_system_value_location(sn, si);
-      assert(first == last);
-      break;
-   case TGSI_FILE_NULL:
-   case TGSI_FILE_CONSTANT:
-   case TGSI_FILE_TEMPORARY:
-   case TGSI_FILE_SAMPLER:
-   case TGSI_FILE_ADDRESS:
-   case TGSI_FILE_IMMEDIATE:
-   case TGSI_FILE_PREDICATE:
-      break;
-   default:
-      NOUVEAU_ERR("unhandled TGSI_FILE %d\n", decl->Declaration.File);
-      return FALSE;
+   for (i = 0; i < info->numOutputs; ++i) {
+      offset = nvc0_shader_output_address(info->out[i].sn,
+                                          info->out[i].si, ubase);
+      if (info->out[i].patch && offset >= 0x20)
+         offset = 0x20 + info->out[i].si * 0x10;
+
+      for (c = 0; c < 4; ++c)
+         info->out[i].slot[c] = (offset + c * 0x4) / 4;
+
+      nvc0_mesa_varying_hack(&info->out[i]);
    }
-   return TRUE;
+
+   return 0;
 }
 
-static void
-prog_inst(struct nvc0_translation_info *ti,
-          const struct tgsi_full_instruction *inst, int id)
+static int
+nvc0_program_assign_varying_slots(struct nv50_ir_prog_info *info)
 {
-   const struct tgsi_dst_register *dst;
-   const struct tgsi_src_register *src;
-   int s, c, k;
-   unsigned mask;
-
-   if (inst->Instruction.Opcode == TGSI_OPCODE_BGNSUB) {
-      ti->subr[ti->num_subrs].first_insn = id - 1;
-      ti->subr[ti->num_subrs].id = ti->num_subrs + 1; /* id 0 is main program */
-      ++ti->num_subrs;
-   }
+   int ret;
 
-   if (inst->Dst[0].Register.File == TGSI_FILE_OUTPUT) {
-      dst = &inst->Dst[0].Register;
+   if (info->type == PIPE_SHADER_VERTEX)
+      ret = nvc0_vp_assign_input_slots(info);
+   else
+      ret = nvc0_sp_assign_input_slots(info);
+   if (ret)
+      return ret;
 
-      for (c = 0; c < 4; ++c) {
-         if (dst->Indirect)
-            nvc0_indirect_outputs(ti, id);
-         if (!(dst->WriteMask & (1 << c)))
-            continue;
-         ti->output_access[dst->Index][c] = id;
-      }
+   if (info->type == PIPE_SHADER_FRAGMENT)
+      ret = nvc0_fp_assign_output_slots(info);
+   else
+      ret = nvc0_sp_assign_output_slots(info);
+   return ret;
+}
 
-      if (inst->Instruction.Opcode == TGSI_OPCODE_MOV &&
-          inst->Src[0].Register.File == TGSI_FILE_INPUT &&
-          dst->Index == ti->edgeflag_out)
-         ti->prog->vp.edgeflag = inst->Src[0].Register.Index;
-   } else
-   if (inst->Dst[0].Register.File == TGSI_FILE_TEMPORARY) {
-      if (inst->Dst[0].Register.Indirect)
-         ti->require_stores = TRUE;
-   }
+static INLINE void
+nvc0_vtgp_hdr_update_oread(struct nvc0_program *vp, uint8_t slot)
+{
+   uint8_t min = (vp->hdr[4] >> 12) & 0xff;
+   uint8_t max = (vp->hdr[4] >> 24);
 
-   for (s = 0; s < inst->Instruction.NumSrcRegs; ++s) {
-      src = &inst->Src[s].Register;
-      if (src->File == TGSI_FILE_TEMPORARY)
-         if (inst->Src[s].Register.Indirect)
-            ti->require_stores = TRUE;
-      if (src->File != TGSI_FILE_INPUT)
-         continue;
-      mask = nvc0_tgsi_src_mask(inst, s);
+   min = MIN2(min, slot);
+   max = MAX2(max, slot);
 
-      if (inst->Src[s].Register.Indirect)
-         nvc0_indirect_inputs(ti, id);
+   vp->hdr[4] = (max << 24) | (min << 12);
+}
+
+/* Common part of header generation for VP, TCP, TEP and GP. */
+static int
+nvc0_vtgp_gen_header(struct nvc0_program *vp, struct nv50_ir_prog_info *info)
+{
+   unsigned i, c, a;
 
+   for (i = 0; i < info->numInputs; ++i) {
+      if (info->in[i].patch)
+         continue;
       for (c = 0; c < 4; ++c) {
-         if (!(mask & (1 << c)))
-            continue;
-         k = tgsi_util_get_full_src_register_swizzle(&inst->Src[s], c);
-         if (k <= TGSI_SWIZZLE_W)
-            ti->input_access[src->Index][k] = id;
+         a = info->in[i].slot[c];
+         if (info->in[i].mask & (1 << c)) {
+            if (info->in[i].sn != NV50_SEMANTIC_TESSCOORD)
+               vp->hdr[5 + a / 32] |= 1 << (a % 32);
+            else
+               nvc0_vtgp_hdr_update_oread(vp, info->in[i].slot[c]);
+         }
       }
    }
-}
 
-/* Probably should introduce something like struct tgsi_function_declaration
- * instead of trying to guess inputs/outputs.
- */
-static void
-prog_subroutine_inst(struct nvc0_subroutine *subr,
-                     const struct tgsi_full_instruction *inst)
-{
-   const struct tgsi_dst_register *dst;
-   const struct tgsi_src_register *src;
-   int s, c, k;
-   unsigned mask;
-
-   for (s = 0; s < inst->Instruction.NumSrcRegs; ++s) {
-      src = &inst->Src[s].Register;
-      if (src->File != TGSI_FILE_TEMPORARY)
+   for (i = 0; i < info->numOutputs; ++i) {
+      if (info->out[i].patch)
          continue;
-      mask = nvc0_tgsi_src_mask(inst, s);
-
       for (c = 0; c < 4; ++c) {
-         k = tgsi_util_get_full_src_register_swizzle(&inst->Src[s], c);
+         if (!(info->out[i].mask & (1 << c)))
+            continue;
+         assert(info->out[i].slot[c] >= 0x40 / 4);
+         a = info->out[i].slot[c] - 0x40 / 4;
+         vp->hdr[13 + a / 32] |= 1 << (a % 32);
+         if (info->out[i].oread)
+            nvc0_vtgp_hdr_update_oread(vp, info->out[i].slot[c]);
+      }
+   }
 
-         if ((mask & (1 << c)) && k < TGSI_SWIZZLE_W)
-            if (!(subr->retv[src->Index / 32][k] & (1 << (src->Index % 32))))
-               subr->argv[src->Index / 32][k] |= 1 << (src->Index % 32);
+   for (i = 0; i < info->numSysVals; ++i) {
+      switch (info->sv[i].sn) {
+      case TGSI_SEMANTIC_PRIMID:
+         vp->hdr[5] |= 1 << 24;
+         break;
+      case TGSI_SEMANTIC_INSTANCEID:
+         vp->hdr[10] |= 1 << 30;
+         break;
+      case TGSI_SEMANTIC_VERTEXID:
+         vp->hdr[10] |= 1 << 31;
+         break;
+      default:
+         break;
       }
    }
 
-   if (inst->Dst[0].Register.File == TGSI_FILE_TEMPORARY) {
-      dst = &inst->Dst[0].Register;
+   vp->vp.clip_enable = info->io.clipDistanceMask;
+   for (i = 0; i < 8; ++i)
+      if (info->io.cullDistanceMask & (1 << i))
+         vp->vp.clip_mode |= 1 << (i * 4);
 
-      for (c = 0; c < 4; ++c)
-         if (dst->WriteMask & (1 << c))
-            subr->retv[dst->Index / 32][c] |= 1 << (dst->Index % 32);
-   }
+   if (info->io.genUserClip < 0)
+      vp->vp.num_ucps = PIPE_MAX_CLIP_PLANES; /* prevent rebuilding */
+
+   return 0;
 }
 
 static int
-nvc0_vp_gp_gen_header(struct nvc0_program *vp, struct nvc0_translation_info *ti)
+nvc0_vp_gen_header(struct nvc0_program *vp, struct nv50_ir_prog_info *info)
 {
-   int i, c;
-   unsigned a;
+   vp->hdr[0] = 0x20061 | (1 << 10);
+   vp->hdr[4] = 0xff000;
 
-   for (a = 0x80/4, i = 0; i <= ti->scan.file_max[TGSI_FILE_INPUT]; ++i) {
-      for (c = 0; c < 4; ++c, ++a)
-         if (ti->input_access[i][c])
-            vp->hdr[5 + a / 32] |= 1 << (a % 32); /* VP_ATTR_EN */
-   }
+   vp->hdr[18] = info->io.clipDistanceMask;
 
-   for (i = 0; i <= ti->scan.file_max[TGSI_FILE_OUTPUT]; ++i) {
-      a = (ti->output_loc[i][0] - 0x40) / 4;
-      for (c = 0; c < 4; ++c, ++a) {
-         if (!ti->output_access[i][c])
-            continue;
-         vp->hdr[13 + a / 32] |= 1 << (a % 32); /* VP_EXPORT_EN */
-      }
+   return nvc0_vtgp_gen_header(vp, info);
+}
+
+#if defined(PIPE_SHADER_HULL) || defined(PIPE_SHADER_DOMAIN)
+static void
+nvc0_tp_get_tess_mode(struct nvc0_program *tp, struct nv50_ir_prog_info *info)
+{
+   if (info->prop.tp.outputPrim == PIPE_PRIM_MAX) {
+      tp->tp.tess_mode = ~0;
+      return;
+   }
+   switch (info->prop.tp.domain) {
+   case PIPE_PRIM_LINES:
+      tp->tp.tess_mode = NVC0_3D_TESS_MODE_PRIM_ISOLINES;
+      break;
+   case PIPE_PRIM_TRIANGLES:
+      tp->tp.tess_mode = NVC0_3D_TESS_MODE_PRIM_TRIANGLES;
+      if (info->prop.tp.winding > 0)
+         tp->tp.tess_mode |= NVC0_3D_TESS_MODE_CW;
+      break;
+   case PIPE_PRIM_QUADS:
+      tp->tp.tess_mode = NVC0_3D_TESS_MODE_PRIM_QUADS;
+      break;
+   default:
+      tp->tp.tess_mode = ~0;
+      return;
    }
+   if (info->prop.tp.outputPrim != PIPE_PRIM_POINTS)
+      tp->tp.tess_mode |= NVC0_3D_TESS_MODE_CONNECTED;
 
-   return 0;
+   switch (info->prop.tp.partitioning) {
+   case PIPE_TESS_PART_INTEGER:
+   case PIPE_TESS_PART_POW2:
+      tp->tp.tess_mode |= NVC0_3D_TESS_MODE_SPACING_EQUAL;
+      break;
+   case PIPE_TESS_PART_FRACT_ODD:
+      tp->tp.tess_mode |= NVC0_3D_TESS_MODE_SPACING_FRACTIONAL_ODD;
+      break;
+   case PIPE_TESS_PART_FRACT_EVEN:
+      tp->tp.tess_mode |= NVC0_3D_TESS_MODE_SPACING_FRACTIONAL_EVEN;
+      break;
+   default:
+      assert(!"invalid tessellator partitioning");
+      break;
+   }
 }
+#endif
 
+#ifdef PIPE_SHADER_HULL
 static int
-nvc0_vp_gen_header(struct nvc0_program *vp, struct nvc0_translation_info *ti)
+nvc0_tcp_gen_header(struct nvc0_program *tcp, struct nv50_ir_prog_info *info)
 {
-   vp->hdr[0] = 0x20461;
-   vp->hdr[4] = 0xff000;
+   unsigned opcs = 6; /* output patch constants (at least the TessFactors) */
+
+   tcp->tp.input_patch_size = info->prop.tp.inputPatchSize;
+
+   if (info->numPatchConstants)
+      opcs = 8 + info->numPatchConstants * 4;
+
+   tcp->hdr[0] = 0x20061 | (2 << 10);
+
+   tcp->hdr[1] = opcs << 24;
+   tcp->hdr[2] = info->prop.tp.outputPatchSize << 24;
+
+   tcp->hdr[4] = 0xff000; /* initial min/max parallel output read address */
+
+   nvc0_vtgp_gen_header(tcp, info);
 
-   vp->hdr[18] = (1 << vp->vp.num_ucps) - 1;
+   nvc0_tp_get_tess_mode(tcp, info);
 
-   return nvc0_vp_gp_gen_header(vp, ti);
+   return 0;
 }
+#endif
 
+#ifdef PIPE_SHADER_DOMAIN
 static int
-nvc0_gp_gen_header(struct nvc0_program *gp, struct nvc0_translation_info *ti)
+nvc0_tep_gen_header(struct nvc0_program *tep, struct nv50_ir_prog_info *info)
 {
-   unsigned invocations = 1;
-   unsigned max_output_verts, output_prim;
-   unsigned i;
+   tep->tp.input_patch_size = ~0;
 
-   gp->hdr[0] = 0x21061;
+   tep->hdr[0] = 0x20061 | (3 << 10);
+   tep->hdr[4] = 0xff000;
 
-   for (i = 0; i < ti->scan.num_properties; ++i) {
-      switch (ti->scan.properties[i].name) {
-      case TGSI_PROPERTY_GS_OUTPUT_PRIM:
-         output_prim = ti->scan.properties[i].data[0];
-         break;
-      case TGSI_PROPERTY_GS_MAX_OUTPUT_VERTICES:
-         max_output_verts = ti->scan.properties[i].data[0];
-         assert(max_output_verts < 512);
-         break;
-         /*
-      case TGSI_PROPERTY_GS_INVOCATIONS:
-         invocations = ti->scan.properties[i].data[0];
-         assert(invocations <= 32);
-         break;
-         */
-      default:
-         break;
-      }
-   }
+   nvc0_vtgp_gen_header(tep, info);
+
+   nvc0_tp_get_tess_mode(tep, info);
+
+   tep->hdr[18] |= 0x3 << 12; /* ? */
 
-   gp->hdr[2] = MIN2(invocations, 32) << 24;
+   return 0;
+}
+#endif
 
-   switch (output_prim) {
+static int
+nvc0_gp_gen_header(struct nvc0_program *gp, struct nv50_ir_prog_info *info)
+{
+   gp->hdr[0] = 0x20061 | (4 << 10);
+
+   gp->hdr[2] = MIN2(info->prop.gp.instanceCount, 32) << 24;
+
+   switch (info->prop.gp.outputPrim) {
    case PIPE_PRIM_POINTS:
       gp->hdr[3] = 0x01000000;
       gp->hdr[0] |= 0xf0000000;
@@ -477,202 +409,342 @@ nvc0_gp_gen_header(struct nvc0_program *gp, struct nvc0_translation_info *ti)
       break;
    }
 
-   gp->hdr[4] = max_output_verts & 0x1ff;
+   gp->hdr[4] = info->prop.gp.maxVertices & 0x1ff;
 
-   return nvc0_vp_gp_gen_header(gp, ti);
+   return nvc0_vtgp_gen_header(gp, info);
+}
+
+#define NVC0_INTERP_FLAT          (1 << 0)
+#define NVC0_INTERP_PERSPECTIVE   (2 << 0)
+#define NVC0_INTERP_LINEAR        (3 << 0)
+#define NVC0_INTERP_CENTROID      (1 << 2)
+
+static uint8_t
+nvc0_hdr_interp_mode(const struct nv50_ir_varying *var)
+{
+   if (var->linear)
+      return NVC0_INTERP_LINEAR;
+   if (var->flat)
+      return NVC0_INTERP_FLAT;
+   return NVC0_INTERP_PERSPECTIVE;
 }
 
 static int
-nvc0_fp_gen_header(struct nvc0_program *fp, struct nvc0_translation_info *ti)
+nvc0_fp_gen_header(struct nvc0_program *fp, struct nv50_ir_prog_info *info)
 {
-   int i, c;
-   unsigned a, m;
-   
-   fp->hdr[0] = 0x21462;
+   unsigned i, c, a, m;
+
+   fp->hdr[0] = 0x20062 | (5 << 10);
    fp->hdr[5] = 0x80000000; /* getting a trap if FRAG_COORD_UMASK.w = 0 */
 
-   if (ti->scan.uses_kill)
+   if (info->prop.fp.usesDiscard)
       fp->hdr[0] |= 0x8000;
-   if (ti->scan.writes_z) {
+   if (info->prop.fp.numColourResults > 1)
+      fp->hdr[0] |= 0x4000;
+   if (info->io.sampleMask < PIPE_MAX_SHADER_OUTPUTS)
+      fp->hdr[19] |= 0x1;
+   if (info->prop.fp.writesDepth) {
       fp->hdr[19] |= 0x2;
-      if (ti->scan.num_outputs > 2)
-         fp->hdr[0] |= 0x4000; /* FP_MULTIPLE_COLOR_OUTPUTS */
-   } else {
-   if (ti->scan.num_outputs > 1)
-      fp->hdr[0] |= 0x4000; /* FP_MULTIPLE_COLOR_OUTPUTS */
+      fp->flags[0] = 0x11; /* deactivate ZCULL */
    }
 
-   for (i = 0; i <= ti->scan.file_max[TGSI_FILE_INPUT]; ++i) {
-      m = ti->interp_mode[i];
+   for (i = 0; i < info->numInputs; ++i) {
+      m = nvc0_hdr_interp_mode(&info->in[i]);
       for (c = 0; c < 4; ++c) {
-         if (!ti->input_access[i][c])
+         if (!(info->in[i].mask & (1 << c)))
             continue;
-         a = ti->input_loc[i][c] / 2;
-         if ((a & ~7) == 0x70/2)
-            fp->hdr[5] |= 1 << (28 + (a & 7) / 2); /* FRAG_COORD_UMASK */
-         else
+         a = info->in[i].slot[c];
+         if (info->in[i].slot[0] >= (0x060 / 4) &&
+             info->in[i].slot[0] <= (0x07c / 4)) {
+            fp->hdr[5] |= 1 << (24 + (a - 0x060 / 4));
+         } else
+         if (info->in[i].slot[0] >= (0x2c0 / 4) &&
+             info->in[i].slot[0] <= (0x2fc / 4)) {
+            fp->hdr[14] |= (1 << (a - 0x280 / 4)) & 0x03ff0000;
+         } else {
+            if (info->in[i].slot[c] < (0x040 / 4) ||
+                info->in[i].slot[c] > (0x380 / 4))
+               continue;
+            a *= 2;
+            if (info->in[i].slot[0] >= (0x300 / 4))
+               a -= 32;
             fp->hdr[4 + a / 32] |= m << (a % 32);
+         }
       }
    }
 
-   for (i = 0; i <= ti->scan.file_max[TGSI_FILE_OUTPUT]; ++i) {
-      if (i != ti->fp_depth_output)
-         fp->hdr[18] |= 0xf << ti->output_loc[i][0];
+   for (i = 0; i < info->numOutputs; ++i) {
+      if (info->out[i].sn == TGSI_SEMANTIC_COLOR)
+         fp->hdr[18] |= info->out[i].mask << info->out[i].slot[0];
    }
 
+   fp->fp.early_z = info->prop.fp.earlyFragTests;
+   if (fp->fp.early_z == FALSE && fp->code_size >= 0x400)
+      fp->fp.early_z = !(info->prop.fp.writesDepth ||
+                         info->prop.fp.usesDiscard ||
+                         (info->io.globalAccess & 2));
+
    return 0;
 }
 
-static boolean
-nvc0_prog_scan(struct nvc0_translation_info *ti)
+static struct nvc0_transform_feedback_state *
+nvc0_program_create_tfb_state(const struct nv50_ir_prog_info *info,
+                              const struct pipe_stream_output_info *pso)
 {
-   struct nvc0_program *prog = ti->prog;
-   struct tgsi_parse_context parse;
-   int ret;
-   unsigned i;
+   struct nvc0_transform_feedback_state *tfb;
+   unsigned b, i, c;
+
+   tfb = MALLOC_STRUCT(nvc0_transform_feedback_state);
+   if (!tfb)
+      return NULL;
+   for (b = 0; b < 4; ++b) {
+      tfb->stride[b] = pso->stride[b] * 4;
+      tfb->varying_count[b] = 0;
+   }
+   memset(tfb->varying_index, 0xff, sizeof(tfb->varying_index)); /* = skip */
 
-#ifdef NOUVEAU_DEBUG
-   tgsi_dump(prog->pipe.tokens, 0);
-#endif
+   for (i = 0; i < pso->num_outputs; ++i) {
+      unsigned s = pso->output[i].start_component;
+      unsigned p = pso->output[i].dst_offset;
+      b = pso->output[i].output_buffer;
 
-   tgsi_scan_shader(prog->pipe.tokens, &ti->scan);
+      for (c = 0; c < pso->output[i].num_components; ++c)
+         tfb->varying_index[b][p++] =
+            info->out[pso->output[i].register_index].slot[s + c];
 
-   if (ti->prog->type == PIPE_SHADER_FRAGMENT) {
-      ti->fp_depth_output = 255;
-      for (i = 0; i < ti->scan.num_outputs; ++i)
-         if (ti->scan.output_semantic_name[i] == TGSI_SEMANTIC_POSITION)
-            ti->fp_depth_output = i;
+      tfb->varying_count[b] = MAX2(tfb->varying_count[b], p);
    }
+   for (b = 0; b < 4; ++b) // zero unused indices (looks nicer)
+      for (c = tfb->varying_count[b]; c & 3; ++c)
+         tfb->varying_index[b][c] = 0;
 
-   ti->subr =
-      CALLOC(ti->scan.opcode_count[TGSI_OPCODE_BGNSUB], sizeof(ti->subr[0]));
-
-   ti->immd32 = (uint32_t *)MALLOC(ti->scan.immediate_count * 16);
-   ti->immd32_ty = (ubyte *)MALLOC(ti->scan.immediate_count * sizeof(ubyte));
+   return tfb;
+}
 
-   ti->insns = MALLOC(ti->scan.num_instructions * sizeof(ti->insns[0]));
+#ifdef DEBUG
+static void
+nvc0_program_dump(struct nvc0_program *prog)
+{
+   unsigned pos;
 
-   tgsi_parse_init(&parse, prog->pipe.tokens);
-   while (!tgsi_parse_end_of_tokens(&parse)) {
-      tgsi_parse_token(&parse);
+   for (pos = 0; pos < sizeof(prog->hdr) / sizeof(prog->hdr[0]); ++pos)
+      debug_printf("HDR[%02lx] = 0x%08x\n",
+                   pos * sizeof(prog->hdr[0]), prog->hdr[pos]);
 
-      switch (parse.FullToken.Token.Type) {
-      case TGSI_TOKEN_TYPE_IMMEDIATE:
-         prog_immediate(ti, &parse.FullToken.FullImmediate);
-         break;
-      case TGSI_TOKEN_TYPE_DECLARATION:
-         prog_decl(ti, &parse.FullToken.FullDeclaration);
-         break;
-      case TGSI_TOKEN_TYPE_INSTRUCTION:
-         ti->insns[ti->num_insns] = parse.FullToken.FullInstruction;
-         prog_inst(ti, &parse.FullToken.FullInstruction, ++ti->num_insns);
-         break;
-      default:
-         break;
-      }
+   debug_printf("shader binary code (0x%x bytes):", prog->code_size);
+   for (pos = 0; pos < prog->code_size / 4; ++pos) {
+      if ((pos % 8) == 0)
+         debug_printf("\n");
+      debug_printf("%08x ", prog->code[pos]);
    }
+   debug_printf("\n");
+}
+#endif
+
+boolean
+nvc0_program_translate(struct nvc0_program *prog)
+{
+   struct nv50_ir_prog_info *info;
+   int ret;
+
+   info = CALLOC_STRUCT(nv50_ir_prog_info);
+   if (!info)
+      return FALSE;
+
+   info->type = prog->type;
+   info->target = 0xc0;
+   info->bin.sourceRep = NV50_PROGRAM_IR_TGSI;
+   info->bin.source = (void *)prog->pipe.tokens;
+
+   info->io.genUserClip = prog->vp.num_ucps;
+
+   info->assignSlots = nvc0_program_assign_varying_slots;
+
+#ifdef DEBUG
+   info->optLevel = debug_get_num_option("NV50_PROG_OPTIMIZE", 3);
+   info->dbgFlags = debug_get_num_option("NV50_PROG_DEBUG", 0);
+#else
+   info->optLevel = 3;
+#endif
 
-   for (i = 0; i < ti->num_subrs; ++i) {
-      unsigned pc = ti->subr[i].id;
-      while (ti->insns[pc].Instruction.Opcode != TGSI_OPCODE_ENDSUB)
-         prog_subroutine_inst(&ti->subr[i], &ti->insns[pc++]);
+   ret = nv50_ir_generate_code(info);
+   if (ret) {
+      NOUVEAU_ERR("shader translation failed: %i\n", ret);
+      goto out;
    }
 
+   prog->code = info->bin.code;
+   prog->code_size = info->bin.codeSize;
+   prog->immd_data = info->immd.buf;
+   prog->immd_size = info->immd.bufSize;
+   prog->relocs = info->bin.relocData;
+   prog->max_gpr = MAX2(4, (info->bin.maxGPR + 1));
+
+   prog->vp.need_vertex_id = info->io.vertexId < PIPE_MAX_SHADER_INPUTS;
+
+   if (info->io.edgeFlagOut < PIPE_MAX_ATTRIBS)
+      info->out[info->io.edgeFlagOut].mask = 0; /* for headergen */
+   prog->vp.edgeflag = info->io.edgeFlagIn;
+
    switch (prog->type) {
    case PIPE_SHADER_VERTEX:
-      ti->input_file = NV_FILE_MEM_A;
-      ti->output_file = NV_FILE_MEM_V;
-      ret = nvc0_vp_gen_header(prog, ti);
+      ret = nvc0_vp_gen_header(prog, info);
       break;
-      /*
-   case PIPE_SHADER_TESSELLATION_CONTROL:
-      ret = nvc0_tcp_gen_header(ti);
+#ifdef PIPE_SHADER_HULL
+   case PIPE_SHADER_HULL:
+      ret = nvc0_tcp_gen_header(prog, info);
       break;
-   case PIPE_SHADER_TESSELLATION_EVALUATION:
-      ret = nvc0_tep_gen_header(ti);
+#endif
+#ifdef PIPE_SHADER_DOMAIN
+   case PIPE_SHADER_DOMAIN:
+      ret = nvc0_tep_gen_header(prog, info);
       break;
+#endif
    case PIPE_SHADER_GEOMETRY:
-      ret = nvc0_gp_gen_header(ti);
+      ret = nvc0_gp_gen_header(prog, info);
       break;
-      */
    case PIPE_SHADER_FRAGMENT:
-      ti->input_file = NV_FILE_MEM_V;
-      ti->output_file = NV_FILE_GPR;
-
-      if (ti->scan.writes_z)
-         prog->flags[0] = 0x11; /* ? */
-      else
-      if (!ti->global_stores)
-         prog->fp.early_z = 1;
-
-      ret = nvc0_fp_gen_header(prog, ti);
+      ret = nvc0_fp_gen_header(prog, info);
       break;
    default:
-      assert(!"unsupported program type");
       ret = -1;
+      NOUVEAU_ERR("unknown program type: %u\n", prog->type);
       break;
    }
+   if (ret)
+      goto out;
 
-   assert(!ret);
-   return ret;
+   if (info->bin.tlsSpace) {
+      assert(info->bin.tlsSpace < (1 << 24));
+      prog->hdr[0] |= 1 << 26;
+      prog->hdr[1] |= info->bin.tlsSpace; /* l[] size */
+      prog->need_tls = TRUE;
+   }
+   /* TODO: factor 2 only needed where joinat/precont is used,
+    *       and we only have to count non-uniform branches
+    */
+   /*
+   if ((info->maxCFDepth * 2) > 16) {
+      prog->hdr[2] |= (((info->maxCFDepth * 2) + 47) / 48) * 0x200;
+      prog->need_tls = TRUE;
+   }
+   */
+   if (info->io.globalAccess)
+      prog->hdr[0] |= 1 << 16;
+
+   if (prog->pipe.stream_output.num_outputs)
+      prog->tfb = nvc0_program_create_tfb_state(info,
+                                                &prog->pipe.stream_output);
+
+out:
+   FREE(info);
+   return !ret;
 }
 
 boolean
-nvc0_program_translate(struct nvc0_program *prog)
+nvc0_program_upload_code(struct nvc0_context *nvc0, struct nvc0_program *prog)
 {
-   struct nvc0_translation_info *ti;
+   struct nvc0_screen *screen = nvc0->screen;
    int ret;
+   uint32_t size = prog->code_size + NVC0_SHADER_HEADER_SIZE;
+   uint32_t lib_pos = screen->lib_code->start;
+   uint32_t code_pos;
+
+   /* c[] bindings need to be aligned to 0x100, but we could use relocations
+    * to save space. */
+   if (prog->immd_size) {
+      prog->immd_base = size;
+      size = align(size, 0x40);
+      size += prog->immd_size + 0xc0; /* add 0xc0 for align 0x40 -> 0x100 */
+   }
+   size = align(size, 0x40); /* required by SP_START_ID */
 
-   ti = CALLOC_STRUCT(nvc0_translation_info);
-   ti->prog = prog;
+   ret = nouveau_heap_alloc(screen->text_heap, size, prog, &prog->mem);
+   if (ret) {
+      NOUVEAU_ERR("out of code space\n");
+      return FALSE;
+   }
+   prog->code_base = prog->mem->start;
+   prog->immd_base = align(prog->mem->start + prog->immd_base, 0x100);
+   assert((prog->immd_size == 0) || (prog->immd_base + prog->immd_size <=
+                                     prog->mem->start + prog->mem->size));
 
-   ti->edgeflag_out = PIPE_MAX_SHADER_OUTPUTS;
+   code_pos = prog->code_base + NVC0_SHADER_HEADER_SIZE;
 
-   if (prog->type == PIPE_SHADER_VERTEX && prog->vp.num_ucps)
-      ti->append_ucp = TRUE;
+   if (prog->relocs)
+      nv50_ir_relocate_code(prog->relocs, prog->code, code_pos, lib_pos, 0);
 
-   ret = nvc0_prog_scan(ti);
-   if (ret) {
-      NOUVEAU_ERR("unsupported shader program\n");
-      goto out;
-   }
+#ifdef DEBUG
+   if (debug_get_bool_option("NV50_PROG_DEBUG", FALSE))
+      nvc0_program_dump(prog);
+#endif
 
-   ret = nvc0_generate_code(ti);
-   if (ret)
-      NOUVEAU_ERR("shader translation failed\n");
+   nvc0_m2mf_push_linear(&nvc0->base, screen->text, prog->code_base,
+                         NOUVEAU_BO_VRAM, NVC0_SHADER_HEADER_SIZE, prog->hdr);
+   nvc0_m2mf_push_linear(&nvc0->base, screen->text,
+                         prog->code_base + NVC0_SHADER_HEADER_SIZE,
+                         NOUVEAU_BO_VRAM, prog->code_size, prog->code);
+   if (prog->immd_size)
+      nvc0_m2mf_push_linear(&nvc0->base,
+                            screen->text, prog->immd_base, NOUVEAU_BO_VRAM,
+                            prog->immd_size, prog->immd_data);
 
-   {
-      unsigned i;
-      for (i = 0; i < sizeof(prog->hdr) / sizeof(prog->hdr[0]); ++i)
-         debug_printf("HDR[%02lx] = 0x%08x\n",
-                      i * sizeof(prog->hdr[0]), prog->hdr[i]);
-   }
+   BEGIN_NVC0(nvc0->base.pushbuf, NVC0_3D(MEM_BARRIER), 1);
+   PUSH_DATA (nvc0->base.pushbuf, 0x1111);
 
-out:
-   if (ti->immd32)
-      FREE(ti->immd32);
-   if (ti->immd32_ty)
-      FREE(ti->immd32_ty);
-   if (ti->insns)
-      FREE(ti->insns);
-   if (ti->subr)
-      FREE(ti->subr);
-   FREE(ti);
-   return ret ? FALSE : TRUE;
+   return TRUE;
+}
+
+/* Upload code for builtin functions like integer division emulation. */
+void
+nvc0_program_library_upload(struct nvc0_context *nvc0)
+{
+   struct nvc0_screen *screen = nvc0->screen;
+   int ret;
+   uint32_t size;
+   const uint32_t *code;
+
+   if (screen->lib_code)
+      return;
+
+   nv50_ir_get_target_library(screen->base.device->chipset, &code, &size);
+   if (!size)
+      return;
+
+   ret = nouveau_heap_alloc(screen->text_heap, align(size, 0x100), NULL,
+                            &screen->lib_code);
+   if (ret)
+      return;
+
+   nvc0_m2mf_push_linear(&nvc0->base,
+                         screen->text, screen->lib_code->start, NOUVEAU_BO_VRAM,
+                         size, code);
+   /* no need for a memory barrier, will be emitted with first program */
 }
 
 void
 nvc0_program_destroy(struct nvc0_context *nvc0, struct nvc0_program *prog)
 {
-   if (prog->res)
-      nouveau_resource_free(&prog->res);
+   const struct pipe_shader_state pipe = prog->pipe;
+   const ubyte type = prog->type;
+
+   if (prog->mem)
+      nouveau_heap_free(&prog->mem);
 
    if (prog->code)
       FREE(prog->code);
+   if (prog->immd_data)
+      FREE(prog->immd_data);
    if (prog->relocs)
       FREE(prog->relocs);
+   if (prog->tfb) {
+      if (nvc0->state.tfb == prog->tfb)
+         nvc0->state.tfb = NULL;
+      FREE(prog->tfb);
+   }
 
-   memset(prog->hdr, 0, sizeof(prog->hdr));
+   memset(prog, 0, sizeof(*prog));
 
-   prog->translated = FALSE;
+   prog->pipe = pipe;
+   prog->type = type;
 }