slang: support uniform arrays
[mesa.git] / src / mesa / shader / slang / slang_codegen.c
index d0e6b6cc5e3a2c88610beb00be5ec2327cd910c3..8263aae3343e9b5f3a4ed0878d70aadaf3dc6ecf 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Mesa 3-D graphics library
- * Version:  7.1
  *
  * Copyright (C) 2005-2007  Brian Paul   All Rights Reserved.
+ * Copyright (C) 2008 VMware, Inc.  All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
 #include "slang_print.h"
 
 
+/** Max iterations to unroll */
+const GLuint MAX_FOR_LOOP_UNROLL_ITERATIONS = 32;
+
+/** Max for-loop body size (in slang operations) to unroll */
+const GLuint MAX_FOR_LOOP_UNROLL_BODY_SIZE = 50;
+
+/** Max for-loop body complexity to unroll.
+ * We'll compute complexity as the product of the number of iterations
+ * and the size of the body.  So long-ish loops with very simple bodies
+ * can be unrolled, as well as short loops with larger bodies.
+ */
+const GLuint MAX_FOR_LOOP_UNROLL_COMPLEXITY = 256;
+
+
+
 static slang_ir_node *
 _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper);
 
@@ -213,7 +228,14 @@ _slang_sizeof_type_specifier(const slang_type_specifier *spec)
       break;
    case SLANG_SPEC_STRUCT:
       sz = _slang_field_offset(spec, 0); /* special use */
-      if (sz > 4) {
+      if (sz == 1) {
+         /* 1-float structs are actually troublesome to deal with since they
+          * might get placed at R.x, R.y, R.z or R.z.  Return size=2 to
+          * ensure the object is placed at R.x
+          */
+         sz = 2;
+      }
+      else if (sz > 4) {
          sz = (sz + 3) & ~0x3; /* round up to multiple of four */
       }
       break;
@@ -234,41 +256,43 @@ _slang_sizeof_type_specifier(const slang_type_specifier *spec)
 
 
 /**
- * Establish the binding between a slang_ir_node and a slang_variable.
- * Then, allocate/attach a slang_ir_storage object to the IR node if needed.
- * The IR node must be a IR_VAR or IR_VAR_DECL node.
- * \param n  the IR node
- * \param var  the variable to associate with the IR node
+ * Query variable/array length (number of elements).
+ * This is slightly non-trivial because there are two ways to express
+ * arrays: "float x[3]" vs. "float[3] x".
+ * \return the length of the array for the given variable, or 0 if not an array
  */
-static void
-_slang_attach_storage(slang_ir_node *n, slang_variable *var)
+static GLint
+_slang_array_length(const slang_variable *var)
 {
-   assert(n);
-   assert(var);
-   assert(n->Opcode == IR_VAR || n->Opcode == IR_VAR_DECL);
-   assert(!n->Var || n->Var == var);
-
-   n->Var = var;
-
-   if (!n->Store) {
-      /* need to setup storage */
-      if (n->Var && n->Var->store) {
-         /* node storage info = var storage info */
-         n->Store = n->Var->store;
-      }
-      else {
-         /* alloc new storage info */
-         n->Store = _slang_new_ir_storage(PROGRAM_UNDEFINED, -7, -5);
-#if 0
-         printf("%s var=%s Store=%p Size=%d\n", __FUNCTION__,
-                (char*) var->a_name,
-                (void*) n->Store, n->Store->Size);
-#endif
-         if (n->Var)
-            n->Var->store = n->Store;
-         assert(n->Var->store);
-      }
+   if (var->type.array_len > 0) {
+      /* Ex: float[4] x; */
+      return var->type.array_len;
+   }
+   if (var->array_len > 0) {
+      /* Ex: float x[4]; */
+      return var->array_len;
+   }
+   return 0;
+}
+
+
+/**
+ * Compute total size of array give size of element, number of elements.
+ * \return size in floats
+ */
+static GLint
+_slang_array_size(GLint elemSize, GLint arrayLen)
+{
+   GLint total;
+   assert(elemSize > 0);
+   if (arrayLen > 1) {
+      /* round up base type to multiple of 4 */
+      total = ((elemSize + 3) & ~0x3) * MAX2(arrayLen, 1);
+   }
+   else {
+      total = elemSize;
    }
+   return total;
 }
 
 
@@ -349,7 +373,7 @@ _slang_input_index(const char *name, GLenum target, GLuint *swizzleOut)
    const struct input_info *inputs
       = (target == GL_VERTEX_PROGRAM_ARB) ? vertInputs : fragInputs;
 
-   ASSERT(MAX_TEXTURE_UNITS == 8); /* if this fails, fix vertInputs above */
+   ASSERT(MAX_TEXTURE_COORD_UNITS == 8); /* if this fails, fix vertInputs above */
 
    for (i = 0; inputs[i].Name; i++) {
       if (strcmp(inputs[i].Name, name) == 0) {
@@ -386,8 +410,8 @@ _slang_output_index(const char *name, GLenum target)
       { NULL, 0 }
    };
    static const struct output_info fragOutputs[] = {
-      { "gl_FragColor", FRAG_RESULT_COLR },
-      { "gl_FragDepth", FRAG_RESULT_DEPR },
+      { "gl_FragColor", FRAG_RESULT_COLOR },
+      { "gl_FragDepth", FRAG_RESULT_DEPTH },
       { "gl_FragData", FRAG_RESULT_DATA0 },
       { NULL, 0 }
    };
@@ -427,6 +451,9 @@ static slang_asm_info AsmInfo[] = {
    { "vec4_multiply", IR_MUL, 1, 2 },
    { "vec4_dot", IR_DOT4, 1, 2 },
    { "vec3_dot", IR_DOT3, 1, 2 },
+   { "vec2_dot", IR_DOT2, 1, 2 },
+   { "vec3_nrm", IR_NRM3, 1, 1 },
+   { "vec4_nrm", IR_NRM4, 1, 1 },
    { "vec3_cross", IR_CROSS, 1, 2 },
    { "vec4_lrp", IR_LRP, 1, 3 },
    { "vec4_min", IR_MIN, 1, 2 },
@@ -449,18 +476,28 @@ static slang_asm_info AsmInfo[] = {
    /* float binary op */
    { "float_power", IR_POW, 1, 2 },
    /* texture / sampler */
-   { "vec4_tex1d", IR_TEX, 1, 2 },
-   { "vec4_texb1d", IR_TEXB, 1, 2 },  /* 1d w/ bias */
-   { "vec4_texp1d", IR_TEXP, 1, 2 },  /* 1d w/ projection */
-   { "vec4_tex2d", IR_TEX, 1, 2 },
-   { "vec4_texb2d", IR_TEXB, 1, 2 },  /* 2d w/ bias */
-   { "vec4_texp2d", IR_TEXP, 1, 2 },  /* 2d w/ projection */
-   { "vec4_tex3d", IR_TEX, 1, 2 },
-   { "vec4_texb3d", IR_TEXB, 1, 2 },  /* 3d w/ bias */
-   { "vec4_texp3d", IR_TEXP, 1, 2 },  /* 3d w/ projection */
-   { "vec4_texcube", IR_TEX, 1, 2 },  /* cubemap */
-   { "vec4_tex_rect", IR_TEX, 1, 2 }, /* rectangle */
-   { "vec4_texp_rect", IR_TEX, 1, 2 },/* rectangle w/ projection */
+   { "vec4_tex_1d", IR_TEX, 1, 2 },
+   { "vec4_tex_1d_bias", IR_TEXB, 1, 2 },  /* 1d w/ bias */
+   { "vec4_tex_1d_proj", IR_TEXP, 1, 2 },  /* 1d w/ projection */
+   { "vec4_tex_2d", IR_TEX, 1, 2 },
+   { "vec4_tex_2d_bias", IR_TEXB, 1, 2 },  /* 2d w/ bias */
+   { "vec4_tex_2d_proj", IR_TEXP, 1, 2 },  /* 2d w/ projection */
+   { "vec4_tex_3d", IR_TEX, 1, 2 },
+   { "vec4_tex_3d_bias", IR_TEXB, 1, 2 },  /* 3d w/ bias */
+   { "vec4_tex_3d_proj", IR_TEXP, 1, 2 },  /* 3d w/ projection */
+   { "vec4_tex_cube", IR_TEX, 1, 2 },      /* cubemap */
+   { "vec4_tex_rect", IR_TEX, 1, 2 },      /* rectangle */
+   { "vec4_tex_rect_bias", IR_TEX, 1, 2 }, /* rectangle w/ projection */
+
+   /* texture / sampler but with shadow comparison */
+   { "vec4_tex_1d_shadow", IR_TEX_SH, 1, 2 },
+   { "vec4_tex_1d_bias_shadow", IR_TEXB_SH, 1, 2 },
+   { "vec4_tex_1d_proj_shadow", IR_TEXP_SH, 1, 2 },
+   { "vec4_tex_2d_shadow", IR_TEX_SH, 1, 2 },
+   { "vec4_tex_2d_bias_shadow", IR_TEXB_SH, 1, 2 },
+   { "vec4_tex_2d_proj_shadow", IR_TEXP_SH, 1, 2 },
+   { "vec4_tex_rect_shadow", IR_TEX_SH, 1, 2 },
+   { "vec4_tex_rect_proj_shadow", IR_TEXP_SH, 1, 2 },
 
    /* unary op */
    { "ivec4_to_vec4", IR_I_TO_F, 1, 1 }, /* int[4] to float[4] */
@@ -666,18 +703,16 @@ new_if(slang_ir_node *cond, slang_ir_node *ifPart, slang_ir_node *elsePart)
 static slang_ir_node *
 new_var(slang_assemble_ctx *A, slang_variable *var)
 {
-   slang_ir_node *n;
-   if (!var)
-      return NULL;
-
-   assert(var->declared);
-
-   n = new_node0(IR_VAR);
+   slang_ir_node *n = new_node0(IR_VAR);
    if (n) {
-      _slang_attach_storage(n, var);
-      /*
-      printf("new_var %s store=%p\n", (char*)name, (void*) n->Store);
-      */
+      ASSERT(var);
+      ASSERT(var->store);
+      ASSERT(!n->Store);
+      ASSERT(!n->Var);
+
+      /* Set IR node's Var and Store pointers */
+      n->Var = var;
+      n->Store = var->store;
    }
    return n;
 }
@@ -1404,6 +1439,12 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun,
    /*_slang_label_delete(A->curFuncEndLabel);*/
    A->curFuncEndLabel = prevFuncEndLabel;
 
+   if (A->pragmas->Debug) {
+      char s[1000];
+      snprintf(s, sizeof(s), "Call/inline %s()", (char *) fun->header.a_name);
+      n->Comment = _slang_strdup(s);
+   }
+
    return n;
 }
 
@@ -1421,27 +1462,6 @@ slang_find_asm_info(const char *name)
 }
 
 
-/**
- * Return the default swizzle mask for accessing a variable of the
- * given size (in floats).  If size = 1, comp is used to identify
- * which component [0..3] of the register holds the variable.
- */
-static GLuint
-_slang_var_swizzle(GLint size, GLint comp)
-{
-   switch (size) {
-   case 1:
-      return MAKE_SWIZZLE4(comp, comp, comp, comp);
-   case 2:
-      return MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_NIL, SWIZZLE_NIL);
-   case 3:
-      return MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_NIL);
-   default:
-      return SWIZZLE_XYZW;
-   }
-}
-
-
 /**
  * Some write-masked assignments are simple, but others are hard.
  * Simple example:
@@ -1555,6 +1575,7 @@ swizzle_to_writemask(slang_assemble_ctx *A, GLuint swizzle,
 }
 
 
+#if 0 /* not used, but don't remove just yet */
 /**
  * Recursively traverse 'oper' to produce a swizzle mask in the event
  * of any vector subscripts and swizzle suffixes.
@@ -1608,8 +1629,10 @@ resolve_swizzle(const slang_operation *oper)
       return SWIZZLE_XYZW;
    }
 }
+#endif
 
 
+#if 0
 /**
  * Recursively descend through swizzle nodes to find the node's storage info.
  */
@@ -1621,7 +1644,7 @@ get_store(const slang_ir_node *n)
    }
    return n->Store;
 }
-
+#endif
 
 
 /**
@@ -1694,6 +1717,7 @@ _slang_gen_asm(slang_assemble_ctx *A, slang_operation *oper,
 }
 
 
+#if 0
 static void
 print_funcs(struct slang_function_scope_ *scope, const char *name)
 {
@@ -1707,6 +1731,7 @@ print_funcs(struct slang_function_scope_ *scope, const char *name)
    if (scope->outer_scope)
       print_funcs(scope->outer_scope, name);
 }
+#endif
 
 
 /**
@@ -1961,7 +1986,7 @@ _slang_make_array_constructor(slang_assemble_ctx *A, slang_operation *oper)
          */
          slang_variable *p = slang_variable_scope_grow(fun->parameters);
          char name[10];
-         snprintf(name, sizeof(name), "p%d", i);
+         _mesa_snprintf(name, sizeof(name), "p%d", i);
          p->a_name = slang_atom_pool_atom(A->atoms, name);
          p->type.qualifier = SLANG_QUAL_CONST;
          p->type.specifier.type = baseType;
@@ -2249,7 +2274,7 @@ _slang_gen_method_call(slang_assemble_ctx *A, slang_operation *oper)
    /* Create a float/literal IR node encoding the array length */
    n = new_node0(IR_FLOAT);
    if (n) {
-      n->Value[0] = (float) var->array_len;
+      n->Value[0] = (float) _slang_array_length(var);
       n->Store = _slang_new_ir_storage(PROGRAM_CONSTANT, -1, 1);
    }
    return n;
@@ -2424,40 +2449,288 @@ _slang_gen_do(slang_assemble_ctx * A, const slang_operation *oper)
 
 
 /**
- * Generate for-loop using high-level IR_LOOP instruction.
+ * Recursively count the number of operations rooted at 'oper'.
+ * This gives some kind of indication of the size/complexity of an operation.
+ */
+static GLuint
+sizeof_operation(const slang_operation *oper)
+{
+   if (oper) {
+      GLuint count = 1; /* me */
+      GLuint i;
+      for (i = 0; i < oper->num_children; i++) {
+         count += sizeof_operation(&oper->children[i]);
+      }
+      return count;
+   }
+   else {
+      return 0;
+   }
+}
+
+
+/**
+ * Determine if a for-loop can be unrolled.
+ * At this time, only a rather narrow class of for loops can be unrolled.
+ * See code for details.
+ * When a loop can't be unrolled because it's too large we'll emit a
+ * message to the log.
+ */
+static GLboolean
+_slang_can_unroll_for_loop(slang_assemble_ctx * A, const slang_operation *oper)
+{
+   GLuint bodySize;
+   GLint start, end;
+   const char *varName;
+   slang_atom varId;
+
+   assert(oper->type == SLANG_OPER_FOR);
+   assert(oper->num_children == 4);
+
+   /* children[0] must be either "int i=constant" or "i=constant" */
+   if (oper->children[0].type == SLANG_OPER_BLOCK_NO_NEW_SCOPE) {
+      slang_variable *var;
+
+      if (oper->children[0].children[0].type != SLANG_OPER_VARIABLE_DECL)
+         return GL_FALSE;
+
+      varId = oper->children[0].children[0].a_id;
+
+      var = _slang_variable_locate(oper->children[0].children[0].locals,
+                                   varId, GL_TRUE);
+      if (!var)
+         return GL_FALSE;
+      if (!var->initializer)
+         return GL_FALSE;
+      if (var->initializer->type != SLANG_OPER_LITERAL_INT)
+         return GL_FALSE;
+      start = (GLint) var->initializer->literal[0];
+   }
+   else if (oper->children[0].type == SLANG_OPER_EXPRESSION) {
+      if (oper->children[0].children[0].type != SLANG_OPER_ASSIGN)
+         return GL_FALSE;
+      if (oper->children[0].children[0].children[0].type != SLANG_OPER_IDENTIFIER)
+         return GL_FALSE;
+      if (oper->children[0].children[0].children[1].type != SLANG_OPER_LITERAL_INT)
+         return GL_FALSE;
+
+      varId = oper->children[0].children[0].children[0].a_id;
+
+      start = (GLint) oper->children[0].children[0].children[1].literal[0];
+   }
+   else {
+      return GL_FALSE;
+   }
+
+   /* children[1] must be "i<constant" */
+   if (oper->children[1].type != SLANG_OPER_EXPRESSION)
+      return GL_FALSE;
+   if (oper->children[1].children[0].type != SLANG_OPER_LESS)
+      return GL_FALSE;
+   if (oper->children[1].children[0].children[0].type != SLANG_OPER_IDENTIFIER)
+      return GL_FALSE;
+   if (oper->children[1].children[0].children[1].type != SLANG_OPER_LITERAL_INT)
+      return GL_FALSE;
+
+   end = (GLint) oper->children[1].children[0].children[1].literal[0];
+
+   /* children[2] must be "i++" or "++i" */
+   if (oper->children[2].type != SLANG_OPER_POSTINCREMENT &&
+       oper->children[2].type != SLANG_OPER_PREINCREMENT)
+      return GL_FALSE;
+   if (oper->children[2].children[0].type != SLANG_OPER_IDENTIFIER)
+      return GL_FALSE;
+
+   /* make sure the same variable name is used in all places */
+   if ((oper->children[1].children[0].children[0].a_id != varId) ||
+       (oper->children[2].children[0].a_id != varId))
+      return GL_FALSE;
+
+   varName = (const char *) varId;
+
+   /* children[3], the loop body, can't be too large */
+   bodySize = sizeof_operation(&oper->children[3]);
+   if (bodySize > MAX_FOR_LOOP_UNROLL_BODY_SIZE) {
+      slang_info_log_print(A->log,
+                           "Note: 'for (%s ... )' body is too large/complex"
+                           " to unroll",
+                           varName);
+      return GL_FALSE;
+   }
+
+   if (start >= end)
+      return GL_FALSE; /* degenerate case */
+
+   if (end - start > MAX_FOR_LOOP_UNROLL_ITERATIONS) {
+      slang_info_log_print(A->log,
+                           "Note: 'for (%s=%d; %s<%d; ++%s)' is too"
+                           " many iterations to unroll",
+                           varName, start, varName, end, varName);
+      return GL_FALSE;
+   }
+
+   if ((end - start) * bodySize > MAX_FOR_LOOP_UNROLL_COMPLEXITY) {
+      slang_info_log_print(A->log,
+                           "Note: 'for (%s=%d; %s<%d; ++%s)' will generate"
+                           " too much code to unroll",
+                           varName, start, varName, end, varName);
+      return GL_FALSE;
+   }
+
+   return GL_TRUE; /* we can unroll the loop */
+}
+
+
+static void
+_unroll_loop_inc(slang_assemble_ctx * A)
+{
+   A->UnrollLoop++;
+}
+
+
+static void
+_unroll_loop_dec(slang_assemble_ctx * A)
+{
+   A->UnrollLoop--;
+}
+
+
+/**
+ * Unroll a for-loop.
+ * First we determine the number of iterations to unroll.
+ * Then for each iteration:
+ *   make a copy of the loop body
+ *   replace instances of the loop variable with the current iteration value
+ *   generate IR code for the body
+ * \return pointer to generated IR code or NULL if error, out of memory, etc.
+ */
+static slang_ir_node *
+_slang_unroll_for_loop(slang_assemble_ctx * A, const slang_operation *oper)
+{
+   GLint start, end, iter;
+   slang_ir_node *n, *root = NULL;
+   slang_atom varId;
+
+   /* Set flag so code generator knows we're unrolling loops */
+   _unroll_loop_inc( A );
+
+   if (oper->children[0].type == SLANG_OPER_BLOCK_NO_NEW_SCOPE) {
+      /* for (int i=0; ... */
+      slang_variable *var;
+
+      varId = oper->children[0].children[0].a_id;
+      var = _slang_variable_locate(oper->children[0].children[0].locals,
+                                   varId, GL_TRUE);
+      start = (GLint) var->initializer->literal[0];
+   }
+   else {
+      /* for (i=0; ... */
+      varId = oper->children[0].children[0].children[0].a_id;
+      start = (GLint) oper->children[0].children[0].children[1].literal[0];
+   }
+
+   end = (GLint) oper->children[1].children[0].children[1].literal[0];
+
+   for (iter = start; iter < end; iter++) {
+      slang_operation *body;
+
+      /* make a copy of the loop body */
+      body = slang_operation_new(1);
+      if (!body) {
+         _unroll_loop_dec( A );
+         return NULL;
+      }
+
+      if (!slang_operation_copy(body, &oper->children[3])) {
+         _unroll_loop_dec( A );
+         return NULL;
+      }
+
+      /* in body, replace instances of 'varId' with literal 'iter' */
+      {
+         slang_variable *oldVar;
+         slang_operation *newOper;
+
+         oldVar = _slang_variable_locate(oper->locals, varId, GL_TRUE);
+         if (!oldVar) {
+            /* undeclared loop variable */
+            slang_operation_delete(body);
+            _unroll_loop_dec( A );
+            return NULL;
+         }
+
+         newOper = slang_operation_new(1);
+         newOper->type = SLANG_OPER_LITERAL_INT;
+         newOper->literal_size = 1;
+         newOper->literal[0] = iter;
+
+         /* replace instances of the loop variable with newOper */
+         slang_substitute(A, body, 1, &oldVar, &newOper, GL_FALSE);
+      }
+
+      /* do IR codegen for body */
+      n = _slang_gen_operation(A, body);
+      if (!n) {
+         _unroll_loop_dec( A );
+         return NULL;
+      }
+
+      root = new_seq(root, n);
+
+      slang_operation_delete(body);
+   }
+
+   _unroll_loop_dec( A );
+
+   return root;
+}
+
+
+/**
+ * Generate IR for a for-loop.  Unrolling will be done when possible.
  */
 static slang_ir_node *
 _slang_gen_for(slang_assemble_ctx * A, const slang_operation *oper)
 {
-   /*
-    * init code (child[0])
-    * LOOP:
-    *    BREAK if !expr (child[1])
-    *    body code (child[3])
-    *    tail code:
-    *       incr code (child[2])   // XXX continue here
-    */
-   slang_ir_node *prevLoop, *loop, *cond, *breakIf, *body, *init, *incr;
+   GLboolean unroll = _slang_can_unroll_for_loop(A, oper);
 
-   init = _slang_gen_operation(A, &oper->children[0]);
-   loop = new_loop(NULL);
+   if (unroll) {
+      slang_ir_node *code = _slang_unroll_for_loop(A, oper);
+      if (code)
+         return code;
+   }
 
-   /* save old, push new loop */
-   prevLoop = A->CurLoop;
-   A->CurLoop = loop;
+   /* conventional for-loop code generation */
+   {
+      /*
+       * init code (child[0])
+       * LOOP:
+       *    BREAK if !expr (child[1])
+       *    body code (child[3])
+       *    tail code:
+       *       incr code (child[2])   // XXX continue here
+       */
+      slang_ir_node *prevLoop, *loop, *cond, *breakIf, *body, *init, *incr;
+      init = _slang_gen_operation(A, &oper->children[0]);
+      loop = new_loop(NULL);
 
-   cond = new_cond(new_not(_slang_gen_operation(A, &oper->children[1])));
-   breakIf = new_break_if_true(A->CurLoop, cond);
-   body = _slang_gen_operation(A, &oper->children[3]);
-   incr = _slang_gen_operation(A, &oper->children[2]);
+      /* save old, push new loop */
+      prevLoop = A->CurLoop;
+      A->CurLoop = loop;
 
-   loop->Children[0] = new_seq(breakIf, body);
-   loop->Children[1] = incr;  /* tail code */
+      cond = new_cond(new_not(_slang_gen_operation(A, &oper->children[1])));
+      breakIf = new_break_if_true(A->CurLoop, cond);
+      body = _slang_gen_operation(A, &oper->children[3]);
+      incr = _slang_gen_operation(A, &oper->children[2]);
 
-   /* pop loop, restore prev */
-   A->CurLoop = prevLoop;
+      loop->Children[0] = new_seq(breakIf, body);
+      loop->Children[1] = incr;  /* tail code */
+
+      /* pop loop, restore prev */
+      A->CurLoop = prevLoop;
 
-   return new_seq(init, loop);
+      return new_seq(init, loop);
+   }
 }
 
 
@@ -2545,18 +2818,24 @@ _slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper)
    if (is_operation_type(&oper->children[1], SLANG_OPER_BREAK)
        && !haveElseClause) {
       /* Special case: generate a conditional break */
+      if (!A->CurLoop && A->UnrollLoop) /* trying to unroll */
+         return NULL;
       ifBody = new_break_if_true(A->CurLoop, cond);
       return ifBody;
    }
    else if (is_operation_type(&oper->children[1], SLANG_OPER_CONTINUE)
             && !haveElseClause) {
-      /* Special case: generate a conditional break */
+      /* Special case: generate a conditional continue */
+      if (!A->CurLoop && A->UnrollLoop) /* trying to unroll */
+         return NULL;
       ifBody = new_cont_if_true(A->CurLoop, cond);
       return ifBody;
    }
    else {
       /* general case */
       ifBody = _slang_gen_operation(A, &oper->children[1]);
+      if (!ifBody)
+         return NULL;
       if (haveElseClause)
          elseBody = _slang_gen_operation(A, &oper->children[2]);
       else
@@ -2638,83 +2917,147 @@ _slang_gen_temporary(GLint size)
 
 
 /**
- * Generate IR node for allocating/declaring a variable.
+ * Generate program constants for an array.
+ * Ex: const vec2[3] v = vec2[3](vec2(1,1), vec2(2,2), vec2(3,3));
+ * This will allocate and initialize three vector constants, storing
+ * the array in constant memory, not temporaries like a non-const array.
+ * This can also be used for uniform array initializers.
+ * \return GL_TRUE for success, GL_FALSE if failure (semantic error, etc).
+ */
+static GLboolean
+make_constant_array(slang_assemble_ctx *A,
+                    slang_variable *var,
+                    slang_operation *initializer)
+{
+   struct gl_program *prog = A->program;
+   const GLenum datatype = _slang_gltype_from_specifier(&var->type.specifier);
+   const char *varName = (char *) var->a_name;
+   const GLuint numElements = initializer->num_children;
+   GLint size;
+   GLuint i, j;
+   GLfloat *values;
+
+   if (!var->store) {
+      var->store = _slang_new_ir_storage(PROGRAM_UNDEFINED, -6, -6);
+   }
+   size = var->store->Size;
+
+   assert(var->type.qualifier == SLANG_QUAL_CONST ||
+          var->type.qualifier == SLANG_QUAL_UNIFORM);
+   assert(initializer->type == SLANG_OPER_CALL);
+   assert(initializer->array_constructor);
+
+   values = (GLfloat *) _mesa_malloc(numElements * 4 * sizeof(GLfloat));
+
+   /* convert constructor params into ordinary floats */
+   for (i = 0; i < numElements; i++) {
+      const slang_operation *op = &initializer->children[i];
+      if (op->type != SLANG_OPER_LITERAL_FLOAT) {
+         /* unsupported type for this optimization */
+         free(values);
+         return GL_FALSE;
+      }
+      for (j = 0; j < op->literal_size; j++) {
+         values[i * 4 + j] = op->literal[j];
+      }
+      for ( ; j < 4; j++) {
+         values[i * 4 + j] = 0.0f;
+      }
+   }
+
+   /* slightly different paths for constants vs. uniforms */
+   if (var->type.qualifier == SLANG_QUAL_UNIFORM) {
+      var->store->File = PROGRAM_UNIFORM;
+      var->store->Index = _mesa_add_uniform(prog->Parameters, varName,
+                                            size, datatype, values);
+   }
+   else {
+      var->store->File = PROGRAM_CONSTANT;
+      var->store->Index = _mesa_add_named_constant(prog->Parameters, varName,
+                                                   values, size);
+   }
+   assert(var->store->Size == size);
+
+   _mesa_free(values);
+
+   return GL_TRUE;
+}
+
+
+
+/**
+ * Generate IR node for allocating/declaring a variable (either a local or
+ * a global).
+ * Generally, this involves allocating an slang_ir_storage instance for the
+ * variable, choosing a register file (temporary, constant, etc).
+ * For ordinary variables we do not yet allocate storage though.  We do that
+ * when we find the first actual use of the variable to avoid allocating temp
+ * regs that will never get used.
+ * At this time, uniforms are always allocated space in this function.
+ *
  * \param initializer  Optional initializer expression for the variable.
  */
 static slang_ir_node *
 _slang_gen_var_decl(slang_assemble_ctx *A, slang_variable *var,
                     slang_operation *initializer)
 {
+   const char *varName = (const char *) var->a_name;
+   const GLenum datatype = _slang_gltype_from_specifier(&var->type.specifier);
    slang_ir_node *varDecl, *n;
    slang_ir_storage *store;
+   GLint arrayLen, size, totalSize;  /* if array then totalSize > size */
+   gl_register_file file;
 
    /*assert(!var->declared);*/
    var->declared = GL_TRUE;
 
-   varDecl = new_node0(IR_VAR_DECL);
-   if (!varDecl)
-      return NULL;
-
-   _slang_attach_storage(varDecl, var);
-   assert(var->store);
-   assert(varDecl->Store == var->store);
-   assert(varDecl->Store);
-   assert(varDecl->Store->Index < 0);
-   store = var->store;
-
-   assert(store == varDecl->Store);
-
-   /* determine GPU storage file */
-   /* XXX if the variable is const, use PROGRAM_CONSTANT */
+   /* determine GPU register file for simple cases */
    if (is_sampler_type(&var->type)) {
-      store->File = PROGRAM_SAMPLER;
+      file = PROGRAM_SAMPLER;
+   }
+   else if (var->type.qualifier == SLANG_QUAL_UNIFORM) {
+      file = PROGRAM_UNIFORM;
    }
    else {
-      store->File = PROGRAM_TEMPORARY;
+      file = PROGRAM_TEMPORARY;
    }
 
-   store->Size = _slang_sizeof_type_specifier(&varDecl->Var->type.specifier);
-
-   if (store->Size <= 0) {
-      slang_info_log_error(A->log, "invalid declaration for '%s'",
-                           (char*) var->a_name);
+   size = _slang_sizeof_type_specifier(&var->type.specifier);
+   if (size <= 0) {
+      slang_info_log_error(A->log, "invalid declaration for '%s'", varName);
       return NULL;
    }
 
-#if 0
-   printf("%s var %p %s  store=%p index=%d size=%d\n",
-          __FUNCTION__, (void *) var, (char *) var->a_name,
-          (void *) store, store->Index, store->Size);
-#endif
+   arrayLen = _slang_array_length(var);
+   totalSize = _slang_array_size(size, arrayLen);
 
-   if (var->type.array_len > 0) {
-      /* the type is an array, ex: float[4] x; */
-      GLint sz = (store->Size + 3) & ~3;
-      /* total size = element size * array length */
-      sz *= var->type.array_len;
-      store->Size = sz;
-   }
+   /* Allocate IR node for the declaration */
+   varDecl = new_node0(IR_VAR_DECL);
+   if (!varDecl)
+      return NULL;
 
-   if (var->array_len > 0) {
-      /* this is an array */
-      /* round up the element size to a multiple of 4 */
-      GLint sz = (store->Size + 3) & ~3;
-      /* total size = element size * array length */
-      sz *= var->array_len;
-      store->Size = sz;
+   /* Allocate slang_ir_storage for this variable if needed.
+    * Note that we may not actually allocate a constant or temporary register
+    * until later.
+    */
+   if (!var->store) {
+      GLint index = -7;  /* TBD / unknown */
+      var->store = _slang_new_ir_storage(file, index, totalSize);
+      if (!var->store)
+         return NULL; /* out of memory */
    }
 
+   /* set the IR node's Var and Store pointers */
+   varDecl->Var = var;
+   varDecl->Store = var->store;
+
+
+   store = var->store;
+
    /* if there's an initializer, generate IR for the expression */
    if (initializer) {
-      const char *varName = (const char *) var->a_name;
       slang_ir_node *varRef, *init;
 
-      varRef = new_var(A, var);
-      if (!varRef) {
-         slang_info_log_error(A->log, "undefined variable '%s'", varName);
-         return NULL;
-      }
-
       if (var->type.qualifier == SLANG_QUAL_CONST) {
          /* if the variable is const, the initializer must be a const
           * expression as well.
@@ -2728,21 +3071,63 @@ _slang_gen_var_decl(slang_assemble_ctx *A, slang_variable *var,
 #endif
       }
 
+      /* IR for the variable we're initializing */
+      varRef = new_var(A, var);
+      if (!varRef) {
+         slang_info_log_error(A->log, "out of memory");
+         return NULL;
+      }
+
       /* constant-folding, etc here */
       _slang_simplify(initializer, &A->space, A->atoms); 
 
+      /* look for simple constant-valued variables and uniforms */
+      if (var->type.qualifier == SLANG_QUAL_CONST ||
+          var->type.qualifier == SLANG_QUAL_UNIFORM) {
+
+         if (initializer->type == SLANG_OPER_CALL &&
+             initializer->array_constructor) {
+            /* array initializer */
+            if (make_constant_array(A, var, initializer))
+               return varRef;
+         }
+         else if (initializer->type == SLANG_OPER_LITERAL_FLOAT ||
+                  initializer->type == SLANG_OPER_LITERAL_INT) {
+            /* simple float/vector initializer */
+            if (store->File == PROGRAM_UNIFORM) {
+               store->Index = _mesa_add_uniform(A->program->Parameters,
+                                                varName,
+                                                totalSize, datatype,
+                                                initializer->literal);
+               store->Swizzle = _slang_var_swizzle(size, 0);
+               return varRef;
+            }
+#if 0
+            else {
+               store->File = PROGRAM_CONSTANT;
+               store->Index = _mesa_add_named_constant(A->program->Parameters,
+                                                       varName,
+                                                       initializer->literal,
+                                                       totalSize);
+               store->Swizzle = _slang_var_swizzle(size, 0);
+               return varRef;
+            }
+#endif
+         }
+      }
+
+      /* IR for initializer */
       init = _slang_gen_operation(A, initializer);
       if (!init)
          return NULL;
 
-      /*assert(init->Store);*/
-
       /* XXX remove this when type checking is added above */
-      if (init->Store && varRef->Store->Size != init->Store->Size) {
+      if (init->Store && init->Store->Size != totalSize) {
          slang_info_log_error(A->log, "invalid assignment (wrong types)");
          return NULL;
       }
 
+      /* assign RHS to LHS */
       n = new_node2(IR_COPY, varRef, init);
       n = new_seq(varDecl, n);
    }
@@ -2751,6 +3136,19 @@ _slang_gen_var_decl(slang_assemble_ctx *A, slang_variable *var,
       n = varDecl;
    }
 
+   if (store->File == PROGRAM_UNIFORM && store->Index < 0) {
+      /* always need to allocate storage for uniforms at this point */
+      store->Index = _mesa_add_uniform(A->program->Parameters, varName,
+                                       totalSize, datatype, NULL);
+      store->Swizzle = _slang_var_swizzle(size, 0);
+   }
+
+#if 0
+   printf("%s var %p %s  store=%p index=%d size=%d\n",
+          __FUNCTION__, (void *) var, (char *) varName,
+          (void *) store, store->Index, store->Size);
+#endif
+
    return n;
 }
 
@@ -2956,6 +3354,7 @@ _slang_gen_return(slang_assemble_ctx * A, slang_operation *oper)
 }
 
 
+#if 0
 /**
  * Determine if the given operation/expression is const-valued.
  */
@@ -2979,6 +3378,7 @@ _slang_is_constant_expr(const slang_operation *oper)
       return GL_TRUE;
    }
 }
+#endif
 
 
 /**
@@ -3041,9 +3441,9 @@ _slang_assignment_compatible(slang_assemble_ctx *A,
 }
 
 
-
 /**
  * Generate IR tree for a local variable declaration.
+ * Basically do some error checking and call _slang_gen_var_decl().
  */
 static slang_ir_node *
 _slang_gen_declaration(slang_assemble_ctx *A, slang_operation *oper)
@@ -3097,25 +3497,27 @@ _slang_gen_declaration(slang_assemble_ctx *A, slang_operation *oper)
          return NULL;
       }         
    }
+   else {
+      if (var->type.qualifier == SLANG_QUAL_CONST) {
+         slang_info_log_error(A->log,
+                       "const-qualified variable '%s' requires initializer",
+                       varName);
+         return NULL;
+      }
+   }
 
    /* Generate IR node */
    varDecl = _slang_gen_var_decl(A, var, initializer);
    if (!varDecl)
       return NULL;
 
-   if (var->type.qualifier == SLANG_QUAL_CONST && !initializer) {
-      slang_info_log_error(A->log,
-                           "const-qualified variable '%s' requires initializer",
-                           varName);
-      return NULL;
-   }
-
    return varDecl;
 }
 
 
 /**
- * Generate IR tree for a variable (such as in an expression).
+ * Generate IR tree for a reference to a variable (such as in an expression).
+ * This is different from a variable declaration.
  */
 static slang_ir_node *
 _slang_gen_variable(slang_assemble_ctx * A, slang_operation *oper)
@@ -3125,11 +3527,13 @@ _slang_gen_variable(slang_assemble_ctx * A, slang_operation *oper)
     */
    slang_atom name = oper->var ? oper->var->a_name : oper->a_id;
    slang_variable *var = _slang_variable_locate(oper->locals, name, GL_TRUE);
-   slang_ir_node *n = new_var(A, var);
-   if (!n) {
+   slang_ir_node *n;
+   if (!var) {
       slang_info_log_error(A->log, "undefined variable '%s'", (char *) name);
       return NULL;
    }
+   assert(var->declared);
+   n = new_var(A, var);
    return n;
 }
 
@@ -3185,6 +3589,22 @@ is_store_writable(const slang_assemble_ctx *A, const slang_ir_storage *store)
 }
 
 
+/**
+ * Walk up an IR storage path to compute the final swizzle.
+ * This is used when we find an expression such as "foo.xz.yx".
+ */
+static GLuint
+root_swizzle(const slang_ir_storage *st)
+{
+   GLuint swizzle = st->Swizzle;
+   while (st->Parent) {
+      st = st->Parent;
+      swizzle = _slang_swizzle_swizzle(st->Swizzle, swizzle);
+   }
+   return swizzle;
+}
+
+
 /**
  * Generate IR tree for an assignment (=).
  */
@@ -3260,9 +3680,9 @@ _slang_gen_assignment(slang_assemble_ctx * A, slang_operation *oper)
       rhs = _slang_gen_operation(A, &oper->children[1]);
       if (lhs && rhs) {
          /* convert lhs swizzle into writemask */
-         GLuint writemask, newSwizzle;
-         if (!swizzle_to_writemask(A, lhs->Store->Swizzle,
-                                   &writemask, &newSwizzle)) {
+         const GLuint swizzle = root_swizzle(lhs->Store);
+         GLuint writemask, newSwizzle = 0x0;
+         if (!swizzle_to_writemask(A, swizzle, &writemask, &newSwizzle)) {
             /* Non-simple writemask, need to swizzle right hand side in
              * order to put components into the right place.
              */
@@ -3403,8 +3823,16 @@ _slang_gen_array_element(slang_assemble_ctx * A, slang_operation *oper)
       index = (GLint) oper->children[1].literal[0];
       if (oper->children[1].type != SLANG_OPER_LITERAL_INT ||
           index >= (GLint) max) {
+#if 0
          slang_info_log_error(A->log, "Invalid array index for vector type");
+         printf("type = %d\n", oper->children[1].type);
+         printf("index = %d, max = %d\n", index, max);
+         printf("array = %s\n", (char*)oper->children[0].a_id);
+         printf("index = %s\n", (char*)oper->children[1].a_id);
          return NULL;
+#else
+         index = 0;
+#endif
       }
 
       n = _slang_gen_operation(A, &oper->children[0]);
@@ -3472,7 +3900,7 @@ _slang_gen_array_element(slang_assemble_ctx * A, slang_operation *oper)
          elem->Store = _slang_new_ir_storage(array->Store->File,
                                              array->Store->Index,
                                              elemSize);
-
+         elem->Store->Swizzle = _slang_var_swizzle(elemSize, 0);
          return elem;
       }
       else {
@@ -3620,13 +4048,15 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
       return _slang_gen_while(A, oper);
    case SLANG_OPER_BREAK:
       if (!A->CurLoop) {
-         slang_info_log_error(A->log, "'break' not in loop");
+         if (!A->UnrollLoop)
+            slang_info_log_error(A->log, "'break' not in loop");
          return NULL;
       }
       return new_break(A->CurLoop);
    case SLANG_OPER_CONTINUE:
       if (!A->CurLoop) {
-         slang_info_log_error(A->log, "'continue' not in loop");
+         if (!A->UnrollLoop)
+            slang_info_log_error(A->log, "'continue' not in loop");
          return NULL;
       }
       return _slang_gen_continue(A, oper);
@@ -3830,23 +4260,20 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
 
 
 /**
- * Compute total size of array give size of element, number of elements.
+ * Check if the given type specifier is a rectangular texture sampler.
  */
-static GLint
-array_size(GLint baseSize, GLint arrayLen)
+static GLboolean
+is_rect_sampler_spec(const slang_type_specifier *spec)
 {
-   GLint total;
-   if (arrayLen > 1) {
-      /* round up base type to multiple of 4 */
-      total = ((baseSize + 3) & ~0x3) * MAX2(arrayLen, 1);
-   }
-   else {
-      total = baseSize;
+   while (spec->_array) {
+      spec = spec->_array;
    }
-   return total;
+   return spec->type == SLANG_SPEC_SAMPLER2DRECT ||
+          spec->type == SLANG_SPEC_SAMPLER2DRECTSHADOW;
 }
 
 
+
 /**
  * Called by compiler when a global variable has been parsed/compiled.
  * Here we examine the variable's type to determine what kind of register
@@ -3870,8 +4297,14 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
    slang_ir_storage *store = NULL;
    int dbg = 0;
    const GLenum datatype = _slang_gltype_from_specifier(&var->type.specifier);
-   const GLint texIndex = sampler_to_texture_index(var->type.specifier.type);
    const GLint size = _slang_sizeof_type_specifier(&var->type.specifier);
+   const GLint arrayLen = _slang_array_length(var);
+   const GLint totalSize = _slang_array_size(size, arrayLen);
+   GLint texIndex = sampler_to_texture_index(var->type.specifier.type);
+
+   /* check for sampler2D arrays */
+   if (texIndex == -1 && var->type.specifier._array)
+      texIndex = sampler_to_texture_index(var->type.specifier._array->type);
 
    if (texIndex != -1) {
       /* This is a texture sampler variable...
@@ -3885,33 +4318,61 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
       }
 #if FEATURE_es2_glsl /* XXX should use FEATURE_texture_rect */
       /* disallow rect samplers */
-      if (var->type.specifier.type == SLANG_SPEC_SAMPLER2DRECT ||
-          var->type.specifier.type == SLANG_SPEC_SAMPLER2DRECTSHADOW) {
+      if (is_rect_sampler_spec(&var->type.specifier)) {
          slang_info_log_error(A->log, "invalid sampler type for '%s'", varName);
          return GL_FALSE;
       }
+#else
+      (void) is_rect_sampler_spec; /* silence warning */
 #endif
       {
          GLint sampNum = _mesa_add_sampler(prog->Parameters, varName, datatype);
-         store = _slang_new_ir_storage(PROGRAM_SAMPLER, sampNum, texIndex);
+         store = _slang_new_ir_storage_sampler(sampNum, texIndex, totalSize);
+
+         /* If we have a sampler array, then we need to allocate the 
+         * additional samplers to ensure we don't allocate them elsewhere.
+         * We can't directly use _mesa_add_sampler() as that checks the
+         * varName and gets a match, so we call _mesa_add_parameter()
+         * directly and use the last sampler number from the call above.
+         */
+        if (arrayLen > 0) {
+           GLint a = arrayLen - 1;
+           GLint i;
+           for (i = 0; i < a; i++) {
+               GLfloat value = (GLfloat)(i + sampNum + 1);
+               (void) _mesa_add_parameter(prog->Parameters, PROGRAM_SAMPLER,
+                                 varName, 1, datatype, &value, NULL, 0x0);
+           }
+        }
       }
       if (dbg) printf("SAMPLER ");
    }
    else if (var->type.qualifier == SLANG_QUAL_UNIFORM) {
       /* Uniform variable */
-      const GLint totalSize = array_size(size, var->array_len);
       const GLuint swizzle = _slang_var_swizzle(totalSize, 0);
 
       if (prog) {
          /* user-defined uniform */
          if (datatype == GL_NONE) {
-            if (var->type.specifier.type == SLANG_SPEC_STRUCT) {
+           if ((var->type.specifier.type == SLANG_SPEC_ARRAY &&
+                var->type.specifier._array->type == SLANG_SPEC_STRUCT) ||
+                (var->type.specifier.type == SLANG_SPEC_STRUCT)) {
                /* temporary work-around */
                GLenum datatype = GL_FLOAT;
                GLint uniformLoc = _mesa_add_uniform(prog->Parameters, varName,
                                                     totalSize, datatype, NULL);
                store = _slang_new_ir_storage_swz(PROGRAM_UNIFORM, uniformLoc,
                                                  totalSize, swizzle);
+        
+              if (arrayLen > 0) {
+                 GLint a = arrayLen - 1;
+                 GLint i;
+                 for (i = 0; i < a; i++) {
+                     GLfloat value = (GLfloat)(i + uniformLoc + 1);
+                     (void) _mesa_add_parameter(prog->Parameters, PROGRAM_UNIFORM,
+                                 varName, 1, datatype, &value, NULL, 0x0);
+                  }
+              }
 
                /* XXX what we need to do is unroll the struct into its
                 * basic types, creating a uniform variable for each.
@@ -3941,27 +4402,10 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
             }
          }
          else {
-            GLint uniformLoc;
-            const GLfloat *initialValues = NULL;
-            if (var->initializer) {
-               _slang_simplify(var->initializer, &A->space, A->atoms);
-               if (var->initializer->type == SLANG_OPER_LITERAL_FLOAT ||
-                   var->initializer->type == SLANG_OPER_LITERAL_INT) {
-                  /* simple float/vector initializer */
-                  initialValues = var->initializer->literal;
-               }
-               else {
-                  /* complex initializer */
-                  slang_info_log_error(A->log,
-                     "unsupported initializer for uniform '%s'", varName);
-                  return GL_FALSE;
-               }
-            }
-
-            uniformLoc = _mesa_add_uniform(prog->Parameters, varName,
-                                           totalSize, datatype, initialValues);
-            store = _slang_new_ir_storage_swz(PROGRAM_UNIFORM, uniformLoc,
-                                              totalSize, swizzle);
+            /* non-struct uniform */
+            if (!_slang_gen_var_decl(A, var, var->initializer))
+               return GL_FALSE;
+            store = var->store;
          }
       }
       else {
@@ -3975,8 +4419,6 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
       if (dbg) printf("UNIFORM (sz %d) ", totalSize);
    }
    else if (var->type.qualifier == SLANG_QUAL_VARYING) {
-      const GLint totalSize = array_size(size, var->array_len);
-
       /* varyings must be float, vec or mat */
       if (!_slang_type_is_float_vec_mat(var->type.specifier.type) &&
           var->type.specifier.type != SLANG_SPEC_ARRAY) {
@@ -4097,7 +4539,7 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
       n = _slang_gen_var_decl(A, var, var->initializer);
 
       /* emit GPU instructions */
-      success = _slang_emit_code(n, A->vartable, A->program, GL_FALSE, A->log);
+      success = _slang_emit_code(n, A->vartable, A->program, A->pragmas, GL_FALSE, A->log);
 
       _slang_free_ir_tree(n);
    }
@@ -4207,7 +4649,7 @@ _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun)
 #endif
 
    /* Emit program instructions */
-   success = _slang_emit_code(n, A->vartable, A->program, GL_TRUE, A->log);
+   success = _slang_emit_code(n, A->vartable, A->program, A->pragmas, GL_TRUE, A->log);
    _slang_free_ir_tree(n);
 
    /* free codegen context */