Be more consistant with paths in #includes. Eventually, eliminate a bunch of -I...
[mesa.git] / src / mesa / shader / slang / slang_emit.c
index 764b5f613859a78a9422ef06d7a3e6c4f60acded..02c74095a9e500b4f003ce7dca4061e32cb2e064 100644 (file)
  * \author Brian Paul
  */
 
-#include "imports.h"
-#include "context.h"
-#include "macros.h"
-#include "program.h"
-#include "prog_instruction.h"
-#include "prog_parameter.h"
-#include "prog_print.h"
+/***
+ *** NOTES
+ ***
+ *** To emit GPU instructions, we basically just do an in-order traversal
+ *** of the IR tree.
+ ***/
+
+
+#include "main/imports.h"
+#include "main/context.h"
+#include "main/macros.h"
+#include "shader/program.h"
+#include "shader/prog_instruction.h"
+#include "shader/prog_parameter.h"
+#include "shader/prog_print.h"
 #include "slang_builtin.h"
 #include "slang_emit.h"
-#include "slang_error.h"
+#include "slang_mem.h"
 
 
 #define PEEPHOLE_OPTIMIZATIONS 1
 #define ANNOTATE 0
 
 
-/* XXX temporarily here */
-static GLboolean EmitHighLevelInstructions = GL_TRUE;
+typedef struct
+{
+   slang_info_log *log;
+   slang_var_table *vt;
+   struct gl_program *prog;
+   struct gl_program **Subroutines;
+   GLuint NumSubroutines;
 
+   /* code-gen options */
+   GLboolean EmitHighLevelInstructions;
+   GLboolean EmitCondCodes;
+   GLboolean EmitComments;
+   GLboolean EmitBeginEndSub; /* XXX TEMPORARY */
+} slang_emit_info;
 
 
-/**
- * Assembly and IR info
- */
-typedef struct
-{
-   slang_ir_opcode IrOpcode;
-   const char *IrName;
-   gl_inst_opcode InstOpcode;
-   GLuint ResultSize, NumParams;
-} slang_ir_info;
-
-
-
-static const slang_ir_info IrInfo[] = {
-   /* binary ops */
-   { IR_ADD, "IR_ADD", OPCODE_ADD, 4, 2 },
-   { IR_SUB, "IR_SUB", OPCODE_SUB, 4, 2 },
-   { IR_MUL, "IR_MUL", OPCODE_MUL, 4, 2 },
-   { IR_DIV, "IR_DIV", OPCODE_NOP, 0, 2 }, /* XXX broke */
-   { IR_DOT4, "IR_DOT_4", OPCODE_DP4, 1, 2 },
-   { IR_DOT3, "IR_DOT_3", OPCODE_DP3, 1, 2 },
-   { IR_CROSS, "IR_CROSS", OPCODE_XPD, 3, 2 },
-   { IR_LRP, "IR_LRP", OPCODE_LRP, 4, 3 },
-   { IR_MIN, "IR_MIN", OPCODE_MIN, 4, 2 },
-   { IR_MAX, "IR_MAX", OPCODE_MAX, 4, 2 },
-   { IR_CLAMP, "IR_CLAMP", OPCODE_NOP, 4, 3 }, /* special case: emit_clamp() */
-   { IR_SEQUAL, "IR_SEQUAL", OPCODE_SEQ, 4, 2 },
-   { IR_SNEQUAL, "IR_SNEQUAL", OPCODE_SNE, 4, 2 },
-   { IR_SGE, "IR_SGE", OPCODE_SGE, 4, 2 },
-   { IR_SGT, "IR_SGT", OPCODE_SGT, 4, 2 },
-   { IR_POW, "IR_POW", OPCODE_POW, 1, 2 },
-   /* unary ops */
-   { IR_I_TO_F, "IR_I_TO_F", OPCODE_NOP, 1, 1 },
-   { IR_F_TO_I, "IR_F_TO_I", OPCODE_INT, 4, 1 }, /* 4 floats to 4 ints */
-   { IR_EXP, "IR_EXP", OPCODE_EXP, 1, 1 },
-   { IR_EXP2, "IR_EXP2", OPCODE_EX2, 1, 1 },
-   { IR_LOG2, "IR_LOG2", OPCODE_LG2, 1, 1 },
-   { IR_RSQ, "IR_RSQ", OPCODE_RSQ, 1, 1 },
-   { IR_RCP, "IR_RCP", OPCODE_RCP, 1, 1 },
-   { IR_FLOOR, "IR_FLOOR", OPCODE_FLR, 4, 1 },
-   { IR_FRAC, "IR_FRAC", OPCODE_FRC, 4, 1 },
-   { IR_ABS, "IR_ABS", OPCODE_ABS, 4, 1 },
-   { IR_NEG, "IR_NEG", OPCODE_NOP, 4, 1 }, /* special case: emit_negation() */
-   { IR_DDX, "IR_DDX", OPCODE_DDX, 4, 1 },
-   { IR_DDX, "IR_DDY", OPCODE_DDX, 4, 1 },
-   { IR_SIN, "IR_SIN", OPCODE_SIN, 1, 1 },
-   { IR_COS, "IR_COS", OPCODE_COS, 1, 1 },
-   { IR_NOISE1, "IR_NOISE1", OPCODE_NOISE1, 1, 1 },
-   { IR_NOISE2, "IR_NOISE2", OPCODE_NOISE2, 1, 1 },
-   { IR_NOISE3, "IR_NOISE3", OPCODE_NOISE3, 1, 1 },
-   { IR_NOISE4, "IR_NOISE4", OPCODE_NOISE4, 1, 1 },
-
-   /* other */
-   { IR_SEQ, "IR_SEQ", OPCODE_NOP, 0, 0 },
-   { IR_SCOPE, "IR_SCOPE", OPCODE_NOP, 0, 0 },
-   { IR_LABEL, "IR_LABEL", OPCODE_NOP, 0, 0 },
-   { IR_JUMP, "IR_JUMP", OPCODE_NOP, 0, 0 },
-   { IR_CJUMP0, "IR_CJUMP0", OPCODE_NOP, 0, 0 },
-   { IR_CJUMP1, "IR_CJUMP1", OPCODE_NOP, 0, 0 },
-   { IR_IF, "IR_IF", OPCODE_NOP, 0, 0 },
-   { IR_KILL, "IR_KILL", OPCODE_NOP, 0, 0 },
-   { IR_COND, "IR_COND", OPCODE_NOP, 0, 0 },
-   { IR_CALL, "IR_CALL", OPCODE_NOP, 0, 0 },
-   { IR_MOVE, "IR_MOVE", OPCODE_NOP, 0, 1 },
-   { IR_NOT, "IR_NOT", OPCODE_NOP, 1, 1 },
-   { IR_VAR, "IR_VAR", OPCODE_NOP, 0, 0 },
-   { IR_VAR_DECL, "IR_VAR_DECL", OPCODE_NOP, 0, 0 },
-   { IR_TEX, "IR_TEX", OPCODE_TEX, 4, 1 },
-   { IR_TEXB, "IR_TEXB", OPCODE_TXB, 4, 1 },
-   { IR_TEXP, "IR_TEXP", OPCODE_TXP, 4, 1 },
-   { IR_FLOAT, "IR_FLOAT", OPCODE_NOP, 0, 0 }, /* float literal */
-   { IR_FIELD, "IR_FIELD", OPCODE_NOP, 0, 0 },
-   { IR_ELEMENT, "IR_ELEMENT", OPCODE_NOP, 0, 0 },
-   { IR_SWIZZLE, "IR_SWIZZLE", OPCODE_NOP, 0, 0 },
-   { IR_NOP, NULL, OPCODE_NOP, 0, 0 }
-};
-
-
-static const slang_ir_info *
-slang_find_ir_info(slang_ir_opcode opcode)
+
+static struct gl_program *
+new_subroutine(slang_emit_info *emitInfo, GLuint *id)
 {
-   GLuint i;
-   for (i = 0; IrInfo[i].IrName; i++) {
-      if (IrInfo[i].IrOpcode == opcode) {
-        return IrInfo + i;
-      }
-   }
-   return NULL;
+   GET_CURRENT_CONTEXT(ctx);
+   const GLuint n = emitInfo->NumSubroutines;
+
+   emitInfo->Subroutines = (struct gl_program **)
+      _mesa_realloc(emitInfo->Subroutines,
+                    n * sizeof(struct gl_program),
+                    (n + 1) * sizeof(struct gl_program));
+   emitInfo->Subroutines[n] = _mesa_new_program(ctx, emitInfo->prog->Target, 0);
+   emitInfo->Subroutines[n]->Parameters = emitInfo->prog->Parameters;
+   emitInfo->NumSubroutines++;
+   *id = n;
+   return emitInfo->Subroutines[n];
 }
 
-static const char *
-slang_ir_name(slang_ir_opcode opcode)
+
+/**
+ * Convert a writemask to a swizzle.  Used for testing cond codes because
+ * we only want to test the cond code component(s) that was set by the
+ * previous instruction.
+ */
+static GLuint
+writemask_to_swizzle(GLuint writemask)
 {
-   return slang_find_ir_info(opcode)->IrName;
+   if (writemask == WRITEMASK_X)
+      return SWIZZLE_XXXX;
+   if (writemask == WRITEMASK_Y)
+      return SWIZZLE_YYYY;
+   if (writemask == WRITEMASK_Z)
+      return SWIZZLE_ZZZZ;
+   if (writemask == WRITEMASK_W)
+      return SWIZZLE_WWWW;
+   return SWIZZLE_XYZW;  /* shouldn't be hit */
 }
 
 
@@ -166,7 +127,7 @@ slang_ir_storage *
 _slang_new_ir_storage(enum register_file file, GLint index, GLint size)
 {
    slang_ir_storage *st;
-   st = (slang_ir_storage *) _mesa_calloc(sizeof(slang_ir_storage));
+   st = (slang_ir_storage *) _slang_alloc(sizeof(slang_ir_storage));
    if (st) {
       st->File = file;
       st->Index = index;
@@ -177,236 +138,23 @@ _slang_new_ir_storage(enum register_file file, GLint index, GLint size)
 }
 
 
-static const char *
-swizzle_string(GLuint swizzle)
-{
-   static char s[6];
-   GLuint i;
-   s[0] = '.';
-   for (i = 1; i < 5; i++) {
-      s[i] = "xyzw"[GET_SWZ(swizzle, i-1)];
-   }
-   s[i] = 0;
-   return s;
-}
-
-static const char *
-writemask_string(GLuint writemask)
-{
-   static char s[6];
-   GLuint i, j = 0;
-   s[j++] = '.';
-   for (i = 0; i < 4; i++) {
-      if (writemask & (1 << i))
-         s[j++] = "xyzw"[i];
-   }
-   s[j] = 0;
-   return s;
-}
-
-static const char *
-storage_string(const slang_ir_storage *st)
-{
-   static const char *files[] = {
-      "TEMP",
-      "LOCAL_PARAM",
-      "ENV_PARAM",
-      "STATE",
-      "INPUT",
-      "OUTPUT",
-      "NAMED_PARAM",
-      "CONSTANT",
-      "UNIFORM",
-      "WRITE_ONLY",
-      "ADDRESS",
-      "SAMPLER",
-      "UNDEFINED"
-   };
-   static char s[100];
-#if 0
-   if (st->Size == 1)
-      sprintf(s, "%s[%d]", files[st->File], st->Index);
-   else
-      sprintf(s, "%s[%d..%d]", files[st->File], st->Index,
-              st->Index + st->Size - 1);
-#endif
-   assert(st->File < (GLint) (sizeof(files) / sizeof(files[0])));
-   sprintf(s, "%s[%d]", files[st->File], st->Index);
-   return s;
-}
-
-
-static void
-spaces(int n)
-{
-   while (n-- > 0) {
-      printf(" ");
-   }
-}
-
-#define IND 0
-void
-slang_print_ir(const slang_ir_node *n, int indent)
-{
-   if (!n)
-      return;
-#if !IND
-   if (n->Opcode != IR_SEQ)
-#else
-      printf("%3d:", indent);
-#endif
-      spaces(indent);
-
-   switch (n->Opcode) {
-   case IR_SEQ:
-#if IND
-      printf("SEQ  at %p\n", (void*) n);
-#endif
-      assert(n->Children[0]);
-      assert(n->Children[1]);
-      slang_print_ir(n->Children[0], indent + IND);
-      slang_print_ir(n->Children[1], indent + IND);
-      break;
-   case IR_SCOPE:
-      printf("NEW SCOPE\n");
-      assert(!n->Children[1]);
-      slang_print_ir(n->Children[0], indent + 3);
-      break;
-   case IR_MOVE:
-      printf("MOVE (writemask = %s)\n", writemask_string(n->Writemask));
-      slang_print_ir(n->Children[0], indent+3);
-      slang_print_ir(n->Children[1], indent+3);
-      break;
-   case IR_LABEL:
-      printf("LABEL: %s\n", n->Label->Name);
-      break;
-   case IR_COND:
-      printf("COND\n");
-      slang_print_ir(n->Children[0], indent + 3);
-      break;
-   case IR_JUMP:
-      printf("JUMP %s\n", n->Label->Name);
-      break;
-   case IR_CJUMP0:
-      printf("CJUMP0 %s\n", n->Label->Name);
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-   case IR_CJUMP1:
-      printf("CJUMP1 %s\n", n->Label->Name);
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-
-   case IR_IF:
-      printf("IF \n");
-      slang_print_ir(n->Children[0], indent+3);
-      spaces(indent);
-      printf("THEN\n");
-      slang_print_ir(n->Children[1], indent+3);
-      if (n->Children[2]) {
-         spaces(indent);
-         printf("ELSE\n");
-         slang_print_ir(n->Children[2], indent+3);
-      }
-      printf("ENDIF\n");
-      break;
-
-   case IR_BEGIN_SUB:
-      printf("BEGIN_SUB\n");
-      break;
-   case IR_END_SUB:
-      printf("END_SUB\n");
-      break;
-   case IR_RETURN:
-      printf("RETURN\n");
-      break;
-   case IR_CALL:
-      printf("CALL\n");
-      break;
-
-   case IR_LOOP:
-      printf("LOOP\n");
-      slang_print_ir(n->Children[0], indent+3);
-      spaces(indent);
-      printf("ENDLOOP\n");
-      break;
-   case IR_CONT:
-      printf("CONT\n");
-      break;
-   case IR_BREAK:
-      printf("BREAK\n");
-      break;
-   case IR_BREAK_IF_FALSE:
-      printf("BREAK_IF_FALSE\n");
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-   case IR_BREAK_IF_TRUE:
-      printf("BREAK_IF_TRUE\n");
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-   case IR_CONT_IF_FALSE:
-      printf("CONT_IF_FALSE\n");
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-   case IR_CONT_IF_TRUE:
-      printf("CONT_IF_TRUE\n");
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-
-   case IR_VAR:
-      printf("VAR %s%s at %s  store %p\n",
-             (n->Var ? (char *) n->Var->a_name : "TEMP"),
-             swizzle_string(n->Store->Swizzle),
-             storage_string(n->Store), (void*) n->Store);
-      break;
-   case IR_VAR_DECL:
-      printf("VAR_DECL %s (%p) at %s  store %p\n",
-             (n->Var ? (char *) n->Var->a_name : "TEMP"),
-             (void*) n->Var, storage_string(n->Store),
-             (void*) n->Store);
-      break;
-   case IR_FIELD:
-      printf("FIELD %s of\n", n->Field);
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-   case IR_FLOAT:
-      printf("FLOAT %g %g %g %g\n",
-             n->Value[0], n->Value[1], n->Value[2], n->Value[3]);
-      break;
-   case IR_I_TO_F:
-      printf("INT_TO_FLOAT\n");
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-   case IR_F_TO_I:
-      printf("FLOAT_TO_INT\n");
-      slang_print_ir(n->Children[0], indent+3);
-      break;
-   case IR_SWIZZLE:
-      printf("SWIZZLE %s of  (store %p) \n",
-             swizzle_string(n->Store->Swizzle), (void*) n->Store);
-      slang_print_ir(n->Children[0], indent + 3);
-      break;
-   default:
-      printf("%s (%p, %p)  (store %p)\n", slang_ir_name(n->Opcode),
-             (void*) n->Children[0], (void*) n->Children[1], (void*) n->Store);
-      slang_print_ir(n->Children[0], indent+3);
-      slang_print_ir(n->Children[1], indent+3);
-   }
-}
-
-
 /**
  * Allocate temporary storage for an intermediate result (such as for
  * a multiply or add, etc.
  */
 static GLboolean
-alloc_temp_storage(slang_var_table *vt, slang_ir_node *n, GLint size)
+alloc_temp_storage(slang_emit_info *emitInfo, slang_ir_node *n, GLint size)
 {
    assert(!n->Var);
    assert(!n->Store);
    assert(size > 0);
    n->Store = _slang_new_ir_storage(PROGRAM_TEMPORARY, -1, size);
-   if (!_slang_alloc_temp(vt, n->Store)) {
-      RETURN_ERROR("Ran out of registers, too many temporaries", 0);
+   if (!_slang_alloc_temp(emitInfo->vt, n->Store)) {
+      slang_info_log_error(emitInfo->log,
+                           "Ran out of registers, too many temporaries");
+      _slang_free(n->Store);
+      n->Store = NULL;
+      return GL_FALSE;
    }
    return GL_TRUE;
 }
@@ -419,11 +167,15 @@ alloc_temp_storage(slang_var_table *vt, slang_ir_node *n, GLint size)
 static void
 free_temp_storage(slang_var_table *vt, slang_ir_node *n)
 {
-   if (n->Store->File == PROGRAM_TEMPORARY && n->Store->Index >= 0) {
+   if (n->Store->File == PROGRAM_TEMPORARY &&
+       n->Store->Index >= 0 &&
+       n->Opcode != IR_SWIZZLE) {
       if (_slang_is_temp(vt, n->Store)) {
          _slang_free_temp(vt, n->Store);
          n->Store->Index = -1;
          n->Store->Size = -1;
+         /*_mesa_free(n->Store);*/ /* XXX leak */
+         n->Store = NULL;
       }
    }
 }
@@ -436,13 +188,7 @@ static void
 storage_to_dst_reg(struct prog_dst_register *dst, const slang_ir_storage *st,
                    GLuint writemask)
 {
-   static const GLuint defaultWritemask[4] = {
-      WRITEMASK_X,
-      WRITEMASK_X | WRITEMASK_Y,
-      WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z,
-      WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W
-   };
-   assert(st->Index >= 0 && st->Index <= 16);
+   assert(st->Index >= 0);
    dst->File = st->File;
    dst->Index = st->Index;
    assert(st->File != PROGRAM_UNDEFINED);
@@ -451,11 +197,10 @@ storage_to_dst_reg(struct prog_dst_register *dst, const slang_ir_storage *st,
    if (st->Size == 1) {
       GLuint comp = GET_SWZ(st->Swizzle, 0);
       assert(comp < 4);
-      assert(writemask & WRITEMASK_X);
       dst->WriteMask = WRITEMASK_X << comp;
    }
    else {
-      dst->WriteMask = defaultWritemask[st->Size - 1] & writemask;
+      dst->WriteMask = writemask;
    }
 }
 
@@ -472,24 +217,45 @@ storage_to_src_reg(struct prog_src_register *src, const slang_ir_storage *st)
       MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W),
       MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W)
    };
-   assert(st->File >= 0 && st->File <= 16);
-   src->File = st->File;
-   src->Index = st->Index;
-   assert(st->File != PROGRAM_UNDEFINED);
+   assert(st->File >= 0);
+   assert(st->File < PROGRAM_UNDEFINED);
    assert(st->Size >= 1);
    assert(st->Size <= 4);
+   src->File = st->File;
+   src->Index = st->Index;
    if (st->Swizzle != SWIZZLE_NOOP)
       src->Swizzle = st->Swizzle;
    else
       src->Swizzle = defaultSwizzle[st->Size - 1]; /*XXX really need this?*/
 
-   assert(GET_SWZ(src->Swizzle, 0) != SWIZZLE_NIL);
-   assert(GET_SWZ(src->Swizzle, 1) != SWIZZLE_NIL);
-   assert(GET_SWZ(src->Swizzle, 2) != SWIZZLE_NIL);
-   assert(GET_SWZ(src->Swizzle, 3) != SWIZZLE_NIL);
+   assert(GET_SWZ(src->Swizzle, 0) <= 3);
+   assert(GET_SWZ(src->Swizzle, 1) <= 3);
+   assert(GET_SWZ(src->Swizzle, 2) <= 3);
+   assert(GET_SWZ(src->Swizzle, 3) <= 3);
 }
 
 
+/*
+ * Setup an instrucion src register to point to a scalar constant.
+ */
+static void
+constant_to_src_reg(struct prog_src_register *src, GLfloat val,
+                    slang_emit_info *emitInfo)
+{
+   GLuint zeroSwizzle;
+   GLint zeroReg;
+   GLfloat value[4];
+
+   value[0] = val;
+   zeroReg = _mesa_add_unnamed_constant(emitInfo->prog->Parameters,
+                                        value, 1, &zeroSwizzle);
+   assert(zeroReg >= 0);
+
+   src->File = PROGRAM_CONSTANT;
+   src->Index = zeroReg;
+   src->Swizzle = zeroSwizzle;
+}
+
 
 /**
  * Add new instruction at end of given program.
@@ -498,9 +264,17 @@ storage_to_src_reg(struct prog_src_register *src, const slang_ir_storage *st)
  * \return pointer to the new instruction
  */
 static struct prog_instruction *
-new_instruction(struct gl_program *prog, gl_inst_opcode opcode)
+new_instruction(slang_emit_info *emitInfo, gl_inst_opcode opcode)
 {
+   struct gl_program *prog = emitInfo->prog;
    struct prog_instruction *inst;
+
+#if 0
+   /* print prev inst */
+   if (prog->NumInstructions > 0) {
+      _mesa_print_instruction(prog->Instructions + prog->NumInstructions - 1);
+   }
+#endif
    prog->Instructions = _mesa_realloc_instructions(prog->Instructions,
                                                    prog->NumInstructions,
                                                    prog->NumInstructions + 1);
@@ -509,27 +283,30 @@ new_instruction(struct gl_program *prog, gl_inst_opcode opcode)
    _mesa_init_instructions(inst, 1);
    inst->Opcode = opcode;
    inst->BranchTarget = -1; /* invalid */
+   /*
+   printf("New inst %d: %p %s\n", prog->NumInstructions-1,(void*)inst,
+          _mesa_opcode_string(inst->Opcode));
+   */
    return inst;
 }
 
 
-#if 0
 /**
  * Return pointer to last instruction in program.
  */
 static struct prog_instruction *
-prev_instruction(struct gl_program *prog)
+prev_instruction(slang_emit_info *emitInfo)
 {
+   struct gl_program *prog = emitInfo->prog;
    if (prog->NumInstructions == 0)
       return NULL;
    else
       return prog->Instructions + prog->NumInstructions - 1;
 }
-#endif
 
 
 static struct prog_instruction *
-emit(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog);
+emit(slang_emit_info *emitInfo, slang_ir_node *n);
 
 
 /**
@@ -663,16 +440,29 @@ instruction_annotation(gl_inst_opcode opcode, char *dstAnnot,
 }
 
 
+/**
+ * Emit an instruction that's just a comment.
+ */
+static struct prog_instruction *
+emit_comment(slang_emit_info *emitInfo, const char *s)
+{
+   struct prog_instruction *inst = new_instruction(emitInfo, OPCODE_NOP);
+   if (inst) {
+      inst->Comment = _mesa_strdup(s);
+   }
+   return inst;
+}
+
 
 /**
  * Generate code for a simple arithmetic instruction.
  * Either 1, 2 or 3 operands.
  */
 static struct prog_instruction *
-emit_arith(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_arith(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    struct prog_instruction *inst;
-   const slang_ir_info *info = slang_find_ir_info(n->Opcode);
+   const slang_ir_info *info = _slang_ir_info(n->Opcode);
    char *srcAnnot[3], *dstAnnot;
    GLuint i;
 
@@ -686,34 +476,34 @@ emit_arith(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
    if (info->NumParams == 2 &&
        n->Opcode == IR_ADD && n->Children[0]->Opcode == IR_MUL) {
       /* found pattern IR_ADD(IR_MUL(A, B), C) */
-      emit(vt, n->Children[0]->Children[0], prog);  /* A */
-      emit(vt, n->Children[0]->Children[1], prog);  /* B */
-      emit(vt, n->Children[1], prog);  /* C */
+      emit(emitInfo, n->Children[0]->Children[0]);  /* A */
+      emit(emitInfo, n->Children[0]->Children[1]);  /* B */
+      emit(emitInfo, n->Children[1]);  /* C */
       /* generate MAD instruction */
-      inst = new_instruction(prog, OPCODE_MAD);
+      inst = new_instruction(emitInfo, OPCODE_MAD);
       /* operands: A, B, C: */
       storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Children[0]->Store);
       storage_to_src_reg(&inst->SrcReg[1], n->Children[0]->Children[1]->Store);
       storage_to_src_reg(&inst->SrcReg[2], n->Children[1]->Store);
-      free_temp_storage(vt, n->Children[0]->Children[0]);
-      free_temp_storage(vt, n->Children[0]->Children[1]);
-      free_temp_storage(vt, n->Children[1]);
+      free_temp_storage(emitInfo->vt, n->Children[0]->Children[0]);
+      free_temp_storage(emitInfo->vt, n->Children[0]->Children[1]);
+      free_temp_storage(emitInfo->vt, n->Children[1]);
    }
    else if (info->NumParams == 2 &&
             n->Opcode == IR_ADD && n->Children[1]->Opcode == IR_MUL) {
       /* found pattern IR_ADD(A, IR_MUL(B, C)) */
-      emit(vt, n->Children[0], prog);  /* A */
-      emit(vt, n->Children[1]->Children[0], prog);  /* B */
-      emit(vt, n->Children[1]->Children[1], prog);  /* C */
+      emit(emitInfo, n->Children[0]);  /* A */
+      emit(emitInfo, n->Children[1]->Children[0]);  /* B */
+      emit(emitInfo, n->Children[1]->Children[1]);  /* C */
       /* generate MAD instruction */
-      inst = new_instruction(prog, OPCODE_MAD);
+      inst = new_instruction(emitInfo, OPCODE_MAD);
       /* operands: B, C, A */
       storage_to_src_reg(&inst->SrcReg[0], n->Children[1]->Children[0]->Store);
       storage_to_src_reg(&inst->SrcReg[1], n->Children[1]->Children[1]->Store);
       storage_to_src_reg(&inst->SrcReg[2], n->Children[0]->Store);
-      free_temp_storage(vt, n->Children[1]->Children[0]);
-      free_temp_storage(vt, n->Children[1]->Children[1]);
-      free_temp_storage(vt, n->Children[0]);
+      free_temp_storage(emitInfo->vt, n->Children[1]->Children[0]);
+      free_temp_storage(emitInfo->vt, n->Children[1]->Children[1]);
+      free_temp_storage(emitInfo->vt, n->Children[0]);
    }
    else
 #endif
@@ -721,31 +511,37 @@ emit_arith(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
       /* normal case */
 
       /* gen code for children */
-      for (i = 0; i < info->NumParams; i++)
-         emit(vt, n->Children[i], prog);
+      for (i = 0; i < info->NumParams; i++) {
+         emit(emitInfo, n->Children[i]);
+         if (!n->Children[i] || !n->Children[i]->Store) {
+            /* error recovery */
+            return NULL;
+         }
+      }
 
       /* gen this instruction and src registers */
-      inst = new_instruction(prog, info->InstOpcode);
+      inst = new_instruction(emitInfo, info->InstOpcode);
       for (i = 0; i < info->NumParams; i++)
          storage_to_src_reg(&inst->SrcReg[i], n->Children[i]->Store);
 
       /* annotation */
       for (i = 0; i < info->NumParams; i++)
-         srcAnnot[i] = storage_annotation(n->Children[i], prog);
+         srcAnnot[i] = storage_annotation(n->Children[i], emitInfo->prog);
 
       /* free temps */
       for (i = 0; i < info->NumParams; i++)
-         free_temp_storage(vt, n->Children[i]);
+         free_temp_storage(emitInfo->vt, n->Children[i]);
    }
 
    /* result storage */
    if (!n->Store) {
-      if (!alloc_temp_storage(vt, n, info->ResultSize))
+      /* XXX this size isn't correct, it depends on the operands */
+      if (!alloc_temp_storage(emitInfo, n, info->ResultSize))
          return NULL;
    }
    storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
 
-   dstAnnot = storage_annotation(n, prog);
+   dstAnnot = storage_annotation(n, emitInfo->prog);
 
    inst->Comment = instruction_annotation(inst->Opcode, dstAnnot, srcAnnot[0],
                                           srcAnnot[1], srcAnnot[2]);
@@ -755,11 +551,130 @@ emit_arith(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
 }
 
 
+/**
+ * Emit code for == and != operators.  These could normally be handled
+ * by emit_arith() except we need to be able to handle structure comparisons.
+ */
+static struct prog_instruction *
+emit_compare(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+   struct prog_instruction *inst;
+   GLint size;
+
+   assert(n->Opcode == IR_EQUAL || n->Opcode == IR_NOTEQUAL);
+
+   /* gen code for children */
+   emit(emitInfo, n->Children[0]);
+   emit(emitInfo, n->Children[1]);
+
+   assert(n->Children[0]->Store->Size == n->Children[1]->Store->Size);
+   size = n->Children[0]->Store->Size;
+
+   if (size == 1) {
+      gl_inst_opcode opcode;
+
+      if (!n->Store) {
+         if (!alloc_temp_storage(emitInfo, n, 1))  /* 1 bool */
+            return NULL;
+      }
+
+      opcode = n->Opcode == IR_EQUAL ? OPCODE_SEQ : OPCODE_SNE;
+      inst = new_instruction(emitInfo, opcode);
+      storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+      storage_to_src_reg(&inst->SrcReg[1], n->Children[1]->Store);
+      storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+   }
+   else if (size <= 4) {
+      GLuint swizzle;
+      gl_inst_opcode dotOp;
+      
+      assert(!n->Store);
+      if (!n->Store) {
+         if (!alloc_temp_storage(emitInfo, n, size))  /* 'size' bools */
+            return NULL;
+      }
+
+      if (size == 4) {
+         dotOp = OPCODE_DP4;
+         swizzle = SWIZZLE_XYZW;
+      }
+      else if (size == 3) {
+         dotOp = OPCODE_DP3;
+         swizzle = SWIZZLE_XYZW;
+      }
+      else {
+         assert(size == 2);
+         dotOp = OPCODE_DP3;
+         swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y);
+      }
+
+      /* Compute equality, inequality (tmp1 = (A ?= B)) */
+      inst = new_instruction(emitInfo, OPCODE_SNE);
+      storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+      storage_to_src_reg(&inst->SrcReg[1], n->Children[1]->Store);
+      storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+      inst->Comment = _mesa_strdup("Compare values");
+
+      /* Compute tmp2 = DOT(tmp1, tmp1)  (reduction) */
+      inst = new_instruction(emitInfo, dotOp);
+      storage_to_src_reg(&inst->SrcReg[0], n->Store);
+      storage_to_src_reg(&inst->SrcReg[1], n->Store);
+      inst->SrcReg[0].Swizzle = inst->SrcReg[1].Swizzle = swizzle; /*override*/
+      free_temp_storage(emitInfo->vt, n); /* free tmp1 */
+      if (!alloc_temp_storage(emitInfo, n, 1))  /* alloc tmp2 */
+         return NULL;
+      storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+      inst->Comment = _mesa_strdup("Reduce vec to bool");
+
+      if (n->Opcode == IR_EQUAL) {
+         /* compute tmp2.x = !tmp2.x  via tmp2.x = (tmp2.x == 0) */
+         inst = new_instruction(emitInfo, OPCODE_SEQ);
+         storage_to_src_reg(&inst->SrcReg[0], n->Store);
+         constant_to_src_reg(&inst->SrcReg[1], 0.0, emitInfo);
+         storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+         inst->Comment = _mesa_strdup("Invert true/false");
+      }
+   }
+   else {
+      /* size > 4, struct compare */
+#if 0
+      GLint i, num = (n->Children[0]->Store->Size + 3) / 4;
+      /*printf("BEGIN COMPARE size %d\n", num);*/
+      for (i = 0; i < num; i++) {
+         inst = new_instruction(emitInfo, opcode);
+         inst->SrcReg[0].File = n->Children[0]->Store->File;
+         inst->SrcReg[0].Index = n->Children[0]->Store->Index + i;
+         inst->SrcReg[1].File = n->Children[1]->Store->File;
+         inst->SrcReg[1].Index = n->Children[1]->Store->Index + i;
+         inst->DstReg.File = n->Store->File;
+         inst->DstReg.Index = n->Store->Index;
+
+         inst->CondUpdate = 1; /* update cond code */
+         if (i > 0) {
+            inst->DstReg.CondMask = COND_NE; /* update if !=0 */
+         }
+         /*_mesa_print_instruction(inst);*/
+      }
+      storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+#endif
+      _mesa_problem(NULL, "struct comparison not implemented yet");
+      inst = NULL;
+   }
+
+   /* free temps */
+   free_temp_storage(emitInfo->vt, n->Children[0]);
+   free_temp_storage(emitInfo->vt, n->Children[1]);
+
+   return inst;
+}
+
+
+
 /**
  * Generate code for an IR_CLAMP instruction.
  */
 static struct prog_instruction *
-emit_clamp(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_clamp(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    struct prog_instruction *inst;
 
@@ -769,7 +684,7 @@ emit_clamp(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
     * ch[2] = max limit
     */
 
-   inst = emit(vt, n->Children[0], prog);
+   inst = emit(emitInfo, n->Children[0]);
 
    /* If lower limit == 0.0 and upper limit == 1.0,
     *    set prev instruction's SaturateMode field to SATURATE_ZERO_ONE.
@@ -801,20 +716,20 @@ emit_clamp(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
 #endif
 
    if (!n->Store)
-      if (!alloc_temp_storage(vt, n, n->Children[0]->Store->Size))
+      if (!alloc_temp_storage(emitInfo, n, n->Children[0]->Store->Size))
          return NULL;
 
-   emit(vt, n->Children[1], prog);
-   emit(vt, n->Children[2], prog);
+   emit(emitInfo, n->Children[1]);
+   emit(emitInfo, n->Children[2]);
 
    /* tmp = max(ch[0], ch[1]) */
-   inst = new_instruction(prog, OPCODE_MAX);
+   inst = new_instruction(emitInfo, OPCODE_MAX);
    storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
    storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
    storage_to_src_reg(&inst->SrcReg[1], n->Children[1]->Store);
 
    /* tmp = min(tmp, ch[2]) */
-   inst = new_instruction(prog, OPCODE_MIN);
+   inst = new_instruction(emitInfo, OPCODE_MIN);
    storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
    storage_to_src_reg(&inst->SrcReg[0], n->Store);
    storage_to_src_reg(&inst->SrcReg[1], n->Children[2]->Store);
@@ -824,7 +739,7 @@ emit_clamp(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
 
 
 static struct prog_instruction *
-emit_negation(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_negation(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    /* Implement as MOV dst, -src; */
    /* XXX we could look at the previous instruction and in some circumstances
@@ -832,13 +747,13 @@ emit_negation(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
     */
    struct prog_instruction *inst;
 
-   emit(vt, n->Children[0], prog);
+   emit(emitInfo, n->Children[0]);
 
    if (!n->Store)
-      if (!alloc_temp_storage(vt, n, n->Children[0]->Store->Size))
+      if (!alloc_temp_storage(emitInfo, n, n->Children[0]->Store->Size))
          return NULL;
 
-   inst = new_instruction(prog, OPCODE_MOV);
+   inst = new_instruction(emitInfo, OPCODE_MOV);
    storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
    storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
    inst->SrcReg[0].NegateBase = NEGATE_XYZW;
@@ -847,90 +762,147 @@ emit_negation(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
 
 
 static struct prog_instruction *
-emit_label(const slang_ir_node *n, struct gl_program *prog)
+emit_label(slang_emit_info *emitInfo, const slang_ir_node *n)
 {
    assert(n->Label);
+#if 0
+   /* XXX this fails in loop tail code - investigate someday */
    assert(_slang_label_get_location(n->Label) < 0);
-   _slang_label_set_location(n->Label, prog->NumInstructions, prog);
+   _slang_label_set_location(n->Label, emitInfo->prog->NumInstructions,
+                             emitInfo->prog);
+#else
+   if (_slang_label_get_location(n->Label) < 0)
+      _slang_label_set_location(n->Label, emitInfo->prog->NumInstructions,
+                                emitInfo->prog);
+#endif
    return NULL;
 }
 
 
+/**
+ * Emit code for an inlined function call (subroutine).
+ */
 static struct prog_instruction *
-emit_cjump(slang_ir_node *n, struct gl_program *prog, GLuint zeroOrOne)
+emit_func(slang_emit_info *emitInfo, slang_ir_node *n)
 {
+   struct gl_program *progSave;
    struct prog_instruction *inst;
-   assert(n->Opcode == IR_CJUMP0 || n->Opcode == IR_CJUMP1);
-   inst = new_instruction(prog, OPCODE_BRA);
-   if (zeroOrOne)
-      inst->DstReg.CondMask = COND_NE;  /* branch if non-zero */
-   else
-      inst->DstReg.CondMask = COND_EQ;  /* branch if equal to zero */
-   inst->DstReg.CondSwizzle = SWIZZLE_X;
-   inst->BranchTarget = _slang_label_get_location(n->Label);
-   if (inst->BranchTarget < 0) {
-      _slang_label_add_reference(n->Label, prog->NumInstructions - 1);
+   GLuint subroutineId;
+
+   assert(n->Opcode == IR_FUNC);
+   assert(n->Label);
+
+   /* save/push cur program */
+   progSave = emitInfo->prog;
+   emitInfo->prog = new_subroutine(emitInfo, &subroutineId);
+
+   _slang_label_set_location(n->Label, emitInfo->prog->NumInstructions,
+                             emitInfo->prog);
+
+   if (emitInfo->EmitBeginEndSub) {
+      /* BGNSUB isn't a real instruction.
+       * We require a label (i.e. "foobar:") though, if we're going to
+       * print the program in the NV format.  The BNGSUB instruction is
+       * really just a NOP to attach the label to.
+       */
+      inst = new_instruction(emitInfo, OPCODE_BGNSUB);
+      inst->Comment = _mesa_strdup(n->Label->Name);
    }
+
+   /* body of function: */
+   emit(emitInfo, n->Children[0]);
+   n->Store = n->Children[0]->Store;
+
+   /* add RET instruction now, if needed */
+   inst = prev_instruction(emitInfo);
+   if (inst && inst->Opcode != OPCODE_RET) {
+      inst = new_instruction(emitInfo, OPCODE_RET);
+   }
+
+   if (emitInfo->EmitBeginEndSub) {
+      inst = new_instruction(emitInfo, OPCODE_ENDSUB);
+      inst->Comment = _mesa_strdup(n->Label->Name);
+   }
+
+   /* pop/restore cur program */
+   emitInfo->prog = progSave;
+
+   /* emit the function call */
+   inst = new_instruction(emitInfo, OPCODE_CAL);
+   /* The branch target is just the subroutine number (changed later) */
+   inst->BranchTarget = subroutineId;
+   inst->Comment = _mesa_strdup(n->Label->Name);
+   assert(inst->BranchTarget >= 0);
+
    return inst;
 }
 
 
+/**
+ * Emit code for a 'return' statement.
+ */
 static struct prog_instruction *
-emit_jump(slang_ir_node *n, struct gl_program *prog)
+emit_return(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    struct prog_instruction *inst;
-   inst = new_instruction(prog, OPCODE_BRA);
-   inst->DstReg.CondMask = COND_TR;  /* always branch */
-   inst->BranchTarget = _slang_label_get_location(n->Label);
-   if (inst->BranchTarget < 0) {
-      _slang_label_add_reference(n->Label, prog->NumInstructions - 1);
-   }
+   assert(n);
+   assert(n->Opcode == IR_RETURN);
+   assert(n->Label);
+   inst = new_instruction(emitInfo, OPCODE_RET);
+   inst->DstReg.CondMask = COND_TR;  /* always return */
    return inst;
 }
 
 
 static struct prog_instruction *
-emit_kill(struct gl_program *prog)
+emit_kill(slang_emit_info *emitInfo)
 {
    struct prog_instruction *inst;
    /* NV-KILL - discard fragment depending on condition code.
     * Note that ARB-KILL depends on sign of vector operand.
     */
-   inst = new_instruction(prog, OPCODE_KIL_NV);
+   inst = new_instruction(emitInfo, OPCODE_KIL_NV);
    inst->DstReg.CondMask = COND_TR;  /* always branch */
    return inst;
 }
 
 
 static struct prog_instruction *
-emit_tex(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_tex(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    struct prog_instruction *inst;
+
+   (void) emit(emitInfo, n->Children[1]);
+
    if (n->Opcode == IR_TEX) {
-      inst = new_instruction(prog, OPCODE_TEX);
+      inst = new_instruction(emitInfo, OPCODE_TEX);
    }
    else if (n->Opcode == IR_TEXB) {
-      inst = new_instruction(prog, OPCODE_TXB);
+      inst = new_instruction(emitInfo, OPCODE_TXB);
    }
    else {
       assert(n->Opcode == IR_TEXP);
-      inst = new_instruction(prog, OPCODE_TXP);
+      inst = new_instruction(emitInfo, OPCODE_TXP);
    }
 
    if (!n->Store)
-      if (!alloc_temp_storage(vt, n, 4))
+      if (!alloc_temp_storage(emitInfo, n, 4))
          return NULL;
 
    storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
 
-   (void) emit(vt, n->Children[1], prog);
-
    /* Child[1] is the coord */
+   assert(n->Children[1]->Store->File != PROGRAM_UNDEFINED);
+   assert(n->Children[1]->Store->Index >= 0);
    storage_to_src_reg(&inst->SrcReg[0], n->Children[1]->Store);
 
    /* Child[0] is the sampler (a uniform which'll indicate the texture unit) */
    assert(n->Children[0]->Store);
+   /* Store->Index is the sampler index */
+   assert(n->Children[0]->Store->Index >= 0);
+   /* Store->Size is the texture target */
    assert(n->Children[0]->Store->Size >= TEXTURE_1D_INDEX);
+   assert(n->Children[0]->Store->Size <= TEXTURE_RECT_INDEX);
 
    inst->Sampler = n->Children[0]->Store->Index; /* i.e. uniform's index */
    inst->TexSrcTarget = n->Children[0]->Store->Size;
@@ -942,33 +914,50 @@ emit_tex(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
 
 
 static struct prog_instruction *
-emit_move(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_move(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    struct prog_instruction *inst;
 
+   /* lhs */
+   emit(emitInfo, n->Children[0]);
+   if (!n->Children[0]->Store || n->Children[0]->Store->Index < 0) {
+      /* an error should have been already recorded */
+      return NULL;
+   }
+
    /* rhs */
    assert(n->Children[1]);
-   inst = emit(vt, n->Children[1], prog);
+   inst = emit(emitInfo, n->Children[1]);
+
+   if (!n->Children[1]->Store || n->Children[1]->Store->Index < 0) {
+      if (!emitInfo->log->text) {
+         slang_info_log_error(emitInfo->log, "invalid assignment");
+      }
+      return NULL;
+   }
 
    assert(n->Children[1]->Store->Index >= 0);
 
-   /* lhs */
-   emit(vt, n->Children[0], prog);
+   /*assert(n->Children[0]->Store->Size == n->Children[1]->Store->Size);*/
 
-   assert(!n->Store);
    n->Store = n->Children[0]->Store;
 
 #if PEEPHOLE_OPTIMIZATIONS
-   if (inst && _slang_is_temp(vt, n->Children[1]->Store)) {
+   if (inst &&
+       _slang_is_temp(emitInfo->vt, n->Children[1]->Store) &&
+       (inst->DstReg.File == n->Children[1]->Store->File) &&
+       (inst->DstReg.Index == n->Children[1]->Store->Index)) {
       /* Peephole optimization:
-       * Just modify the RHS to put its result into the dest of this
-       * MOVE operation.  Then, this MOVE is a no-op.
+       * The Right-Hand-Side has its results in a temporary place.
+       * Modify the RHS (and the prev instruction) to store its results
+       * in the destination specified by n->Children[0].
+       * Then, this MOVE is a no-op.
        */
-      _slang_free_temp(vt, n->Children[1]->Store);
+      if (n->Children[1]->Opcode != IR_SWIZZLE)
+         _slang_free_temp(emitInfo->vt, n->Children[1]->Store);
       *n->Children[1]->Store = *n->Children[0]->Store;
-      /* fixup the prev (RHS) instruction */
+      /* fixup the previous instruction (which stored the RHS result) */
       assert(n->Children[0]->Store->Index >= 0);
-      assert(n->Children[0]->Store->Index < 16);
       storage_to_dst_reg(&inst->DstReg, n->Children[0]->Store, n->Writemask);
       return inst;
    }
@@ -985,7 +974,7 @@ emit_move(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
          dstStore.Size = 4;
          srcStore.Size = 4;
          while (size >= 4) {
-            inst = new_instruction(prog, OPCODE_MOV);
+            inst = new_instruction(emitInfo, OPCODE_MOV);
             inst->Comment = _mesa_strdup("IR_MOVE block");
             storage_to_dst_reg(&inst->DstReg, &dstStore, n->Writemask);
             storage_to_src_reg(&inst->SrcReg[0], &srcStore);
@@ -997,52 +986,79 @@ emit_move(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
       else {
          /* single register move */
          char *srcAnnot, *dstAnnot;
-         inst = new_instruction(prog, OPCODE_MOV);
+         inst = new_instruction(emitInfo, OPCODE_MOV);
          assert(n->Children[0]->Store->Index >= 0);
-         assert(n->Children[0]->Store->Index < 16);
          storage_to_dst_reg(&inst->DstReg, n->Children[0]->Store, n->Writemask);
          storage_to_src_reg(&inst->SrcReg[0], n->Children[1]->Store);
-         dstAnnot = storage_annotation(n->Children[0], prog);
-         srcAnnot = storage_annotation(n->Children[1], prog);
+         dstAnnot = storage_annotation(n->Children[0], emitInfo->prog);
+         srcAnnot = storage_annotation(n->Children[1], emitInfo->prog);
          inst->Comment = instruction_annotation(inst->Opcode, dstAnnot,
                                                 srcAnnot, NULL, NULL);
       }
-      free_temp_storage(vt, n->Children[1]);
+      free_temp_storage(emitInfo->vt, n->Children[1]);
       return inst;
    }
 }
 
 
+/**
+ * An IR_COND node wraps a boolean expression which is used by an
+ * IF or WHILE test.  This is where we'll set condition codes, if needed.
+ */
 static struct prog_instruction *
-emit_cond(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_cond(slang_emit_info *emitInfo, slang_ir_node *n)
 {
-   /* Conditional expression (in if/while/for stmts).
-    * Need to update condition code register.
-    * Next instruction is typically an IR_CJUMP0/1.
-    */
-   /* last child expr instruction: */
-   struct prog_instruction *inst = emit(vt, n->Children[0], prog);
-   if (inst) {
-      /* set inst's CondUpdate flag */
-      inst->CondUpdate = GL_TRUE;
-      return inst; /* XXX or null? */
+   struct prog_instruction *inst;
+
+   assert(n->Opcode == IR_COND);
+
+   if (!n->Children[0])
+      return NULL;
+
+   /* emit code for the expression */
+   inst = emit(emitInfo, n->Children[0]);
+
+   if (!n->Children[0]->Store) {
+      /* error recovery */
+      return NULL;
+   }
+
+   assert(n->Children[0]->Store);
+   /*assert(n->Children[0]->Store->Size == 1);*/
+
+   if (emitInfo->EmitCondCodes) {
+      if (inst &&
+          n->Children[0]->Store &&
+          inst->DstReg.File == n->Children[0]->Store->File &&
+          inst->DstReg.Index == n->Children[0]->Store->Index) {
+         /* The previous instruction wrote to the register who's value
+          * we're testing.  Just fix that instruction so that the
+          * condition codes are computed.
+          */
+         inst->CondUpdate = GL_TRUE;
+         n->Store = n->Children[0]->Store;
+         return inst;
+      }
+      else {
+         /* This'll happen for things like "if (i) ..." where no code
+          * is normally generated for the expression "i".
+          * Generate a move instruction just to set condition codes.
+          */
+         if (!alloc_temp_storage(emitInfo, n, 1))
+            return NULL;
+         inst = new_instruction(emitInfo, OPCODE_MOV);
+         inst->CondUpdate = GL_TRUE;
+         storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+         storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+         _slang_free_temp(emitInfo->vt, n->Store);
+         inst->Comment = _mesa_strdup("COND expr");
+         return inst;
+      }
    }
    else {
-      /* This'll happen for things like "if (i) ..." where no code
-       * is normally generated for the expression "i".
-       * Generate a move instruction just to set condition codes.
-       * Note: must use full 4-component vector since all four
-       * condition codes must be set identically.
-       */
-      if (!alloc_temp_storage(vt, n, 4))
-         return NULL;
-      inst = new_instruction(prog, OPCODE_MOV);
-      inst->CondUpdate = GL_TRUE;
-      storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
-      storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
-      _slang_free_temp(vt, n->Store);
-      inst->Comment = _mesa_strdup("COND expr");
-      return inst; /* XXX or null? */
+      /* No-op: the boolean result of the expression is in a regular reg */
+      n->Store = n->Children[0]->Store;
+      return inst;
    }
 }
 
@@ -1051,32 +1067,48 @@ emit_cond(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
  * Logical-NOT
  */
 static struct prog_instruction *
-emit_not(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_not(slang_emit_info *emitInfo, slang_ir_node *n)
 {
-   GLfloat zero = 0.0;
-   slang_ir_storage st;
+   static const struct {
+      gl_inst_opcode op, opNot;
+   } operators[] = {
+      { OPCODE_SLT, OPCODE_SGE },
+      { OPCODE_SLE, OPCODE_SGT },
+      { OPCODE_SGT, OPCODE_SLE },
+      { OPCODE_SGE, OPCODE_SLT },
+      { OPCODE_SEQ, OPCODE_SNE },
+      { OPCODE_SNE, OPCODE_SEQ },
+      { 0, 0 }
+   };
    struct prog_instruction *inst;
-
-   /* need zero constant */
-   st.File = PROGRAM_CONSTANT;
-   st.Size = 1;
-   st.Index = _mesa_add_unnamed_constant(prog->Parameters, &zero,
-                                         1, &st.Swizzle);
+   GLuint i;
 
    /* child expr */
-   (void) emit(vt, n->Children[0], prog);
-   /* XXXX if child instr is SGT convert to SLE, if SEQ, SNE, etc */
+   inst = emit(emitInfo, n->Children[0]);
 
+#if PEEPHOLE_OPTIMIZATIONS
+   if (inst) {
+      /* if the prev instruction was a comparison instruction, invert it */
+      for (i = 0; operators[i].op; i++) {
+         if (inst->Opcode == operators[i].op) {
+            inst->Opcode = operators[i].opNot;
+            n->Store = n->Children[0]->Store;
+            return inst;
+         }
+      }
+   }
+#endif
+
+   /* else, invert using SEQ (v = v == 0) */
    if (!n->Store)
-      if (!alloc_temp_storage(vt, n, n->Children[0]->Store->Size))
+      if (!alloc_temp_storage(emitInfo, n, n->Children[0]->Store->Size))
          return NULL;
 
-   inst = new_instruction(prog, OPCODE_SEQ);
+   inst = new_instruction(emitInfo, OPCODE_SEQ);
    storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
    storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
-   storage_to_src_reg(&inst->SrcReg[1], &st);
-
-   free_temp_storage(vt, n->Children[0]);
+   constant_to_src_reg(&inst->SrcReg[1], 0.0, emitInfo);
+   free_temp_storage(emitInfo->vt, n->Children[0]);
 
    inst->Comment = _mesa_strdup("NOT");
    return inst;
@@ -1084,110 +1116,138 @@ emit_not(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
 
 
 static struct prog_instruction *
-emit_if(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_if(slang_emit_info *emitInfo, slang_ir_node *n)
 {
-   struct prog_instruction *ifInst;
-   GLuint ifInstLoc, elseInstLoc;
+   struct gl_program *prog = emitInfo->prog;
+   GLuint ifInstLoc, elseInstLoc = 0;
+   GLuint condWritemask = 0;
+
+   /* emit condition expression code */
+   {
+      struct prog_instruction *inst;
+      inst = emit(emitInfo, n->Children[0]);
+      if (emitInfo->EmitCondCodes) {
+         if (!inst) {
+            /* error recovery */
+            return NULL;
+         }
+         condWritemask = inst->DstReg.WriteMask;
+      }
+   }
+
+#if 0
+   assert(n->Children[0]->Store->Size == 1); /* a bool! */
+#endif
 
-   emit(vt, n->Children[0], prog);  /* the condition */
    ifInstLoc = prog->NumInstructions;
-   if (EmitHighLevelInstructions) {
-      ifInst = new_instruction(prog, OPCODE_IF);
-      ifInst->DstReg.CondMask = COND_NE;  /* if cond is non-zero */
-      ifInst->DstReg.CondSwizzle = SWIZZLE_X;
+   if (emitInfo->EmitHighLevelInstructions) {
+      struct prog_instruction *ifInst = new_instruction(emitInfo, OPCODE_IF);
+      if (emitInfo->EmitCondCodes) {
+         ifInst->DstReg.CondMask = COND_NE;  /* if cond is non-zero */
+         /* only test the cond code (1 of 4) that was updated by the
+          * previous instruction.
+          */
+         ifInst->DstReg.CondSwizzle = writemask_to_swizzle(condWritemask);
+      }
+      else {
+         /* test reg.x */
+         storage_to_src_reg(&ifInst->SrcReg[0], n->Children[0]->Store);
+      }
    }
    else {
       /* conditional jump to else, or endif */
-      ifInst = new_instruction(prog, OPCODE_BRA);
+      struct prog_instruction *ifInst = new_instruction(emitInfo, OPCODE_BRA);
       ifInst->DstReg.CondMask = COND_EQ;  /* BRA if cond is zero */
-      ifInst->DstReg.CondSwizzle = SWIZZLE_X;
       ifInst->Comment = _mesa_strdup("if zero");
+      ifInst->DstReg.CondSwizzle = writemask_to_swizzle(condWritemask);
    }
 
    /* if body */
-   emit(vt, n->Children[1], prog);
+   emit(emitInfo, n->Children[1]);
 
    if (n->Children[2]) {
       /* have else body */
       elseInstLoc = prog->NumInstructions;
-      if (EmitHighLevelInstructions) {
-         (void) new_instruction(prog, OPCODE_ELSE);
+      if (emitInfo->EmitHighLevelInstructions) {
+         (void) new_instruction(emitInfo, OPCODE_ELSE);
       }
       else {
          /* jump to endif instruction */
          struct prog_instruction *inst;
-         inst = new_instruction(prog, OPCODE_BRA);
+         inst = new_instruction(emitInfo, OPCODE_BRA);
          inst->Comment = _mesa_strdup("else");
          inst->DstReg.CondMask = COND_TR;  /* always branch */
       }
-      ifInst = prog->Instructions + ifInstLoc;
-      ifInst->BranchTarget = prog->NumInstructions;
-
-      emit(vt, n->Children[2], prog);
+      prog->Instructions[ifInstLoc].BranchTarget = prog->NumInstructions;
+      emit(emitInfo, n->Children[2]);
    }
    else {
       /* no else body */
-      ifInst = prog->Instructions + ifInstLoc;
-      ifInst->BranchTarget = prog->NumInstructions + 1;
+      prog->Instructions[ifInstLoc].BranchTarget = prog->NumInstructions;
    }
 
-   if (EmitHighLevelInstructions) {
-      (void) new_instruction(prog, OPCODE_ENDIF);
+   if (emitInfo->EmitHighLevelInstructions) {
+      (void) new_instruction(emitInfo, OPCODE_ENDIF);
    }
 
    if (n->Children[2]) {
-      struct prog_instruction *elseInst;
-      elseInst = prog->Instructions + elseInstLoc;
-      elseInst->BranchTarget = prog->NumInstructions;
+      prog->Instructions[elseInstLoc].BranchTarget = prog->NumInstructions;
    }
    return NULL;
 }
 
 
 static struct prog_instruction *
-emit_loop(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_loop(slang_emit_info *emitInfo, slang_ir_node *n)
 {
-   struct prog_instruction *beginInst, *endInst;
-   GLuint beginInstLoc, endInstLoc;
+   struct gl_program *prog = emitInfo->prog;
+   struct prog_instruction *endInst;
+   GLuint beginInstLoc, tailInstLoc, endInstLoc;
    slang_ir_node *ir;
 
    /* emit OPCODE_BGNLOOP */
    beginInstLoc = prog->NumInstructions;
-   if (EmitHighLevelInstructions) {
-      (void) new_instruction(prog, OPCODE_BGNLOOP);
+   if (emitInfo->EmitHighLevelInstructions) {
+      (void) new_instruction(emitInfo, OPCODE_BGNLOOP);
    }
 
    /* body */
-   emit(vt, n->Children[0], prog);
+   emit(emitInfo, n->Children[0]);
+
+   /* tail */
+   tailInstLoc = prog->NumInstructions;
+   if (n->Children[1]) {
+      if (emitInfo->EmitComments)
+         emit_comment(emitInfo, "Loop tail code:");
+      emit(emitInfo, n->Children[1]);
+   }
 
    endInstLoc = prog->NumInstructions;
-   if (EmitHighLevelInstructions) {
+   if (emitInfo->EmitHighLevelInstructions) {
       /* emit OPCODE_ENDLOOP */
-      endInst = new_instruction(prog, OPCODE_ENDLOOP);
+      endInst = new_instruction(emitInfo, OPCODE_ENDLOOP);
    }
    else {
       /* emit unconditional BRA-nch */
-      endInst = new_instruction(prog, OPCODE_BRA);
+      endInst = new_instruction(emitInfo, OPCODE_BRA);
       endInst->DstReg.CondMask = COND_TR;  /* always true */
    }
-   /* end instruction's BranchTarget points to top of loop */
+   /* ENDLOOP's BranchTarget points to the BGNLOOP inst */
    endInst->BranchTarget = beginInstLoc;
 
-   if (EmitHighLevelInstructions) {
+   if (emitInfo->EmitHighLevelInstructions) {
       /* BGNLOOP's BranchTarget points to the ENDLOOP inst */
-      beginInst = prog->Instructions + beginInstLoc;
-      beginInst->BranchTarget = prog->NumInstructions - 1;
+      prog->Instructions[beginInstLoc].BranchTarget = prog->NumInstructions -1;
    }
 
    /* Done emitting loop code.  Now walk over the loop's linked list of
     * BREAK and CONT nodes, filling in their BranchTarget fields (which
     * will point to the ENDLOOP+1 or BGNLOOP instructions, respectively).
     */
-   for (ir = n->BranchNode; ir; ir = ir->BranchNode) {
+   for (ir = n->List; ir; ir = ir->List) {
       struct prog_instruction *inst = prog->Instructions + ir->InstLocation;
       assert(inst->BranchTarget < 0);
       if (ir->Opcode == IR_BREAK ||
-          ir->Opcode == IR_BREAK_IF_FALSE ||
           ir->Opcode == IR_BREAK_IF_TRUE) {
          assert(inst->Opcode == OPCODE_BRK ||
                 inst->Opcode == OPCODE_BRA);
@@ -1196,12 +1256,11 @@ emit_loop(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
       }
       else {
          assert(ir->Opcode == IR_CONT ||
-                ir->Opcode == IR_CONT_IF_FALSE ||
                 ir->Opcode == IR_CONT_IF_TRUE);
          assert(inst->Opcode == OPCODE_CONT ||
                 inst->Opcode == OPCODE_BRA);
-         /* to go instruction at top of loop */
-         inst->BranchTarget = beginInstLoc;
+         /* go to instruction at tail of loop */
+         inst->BranchTarget = endInstLoc;
       }
    }
    return NULL;
@@ -1209,22 +1268,37 @@ emit_loop(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
 
 
 /**
- * "Continue" or "break" statement.
+ * Unconditional "continue" or "break" statement.
  * Either OPCODE_CONT, OPCODE_BRK or OPCODE_BRA will be emitted.
  */
 static struct prog_instruction *
-emit_cont_break(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_cont_break(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    gl_inst_opcode opcode;
    struct prog_instruction *inst;
-   n->InstLocation = prog->NumInstructions;
-   if (EmitHighLevelInstructions) {
+
+   if (n->Opcode == IR_CONT) {
+      /* we need to execute the loop's tail code before doing CONT */
+      assert(n->Parent);
+      assert(n->Parent->Opcode == IR_LOOP);
+      if (n->Parent->Children[1]) {
+         /* emit tail code */
+         if (emitInfo->EmitComments) {
+            emit_comment(emitInfo, "continue - tail code:");
+         }
+         emit(emitInfo, n->Parent->Children[1]);
+      }
+   }
+
+   /* opcode selection */
+   if (emitInfo->EmitHighLevelInstructions) {
       opcode = (n->Opcode == IR_CONT) ? OPCODE_CONT : OPCODE_BRK;
    }
    else {
       opcode = OPCODE_BRA;
    }
-   inst = new_instruction(prog, opcode);
+   n->InstLocation = emitInfo->prog->NumInstructions;
+   inst = new_instruction(emitInfo, opcode);
    inst->DstReg.CondMask = COND_TR;  /* always true */
    return inst;
 }
@@ -1235,31 +1309,63 @@ emit_cont_break(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
  * Either OPCODE_CONT, OPCODE_BRK or OPCODE_BRA will be emitted.
  */
 static struct prog_instruction *
-emit_cont_break_if(slang_var_table *vt, slang_ir_node *n,
-                   struct gl_program *prog, GLboolean breakTrue)
+emit_cont_break_if_true(slang_emit_info *emitInfo, slang_ir_node *n)
 {
-   gl_inst_opcode opcode;
    struct prog_instruction *inst;
 
+   assert(n->Opcode == IR_CONT_IF_TRUE ||
+          n->Opcode == IR_BREAK_IF_TRUE);
+
    /* evaluate condition expr, setting cond codes */
-   inst = emit(vt, n->Children[0], prog);
-   assert(inst);
-   inst->CondUpdate = GL_TRUE;
-
-   n->InstLocation = prog->NumInstructions;
-   if (EmitHighLevelInstructions) {
-      if (n->Opcode == IR_CONT_IF_TRUE ||
-          n->Opcode == IR_CONT_IF_FALSE)
-         opcode = OPCODE_CONT;
-      else
-         opcode = OPCODE_BRK;
+   inst = emit(emitInfo, n->Children[0]);
+   if (emitInfo->EmitCondCodes) {
+      assert(inst);
+      inst->CondUpdate = GL_TRUE;
+   }
+
+   n->InstLocation = emitInfo->prog->NumInstructions;
+
+   /* opcode selection */
+   if (emitInfo->EmitHighLevelInstructions) {
+      const gl_inst_opcode opcode
+         = (n->Opcode == IR_CONT_IF_TRUE) ? OPCODE_CONT : OPCODE_BRK;
+      if (emitInfo->EmitCondCodes) {
+         /* Get the writemask from the previous instruction which set
+          * the condcodes.  Use that writemask as the CondSwizzle.
+          */
+         const GLuint condWritemask = inst->DstReg.WriteMask;
+         inst = new_instruction(emitInfo, opcode);
+         inst->DstReg.CondMask = COND_NE;
+         inst->DstReg.CondSwizzle = writemask_to_swizzle(condWritemask);
+         return inst;
+      }
+      else {
+         /* IF reg
+          *    BRK/CONT;
+          * ENDIF
+          */
+         GLint ifInstLoc;
+         ifInstLoc = emitInfo->prog->NumInstructions;
+         inst = new_instruction(emitInfo, OPCODE_IF);
+         storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+         n->InstLocation = emitInfo->prog->NumInstructions;
+
+         inst = new_instruction(emitInfo, opcode);
+         inst = new_instruction(emitInfo, OPCODE_ENDIF);
+
+         emitInfo->prog->Instructions[ifInstLoc].BranchTarget
+            = emitInfo->prog->NumInstructions;
+         return inst;
+      }
    }
    else {
-      opcode = OPCODE_BRA;
+      const GLuint condWritemask = inst->DstReg.WriteMask;
+      assert(emitInfo->EmitCondCodes);
+      inst = new_instruction(emitInfo, OPCODE_BRA);
+      inst->DstReg.CondMask = COND_NE;
+      inst->DstReg.CondSwizzle = writemask_to_swizzle(condWritemask);
+      return inst;
    }
-   inst = new_instruction(prog, opcode);
-   inst->DstReg.CondMask = breakTrue ? COND_NE : COND_EQ;
-   return inst;
 }
 
 
@@ -1282,22 +1388,30 @@ fix_swizzle(GLuint swizzle)
 }
 
 
+/**
+ * Return the number of components actually named by the swizzle.
+ * Recall that swizzles may have undefined/don't-care values.
+ */
+static GLuint
+swizzle_size(GLuint swizzle)
+{
+   GLuint size = 0, i;
+   for (i = 0; i < 4; i++) {
+      GLuint swz = GET_SWZ(swizzle, i);
+      size += (swz >= 0 && swz <= 3);
+   }
+   return size;
+}
+
+
 static struct prog_instruction *
-emit_swizzle(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit_swizzle(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    GLuint swizzle;
+   struct prog_instruction *inst;
 
-   /* swizzled storage access */
-   (void) emit(vt, n->Children[0], prog);
-
-   /* "pull-up" the child's storage info, applying our swizzle info */
-   n->Store->File  = n->Children[0]->Store->File;
-   n->Store->Index = n->Children[0]->Store->Index;
-   n->Store->Size  = n->Children[0]->Store->Size;
-   /*n->Var = n->Children[0]->Var; XXX for debug */
-   assert(n->Store->Index >= 0);
+   inst = emit(emitInfo, n->Children[0]);
 
-   swizzle = fix_swizzle(n->Store->Swizzle);
 #ifdef DEBUG
    {
       GLuint s = n->Children[0]->Store->Swizzle;
@@ -1307,11 +1421,25 @@ emit_swizzle(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
       assert(GET_SWZ(s, 3) != SWIZZLE_NIL);
    }
 #endif
+   /* For debug: n->Var = n->Children[0]->Var; */
+
+   /* "pull-up" the child's storage info, applying our swizzle info */
+   n->Store->File  = n->Children[0]->Store->File;
+   n->Store->Index = n->Children[0]->Store->Index;
+   n->Store->Size = swizzle_size(n->Store->Swizzle);
+#if 0
+   printf("Emit Swizzle %s  reg %d  chSize %d  mySize %d\n",
+          _mesa_swizzle_string(n->Store->Swizzle, 0, 0),
+          n->Store->Index, n->Children[0]->Store->Size,
+          n->Store->Size);
+#endif
 
    /* apply this swizzle to child's swizzle to get composed swizzle */
+   swizzle = fix_swizzle(n->Store->Swizzle); /* remove the don't care terms */
    n->Store->Swizzle = swizzle_swizzle(n->Children[0]->Store->Swizzle,
                                        swizzle);
-   return NULL;
+
+   return inst;
 }
 
 
@@ -1320,19 +1448,17 @@ emit_swizzle(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
  * element represented by this node.
  */
 static struct prog_instruction *
-emit_array_element(slang_var_table *vt, slang_ir_node *n,
-                   struct gl_program *prog)
+emit_array_element(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    assert(n->Store);
    assert(n->Store->File != PROGRAM_UNDEFINED);
    assert(n->Store->Size > 0);
 
    if (n->Store->File == PROGRAM_STATE_VAR) {
-      n->Store->Index = _slang_alloc_statevar(n, prog->Parameters);
+      n->Store->Index = _slang_alloc_statevar(n, emitInfo->prog->Parameters);
       return NULL;
    }
 
-
    if (n->Children[1]->Opcode == IR_FLOAT) {
       /* Constant index */
       const GLint arrayAddr = n->Children[0]->Store->Index;
@@ -1354,22 +1480,29 @@ emit_array_element(slang_var_table *vt, slang_ir_node *n,
  * Resolve storage for accessing a structure field.
  */
 static struct prog_instruction *
-emit_struct_field(slang_var_table *vt, slang_ir_node *n,
-                  struct gl_program *prog)
+emit_struct_field(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    if (n->Store->File == PROGRAM_STATE_VAR) {
-      n->Store->Index = _slang_alloc_statevar(n, prog->Parameters);
-      return NULL;
+      n->Store->Index = _slang_alloc_statevar(n, emitInfo->prog->Parameters);
    }
    else {
-      _mesa_problem(NULL, "structs/fields not supported yet");
+      GLint offset = n->FieldOffset / 4;
+      assert(n->Children[0]->Store->Index >= 0);
+      n->Store->Index = n->Children[0]->Store->Index + offset;
+      if (n->Store->Size == 1) {
+         GLint swz = n->FieldOffset % 4;
+         n->Store->Swizzle = MAKE_SWIZZLE4(swz, swz, swz, swz);
+      }
+      else {
+         n->Store->Swizzle = SWIZZLE_XYZW;
+      }
    }
    return NULL; /* no instruction */
 }
 
 
 static struct prog_instruction *
-emit(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
+emit(slang_emit_info *emitInfo, slang_ir_node *n)
 {
    struct prog_instruction *inst;
    if (!n)
@@ -1380,17 +1513,19 @@ emit(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
       /* sequence of two sub-trees */
       assert(n->Children[0]);
       assert(n->Children[1]);
-      emit(vt, n->Children[0], prog);
-      inst = emit(vt, n->Children[1], prog);
+      emit(emitInfo, n->Children[0]);
+      inst = emit(emitInfo, n->Children[1]);
+#if 0
       assert(!n->Store);
+#endif
       n->Store = n->Children[1]->Store;
       return inst;
 
    case IR_SCOPE:
       /* new variable scope */
-      _slang_push_var_table(vt);
-      inst = emit(vt, n->Children[0], prog);
-      _slang_pop_var_table(vt);
+      _slang_push_var_table(emitInfo->vt);
+      inst = emit(emitInfo, n->Children[0]);
+      _slang_pop_var_table(emitInfo->vt);
       return inst;
 
    case IR_VAR_DECL:
@@ -1398,39 +1533,42 @@ emit(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
       assert(n->Store);
       assert(n->Store->File != PROGRAM_UNDEFINED);
       assert(n->Store->Size > 0);
-      assert(n->Store->Index < 0);
+      /*assert(n->Store->Index < 0);*/
       if (!n->Var || n->Var->isTemp) {
          /* a nameless/temporary variable, will be freed after first use */
-         if (!_slang_alloc_temp(vt, n->Store))
-            RETURN_ERROR("Ran out of registers, too many temporaries", 0);
+         /*NEW*/
+         if (n->Store->Index < 0 && !_slang_alloc_temp(emitInfo->vt, n->Store)) {
+            slang_info_log_error(emitInfo->log,
+                                 "Ran out of registers, too many temporaries");
+            return NULL;
+         }
       }
       else {
          /* a regular variable */
-         _slang_add_variable(vt, n->Var);
-         if (!_slang_alloc_var(vt, n->Store))
-            RETURN_ERROR("Ran out of registers, too many variables", 0);
+         _slang_add_variable(emitInfo->vt, n->Var);
+         if (!_slang_alloc_var(emitInfo->vt, n->Store)) {
+            slang_info_log_error(emitInfo->log,
+                                 "Ran out of registers, too many variables");
+            return NULL;
+         }
          /*
          printf("IR_VAR_DECL %s %d store %p\n",
                 (char*) n->Var->a_name, n->Store->Index, (void*) n->Store);
          */
          assert(n->Var->aux == n->Store);
       }
-#ifdef DEBUG_foo
-      /* emit NOP with comment describing the variable's storage location */
-      {
+      if (emitInfo->EmitComments) {
+         /* emit NOP with comment describing the variable's storage location */
          char s[1000];
-         sprintf(s, "TEMP[%d]%s = %s (size %d)",
+         sprintf(s, "TEMP[%d]%s = variable %s (size %d)",
                  n->Store->Index,
                  _mesa_swizzle_string(n->Store->Swizzle, 0, GL_FALSE), 
-                 (char *) n->Var->a_name,
+                 (n->Var ? (char *) n->Var->a_name : "anonymous"),
                  n->Store->Size);
-         inst = new_instruction(prog, OPCODE_NOP);
-         inst->Comment = _mesa_strdup(s);
+         inst = emit_comment(emitInfo, s);
          return inst;
       }
-#else
       return NULL;
-#endif
 
    case IR_VAR:
       /* Reference to a variable
@@ -1441,27 +1579,35 @@ emit(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
 
       if (n->Store->File == PROGRAM_STATE_VAR &&
           n->Store->Index < 0) {
-         n->Store->Index = _slang_alloc_statevar(n, prog->Parameters);
+         n->Store->Index = _slang_alloc_statevar(n, emitInfo->prog->Parameters);
       }
 
       if (n->Store->Index < 0) {
-         printf("#### VAR %s not allocated!\n", (char*)n->Var->a_name);
+         /* probably ran out of registers */
+         return NULL;
       }
-      assert(n->Store->Index >= 0);
       assert(n->Store->Size > 0);
       break;
 
    case IR_ELEMENT:
-      return emit_array_element(vt, n, prog);
+      return emit_array_element(emitInfo, n);
    case IR_FIELD:
-      return emit_struct_field(vt, n, prog);
+      return emit_struct_field(emitInfo, n);
    case IR_SWIZZLE:
-      return emit_swizzle(vt, n, prog);
+      return emit_swizzle(emitInfo, n);
 
    case IR_I_TO_F:
-      {
-         n->Store = n->Children[0]->Store;
+      /* just move */
+      emit(emitInfo, n->Children[0]);
+      inst = new_instruction(emitInfo, OPCODE_MOV);
+      if (!n->Store) {
+         if (!alloc_temp_storage(emitInfo, n, 1))
+            return NULL;
       }
+      storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+      storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+      if (emitInfo->EmitComments)
+         inst->Comment = _mesa_strdup("int to float");
       return NULL;
 
    /* Simple arithmetic */
@@ -1493,101 +1639,206 @@ emit(slang_var_table *vt, slang_ir_node *n, struct gl_program *prog)
    case IR_SNEQUAL:
    case IR_SGE:
    case IR_SGT:
+   case IR_SLE:
+   case IR_SLT:
    case IR_POW:
    case IR_EXP:
    case IR_EXP2:
    /* trinary operators */
    case IR_LRP:
-      return emit_arith(vt, n, prog);
+      return emit_arith(emitInfo, n);
+
+   case IR_EQUAL:
+   case IR_NOTEQUAL:
+      return emit_compare(emitInfo, n);
+
    case IR_CLAMP:
-      return emit_clamp(vt, n, prog);
+      return emit_clamp(emitInfo, n);
    case IR_TEX:
    case IR_TEXB:
    case IR_TEXP:
-      return emit_tex(vt, n, prog);
+      return emit_tex(emitInfo, n);
    case IR_NEG:
-      return emit_negation(vt, n, prog);
+      return emit_negation(emitInfo, n);
    case IR_FLOAT:
       /* find storage location for this float constant */
-      n->Store->Index = _mesa_add_unnamed_constant(prog->Parameters, n->Value,
+      n->Store->Index = _mesa_add_unnamed_constant(emitInfo->prog->Parameters,
+                                                   n->Value,
                                                    n->Store->Size,
                                                    &n->Store->Swizzle);
       if (n->Store->Index < 0) {
-         RETURN_ERROR("Ran out of space for constants.", 0);
+         slang_info_log_error(emitInfo->log, "Ran out of space for constants");
+         return NULL;
       }
       return NULL;
 
    case IR_MOVE:
-      return emit_move(vt, n, prog);
+      return emit_move(emitInfo, n);
 
    case IR_COND:
-      return emit_cond(vt, n, prog);
+      return emit_cond(emitInfo, n);
 
    case IR_NOT:
-      return emit_not(vt, n, prog);
+      return emit_not(emitInfo, n);
 
    case IR_LABEL:
-      return emit_label(n, prog);
-   case IR_JUMP:
-      return emit_jump(n, prog);
-   case IR_CJUMP0:
-      return emit_cjump(n, prog, 0);
-   case IR_CJUMP1:
-      return emit_cjump(n, prog, 1);
+      return emit_label(emitInfo, n);
+
    case IR_KILL:
-      return emit_kill(prog);
+      return emit_kill(emitInfo);
+
+   case IR_FUNC:
+      /* new variable scope for subroutines/function calls*/
+      _slang_push_var_table(emitInfo->vt);
+      inst = emit_func(emitInfo, n);
+      _slang_pop_var_table(emitInfo->vt);
+      return inst;
 
    case IR_IF:
-      return emit_if(vt, n, prog);
+      return emit_if(emitInfo, n);
 
    case IR_LOOP:
-      return emit_loop(vt, n, prog);
-   case IR_BREAK_IF_FALSE:
-   case IR_CONT_IF_FALSE:
-      return emit_cont_break_if(vt, n, prog, GL_FALSE);
+      return emit_loop(emitInfo, n);
    case IR_BREAK_IF_TRUE:
    case IR_CONT_IF_TRUE:
-      return emit_cont_break_if(vt, n, prog, GL_TRUE);
+      return emit_cont_break_if_true(emitInfo, n);
    case IR_BREAK:
       /* fall-through */
    case IR_CONT:
-      return emit_cont_break(vt, n, prog);
+      return emit_cont_break(emitInfo, n);
 
    case IR_BEGIN_SUB:
-      return new_instruction(prog, OPCODE_BGNSUB);
+      return new_instruction(emitInfo, OPCODE_BGNSUB);
    case IR_END_SUB:
-      return new_instruction(prog, OPCODE_ENDSUB);
+      return new_instruction(emitInfo, OPCODE_ENDSUB);
    case IR_RETURN:
-      return new_instruction(prog, OPCODE_RET);
+      return emit_return(emitInfo, n);
 
    case IR_NOP:
       return NULL;
 
    default:
       _mesa_problem(NULL, "Unexpected IR opcode in emit()\n");
-      abort();
    }
    return NULL;
 }
 
 
+/**
+ * After code generation, any subroutines will be in separate program
+ * objects.  This function appends all the subroutines onto the main
+ * program and resolves the linking of all the branch/call instructions.
+ * XXX this logic should really be part of the linking process...
+ */
+static void
+_slang_resolve_subroutines(slang_emit_info *emitInfo)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_program *mainP = emitInfo->prog;
+   GLuint *subroutineLoc, i, total;
+
+   subroutineLoc
+      = (GLuint *) _mesa_malloc(emitInfo->NumSubroutines * sizeof(GLuint));
+
+   /* total number of instructions */
+   total = mainP->NumInstructions;
+   for (i = 0; i < emitInfo->NumSubroutines; i++) {
+      subroutineLoc[i] = total;
+      total += emitInfo->Subroutines[i]->NumInstructions;
+   }
+
+   /* adjust BrancTargets within the functions */
+   for (i = 0; i < emitInfo->NumSubroutines; i++) {
+      struct gl_program *sub = emitInfo->Subroutines[i];
+      GLuint j;
+      for (j = 0; j < sub->NumInstructions; j++) {
+         struct prog_instruction *inst = sub->Instructions + j;
+         if (inst->Opcode != OPCODE_CAL && inst->BranchTarget >= 0) {
+            inst->BranchTarget += subroutineLoc[i];
+         }
+      }
+   }
+
+   /* append subroutines' instructions after main's instructions */
+   mainP->Instructions = _mesa_realloc_instructions(mainP->Instructions,
+                                                    mainP->NumInstructions,
+                                                    total);
+   mainP->NumInstructions = total;
+   for (i = 0; i < emitInfo->NumSubroutines; i++) {
+      struct gl_program *sub = emitInfo->Subroutines[i];
+      _mesa_copy_instructions(mainP->Instructions + subroutineLoc[i],
+                              sub->Instructions,
+                              sub->NumInstructions);
+      /* delete subroutine code */
+      sub->Parameters = NULL; /* prevent double-free */
+      _mesa_delete_program(ctx, sub);
+   }
+
+   /* free subroutine list */
+   if (emitInfo->Subroutines) {
+      _mesa_free(emitInfo->Subroutines);
+      emitInfo->Subroutines = NULL;
+   }
+   emitInfo->NumSubroutines = 0;
+
+   /* Examine CAL instructions.
+    * At this point, the BranchTarget field of the CAL instructions is
+    * the number/id of the subroutine to call (an index into the
+    * emitInfo->Subroutines list).
+    * Translate that into an actual instruction location now.
+    */
+   for (i = 0; i < mainP->NumInstructions; i++) {
+      struct prog_instruction *inst = mainP->Instructions + i;
+      if (inst->Opcode == OPCODE_CAL) {
+         const GLuint f = inst->BranchTarget;
+         inst->BranchTarget = subroutineLoc[f];
+      }
+   }
+
+   _mesa_free(subroutineLoc);
+}
+
+
+
+
 GLboolean
 _slang_emit_code(slang_ir_node *n, slang_var_table *vt,
-                 struct gl_program *prog, GLboolean withEnd)
+                 struct gl_program *prog, GLboolean withEnd,
+                 slang_info_log *log)
 {
+   GET_CURRENT_CONTEXT(ctx);
    GLboolean success;
+   slang_emit_info emitInfo;
 
-   (void) emit(vt, n, prog);
+   emitInfo.log = log;
+   emitInfo.vt = vt;
+   emitInfo.prog = prog;
+   emitInfo.Subroutines = NULL;
+   emitInfo.NumSubroutines = 0;
+
+   emitInfo.EmitHighLevelInstructions = ctx->Shader.EmitHighLevelInstructions;
+   emitInfo.EmitCondCodes = ctx->Shader.EmitCondCodes;
+   emitInfo.EmitComments = ctx->Shader.EmitComments;
+   emitInfo.EmitBeginEndSub = GL_TRUE;
+
+   if (!emitInfo.EmitCondCodes) {
+      emitInfo.EmitHighLevelInstructions = GL_TRUE;
+   }      
+
+   (void) emit(&emitInfo, n);
 
    /* finish up by adding the END opcode to program */
    if (withEnd) {
       struct prog_instruction *inst;
-      inst = new_instruction(prog, OPCODE_END);
+      inst = new_instruction(&emitInfo, OPCODE_END);
    }
+
+   _slang_resolve_subroutines(&emitInfo);
+
    success = GL_TRUE;
 
-   printf("*********** End generate code (%u inst):\n", prog->NumInstructions);
 #if 0
+   printf("*********** End emit code (%u inst):\n", prog->NumInstructions);
    _mesa_print_program(prog);
    _mesa_print_program_parameters(ctx,prog);
 #endif