undo a debug/test change
[mesa.git] / src / mesa / shader / slang / slang_codegen.c
index 42c1f0897eb334a3ec4c44fc005b18976dff45d4..a25037cd61c3ac566611a000d744210494310f1a 100644 (file)
 
 /**
  * \file slang_codegen.c
- * Mesa GLSL code generator.  Convert AST to IR tree.
+ * Generate IR tree from AST.
  * \author Brian Paul
  */
 
+
+/***
+ *** NOTES:
+ *** The new_() functions return a new instance of a simple IR node.
+ *** The gen_() functions generate larger IR trees from the simple nodes.
+ ***/
+
+
+
 #include "imports.h"
 #include "macros.h"
+#include "mtypes.h"
+#include "program.h"
+#include "prog_instruction.h"
+#include "prog_parameter.h"
+#include "prog_statevars.h"
 #include "slang_typeinfo.h"
 #include "slang_codegen.h"
 #include "slang_compile.h"
-#include "slang_storage.h"
-#include "slang_error.h"
+#include "slang_label.h"
 #include "slang_simplify.h"
 #include "slang_emit.h"
 #include "slang_vartable.h"
 #include "slang_ir.h"
-#include "mtypes.h"
-#include "program.h"
-#include "prog_instruction.h"
-#include "prog_parameter.h"
-#include "prog_statevars.h"
 #include "slang_print.h"
 
 
-static GLboolean UseHighLevelInstructions = GL_TRUE;
-
 static slang_ir_node *
 _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper);
 
 
-
-
-/**
- * Lookup a named constant and allocate storage for the parameter in
- * the given parameter list.
- * \param swizzleOut  returns swizzle mask for accessing the constant
- * \return position of the constant in the paramList.
- */
-static GLint
-slang_lookup_constant(const char *name,
-                      struct gl_program_parameter_list *paramList,
-                      GLuint *swizzleOut)
-{
-   GLint value = _slang_lookup_constant(name);
-   if (value >= 0) {
-      /* XXX named constant! */
-      GLfloat fvalue = (GLfloat) value;
-      GLint pos;
-      pos = _mesa_add_unnamed_constant(paramList, &fvalue, 1, swizzleOut);
-      return pos;
-   }
-   return -1;
-}
-
-
-/**
- * Determine if 'name' is a state variable.  If so, create a new program
- * parameter for it, and return the param's index.  Else, return -1.
- */
-static GLint
-slang_lookup_statevar(const char *name, GLint index,
-                      struct gl_program_parameter_list *paramList)
-{
-   struct state_info {
-      const char *Name;
-      const GLuint NumRows;  /** for matrices */
-      const GLuint Swizzle;
-      const GLint Indexes[STATE_LENGTH];
-   };
-   static const struct state_info state[] = {
-      { "gl_ModelViewMatrix", 4, SWIZZLE_NOOP,
-        { STATE_MATRIX, STATE_MODELVIEW, 0, 0, 0, 0 } },
-      { "gl_NormalMatrix", 3, SWIZZLE_NOOP,
-        { STATE_MATRIX, STATE_MODELVIEW, 0, 0, 0, 0 } },
-      { "gl_ProjectionMatrix", 4, SWIZZLE_NOOP,
-        { STATE_MATRIX, STATE_PROJECTION, 0, 0, 0, 0 } },
-      { "gl_ModelViewProjectionMatrix", 4, SWIZZLE_NOOP,
-        { STATE_MATRIX, STATE_MVP, 0, 0, 0, 0 } },
-      { "gl_TextureMatrix", 4, SWIZZLE_NOOP,
-        { STATE_MATRIX, STATE_TEXTURE, 0, 0, 0, 0 } },
-      { NULL, 0, 0, {0, 0, 0, 0, 0, 0} }
-   };
-   GLuint i;
-
-   for (i = 0; state[i].Name; i++) {
-      if (strcmp(state[i].Name, name) == 0) {
-         /* found */
-         if (paramList) {
-            if (state[i].NumRows > 1) {
-               /* a matrix */
-               GLuint j;
-               GLint pos[4], indexesCopy[STATE_LENGTH];
-               /* make copy of state tokens */
-               for (j = 0; j < STATE_LENGTH; j++)
-                  indexesCopy[j] = state[i].Indexes[j];
-               /* load rows */
-               for (j = 0; j < state[i].NumRows; j++) {
-                  indexesCopy[3] = indexesCopy[4] = j; /* jth row of matrix */
-                  pos[j] = _mesa_add_state_reference(paramList, indexesCopy);
-                  assert(pos[j] >= 0);
-               }
-               return pos[0];
-            }
-            else {
-               /* non-matrix state */
-               GLint pos
-                  = _mesa_add_state_reference(paramList, state[i].Indexes);
-               assert(pos >= 0);
-               return pos;
-            }
-         }
-      }
-   }
-   return -1;
-}
-
-
 static GLboolean
 is_sampler_type(const slang_fully_specified_type *t)
 {
    switch (t->specifier.type) {
-   case slang_spec_sampler1D:
-   case slang_spec_sampler2D:
-   case slang_spec_sampler3D:
-   case slang_spec_samplerCube:
-   case slang_spec_sampler1DShadow:
-   case slang_spec_sampler2DShadow:
+   case SLANG_SPEC_SAMPLER1D:
+   case SLANG_SPEC_SAMPLER2D:
+   case SLANG_SPEC_SAMPLER3D:
+   case SLANG_SPEC_SAMPLERCUBE:
+   case SLANG_SPEC_SAMPLER1DSHADOW:
+   case SLANG_SPEC_SAMPLER2DSHADOW:
+   case SLANG_SPEC_SAMPLER2DRECT:
+   case SLANG_SPEC_SAMPLER2DRECTSHADOW:
       return GL_TRUE;
    default:
       return GL_FALSE;
@@ -157,11 +78,32 @@ is_sampler_type(const slang_fully_specified_type *t)
 }
 
 
-static GLuint
-_slang_sizeof_struct(const slang_struct *s)
+/**
+ * Return the offset (in floats or ints) of the named field within
+ * the given struct.  Return -1 if field not found.
+ * If field is NULL, return the size of the struct instead.
+ */
+static GLint
+_slang_field_offset(const slang_type_specifier *spec, slang_atom field)
 {
-   /* XXX TBD */
-   return 0;
+   GLint offset = 0;
+   GLuint i;
+   for (i = 0; i < spec->_struct->fields->num_variables; i++) {
+      const slang_variable *v = spec->_struct->fields->variables[i];
+      const GLuint sz = _slang_sizeof_type_specifier(&v->type.specifier);
+      if (sz > 1) {
+         /* types larger than 1 float are register (4-float) aligned */
+         offset = (offset + 3) & ~3;
+      }
+      if (field && v->a_name == field) {
+         return offset;
+      }
+      offset += sz;
+   }
+   if (field)
+      return -1; /* field not found */
+   else
+      return offset;  /* struct size */
 }
 
 
@@ -169,52 +111,62 @@ GLuint
 _slang_sizeof_type_specifier(const slang_type_specifier *spec)
 {
    switch (spec->type) {
-   case slang_spec_void:
-      abort();
+   case SLANG_SPEC_VOID:
       return 0;
-   case slang_spec_bool:
+   case SLANG_SPEC_BOOL:
       return 1;
-   case slang_spec_bvec2:
+   case SLANG_SPEC_BVEC2:
       return 2;
-   case slang_spec_bvec3:
+   case SLANG_SPEC_BVEC3:
       return 3;
-   case slang_spec_bvec4:
+   case SLANG_SPEC_BVEC4:
       return 4;
-   case slang_spec_int:
+   case SLANG_SPEC_INT:
       return 1;
-   case slang_spec_ivec2:
+   case SLANG_SPEC_IVEC2:
       return 2;
-   case slang_spec_ivec3:
+   case SLANG_SPEC_IVEC3:
       return 3;
-   case slang_spec_ivec4:
+   case SLANG_SPEC_IVEC4:
       return 4;
-   case slang_spec_float:
+   case SLANG_SPEC_FLOAT:
       return 1;
-   case slang_spec_vec2:
+   case SLANG_SPEC_VEC2:
       return 2;
-   case slang_spec_vec3:
+   case SLANG_SPEC_VEC3:
       return 3;
-   case slang_spec_vec4:
+   case SLANG_SPEC_VEC4:
       return 4;
-   case slang_spec_mat2:
+   case SLANG_SPEC_MAT2:
       return 2 * 2;
-   case slang_spec_mat3:
+   case SLANG_SPEC_MAT3:
       return 3 * 3;
-   case slang_spec_mat4:
+   case SLANG_SPEC_MAT4:
       return 4 * 4;
-   case slang_spec_sampler1D:
-   case slang_spec_sampler2D:
-   case slang_spec_sampler3D:
-   case slang_spec_samplerCube:
-   case slang_spec_sampler1DShadow:
-   case slang_spec_sampler2DShadow:
-      return 1; /* special case */
-   case slang_spec_struct:
-      return _slang_sizeof_struct(spec->_struct);
-   case slang_spec_array:
-      return 1; /* XXX */
+   case SLANG_SPEC_MAT23:
+   case SLANG_SPEC_MAT32:
+      return 2 * 3;
+   case SLANG_SPEC_MAT24:
+   case SLANG_SPEC_MAT42:
+      return 2 * 4;
+   case SLANG_SPEC_MAT34:
+   case SLANG_SPEC_MAT43:
+      return 3 * 4;
+   case SLANG_SPEC_SAMPLER1D:
+   case SLANG_SPEC_SAMPLER2D:
+   case SLANG_SPEC_SAMPLER3D:
+   case SLANG_SPEC_SAMPLERCUBE:
+   case SLANG_SPEC_SAMPLER1DSHADOW:
+   case SLANG_SPEC_SAMPLER2DSHADOW:
+   case SLANG_SPEC_SAMPLER2DRECT:
+   case SLANG_SPEC_SAMPLER2DRECTSHADOW:
+      return 1; /* a sampler is basically just an integer index */
+   case SLANG_SPEC_STRUCT:
+      return _slang_field_offset(spec, 0); /* special use */
+   case SLANG_SPEC_ARRAY:
+      return _slang_sizeof_type_specifier(spec->_array);
    default:
-      abort();
+      _mesa_problem(NULL, "Unexpected type in _slang_sizeof_type_specifier()");
       return 0;
    }
    return 0;
@@ -222,19 +174,24 @@ _slang_sizeof_type_specifier(const slang_type_specifier *spec)
 
 
 /**
- * Allocate storage info for an IR node (n->Store).
- * If n is an IR_VAR_DECL, allocate a temporary for the variable.
- * Otherwise, if n is an IR_VAR, check if it's a uniform or constant
- * that needs to have storage allocated.
+ * 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
  */
 static void
-slang_allocate_storage(slang_assemble_ctx *A, slang_ir_node *n)
+_slang_attach_storage(slang_ir_node *n, slang_variable *var)
 {
-   assert(A->vartable);
    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) {
-      /* allocate storage info for this node */
+      /* need to setup storage */
       if (n->Var && n->Var->aux) {
          /* node storage info = var storage info */
          n->Store = (slang_ir_storage *) n->Var->aux;
@@ -247,45 +204,6 @@ slang_allocate_storage(slang_assemble_ctx *A, slang_ir_node *n)
          assert(n->Var->aux);
       }
    }
-
-   if (n->Opcode == IR_VAR_DECL) {
-      /* variable declaration */
-      assert(n->Var);
-      assert(!is_sampler_type(&n->Var->type));
-      n->Store->File = PROGRAM_TEMPORARY;
-      n->Store->Size = _slang_sizeof_type_specifier(&n->Var->type.specifier);
-      assert(n->Store->Size > 0);
-      return;
-   }
-   else {
-      assert(n->Opcode == IR_VAR);
-      assert(n->Var);
-
-      if (n->Store->Index < 0) {
-         const char *varName = (char *) n->Var->a_name;
-         struct gl_program *prog = A->program;
-         assert(prog);
-
-         /* determine storage location for this var.
-          * This is probably a pre-defined uniform or constant.
-          * We don't allocate storage for these until they're actually
-          * used to avoid wasting registers.
-          */
-         if (n->Store->File == PROGRAM_STATE_VAR) {
-            GLint i = slang_lookup_statevar(varName, 0, prog->Parameters);
-            assert(i >= 0);
-            n->Store->Index = i;
-         }
-         else if (n->Store->File == PROGRAM_CONSTANT) {
-            /* XXX compile-time constants should be converted to literals */
-            GLint i = slang_lookup_constant(varName, prog->Parameters,
-                                            &n->Store->Swizzle);
-            assert(i >= 0);
-            assert(n->Store->Size == 1);
-            n->Store->Index = i;
-         }
-      }
-   }
 }
 
 
@@ -297,18 +215,22 @@ static GLint
 sampler_to_texture_index(const slang_type_specifier_type type)
 {
    switch (type) {
-   case slang_spec_sampler1D:
+   case SLANG_SPEC_SAMPLER1D:
       return TEXTURE_1D_INDEX;
-   case slang_spec_sampler2D:
+   case SLANG_SPEC_SAMPLER2D:
       return TEXTURE_2D_INDEX;
-   case slang_spec_sampler3D:
+   case SLANG_SPEC_SAMPLER3D:
       return TEXTURE_3D_INDEX;
-   case slang_spec_samplerCube:
+   case SLANG_SPEC_SAMPLERCUBE:
       return TEXTURE_CUBE_INDEX;
-   case slang_spec_sampler1DShadow:
+   case SLANG_SPEC_SAMPLER1DSHADOW:
       return TEXTURE_1D_INDEX; /* XXX fix */
-   case slang_spec_sampler2DShadow:
+   case SLANG_SPEC_SAMPLER2DSHADOW:
       return TEXTURE_2D_INDEX; /* XXX fix */
+   case SLANG_SPEC_SAMPLER2DRECT:
+      return TEXTURE_RECT_INDEX;
+   case SLANG_SPEC_SAMPLER2DRECTSHADOW:
+      return TEXTURE_RECT_INDEX; /* XXX fix */
    default:
       return -1;
    }
@@ -322,35 +244,37 @@ sampler_to_texture_index(const slang_type_specifier_type type)
  * XXX return size too
  */
 static GLint
-_slang_input_index(const char *name, GLenum target)
+_slang_input_index(const char *name, GLenum target, GLuint *swizzleOut)
 {
    struct input_info {
       const char *Name;
       GLuint Attrib;
+      GLuint Swizzle;
    };
    static const struct input_info vertInputs[] = {
-      { "gl_Vertex", VERT_ATTRIB_POS },
-      { "gl_Normal", VERT_ATTRIB_NORMAL },
-      { "gl_Color", VERT_ATTRIB_COLOR0 },
-      { "gl_SecondaryColor", VERT_ATTRIB_COLOR1 },
-      { "gl_FogCoord", VERT_ATTRIB_FOG },
-      { "gl_MultiTexCoord0", VERT_ATTRIB_TEX0 },
-      { "gl_MultiTexCoord1", VERT_ATTRIB_TEX1 },
-      { "gl_MultiTexCoord2", VERT_ATTRIB_TEX2 },
-      { "gl_MultiTexCoord3", VERT_ATTRIB_TEX3 },
-      { "gl_MultiTexCoord4", VERT_ATTRIB_TEX4 },
-      { "gl_MultiTexCoord5", VERT_ATTRIB_TEX5 },
-      { "gl_MultiTexCoord6", VERT_ATTRIB_TEX6 },
-      { "gl_MultiTexCoord7", VERT_ATTRIB_TEX7 },
-      { NULL, 0 }
+      { "gl_Vertex", VERT_ATTRIB_POS, SWIZZLE_NOOP },
+      { "gl_Normal", VERT_ATTRIB_NORMAL, SWIZZLE_NOOP },
+      { "gl_Color", VERT_ATTRIB_COLOR0, SWIZZLE_NOOP },
+      { "gl_SecondaryColor", VERT_ATTRIB_COLOR1, SWIZZLE_NOOP },
+      { "gl_FogCoord", VERT_ATTRIB_FOG, SWIZZLE_XXXX },
+      { "gl_MultiTexCoord0", VERT_ATTRIB_TEX0, SWIZZLE_NOOP },
+      { "gl_MultiTexCoord1", VERT_ATTRIB_TEX1, SWIZZLE_NOOP },
+      { "gl_MultiTexCoord2", VERT_ATTRIB_TEX2, SWIZZLE_NOOP },
+      { "gl_MultiTexCoord3", VERT_ATTRIB_TEX3, SWIZZLE_NOOP },
+      { "gl_MultiTexCoord4", VERT_ATTRIB_TEX4, SWIZZLE_NOOP },
+      { "gl_MultiTexCoord5", VERT_ATTRIB_TEX5, SWIZZLE_NOOP },
+      { "gl_MultiTexCoord6", VERT_ATTRIB_TEX6, SWIZZLE_NOOP },
+      { "gl_MultiTexCoord7", VERT_ATTRIB_TEX7, SWIZZLE_NOOP },
+      { NULL, 0, SWIZZLE_NOOP }
    };
    static const struct input_info fragInputs[] = {
-      { "gl_FragCoord", FRAG_ATTRIB_WPOS },
-      { "gl_Color", FRAG_ATTRIB_COL0 },
-      { "gl_SecondaryColor", FRAG_ATTRIB_COL1 },
-      { "gl_FogFragCoord", FRAG_ATTRIB_FOGC },
-      { "gl_TexCoord", FRAG_ATTRIB_TEX0 },
-      { NULL, 0 }
+      { "gl_FragCoord", FRAG_ATTRIB_WPOS, SWIZZLE_NOOP },
+      { "gl_Color", FRAG_ATTRIB_COL0, SWIZZLE_NOOP },
+      { "gl_SecondaryColor", FRAG_ATTRIB_COL1, SWIZZLE_NOOP },
+      { "gl_FogFragCoord", FRAG_ATTRIB_FOGC, SWIZZLE_XXXX },
+      { "gl_TexCoord", FRAG_ATTRIB_TEX0, SWIZZLE_NOOP },
+      { "gl_FrontFacing", FRAG_ATTRIB_FOGC, SWIZZLE_YYYY }, /*XXX*/
+      { NULL, 0, SWIZZLE_NOOP }
    };
    GLuint i;
    const struct input_info *inputs
@@ -361,6 +285,7 @@ _slang_input_index(const char *name, GLenum target)
    for (i = 0; inputs[i].Name; i++) {
       if (strcmp(inputs[i].Name, name) == 0) {
          /* found */
+         *swizzleOut = inputs[i].Swizzle;
          return inputs[i].Attrib;
       }
    }
@@ -386,7 +311,7 @@ _slang_output_index(const char *name, GLenum target)
       { "gl_BackColor", VERT_RESULT_BFC0 },
       { "gl_FrontSecondaryColor", VERT_RESULT_COL1 },
       { "gl_BackSecondaryColor", VERT_RESULT_BFC1 },
-      { "gl_TexCoord", VERT_RESULT_TEX0 }, /* XXX indexed */
+      { "gl_TexCoord", VERT_RESULT_TEX0 },
       { "gl_FogFragCoord", VERT_RESULT_FOGC },
       { "gl_PointSize", VERT_RESULT_PSIZ },
       { NULL, 0 }
@@ -394,6 +319,7 @@ _slang_output_index(const char *name, GLenum target)
    static const struct output_info fragOutputs[] = {
       { "gl_FragColor", FRAG_RESULT_COLR },
       { "gl_FragDepth", FRAG_RESULT_DEPR },
+      { "gl_FragData", FRAG_RESULT_DATA0 },
       { NULL, 0 }
    };
    GLuint i;
@@ -437,9 +363,12 @@ static slang_asm_info AsmInfo[] = {
    { "vec4_min", IR_MIN, 1, 2 },
    { "vec4_max", IR_MAX, 1, 2 },
    { "vec4_clamp", IR_CLAMP, 1, 3 },
-   { "vec4_seq", IR_SEQ, 1, 2 },
+   { "vec4_seq", IR_SEQUAL, 1, 2 },
+   { "vec4_sne", IR_SNEQUAL, 1, 2 },
    { "vec4_sge", IR_SGE, 1, 2 },
    { "vec4_sgt", IR_SGT, 1, 2 },
+   { "vec4_sle", IR_SLE, 1, 2 },
+   { "vec4_slt", IR_SLT, 1, 2 },
    /* vec4 unary */
    { "vec4_floor", IR_FLOOR, 1, 1 },
    { "vec4_frac", IR_FRAC, 1, 1 },
@@ -448,9 +377,6 @@ static slang_asm_info AsmInfo[] = {
    { "vec4_ddx", IR_DDX, 1, 1 },
    { "vec4_ddy", IR_DDY, 1, 1 },
    /* float binary op */
-   { "float_add", IR_ADD, 1, 2 },
-   { "float_multiply", IR_MUL, 1, 2 },
-   { "float_divide", IR_DIV, 1, 2 },
    { "float_power", IR_POW, 1, 2 },
    /* texture / sampler */
    { "vec4_tex1d", IR_TEX, 1, 2 },
@@ -463,6 +389,8 @@ static slang_asm_info AsmInfo[] = {
    { "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 */
 
    /* unary op */
    { "int_to_float", IR_I_TO_F, 1, 1 },
@@ -483,24 +411,6 @@ static slang_asm_info AsmInfo[] = {
 };
 
 
-/**
- * Recursively free an IR tree.
- */
-static void
-_slang_free_ir_tree(slang_ir_node *n)
-{
-#if 0
-   GLuint i;
-   if (!n)
-      return;
-   for (i = 0; i < 3; i++)
-      _slang_free_ir_tree(n->Children[i]);
-   /* Do not free n->BranchNode since it's a child elsewhere */
-   free(n);
-#endif
-}
-
-
 static slang_ir_node *
 new_node3(slang_ir_opcode op,
           slang_ir_node *c0, slang_ir_node *c1, slang_ir_node *c2)
@@ -547,49 +457,58 @@ new_seq(slang_ir_node *left, slang_ir_node *right)
 }
 
 static slang_ir_node *
-new_label(slang_atom labName)
+new_label(slang_label *label)
 {
    slang_ir_node *n = new_node0(IR_LABEL);
-   n->Target = (char *) labName; /*_mesa_strdup(name);*/
+   assert(label);
+   if (n)
+      n->Label = label;
    return n;
 }
 
 static slang_ir_node *
-new_float_literal(const float v[4])
+new_float_literal(const float v[4], GLuint size)
 {
-   const GLuint size = (v[0] == v[1] && v[0] == v[2] && v[0] == v[3]) ? 1 : 4;
    slang_ir_node *n = new_node0(IR_FLOAT);
+   assert(size <= 4);
    COPY_4V(n->Value, v);
    /* allocate a storage object, but compute actual location (Index) later */
    n->Store = _slang_new_ir_storage(PROGRAM_CONSTANT, -1, size);
    return n;
 }
 
+
+static slang_ir_node *
+new_not(slang_ir_node *n)
+{
+   return new_node1(IR_NOT, n);
+}
+
+
 /**
- * Conditional jump.
- * \param zeroOrOne indicates if the jump is to be taken on zero, or non-zero
- *                  condition code state.
- * XXX maybe pass an IR node as second param to indicate the jump target???
+ * Inlined subroutine.
  */
 static slang_ir_node *
-new_cjump(slang_atom target, GLuint zeroOrOne)
+new_inlined_function_call(slang_ir_node *code, slang_label *name)
 {
-   slang_ir_node *n = new_node0(zeroOrOne ? IR_CJUMP1 : IR_CJUMP0);
+   slang_ir_node *n = new_node1(IR_FUNC, code);
+   assert(name);
    if (n)
-      n->Target = (char *) target;
+      n->Label = name;
    return n;
 }
 
+
 /**
  * Unconditional jump.
- * XXX maybe pass an IR node as second param to indicate the jump target???
  */
 static slang_ir_node *
-new_jump(slang_atom target)
+new_return(slang_label *dest)
 {
-   slang_ir_node *n = new_node0(IR_JUMP);
+   slang_ir_node *n = new_node0(IR_RETURN);
+   assert(dest);
    if (n)
-      n->Target = (char *) target;
+      n->Label = dest;
    return n;
 }
 
@@ -609,28 +528,59 @@ new_break(slang_ir_node *loopNode)
    assert(loopNode->Opcode == IR_LOOP);
    if (n) {
       /* insert this node at head of linked list */
-      n->BranchNode = loopNode->BranchNode;
-      loopNode->BranchNode = n;
+      n->List = loopNode->List;
+      loopNode->List = n;
    }
    return n;
 }
 
 
+/**
+ * Make new IR_BREAK_IF_TRUE.
+ */
 static slang_ir_node *
-new_cont(slang_ir_node *loopNode)
+new_break_if_true(slang_ir_node *loopNode, slang_ir_node *cond)
 {
-   slang_ir_node *n = new_node0(IR_CONT);
+   slang_ir_node *n;
    assert(loopNode);
    assert(loopNode->Opcode == IR_LOOP);
+   n = new_node1(IR_BREAK_IF_TRUE, cond);
    if (n) {
       /* insert this node at head of linked list */
-      n->BranchNode = loopNode->BranchNode;
-      loopNode->BranchNode = n;
+      n->List = loopNode->List;
+      loopNode->List = n;
    }
    return n;
 }
 
 
+/**
+ * Make new IR_CONT_IF_TRUE node.
+ */
+static slang_ir_node *
+new_cont_if_true(slang_ir_node *loopNode, slang_ir_node *cond)
+{
+   slang_ir_node *n;
+   assert(loopNode);
+   assert(loopNode->Opcode == IR_LOOP);
+   n = new_node1(IR_CONT_IF_TRUE, cond);
+   if (n) {
+      /* insert this node at head of linked list */
+      n->List = loopNode->List;
+      loopNode->List = n;
+   }
+   return n;
+}
+
+
+static slang_ir_node *
+new_cond(slang_ir_node *n)
+{
+   slang_ir_node *c = new_node1(IR_COND, n);
+   return c;
+}
+
+
 static slang_ir_node *
 new_if(slang_ir_node *cond, slang_ir_node *ifPart, slang_ir_node *elsePart)
 {
@@ -644,15 +594,17 @@ 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_operation *oper, slang_atom name)
 {
-   slang_variable *v = _slang_locate_variable(oper->locals, name, GL_TRUE);
-   slang_ir_node *n = new_node0(IR_VAR);
-   if (!v)
+   slang_ir_node *n;
+   slang_variable *var = _slang_locate_variable(oper->locals, name, GL_TRUE);
+   if (!var)
       return NULL;
-   assert(!oper->var || oper->var == v);
-   v->used = GL_TRUE;
-   n->Var = v;
-   slang_allocate_storage(A, n);
 
+   assert(!oper->var || oper->var == var);
+
+   n = new_node0(IR_VAR);
+   if (n) {
+      _slang_attach_storage(n, var);
+   }
    return n;
 }
 
@@ -664,17 +616,48 @@ new_var(slang_assemble_ctx *A, slang_operation *oper, slang_atom name)
 static GLboolean
 slang_is_asm_function(const slang_function *fun)
 {
-   if (fun->body->type == slang_oper_block_no_new_scope &&
+   if (fun->body->type == SLANG_OPER_BLOCK_NO_NEW_SCOPE &&
        fun->body->num_children == 1 &&
-       fun->body->children[0].type == slang_oper_asm) {
+       fun->body->children[0].type == SLANG_OPER_ASM) {
       return GL_TRUE;
    }
    return GL_FALSE;
 }
 
 
+static GLboolean
+_slang_is_noop(const slang_operation *oper)
+{
+   if (!oper ||
+       oper->type == SLANG_OPER_VOID ||
+       (oper->num_children == 1 && oper->children[0].type == SLANG_OPER_VOID))
+      return GL_TRUE;
+   else
+      return GL_FALSE;
+}
+
+
+/**
+ * Recursively search tree for a node of the given type.
+ */
+static slang_operation *
+_slang_find_node_type(slang_operation *oper, slang_operation_type type)
+{
+   GLuint i;
+   if (oper->type == type)
+      return oper;
+   for (i = 0; i < oper->num_children; i++) {
+      slang_operation *p = _slang_find_node_type(&oper->children[i], type);
+      if (p)
+         return p;
+   }
+   return NULL;
+}
+
+
 /**
  * Produce inline code for a call to an assembly instruction.
+ * XXX Note: children are passed as asm args in-order, not by name!
  */
 static slang_operation *
 slang_inline_asm_function(slang_assemble_ctx *A,
@@ -685,7 +668,7 @@ slang_inline_asm_function(slang_assemble_ctx *A,
    GLuint i;
    slang_operation *inlined = slang_operation_new(1);
 
-   /*assert(oper->type == slang_oper_call);  or vec4_add, etc */
+   /*assert(oper->type == SLANG_OPER_CALL);  or vec4_add, etc */
    /*
    printf("Inline asm %s\n", (char*) fun->header.a_name);
    */
@@ -693,12 +676,7 @@ slang_inline_asm_function(slang_assemble_ctx *A,
    inlined->a_id = fun->body->children[0].a_id;
    inlined->num_children = numArgs;
    inlined->children = slang_operation_new(numArgs);
-#if 0
-   inlined->locals = slang_variable_scope_copy(oper->locals);
-#else
-   assert(inlined->locals);
    inlined->locals->outer_scope = oper->locals->outer_scope;
-#endif
 
    for (i = 0; i < numArgs; i++) {
       slang_operation_copy(inlined->children + i, args + i);
@@ -711,20 +689,14 @@ slang_inline_asm_function(slang_assemble_ctx *A,
 static void
 slang_resolve_variable(slang_operation *oper)
 {
-   if (oper->type != slang_oper_identifier)
-      return;
-   if (!oper->var) {
-      oper->var = _slang_locate_variable(oper->locals,
-                                         (const slang_atom) oper->a_id,
-                                         GL_TRUE);
-      if (oper->var)
-         oper->var->used = GL_TRUE;
+   if (oper->type == SLANG_OPER_IDENTIFIER && !oper->var) {
+      oper->var = _slang_locate_variable(oper->locals, oper->a_id, GL_TRUE);
    }
 }
 
 
 /**
- * Replace particular variables (slang_oper_identifier) with new expressions.
+ * Replace particular variables (SLANG_OPER_IDENTIFIER) with new expressions.
  */
 static void
 slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
@@ -732,7 +704,7 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
                 slang_operation **substNew, GLboolean isLHS)
 {
    switch (oper->type) {
-   case slang_oper_variable_decl:
+   case SLANG_OPER_VARIABLE_DECL:
       {
          slang_variable *v = _slang_locate_variable(oper->locals,
                                                     oper->a_id, GL_TRUE);
@@ -745,11 +717,12 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
          }
          if (oper->num_children == 1) {
             /* the initializer */
-            slang_substitute(A, &oper->children[0], substCount, substOld, substNew, GL_FALSE);
+            slang_substitute(A, &oper->children[0], substCount,
+                             substOld, substNew, GL_FALSE);
          }
       }
       break;
-   case slang_oper_identifier:
+   case SLANG_OPER_IDENTIFIER:
       assert(oper->num_children == 0);
       if (1/**!isLHS XXX FIX */) {
          slang_atom id = oper->a_id;
@@ -757,19 +730,16 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
         GLuint i;
          v = _slang_locate_variable(oper->locals, id, GL_TRUE);
         if (!v) {
-           printf("var %s not found!\n", (char *) oper->a_id);
-            _slang_print_var_scope(oper->locals, 6);
-
-            abort();
-           break;
+            _mesa_problem(NULL, "var %s not found!\n", (char *) oper->a_id);
+            return;
         }
 
         /* look for a substitution */
         for (i = 0; i < substCount; i++) {
            if (v == substOld[i]) {
-               /* OK, replace this slang_oper_identifier with a new expr */
+               /* OK, replace this SLANG_OPER_IDENTIFIER with a new expr */
 #if 0 /* DEBUG only */
-              if (substNew[i]->type == slang_oper_identifier) {
+              if (substNew[i]->type == SLANG_OPER_IDENTIFIER) {
                   assert(substNew[i]->var);
                   assert(substNew[i]->var->a_name);
                  printf("Substitute %s with %s in id node %p\n",
@@ -788,11 +758,11 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
         }
       }
       break;
-#if 1 /* XXX rely on default case below */
-   case slang_oper_return:
+
+   case SLANG_OPER_RETURN:
       /* do return replacement here too */
       assert(oper->num_children == 0 || oper->num_children == 1);
-      if (oper->num_children == 1) {
+      if (oper->num_children == 1 && !_slang_is_noop(&oper->children[0])) {
          /* replace:
           *   return expr;
           * with:
@@ -801,24 +771,34 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
           * then do substitutions on the assignment.
           */
          slang_operation *blockOper, *assignOper, *returnOper;
+
+         /* check if function actually has a return type */
+         assert(A->CurFunction);
+         if (A->CurFunction->header.type.specifier.type == SLANG_SPEC_VOID) {
+            slang_info_log_error(A->log, "illegal return expression");
+            return;
+         }
+
          blockOper = slang_operation_new(1);
-         blockOper->type = slang_oper_block_no_new_scope;
+         blockOper->type = SLANG_OPER_BLOCK_NO_NEW_SCOPE;
          blockOper->num_children = 2;
+         blockOper->locals->outer_scope = oper->locals->outer_scope;
          blockOper->children = slang_operation_new(2);
          assignOper = blockOper->children + 0;
          returnOper = blockOper->children + 1;
 
-         assignOper->type = slang_oper_assign;
+         assignOper->type = SLANG_OPER_ASSIGN;
          assignOper->num_children = 2;
+         assignOper->locals->outer_scope = blockOper->locals;
          assignOper->children = slang_operation_new(2);
-         assignOper->children[0].type = slang_oper_identifier;
+         assignOper->children[0].type = SLANG_OPER_IDENTIFIER;
          assignOper->children[0].a_id = slang_atom_pool_atom(A->atoms, "__retVal");
-         assignOper->children[0].locals->outer_scope = oper->locals;
-         assignOper->locals = oper->locals;
+         assignOper->children[0].locals->outer_scope = assignOper->locals;
+
          slang_operation_copy(&assignOper->children[1],
                               &oper->children[0]);
 
-         returnOper->type = slang_oper_return;
+         returnOper->type = SLANG_OPER_RETURN; /* return w/ no value */
          assert(returnOper->num_children == 0);
 
          /* do substitutions on the "__retVal = expr" sub-tree */
@@ -829,10 +809,18 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
          slang_operation_copy(oper, blockOper);
          slang_operation_destruct(blockOper);
       }
+      else {
+         /* check if return value was expected */
+         assert(A->CurFunction);
+         if (A->CurFunction->header.type.specifier.type != SLANG_SPEC_VOID) {
+            slang_info_log_error(A->log, "return statement requires an expression");
+            return;
+         }
+      }
       break;
-#endif
-   case slang_oper_assign:
-   case slang_oper_subscript:
+
+   case SLANG_OPER_ASSIGN:
+   case SLANG_OPER_SUBSCRIPT:
       /* special case:
        * child[0] can't have substitutions but child[1] can.
        */
@@ -841,7 +829,7 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
       slang_substitute(A, &oper->children[1],
                        substCount, substOld, substNew, GL_FALSE);
       break;
-   case slang_oper_field:
+   case SLANG_OPER_FIELD:
       /* XXX NEW - test */
       slang_substitute(A, &oper->children[0],
                        substCount, substOld, substNew, GL_TRUE);
@@ -880,8 +868,13 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
    slang_variable **substOld;
    slang_operation **substNew;
    GLuint substCount, numCopyIn, i;
+   slang_function *prevFunction;
 
-   /*assert(oper->type == slang_oper_call); (or (matrix) multiply, etc) */
+   /* save / push */
+   prevFunction = A->CurFunction;
+   A->CurFunction = fun;
+
+   /*assert(oper->type == SLANG_OPER_CALL); (or (matrix) multiply, etc) */
    assert(fun->param_count == totalArgs);
 
    /* allocate temporary arrays */
@@ -909,7 +902,7 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
       slang_variable *resultVar;
 
       commaSeq = slang_operation_new(1);
-      commaSeq->type = slang_oper_sequence;
+      commaSeq->type = SLANG_OPER_SEQUENCE;
       assert(commaSeq->locals);
       commaSeq->locals->outer_scope = oper->locals->outer_scope;
       commaSeq->num_children = 3;
@@ -927,21 +920,19 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
 
       /* child[0] = __resultTmp declaration */
       declOper = &commaSeq->children[0];
-      declOper->type = slang_oper_variable_decl;
+      declOper->type = SLANG_OPER_VARIABLE_DECL;
       declOper->a_id = resultVar->a_name;
-      declOper->locals->outer_scope = commaSeq->locals; /*** ??? **/
+      declOper->locals->outer_scope = commaSeq->locals;
 
       /* child[1] = function body */
       inlined = &commaSeq->children[1];
-      /* XXXX this may be inappropriate!!!!: */
       inlined->locals->outer_scope = commaSeq->locals;
 
       /* child[2] = __resultTmp reference */
       returnOper = &commaSeq->children[2];
-      returnOper->type = slang_oper_identifier;
+      returnOper->type = SLANG_OPER_IDENTIFIER;
       returnOper->a_id = resultVar->a_name;
       returnOper->locals->outer_scope = commaSeq->locals;
-      declOper->locals->outer_scope = commaSeq->locals;
 
       top = commaSeq;
    }
@@ -969,8 +960,8 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
              slang_type_qual_string(p->type.qualifier),
             (char *) p->a_name);
       */
-      if (p->type.qualifier == slang_qual_inout ||
-         p->type.qualifier == slang_qual_out) {
+      if (p->type.qualifier == SLANG_QUAL_INOUT ||
+         p->type.qualifier == SLANG_QUAL_OUT) {
         /* an output param */
          slang_operation *arg;
          if (i < numArgs)
@@ -979,7 +970,7 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
             arg = returnOper;
         paramMode[i] = SUBST;
 
-        if (arg->type == slang_oper_identifier)
+        if (arg->type == SLANG_OPER_IDENTIFIER)
             slang_resolve_variable(arg);
 
          /* replace parameter 'p' with argument 'arg' */
@@ -987,10 +978,10 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
         substNew[substCount] = arg; /* will get copied */
         substCount++;
       }
-      else if (p->type.qualifier == slang_qual_const) {
+      else if (p->type.qualifier == SLANG_QUAL_CONST) {
         /* a constant input param */
-        if (args[i].type == slang_oper_identifier ||
-            args[i].type == slang_oper_literal_float) {
+        if (args[i].type == SLANG_OPER_IDENTIFIER ||
+            args[i].type == SLANG_OPER_LITERAL_FLOAT) {
            /* replace all occurances of this parameter variable with the
             * actual argument variable or a literal.
             */
@@ -1014,8 +1005,8 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
    slang_operation_copy(inlined, fun->body);
 
    /*** XXX review this */
-   assert(inlined->type = slang_oper_block_no_new_scope);
-   inlined->type = slang_oper_block_new_scope;
+   assert(inlined->type = SLANG_OPER_BLOCK_NO_NEW_SCOPE);
+   inlined->type = SLANG_OPER_BLOCK_NEW_SCOPE;
 
 #if 0
    printf("======================= orig body code ======================\n");
@@ -1048,9 +1039,9 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
          /*
          printf("COPY_IN %s from expr\n", (char*)p->a_name);
          */
-        decl->type = slang_oper_variable_decl;
+        decl->type = SLANG_OPER_VARIABLE_DECL;
          assert(decl->locals);
-        decl->locals = fun->parameters;
+         decl->locals->outer_scope = inlined->locals;
         decl->a_id = p->a_name;
         decl->num_children = 1;
         decl->children = slang_operation_new(1);
@@ -1070,9 +1061,8 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
       slang_operation *lab = slang_operation_insert(&inlined->num_children,
                                                     &inlined->children,
                                                     inlined->num_children);
-      lab->type = slang_oper_label;
-      lab->a_id = slang_atom_pool_atom(A->atoms,
-                                       (char *) A->CurFunction->end_label);
+      lab->type = SLANG_OPER_LABEL;
+      lab->label = A->curFuncEndLabel;
    }
 
    for (i = 0; i < totalArgs; i++) {
@@ -1083,15 +1073,14 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
         slang_operation *ass = slang_operation_insert(&inlined->num_children,
                                                       &inlined->children,
                                                       inlined->num_children);
-        ass->type = slang_oper_assign;
+        ass->type = SLANG_OPER_ASSIGN;
         ass->num_children = 2;
-        ass->locals = _slang_variable_scope_new(inlined->locals);
-        assert(ass->locals);
+         ass->locals->outer_scope = inlined->locals;
         ass->children = slang_operation_new(2);
         ass->children[0] = args[i]; /*XXX copy */
-        ass->children[1].type = slang_oper_identifier;
+        ass->children[1].type = SLANG_OPER_IDENTIFIER;
         ass->children[1].a_id = p->a_name;
-        ass->children[1].locals = _slang_variable_scope_new(ass->locals);
+         ass->children[1].locals->outer_scope = ass->locals;
       }
    }
 
@@ -1105,6 +1094,10 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
          fun->parameters->num_variables, numArgs);
    slang_print_tree(top, 0);
 #endif
+
+   /* pop */
+   A->CurFunction = prevFunction;
+
    return top;
 }
 
@@ -1115,16 +1108,13 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun,
 {
    slang_ir_node *n;
    slang_operation *inlined;
-   slang_function *prevFunc;
-
-   prevFunc = A->CurFunction;
-   A->CurFunction = fun;
+   slang_label *prevFuncEndLabel;
+   char name[200];
 
-   if (!A->CurFunction->end_label) {
-      char name[200];
-      sprintf(name, "__endOfFunc_%s_", (char *) A->CurFunction->header.a_name);
-      A->CurFunction->end_label = slang_atom_pool_gen(A->atoms, name);
-   }
+   prevFuncEndLabel = A->curFuncEndLabel;
+   sprintf(name, "__endOfFunc_%s_", (char *) fun->header.a_name);
+   A->curFuncEndLabel = _slang_label_new(name);
+   assert(A->curFuncEndLabel);
 
    if (slang_is_asm_function(fun) && !dest) {
       /* assemble assembly function - tree style */
@@ -1133,16 +1123,28 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun,
    else {
       /* non-assembly function */
       inlined = slang_inline_function_call(A, fun, oper, dest);
+      if (inlined && _slang_find_node_type(inlined, SLANG_OPER_RETURN)) {
+         /* This inlined function has one or more 'return' statements.
+          * So, we can't truly inline this function because we need to
+          * implement 'return' with RET (and CAL).
+          * XXX check if there's one 'return' and if it's the very last
+          * statement in the function - we can optimize that case.
+          */
+         assert(inlined->type == SLANG_OPER_BLOCK_NEW_SCOPE ||
+                inlined->type == SLANG_OPER_SEQUENCE);
+         inlined->type = SLANG_OPER_INLINED_CALL;
+         inlined->fun = fun;
+         inlined->label = _slang_label_new_unique((char*) fun->header.a_name);
+      }
    }
 
+   if (!inlined)
+      return NULL;
+
    /* Replace the function call with the inlined block */
-#if 0
-   slang_operation_construct(oper);
-   slang_operation_copy(oper, inlined);
-#else
+   slang_operation_destruct(oper);
    *oper = *inlined;
-#endif
-
+   /* XXX slang_operation_destruct(inlined) ??? */
 
 #if 0
    assert(inlined->locals);
@@ -1154,9 +1156,9 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun,
 
    n = _slang_gen_operation(A, oper);
 
-   A->CurFunction->end_label = NULL;
-
-   A->CurFunction = prevFunc;
+   /*_slang_label_delete(A->curFuncEndLabel);*/
+   A->curFuncEndLabel = prevFuncEndLabel;
+   assert(A->curFuncEndLabel);
 
    return n;
 }
@@ -1176,7 +1178,7 @@ slang_find_asm_info(const char *name)
 
 
 static GLuint
-make_writemask(char *field)
+make_writemask(const char *field)
 {
    GLuint mask = 0x0;
    while (*field) {
@@ -1194,7 +1196,8 @@ make_writemask(char *field)
          mask |= WRITEMASK_W;
          break;
       default:
-         abort();
+         _mesa_problem(NULL, "invalid writemask in make_writemask()");
+         return 0;
       }
       field++;
    }
@@ -1211,13 +1214,13 @@ make_writemask(char *field)
  */
 static slang_ir_node *
 _slang_gen_asm(slang_assemble_ctx *A, slang_operation *oper,
-                   slang_operation *dest)
+               slang_operation *dest)
 {
    const slang_asm_info *info;
    slang_ir_node *kids[3], *n;
    GLuint j, firstOperand;
 
-   assert(oper->type == slang_oper_asm);
+   assert(oper->type == SLANG_OPER_ASM);
 
    info = slang_find_asm_info((char *) oper->a_id);
    if (!info) {
@@ -1244,6 +1247,8 @@ _slang_gen_asm(slang_assemble_ctx *A, slang_operation *oper,
    kids[0] = kids[1] = kids[2] = NULL;
    for (j = 0; j < info->NumParams; j++) {
       kids[j] = _slang_gen_operation(A, &oper->children[firstOperand + j]);
+      if (!kids[j])
+         return NULL;
    }
 
    n = new_node3(info->Opcode, kids[0], kids[1], kids[2]);
@@ -1257,9 +1262,9 @@ _slang_gen_asm(slang_assemble_ctx *A, slang_operation *oper,
       slang_ir_node *n0;
 
       dest_oper = &oper->children[0];
-      while /*if*/ (dest_oper->type == slang_oper_field) {
+      while (dest_oper->type == SLANG_OPER_FIELD) {
          /* writemask */
-         writemask &= /*=*/make_writemask((char*) dest_oper->a_id);
+         writemask &= make_writemask((char*) dest_oper->a_id);
          dest_oper = &dest_oper->children[0];
       }
 
@@ -1277,27 +1282,6 @@ _slang_gen_asm(slang_assemble_ctx *A, slang_operation *oper,
 }
 
 
-
-static GLboolean
-_slang_is_noop(const slang_operation *oper)
-{
-   if (!oper ||
-       oper->type == slang_oper_void ||
-       (oper->num_children == 1 && oper->children[0].type == slang_oper_void))
-      return GL_TRUE;
-   else
-      return GL_FALSE;
-}
-
-
-static slang_ir_node *
-_slang_gen_cond(slang_ir_node *n)
-{
-   slang_ir_node *c = new_node1(IR_COND, n);
-   return c;
-}
-
-
 static void
 print_funcs(struct slang_function_scope_ *scope, const char *name)
 {
@@ -1317,6 +1301,9 @@ print_funcs(struct slang_function_scope_ *scope, const char *name)
  * Return first function in the scope that has the given name.
  * This is the function we'll try to call when there is no exact match
  * between function parameters and call arguments.
+ *
+ * XXX we should really create a list of candidate functions and try
+ * all of them...
  */
 static slang_function *
 _slang_first_function(struct slang_function_scope_ *scope, const char *name)
@@ -1355,15 +1342,15 @@ _slang_gen_function_call_name(slang_assemble_ctx *A, const char *name,
     * Use 'name' to find the function to call
     */
    fun = _slang_locate_function(A->space.funcs, atom, params, param_count,
-                               &A->space, A->atoms);
+                               &A->space, A->atoms, A->log);
    if (!fun) {
       /* A function with exactly the right parameters/types was not found.
        * Try adapting the parameters.
        */
       fun = _slang_first_function(A->space.funcs, name);
-      if (!_slang_adapt_call(oper, fun, &A->space, A->atoms)) {
-         RETURN_ERROR2("Undefined function (or no matching parameters)",
-                       name, 0);
+      if (!_slang_adapt_call(oper, fun, &A->space, A->atoms, A->log)) {
+         slang_info_log_error(A->log, "Function '%s' not found (check argument types)", name);
+         return NULL;
       }
       assert(fun);
    }
@@ -1372,71 +1359,70 @@ _slang_gen_function_call_name(slang_assemble_ctx *A, const char *name,
 }
 
 
-/**
- * Generate IR tree for a while-loop.  Use BRA-nch instruction.
- */
-static slang_ir_node *
-_slang_gen_while(slang_assemble_ctx * A, const slang_operation *oper)
+static GLboolean
+_slang_is_constant_cond(const slang_operation *oper, GLboolean *value)
 {
-   /*
-    * label "__startWhile"
-    * eval expr (child[0]), updating condcodes
-    * branch if false to "__endWhile"
-    * body code
-    * jump "__startWhile"
-    * label "__endWhile"
-    */
-   slang_atom startAtom = slang_atom_pool_gen(A->atoms, "__startWhile");
-   slang_atom endAtom = slang_atom_pool_gen(A->atoms, "__endWhile");
-   slang_ir_node *startLab, *cond, *bra, *body, *jump, *endLab, *tree;
-   slang_atom prevLoopBreak = A->CurLoopBreak;
-   slang_atom prevLoopCont = A->CurLoopCont;
-
-   /* Push this loop */
-   A->CurLoopBreak = endAtom;
-   A->CurLoopCont = startAtom;
-
-   startLab = new_label(startAtom);
-   cond = _slang_gen_operation(A, &oper->children[0]);
-   cond = _slang_gen_cond(cond);
-   tree = new_seq(startLab, cond);
-
-   bra = new_cjump(endAtom, 0);
-   tree = new_seq(tree, bra);
-
-   body = _slang_gen_operation(A, &oper->children[1]);
-   if (body)
-      tree = new_seq(tree, body);
-
-   jump = new_jump(startAtom);
-   tree = new_seq(tree, jump);
+   if (oper->type == SLANG_OPER_LITERAL_FLOAT ||
+       oper->type == SLANG_OPER_LITERAL_INT ||
+       oper->type == SLANG_OPER_LITERAL_BOOL) {
+      if (oper->literal[0])
+         *value = GL_TRUE;
+      else
+         *value = GL_FALSE;
+      return GL_TRUE;
+   }
+   else if (oper->type == SLANG_OPER_EXPRESSION &&
+            oper->num_children == 1) {
+      return _slang_is_constant_cond(&oper->children[0], value);
+   }
+   return GL_FALSE;
+}
 
-   endLab = new_label(endAtom);
-   tree = new_seq(tree, endLab);
 
-   /* Pop this loop */
-   A->CurLoopBreak = prevLoopBreak;
-   A->CurLoopCont = prevLoopCont;
+/**
+ * Test if an operation is a scalar or boolean.
+ */
+static GLboolean
+_slang_is_scalar_or_boolean(slang_assemble_ctx *A, slang_operation *oper)
+{
+   slang_typeinfo type;
+   GLint size;
 
-   return tree;
+   slang_typeinfo_construct(&type);
+   _slang_typeof_operation(A, oper, &type);
+   size = _slang_sizeof_type_specifier(&type.spec);
+   slang_typeinfo_destruct(&type);
+   return size == 1;
 }
 
 
 /**
- * Generate IR tree for a while-loop using high-level LOOP, IF instructions.
+ * Generate loop code using high-level IR_LOOP instruction
  */
 static slang_ir_node *
-_slang_gen_hl_while(slang_assemble_ctx * A, const slang_operation *oper)
+_slang_gen_while(slang_assemble_ctx * A, const slang_operation *oper)
 {
-   slang_ir_node *prevLoop;
    /*
     * LOOP:
-    *    eval expr (child[0]), updating condcodes
-    *    IF !expr:
-    *       BRK
+    *    BREAK if !expr (child[0])
     *    body code (child[1])
     */
-   slang_ir_node *ifThen, *cond, *body, *loop;
+   slang_ir_node *prevLoop, *loop, *breakIf, *body;
+   GLboolean isConst, constTrue;
+
+   /* type-check expression */
+   if (!_slang_is_scalar_or_boolean(A, &oper->children[0])) {
+      slang_info_log_error(A->log, "scalar/boolean expression expected for 'while'");
+      return NULL;
+   }
+
+   /* Check if loop condition is a constant */
+   isConst = _slang_is_constant_cond(&oper->children[0], &constTrue);
+
+   if (isConst && !constTrue) {
+      /* loop is never executed! */
+      return new_node0(IR_NOP);
+   }
 
    loop = new_loop(NULL);
 
@@ -1444,248 +1430,156 @@ _slang_gen_hl_while(slang_assemble_ctx * A, const slang_operation *oper)
    prevLoop = A->CurLoop;
    A->CurLoop = loop;
 
-   cond = _slang_gen_operation(A, &oper->children[0]);
-   cond = new_node1(IR_NOT, cond);
-   cond = _slang_gen_cond(cond);
-
-   ifThen = new_if(cond,
-                   new_break(A->CurLoop),
-                   NULL);
-
+   if (isConst && constTrue) {
+      /* while(nonzero constant), no conditional break */
+      breakIf = NULL;
+   }
+   else {
+      slang_ir_node *cond
+         = new_cond(new_not(_slang_gen_operation(A, &oper->children[0])));
+      breakIf = new_break_if_true(A->CurLoop, cond);
+   }
    body = _slang_gen_operation(A, &oper->children[1]);
+   loop->Children[0] = new_seq(breakIf, body);
+
+   /* Do infinite loop detection */
+   /* loop->List is head of linked list of break/continue nodes */
+   if (!loop->List && isConst && constTrue) {
+      /* infinite loop detected */
+      A->CurLoop = prevLoop; /* clean-up */
+      slang_info_log_error(A->log, "Infinite loop detected!");
+      return NULL;
+   }
 
-   loop->Children[0] = new_seq(ifThen, body);
-
-
-   A->CurLoop = prevLoop; /* pop loop, restore prev */
+   /* pop loop, restore prev */
+   A->CurLoop = prevLoop;
 
    return loop;
 }
 
 
 /**
- * Generate IR tree for a do-while-loop.
+ * Generate IR tree for a do-while loop using high-level LOOP, IF instructions.
  */
 static slang_ir_node *
 _slang_gen_do(slang_assemble_ctx * A, const slang_operation *oper)
 {
    /*
-    * label "__startDo"
-    * code body
-    * eval expr (child[0]), updating condcodes
-    * branch if true to "__startDo"
-    * label "__endDo"
+    * LOOP:
+    *    body code (child[0])
+    *    tail code:
+    *       BREAK if !expr (child[1])
     */
-   slang_atom startAtom = slang_atom_pool_gen(A->atoms, "__startDo");
-   slang_atom endAtom = slang_atom_pool_gen(A->atoms, "__endDo");
-   slang_ir_node *startLab, *cond, *bra, *body, *endLab, *tree;
-   slang_atom prevLoopBreak = A->CurLoopBreak;
-   slang_atom prevLoopCont = A->CurLoopCont;
+   slang_ir_node *prevLoop, *loop;
+   GLboolean isConst, constTrue;
 
-   /* Push this loop */
-   A->CurLoopBreak = endAtom;
-   A->CurLoopCont = startAtom;
+   /* type-check expression */
+   if (!_slang_is_scalar_or_boolean(A, &oper->children[1])) {
+      slang_info_log_error(A->log, "scalar/boolean expression expected for 'do/while'");
+      return NULL;
+   }
 
-   startLab = new_label(startAtom);
+   loop = new_loop(NULL);
 
-   body = _slang_gen_operation(A, &oper->children[0]);
-   tree = new_seq(startLab, body);
+   /* save old, push new loop */
+   prevLoop = A->CurLoop;
+   A->CurLoop = loop;
 
-   cond = _slang_gen_operation(A, &oper->children[1]);
-   cond = _slang_gen_cond(cond);
-   tree = new_seq(tree, cond);
+   /* loop body: */
+   loop->Children[0] = _slang_gen_operation(A, &oper->children[0]);
 
-   bra = new_cjump(startAtom, 1);
-   tree = new_seq(tree, bra);
+   /* Check if loop condition is a constant */
+   isConst = _slang_is_constant_cond(&oper->children[1], &constTrue);
+   if (isConst && constTrue) {
+      /* do { } while(1)   ==> no conditional break */
+      loop->Children[1] = NULL; /* no tail code */
+   }
+   else {
+      slang_ir_node *cond
+         = new_cond(new_not(_slang_gen_operation(A, &oper->children[1])));
+      loop->Children[1] = new_break_if_true(A->CurLoop, cond);
+   }
 
-   endLab = new_label(endAtom);
-   tree = new_seq(tree, endLab);
+   /* XXX we should do infinite loop detection, as above */
 
-   /* Pop this loop */
-   A->CurLoopBreak = prevLoopBreak;
-   A->CurLoopCont = prevLoopCont;
+   /* pop loop, restore prev */
+   A->CurLoop = prevLoop;
 
-   return tree;
+   return loop;
 }
 
 
 /**
- * Generate IR tree for a for-loop.
+ * Generate for-loop using high-level IR_LOOP instruction.
  */
 static slang_ir_node *
 _slang_gen_for(slang_assemble_ctx * A, const slang_operation *oper)
 {
    /*
     * init code (child[0])
-    * label "__startFor"
-    * eval expr (child[1]), updating condcodes
-    * branch if false to "__endFor"
-    * code body (child[3])
-    * label "__continueFor"
-    * incr code (child[2])
-    * jump "__startFor"
-    * label "__endFor"
+    * LOOP:
+    *    BREAK if !expr (child[1])
+    *    body code (child[3])
+    *    tail code:
+    *       incr code (child[2])   // XXX continue here
     */
-   slang_atom startAtom = slang_atom_pool_gen(A->atoms, "__startFor");
-   slang_atom contAtom = slang_atom_pool_gen(A->atoms, "__continueFor");
-   slang_atom endAtom = slang_atom_pool_gen(A->atoms, "__endFor");
-   slang_ir_node *init, *startLab, *cond, *bra, *body, *contLab;
-   slang_ir_node *incr, *jump, *endLab, *tree;
-   slang_atom prevLoopBreak = A->CurLoopBreak;
-   slang_atom prevLoopCont = A->CurLoopCont;
-
-   /* Push this loop */
-   A->CurLoopBreak = endAtom;
-   A->CurLoopCont = contAtom;
+   slang_ir_node *prevLoop, *loop, *cond, *breakIf, *body, *init, *incr;
 
    init = _slang_gen_operation(A, &oper->children[0]);
-   startLab = new_label(startAtom);
-   tree = new_seq(init, startLab);
-
-   cond = _slang_gen_operation(A, &oper->children[1]);
-   cond = _slang_gen_cond(cond);
-   tree = new_seq(tree, cond);
+   loop = new_loop(NULL);
 
-   bra = new_cjump(endAtom, 0);
-   tree = new_seq(tree, bra);
+   /* save old, push new loop */
+   prevLoop = A->CurLoop;
+   A->CurLoop = loop;
 
+   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]);
-   tree = new_seq(tree, body);
-
-   contLab = new_label(contAtom);
-   tree = new_seq(tree, contLab);
-
    incr = _slang_gen_operation(A, &oper->children[2]);
-   tree = new_seq(tree, incr);
-
-   jump = new_jump(startAtom);
-   tree = new_seq(tree, jump);
 
-   endLab = new_label(endAtom);
-   tree = new_seq(tree, endLab);
+   loop->Children[0] = new_seq(breakIf, body);
+   loop->Children[1] = incr;  /* tail code */
 
-   /* Pop this loop */
-   A->CurLoopBreak = prevLoopBreak;
-   A->CurLoopCont = prevLoopCont;
+   /* pop loop, restore prev */
+   A->CurLoop = prevLoop;
 
-   return tree;
+   return new_seq(init, loop);
 }
 
 
-/**
- * Generate IR tree for a for-loop, using high-level BGNLOOP/ENDLOOP and
- * IF/ENDIF instructions.
- *
- * XXX note done yet!
- */
 static slang_ir_node *
-_slang_gen_hl_for(slang_assemble_ctx * A, const slang_operation *oper)
+_slang_gen_continue(slang_assemble_ctx * A, const slang_operation *oper)
 {
-   /*
-    * init code (child[0])
-    * BGNLOOP
-    *    eval expr (child[1]), updating condcodes
-    *    IF !expr THEN
-    *       BRK
-    *    ENDIF
-    *    code body (child[3])
-    *    label "__continueFor"   // jump here for "continue"
-    *    incr code (child[2])
-    * ENDLOOP
-    */
-   slang_atom startAtom = slang_atom_pool_gen(A->atoms, "__startFor");
-   slang_atom contAtom = slang_atom_pool_gen(A->atoms, "__continueFor");
-   slang_atom endAtom = slang_atom_pool_gen(A->atoms, "__endFor");
-   slang_ir_node *init, *startLab, *cond, *bra, *body, *contLab;
-   slang_ir_node *incr, *jump, *endLab, *tree;
-   slang_atom prevLoopBreak = A->CurLoopBreak;
-   slang_atom prevLoopCont = A->CurLoopCont;
-
-   /* Push this loop */
-   A->CurLoopBreak = endAtom;
-   A->CurLoopCont = contAtom;
-
-   init = _slang_gen_operation(A, &oper->children[0]);
-   startLab = new_label(startAtom);
-   tree = new_seq(init, startLab);
-
-   cond = _slang_gen_operation(A, &oper->children[1]);
-   cond = _slang_gen_cond(cond);
-   tree = new_seq(tree, cond);
-
-   bra = new_cjump(endAtom, 0);
-   tree = new_seq(tree, bra);
-
-   body = _slang_gen_operation(A, &oper->children[3]);
-   tree = new_seq(tree, body);
-
-   contLab = new_label(contAtom);
-   tree = new_seq(tree, contLab);
-
-   incr = _slang_gen_operation(A, &oper->children[2]);
-   tree = new_seq(tree, incr);
-
-   jump = new_jump(startAtom);
-   tree = new_seq(tree, jump);
-
-   endLab = new_label(endAtom);
-   tree = new_seq(tree, endLab);
-
-   /* Pop this loop */
-   A->CurLoopBreak = prevLoopBreak;
-   A->CurLoopCont = prevLoopCont;
-
-   return tree;
+   slang_ir_node *n, *loopNode;
+   assert(oper->type == SLANG_OPER_CONTINUE);
+   loopNode = A->CurLoop;
+   assert(loopNode);
+   assert(loopNode->Opcode == IR_LOOP);
+   n = new_node0(IR_CONT);
+   if (n) {
+      n->Parent = loopNode;
+      /* insert this node at head of linked list */
+      n->List = loopNode->List;
+      loopNode->List = n;
+   }
+   return n;
 }
 
 
 /**
- * Generate IR tree for an if/then/else conditional using BRAnch instructions.
+ * Determine if the given operation is of a specific type.
  */
-static slang_ir_node *
-_slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper)
+static GLboolean
+is_operation_type(const const slang_operation *oper, slang_operation_type type)
 {
-   /*
-    * eval expr (child[0]), updating condcodes
-    * branch if false to _else or _endif
-    * "true" code block
-    * if haveElseClause clause:
-    *    jump "__endif"
-    *    label "__else"
-    *    "false" code block
-    * label "__endif"
-    */
-   const GLboolean haveElseClause = !_slang_is_noop(&oper->children[2]);
-   slang_ir_node *cond, *bra, *trueBody, *endifLab, *tree;
-   slang_atom elseAtom = slang_atom_pool_gen(A->atoms, "__else");
-   slang_atom endifAtom = slang_atom_pool_gen(A->atoms, "__endif");
-
-   cond = _slang_gen_operation(A, &oper->children[0]);
-   cond = _slang_gen_cond(cond);
-   /*assert(cond->Store);*/
-   bra = new_cjump(haveElseClause ? elseAtom : endifAtom, 0);
-   tree = new_seq(cond, bra);
-
-   trueBody = _slang_gen_operation(A, &oper->children[1]);
-   tree = new_seq(tree, trueBody);
-
-   if (haveElseClause) {
-      /* else clause */
-      slang_ir_node *jump, *elseLab, *falseBody;
-      jump = new_jump(endifAtom);
-      tree = new_seq(tree, jump);
-
-      elseLab = new_label(elseAtom);
-      tree = new_seq(tree, elseLab);
-
-      falseBody = _slang_gen_operation(A, &oper->children[2]);
-      tree = new_seq(tree, falseBody);
-   }
-
-   endifLab = new_label(endifAtom);
-   tree = new_seq(tree, endifLab);
-
-   return tree;
+   if (oper->type == type)
+      return GL_TRUE;
+   else if ((oper->type == SLANG_OPER_BLOCK_NEW_SCOPE ||
+             oper->type == SLANG_OPER_BLOCK_NO_NEW_SCOPE) &&
+            oper->num_children == 1)
+      return is_operation_type(&oper->children[0], type);
+   else
+      return GL_FALSE;
 }
 
 
@@ -1694,37 +1588,118 @@ _slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper)
  * IR_IF instruction.
  */
 static slang_ir_node *
-_slang_gen_hl_if(slang_assemble_ctx * A, const slang_operation *oper)
+_slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper)
 {
    /*
-    * eval expr (child[0]), updating condcodes
+    * eval expr (child[0])
     * IF expr THEN
     *    if-body code
     * ELSE
     *    else-body code
     * ENDIF
     */
-   /* XXX special cases to check for:
-    * if body of conditiona is just a "break", emit a conditional break
-    * instruction.
-    */
    const GLboolean haveElseClause = !_slang_is_noop(&oper->children[2]);
    slang_ir_node *ifNode, *cond, *ifBody, *elseBody;
+   GLboolean isConst, constTrue;
+
+   /* type-check expression */
+   if (!_slang_is_scalar_or_boolean(A, &oper->children[0])) {
+      slang_info_log_error(A->log, "scalar/boolean expression expected for 'if'");
+      return NULL;
+   }
+
+   isConst = _slang_is_constant_cond(&oper->children[0], &constTrue);
+   if (isConst) {
+      if (constTrue) {
+         /* if (true) ... */
+         return _slang_gen_operation(A, &oper->children[1]);
+      }
+      else {
+         /* if (false) ... */
+         return _slang_gen_operation(A, &oper->children[2]);
+      }
+   }
 
    cond = _slang_gen_operation(A, &oper->children[0]);
-   cond = _slang_gen_cond(cond);
-   ifBody = _slang_gen_operation(A, &oper->children[1]);
-   if (haveElseClause)
-      elseBody = _slang_gen_operation(A, &oper->children[2]);
-   else
-      elseBody = NULL;
+   cond = new_cond(cond);
+
+   if (is_operation_type(&oper->children[1], SLANG_OPER_BREAK)) {
+      /* Special case: generate a conditional break */
+      ifBody = new_break_if_true(A->CurLoop, cond);
+      if (haveElseClause) {
+         elseBody = _slang_gen_operation(A, &oper->children[2]);
+         return new_seq(ifBody, elseBody);
+      }
+      return ifBody;
+   }
+   else if (is_operation_type(&oper->children[1], SLANG_OPER_CONTINUE)) {
+      /* Special case: generate a conditional break */
+      ifBody = new_cont_if_true(A->CurLoop, cond);
+      if (haveElseClause) {
+         elseBody = _slang_gen_operation(A, &oper->children[2]);
+         return new_seq(ifBody, elseBody);
+      }
+      return ifBody;
+   }
+   else {
+      /* general case */
+      ifBody = _slang_gen_operation(A, &oper->children[1]);
+      if (haveElseClause)
+         elseBody = _slang_gen_operation(A, &oper->children[2]);
+      else
+         elseBody = NULL;
+      ifNode = new_if(cond, ifBody, elseBody);
+      return ifNode;
+   }
+}
 
-   ifNode = new_if(cond, ifBody, elseBody);
 
-   return ifNode;
+
+static slang_ir_node *
+_slang_gen_not(slang_assemble_ctx * A, const slang_operation *oper)
+{
+   slang_ir_node *n;
+
+   assert(oper->type == SLANG_OPER_NOT);
+
+   /* type-check expression */
+   if (!_slang_is_scalar_or_boolean(A, &oper->children[0])) {
+      slang_info_log_error(A->log,
+                           "scalar/boolean expression expected for '!'");
+      return NULL;
+   }
+
+   n = _slang_gen_operation(A, &oper->children[0]);
+   if (n)
+      return new_not(n);
+   else
+      return NULL;
 }
 
 
+static slang_ir_node *
+_slang_gen_xor(slang_assemble_ctx * A, const slang_operation *oper)
+{
+   slang_ir_node *n1, *n2;
+
+   assert(oper->type == SLANG_OPER_LOGICALXOR);
+
+   if (!_slang_is_scalar_or_boolean(A, &oper->children[0]) ||
+       !_slang_is_scalar_or_boolean(A, &oper->children[0])) {
+      slang_info_log_error(A->log,
+                           "scalar/boolean expressions expected for '^^'");
+      return NULL;
+   }
+
+   n1 = _slang_gen_operation(A, &oper->children[0]);
+   if (!n1)
+      return NULL;
+   n2 = _slang_gen_operation(A, &oper->children[1]);
+   if (!n2)
+      return NULL;
+   return new_node2(IR_NOTEQUAL, n1, n2);
+}
+
 
 /**
  * Generate IR node for storage of a temporary of given size.
@@ -1756,39 +1731,39 @@ static slang_ir_node *
 _slang_gen_var_decl(slang_assemble_ctx *A, slang_variable *var)
 {
    slang_ir_node *n;
+   assert(!is_sampler_type(&var->type));
    n = new_node0(IR_VAR_DECL);
    if (n) {
-      n->Var = var;
-      slang_allocate_storage(A, n);
+      _slang_attach_storage(n, var);
+
+      assert(var->aux);
+      assert(n->Store == var->aux);
       assert(n->Store);
       assert(n->Store->Index < 0);
+
+      n->Store->File = PROGRAM_TEMPORARY;
+      n->Store->Size = _slang_sizeof_type_specifier(&n->Var->type.specifier);
       assert(n->Store->Size > 0);
-      assert(var->aux);
-      assert(n->Store == var->aux);
    }
    return n;
 }
 
 
-
-
 /**
  * Generate code for a selection expression:   b ? x : y
- * XXX in some cases we could implement a selection expression
+ * XXX In some cases we could implement a selection expression
  * with an LRP instruction (use the boolean as the interpolant).
+ * Otherwise, we use an IF/ELSE/ENDIF construct.
  */
 static slang_ir_node *
 _slang_gen_select(slang_assemble_ctx *A, slang_operation *oper)
 {
-   slang_atom altAtom = slang_atom_pool_gen(A->atoms, "__selectAlt");
-   slang_atom endAtom = slang_atom_pool_gen(A->atoms, "__selectEnd");
-   slang_ir_node *altLab, *endLab;
-   slang_ir_node *tree, *tmpDecl, *tmpVar, *cond, *cjump, *jump;
-   slang_ir_node *bodx, *body, *assignx, *assigny;
+   slang_ir_node *cond, *ifNode, *trueExpr, *falseExpr, *trueNode, *falseNode;
+   slang_ir_node *tmpDecl, *tmpVar, *tree;
    slang_typeinfo type;
    int size;
 
-   assert(oper->type == slang_oper_select);
+   assert(oper->type == SLANG_OPER_SELECT);
    assert(oper->num_children == 3);
 
    /* size of x or y's type */
@@ -1800,46 +1775,32 @@ _slang_gen_select(slang_assemble_ctx *A, slang_operation *oper)
    /* temporary var */
    tmpDecl = _slang_gen_temporary(size);
 
-   /* eval condition */
+   /* the condition (child 0) */
    cond = _slang_gen_operation(A, &oper->children[0]);
-   cond = _slang_gen_cond(cond);
-   tree = new_seq(tmpDecl, cond);
-
-   /* jump if false to "alt" label */
-   cjump = new_cjump(altAtom, 0);
-   tree = new_seq(tree, cjump);
+   cond = new_cond(cond);
 
-   /* evaluate child 1 (x) and assign to tmp */
+   /* if-true body (child 1) */
    tmpVar = new_node0(IR_VAR);
    tmpVar->Store = tmpDecl->Store;
-   body = _slang_gen_operation(A, &oper->children[1]);
-   assigny = new_node2(IR_MOVE, tmpVar, body);
-   tree = new_seq(tree, assigny);
-
-   /* jump to "end" label */
-   jump = new_jump(endAtom);
-   tree = new_seq(tree, jump);
+   trueExpr = _slang_gen_operation(A, &oper->children[1]);
+   trueNode = new_node2(IR_MOVE, tmpVar, trueExpr);
 
-   /* "alt" label */
-   altLab = new_label(altAtom);
-   tree = new_seq(tree, altLab);
-
-   /* evaluate child 2 (y) and assign to tmp */
+   /* if-false body (child 2) */
    tmpVar = new_node0(IR_VAR);
    tmpVar->Store = tmpDecl->Store;
-   bodx = _slang_gen_operation(A, &oper->children[2]);
-   assignx = new_node2(IR_MOVE, tmpVar, bodx);
-   tree = new_seq(tree, assignx);
-
-   /* "end" label */
-   endLab = new_label(endAtom);
-   tree = new_seq(tree, endLab);
-   
+   falseExpr = _slang_gen_operation(A, &oper->children[2]);
+   falseNode = new_node2(IR_MOVE, tmpVar, falseExpr);
+
+   ifNode = new_if(cond, trueNode, falseNode);
+
    /* tmp var value */
    tmpVar = new_node0(IR_VAR);
    tmpVar->Store = tmpDecl->Store;
-   tree = new_seq(tree, tmpVar);
 
+   tree = new_seq(ifNode, tmpVar);
+   tree = new_seq(tmpDecl, tree);
+
+   /*_slang_print_ir_tree(tree, 10);*/
    return tree;
 }
 
@@ -1855,15 +1816,15 @@ _slang_gen_logical_and(slang_assemble_ctx *A, slang_operation *oper)
    slang_ir_node *n;
 
    select = slang_operation_new(1);
-   select->type = slang_oper_select;
+   select->type = SLANG_OPER_SELECT;
    select->num_children = 3;
    select->children = slang_operation_new(3);
 
    slang_operation_copy(&select->children[0], &oper->children[0]);
    slang_operation_copy(&select->children[1], &oper->children[1]);
-   select->children[2].type = slang_oper_literal_bool;
-   ASSIGN_4V(select->children[2].literal, 0, 0, 0, 0);
-   select->children[2].literal_size = 2;
+   select->children[2].type = SLANG_OPER_LITERAL_BOOL;
+   ASSIGN_4V(select->children[2].literal, 0, 0, 0, 0); /* false */
+   select->children[2].literal_size = 1;
 
    n = _slang_gen_select(A, select);
 
@@ -1886,15 +1847,15 @@ _slang_gen_logical_or(slang_assemble_ctx *A, slang_operation *oper)
    slang_ir_node *n;
 
    select = slang_operation_new(1);
-   select->type = slang_oper_select;
+   select->type = SLANG_OPER_SELECT;
    select->num_children = 3;
    select->children = slang_operation_new(3);
 
    slang_operation_copy(&select->children[0], &oper->children[0]);
-   select->children[1].type = slang_oper_literal_bool;
-   ASSIGN_4V(select->children[2].literal, 1, 1, 1, 1);
+   select->children[1].type = SLANG_OPER_LITERAL_BOOL;
+   ASSIGN_4V(select->children[1].literal, 1, 1, 1, 1); /* true */
+   select->children[1].literal_size = 1;
    slang_operation_copy(&select->children[2], &oper->children[1]);
-   select->children[2].literal_size = 2;
 
    n = _slang_gen_select(A, select);
 
@@ -1906,33 +1867,31 @@ _slang_gen_logical_or(slang_assemble_ctx *A, slang_operation *oper)
 }
 
 
-
 /**
  * Generate IR tree for a return statement.
  */
 static slang_ir_node *
 _slang_gen_return(slang_assemble_ctx * A, slang_operation *oper)
 {
-   if (oper->num_children == 0 ||
-       (oper->num_children == 1 &&
-        oper->children[0].type == slang_oper_void)) {
-      /* Convert from:
-       *   return;
-       * To:
-       *   goto __endOfFunction;
-       */
-      slang_ir_node *n;
-      slang_operation gotoOp;
-      slang_operation_construct(&gotoOp);
-      gotoOp.type = slang_oper_goto;
-      /* XXX don't call function? */
-      gotoOp.a_id = slang_atom_pool_atom(A->atoms,
-                                         (char *) A->CurFunction->end_label);
-      /* assemble the new code */
-      n = _slang_gen_operation(A, &gotoOp);
-      /* destroy temp code */
-      slang_operation_destruct(&gotoOp);
-      return n;
+   const GLboolean haveReturnValue
+      = (oper->num_children == 1 &&
+         oper->children[0].type != SLANG_OPER_VOID);
+
+   /* error checking */
+   assert(A->CurFunction);
+   if (haveReturnValue &&
+       A->CurFunction->header.type.specifier.type == SLANG_SPEC_VOID) {
+      slang_info_log_error(A->log, "illegal return expression");
+      return NULL;
+   }
+   else if (!haveReturnValue &&
+       A->CurFunction->header.type.specifier.type != SLANG_SPEC_VOID) {
+      slang_info_log_error(A->log, "return statement requires an expression");
+      return NULL;
+   }
+
+   if (!haveReturnValue) {
+      return new_return(A->curFuncEndLabel);
    }
    else {
       /*
@@ -1940,9 +1899,9 @@ _slang_gen_return(slang_assemble_ctx * A, slang_operation *oper)
        *   return expr;
        * To:
        *   __retVal = expr;
-       *   goto __endOfFunction;
+       *   return;  // goto __endOfFunction
        */
-      slang_operation *block, *assign, *jump;
+      slang_operation *assign;
       slang_atom a_retVal;
       slang_ir_node *n;
 
@@ -1953,47 +1912,30 @@ _slang_gen_return(slang_assemble_ctx * A, slang_operation *oper)
       {
          slang_variable *v
             = _slang_locate_variable(oper->locals, a_retVal, GL_TRUE);
-         assert(v);
+         if (!v) {
+            /* trying to return a value in a void-valued function */
+            return NULL;
+         }
       }
 #endif
 
-      block = slang_operation_new(1);
-      block->type = slang_oper_block_no_new_scope;
-      block->num_children = 2;
-      block->children = slang_operation_new(2);
-      assert(block->locals);
-      block->locals->outer_scope = oper->locals->outer_scope;
-
-      /* child[0]: __retVal = expr; */
-      assign = &block->children[0];
-      assign->type = slang_oper_assign;
-      assign->locals->outer_scope = block->locals;
+      assign = slang_operation_new(1);
+      assign->type = SLANG_OPER_ASSIGN;
       assign->num_children = 2;
       assign->children = slang_operation_new(2);
       /* lhs (__retVal) */
-      assign->children[0].type = slang_oper_identifier;
+      assign->children[0].type = SLANG_OPER_IDENTIFIER;
       assign->children[0].a_id = a_retVal;
       assign->children[0].locals->outer_scope = assign->locals;
       /* rhs (expr) */
       /* XXX we might be able to avoid this copy someday */
       slang_operation_copy(&assign->children[1], &oper->children[0]);
 
-      /* child[1]: goto __endOfFunction */
-      jump = &block->children[1];
-      jump->type = slang_oper_goto;
-      assert(A->CurFunction->end_label);
-      /* XXX don't call function? */
-      jump->a_id = slang_atom_pool_atom(A->atoms,
-                                        (char *) A->CurFunction->end_label);
-
-#if 0 /* debug */
-      printf("NEW RETURN:\n");
-      slang_print_tree(block, 0);
-#endif
-
       /* assemble the new code */
-      n = _slang_gen_operation(A, block);
-      slang_operation_delete(block);
+      n = new_seq(_slang_gen_operation(A, assign),
+                  new_return(A->curFuncEndLabel));
+
+      slang_operation_delete(assign);
       return n;
    }
 }
@@ -2023,7 +1965,8 @@ _slang_gen_declaration(slang_assemble_ctx *A, slang_operation *oper)
       assert(oper->num_children == 1);
       var = new_var(A, oper, oper->a_id);
       if (!var) {
-         RETURN_ERROR2("Undefined variable:", varName, 0);
+         slang_info_log_error(A->log, "undefined variable '%s'", varName);
+         return NULL;
       }
       /* XXX make copy of this initializer? */
       rhs = _slang_gen_operation(A, &oper->children[0]);
@@ -2036,7 +1979,8 @@ _slang_gen_declaration(slang_assemble_ctx *A, slang_operation *oper)
       slang_ir_node *var, *init, *rhs;
       var = new_var(A, oper, oper->a_id);
       if (!var) {
-         RETURN_ERROR2("Undefined variable:", varName, 0);
+         slang_info_log_error(A->log, "undefined variable '%s'", varName);
+         return NULL;
       }
 #if 0
       /* XXX make copy of this initializer? */
@@ -2051,6 +1995,9 @@ _slang_gen_declaration(slang_assemble_ctx *A, slang_operation *oper)
       _slang_simplify(v->initializer, &A->space, A->atoms); 
       rhs = _slang_gen_operation(A, v->initializer);
 #endif
+      if (!rhs)
+         return NULL;
+
       assert(rhs);
       init = new_node2(IR_MOVE, var, rhs);
       /*
@@ -2077,7 +2024,8 @@ _slang_gen_variable(slang_assemble_ctx * A, slang_operation *oper)
    slang_atom aVar = oper->var ? oper->var->a_name : oper->a_id;
    slang_ir_node *n = new_var(A, oper, aVar);
    if (!n) {
-      RETURN_ERROR2("Undefined variable:", (char *) aVar, 0);
+      slang_info_log_error(A->log, "undefined variable '%s'", (char *) aVar);
+      return NULL;
    }
    return n;
 }
@@ -2090,26 +2038,32 @@ _slang_gen_variable(slang_assemble_ctx * A, slang_operation *oper)
  *    v.xy = vec2(a, b);
  * Hard example:
  *    vec3 v;
- *    v.yz = vec2(a, b);
- * this would have to be transformed/swizzled into:
- *    v.yz = vec2(a, b).*xy*         (* = don't care)
- * Instead, we'll effectively do this:
- *    v.y = vec2(a, b).xxxx;
- *    v.z = vec2(a, b).yyyy;
- *
+ *    v.zy = vec2(a, b);
+ * this gets transformed/swizzled into:
+ *    v.zy = vec2(a, b).*yx*         (* = don't care)
+ * This function helps to determine simple vs. non-simple.
  */
 static GLboolean
-_slang_simple_writemask(GLuint writemask)
+_slang_simple_writemask(GLuint writemask, GLuint swizzle)
 {
    switch (writemask) {
    case WRITEMASK_X:
+      return GET_SWZ(swizzle, 0) == SWIZZLE_X;
    case WRITEMASK_Y:
+      return GET_SWZ(swizzle, 1) == SWIZZLE_Y;
    case WRITEMASK_Z:
+      return GET_SWZ(swizzle, 2) == SWIZZLE_Z;
    case WRITEMASK_W:
+      return GET_SWZ(swizzle, 3) == SWIZZLE_W;
    case WRITEMASK_XY:
+      return (GET_SWZ(swizzle, 0) == SWIZZLE_X)
+         && (GET_SWZ(swizzle, 1) == SWIZZLE_Y);
    case WRITEMASK_XYZ:
+      return (GET_SWZ(swizzle, 0) == SWIZZLE_X)
+         && (GET_SWZ(swizzle, 1) == SWIZZLE_Y)
+         && (GET_SWZ(swizzle, 2) == SWIZZLE_Z);
    case WRITEMASK_XYZW:
-      return GL_TRUE;
+      return swizzle == SWIZZLE_NOOP;
    default:
       return GL_FALSE;
    }
@@ -2160,7 +2114,7 @@ swizzle_to_writemask(GLuint swizzle,
                                newSwizzle[2],
                                newSwizzle[3]);
 
-   if (_slang_simple_writemask(mask)) {
+   if (_slang_simple_writemask(mask, *swizzleOut)) {
       if (size >= 1)
          assert(GET_SWZ(*swizzleOut, 0) == SWIZZLE_X);
       if (size >= 2)
@@ -2180,6 +2134,7 @@ static slang_ir_node *
 _slang_gen_swizzle(slang_ir_node *child, GLuint swizzle)
 {
    slang_ir_node *n = new_node1(IR_SWIZZLE, child);
+   assert(child);
    if (n) {
       n->Store = _slang_new_ir_storage(PROGRAM_UNDEFINED, -1, -1);
       n->Store->Swizzle = swizzle;
@@ -2194,8 +2149,28 @@ _slang_gen_swizzle(slang_ir_node *child, GLuint swizzle)
 static slang_ir_node *
 _slang_gen_assignment(slang_assemble_ctx * A, slang_operation *oper)
 {
-   if (oper->children[0].type == slang_oper_identifier &&
-       oper->children[1].type == slang_oper_call) {
+   if (oper->children[0].type == SLANG_OPER_IDENTIFIER) {
+      /* Check that var is writeable */
+      slang_variable *var
+         = _slang_locate_variable(oper->children[0].locals,
+                                  oper->children[0].a_id, GL_TRUE);
+      if (!var) {
+         slang_info_log_error(A->log, "undefined variable '%s'",
+                              (char *) oper->children[0].a_id);
+         return NULL;
+      }
+      if (var->type.qualifier == SLANG_QUAL_CONST ||
+          var->type.qualifier == SLANG_QUAL_ATTRIBUTE ||
+          var->type.qualifier == SLANG_QUAL_UNIFORM) {
+         slang_info_log_error(A->log,
+                              "illegal assignment to read-only variable '%s'",
+                              (char *) oper->children[0].a_id);
+         return NULL;
+      }
+   }
+
+   if (oper->children[0].type == SLANG_OPER_IDENTIFIER &&
+       oper->children[1].type == SLANG_OPER_CALL) {
       /* Special case of:  x = f(a, b)
        * Replace with f(a, b, x)  (where x == hidden __retVal out param)
        *
@@ -2212,6 +2187,18 @@ _slang_gen_assignment(slang_assemble_ctx * A, slang_operation *oper)
    else {
       slang_ir_node *n, *lhs, *rhs;
       lhs = _slang_gen_operation(A, &oper->children[0]);
+
+      if (lhs) {
+         if (lhs->Store->File != PROGRAM_OUTPUT &&
+             lhs->Store->File != PROGRAM_TEMPORARY &&
+             lhs->Store->File != PROGRAM_VARYING &&
+             lhs->Store->File != PROGRAM_UNDEFINED) {
+            slang_info_log_error(A->log,
+                                 "illegal assignment to read-only l-value");
+            return NULL;
+         }
+      }
+
       rhs = _slang_gen_operation(A, &oper->children[1]);
       if (lhs && rhs) {
          /* convert lhs swizzle into writemask */
@@ -2242,6 +2229,7 @@ _slang_gen_field(slang_assemble_ctx * A, slang_operation *oper)
 {
    slang_typeinfo ti;
 
+   /* type of struct */
    slang_typeinfo_construct(&ti);
    _slang_typeof_operation(A, &oper->children[0], &ti);
 
@@ -2252,7 +2240,7 @@ _slang_gen_field(slang_assemble_ctx * A, slang_operation *oper)
       slang_ir_node *n;
       GLuint swizzle;
       if (!_slang_is_swizzle((char *) oper->a_id, rows, &swz)) {
-         RETURN_ERROR("Bad swizzle", 0);
+         slang_info_log_error(A->log, "Bad swizzle");
       }
       swizzle = MAKE_SWIZZLE4(swz.swizzle[0],
                               swz.swizzle[1],
@@ -2261,16 +2249,17 @@ _slang_gen_field(slang_assemble_ctx * A, slang_operation *oper)
 
       n = _slang_gen_operation(A, &oper->children[0]);
       /* create new parent node with swizzle */
-      n = _slang_gen_swizzle(n, swizzle);
+      if (n)
+         n = _slang_gen_swizzle(n, swizzle);
       return n;
    }
-   else if (ti.spec.type == slang_spec_float) {
+   else if (ti.spec.type == SLANG_SPEC_FLOAT) {
       const GLuint rows = 1;
       slang_swizzle swz;
       slang_ir_node *n;
       GLuint swizzle;
       if (!_slang_is_swizzle((char *) oper->a_id, rows, &swz)) {
-         RETURN_ERROR("Bad swizzle", 0);
+         slang_info_log_error(A->log, "Bad swizzle");
       }
       swizzle = MAKE_SWIZZLE4(swz.swizzle[0],
                               swz.swizzle[1],
@@ -2285,8 +2274,46 @@ _slang_gen_field(slang_assemble_ctx * A, slang_operation *oper)
       /* the field is a structure member (base.field) */
       /* oper->children[0] is the base */
       /* oper->a_id is the field name */
+      slang_ir_node *base, *n;
+      slang_typeinfo field_ti;
+      GLint fieldSize, fieldOffset;
+      /* type of field */
+      slang_typeinfo_construct(&field_ti);
+      _slang_typeof_operation(A, oper, &field_ti);
+
+      fieldSize = _slang_sizeof_type_specifier(&field_ti.spec);
+      fieldOffset = _slang_field_offset(&ti.spec, oper->a_id);
+
+      if (fieldOffset < 0) {
+         slang_info_log_error(A->log,
+                              "\"%s\" is not a member of struct \"%s\"",
+                              (char *) oper->a_id,
+                              (char *) ti.spec._struct->a_name);
+         return NULL;
+      }
+      assert(fieldSize >= 0);
+
+      base = _slang_gen_operation(A, &oper->children[0]);
+      if (!base) {
+         /* error msg should have already been logged */
+         return NULL;
+      }
+
+      n = new_node1(IR_FIELD, base);
+      if (n) {
+         n->Field = (char *) oper->a_id;
+         n->FieldOffset = fieldOffset;
+         assert(n->FieldOffset >= 0);
+         n->Store = _slang_new_ir_storage(base->Store->File,
+                                          base->Store->Index,
+                                          fieldSize);
+      }
+      return n;
+
+#if 0
       _mesa_problem(NULL, "glsl structs/fields not supported yet");
       return NULL;
+#endif
    }
 }
 
@@ -2311,9 +2338,10 @@ _slang_gen_subscript(slang_assemble_ctx * A, slang_operation *oper)
       slang_ir_node *n;
 
       index = (GLint) oper->children[1].literal[0];
-      if (oper->children[1].type != slang_oper_literal_int ||
+      if (oper->children[1].type != SLANG_OPER_LITERAL_INT ||
           index >= max) {
-         RETURN_ERROR("Invalid array index for vector type", 0);
+         slang_info_log_error(A->log, "Invalid array index for vector type");
+         return NULL;
       }
 
       n = _slang_gen_operation(A, &oper->children[0]);
@@ -2333,30 +2361,100 @@ _slang_gen_subscript(slang_assemble_ctx * A, slang_operation *oper)
       /* conventional array */
       slang_typeinfo elem_ti;
       slang_ir_node *elem, *array, *index;
-      GLint elemSize;
+      GLint elemSize, arrayLen;
 
       /* size of array element */
       slang_typeinfo_construct(&elem_ti);
       _slang_typeof_operation(A, oper, &elem_ti);
       elemSize = _slang_sizeof_type_specifier(&elem_ti.spec);
-      assert(elemSize >= 1);
+
+      if (_slang_type_is_matrix(array_ti.spec.type))
+         arrayLen = _slang_type_dim(array_ti.spec.type);
+      else
+         arrayLen = array_ti.array_len;
+
+      slang_typeinfo_destruct(&array_ti);
+      slang_typeinfo_destruct(&elem_ti);
+
+      if (elemSize <= 0) {
+         /* unknown var or type */
+         slang_info_log_error(A->log, "Undefined variable or type");
+         return NULL;
+      }
 
       array = _slang_gen_operation(A, &oper->children[0]);
       index = _slang_gen_operation(A, &oper->children[1]);
       if (array && index) {
+         /* bounds check */
+         if (index->Opcode == IR_FLOAT &&
+             ((int) index->Value[0] < 0 ||
+              (int) index->Value[0] >= arrayLen)) {
+            slang_info_log_error(A->log,
+                                "Array index out of bounds (index=%d size=%d)",
+                                 (int) index->Value[0], arrayLen);
+            _slang_free_ir_tree(array);
+            _slang_free_ir_tree(index);
+            return NULL;
+         }
+
          elem = new_node2(IR_ELEMENT, array, index);
          elem->Store = _slang_new_ir_storage(array->Store->File,
                                              array->Store->Index,
                                              elemSize);
+         /* XXX try to do some array bounds checking here */
          return elem;
       }
       else {
+         _slang_free_ir_tree(array);
+         _slang_free_ir_tree(index);
          return NULL;
       }
    }
 }
 
 
+/**
+ * Look for expressions such as:  gl_ModelviewMatrix * gl_Vertex
+ * and replace with this:  gl_Vertex * gl_ModelviewMatrixTranpose
+ * Since matrices are stored in column-major order, the second form of
+ * multiplication is much more efficient (just 4 dot products).
+ */
+static void
+_slang_check_matmul_optimization(slang_assemble_ctx *A, slang_operation *oper)
+{
+   static const struct {
+      const char *orig;
+      const char *tranpose;
+   } matrices[] = {
+      {"gl_ModelViewMatrix", "gl_ModelViewMatrixTranspose"},
+      {"gl_ProjectionMatrix", "gl_ProjectionMatrixTranspose"},
+      {"gl_ModelViewProjectionMatrix", "gl_ModelViewProjectionMatrixTranspose"},
+      {"gl_TextureMatrix", "gl_TextureMatrixTranspose"},
+      {"gl_NormalMatrix", "__NormalMatrixTranspose"},
+      { NULL, NULL }
+   };
+
+   assert(oper->type == SLANG_OPER_MULTIPLY);
+   if (oper->children[0].type == SLANG_OPER_IDENTIFIER) {
+      GLuint i;
+      for (i = 0; matrices[i].orig; i++) {
+         if (oper->children[0].a_id
+             == slang_atom_pool_atom(A->atoms, matrices[i].orig)) {
+            /*
+            _mesa_printf("Replace %s with %s\n",
+                         matrices[i].orig, matrices[i].tranpose);
+            */
+            assert(oper->children[0].type == SLANG_OPER_IDENTIFIER);
+            oper->children[0].a_id
+               = slang_atom_pool_atom(A->atoms, matrices[i].tranpose);
+            /* finally, swap the operands */
+            _slang_operation_swap(&oper->children[0], &oper->children[1]);
+            return;
+         }
+      }
+   }
+}
+
 
 /**
  * Generate IR tree for a slang_operation (AST node)
@@ -2365,15 +2463,15 @@ static slang_ir_node *
 _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
 {
    switch (oper->type) {
-   case slang_oper_block_new_scope:
+   case SLANG_OPER_BLOCK_NEW_SCOPE:
       {
          slang_ir_node *n;
 
          _slang_push_var_table(A->vartable);
 
-         oper->type = slang_oper_block_no_new_scope; /* temp change */
+         oper->type = SLANG_OPER_BLOCK_NO_NEW_SCOPE; /* temp change */
          n = _slang_gen_operation(A, oper);
-         oper->type = slang_oper_block_new_scope; /* restore */
+         oper->type = SLANG_OPER_BLOCK_NEW_SCOPE; /* restore */
 
          _slang_pop_var_table(A->vartable);
 
@@ -2383,11 +2481,8 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
       }
       break;
 
-   case slang_oper_block_no_new_scope:
+   case SLANG_OPER_BLOCK_NO_NEW_SCOPE:
       /* list of operations */
-      /*
-      assert(oper->num_children > 0);
-      */
       if (oper->num_children > 0)
       {
          slang_ir_node *n, *tree = NULL;
@@ -2424,121 +2519,109 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
 #endif
          return tree;
       }
-      break;
-   case slang_oper_expression:
-      return _slang_gen_operation(A, &oper->children[0]);
-      break;
-   case slang_oper_while:
-      if (UseHighLevelInstructions)
-         return _slang_gen_hl_while(A, oper);
-      else
-         return _slang_gen_while(A, oper);
-   case slang_oper_do:
-      return _slang_gen_do(A, oper);
-   case slang_oper_for:
-      if (UseHighLevelInstructions)
-         return _slang_gen_hl_for(A, oper);
-      else
-         return _slang_gen_for(A, oper);
-   case slang_oper_break:
-      if (!A->CurLoop && !A->CurLoopBreak) {
-         RETURN_ERROR("'break' not in loop", 0);
-      }
-      if (UseHighLevelInstructions) {
-         return new_break(A->CurLoop);
-      }
       else {
-         return new_jump(A->CurLoopBreak);
+         return new_node0(IR_NOP);
       }
-   case slang_oper_continue:
-      if (!A->CurLoop && !A->CurLoopCont) {
-         RETURN_ERROR("'continue' not in loop", 0);
-      }
-      if (UseHighLevelInstructions) {
-         return new_cont(A->CurLoop);
+
+   case SLANG_OPER_EXPRESSION:
+      return _slang_gen_operation(A, &oper->children[0]);
+
+   case SLANG_OPER_FOR:
+      return _slang_gen_for(A, oper);
+   case SLANG_OPER_DO:
+      return _slang_gen_do(A, oper);
+   case SLANG_OPER_WHILE:
+      return _slang_gen_while(A, oper);
+   case SLANG_OPER_BREAK:
+      if (!A->CurLoop) {
+         slang_info_log_error(A->log, "'break' not in loop");
+         return NULL;
       }
-      else {
-         return new_jump(A->CurLoopCont);
+      return new_break(A->CurLoop);
+   case SLANG_OPER_CONTINUE:
+      if (!A->CurLoop) {
+         slang_info_log_error(A->log, "'continue' not in loop");
+         return NULL;
       }
-   case slang_oper_discard:
+      return _slang_gen_continue(A, oper);
+   case SLANG_OPER_DISCARD:
       return new_node0(IR_KILL);
 
-   case slang_oper_equal:
-      return new_node2(IR_SEQUAL,
+   case SLANG_OPER_EQUAL:
+      return new_node2(IR_EQUAL,
                       _slang_gen_operation(A, &oper->children[0]),
                       _slang_gen_operation(A, &oper->children[1]));
-   case slang_oper_notequal:
-      return new_node2(IR_SNEQUAL,
+   case SLANG_OPER_NOTEQUAL:
+      return new_node2(IR_NOTEQUAL,
                       _slang_gen_operation(A, &oper->children[0]),
                       _slang_gen_operation(A, &oper->children[1]));
-   case slang_oper_greater:
+   case SLANG_OPER_GREATER:
       return new_node2(IR_SGT,
                       _slang_gen_operation(A, &oper->children[0]),
                       _slang_gen_operation(A, &oper->children[1]));
-   case slang_oper_less:
-      /* child[0] < child[1]  ---->   child[1] > child[0] */
-      return new_node2(IR_SGT,
-                      _slang_gen_operation(A, &oper->children[1]),
-                      _slang_gen_operation(A, &oper->children[0]));
-   case slang_oper_greaterequal:
-      return new_node2(IR_SGE,
+   case SLANG_OPER_LESS:
+      return new_node2(IR_SLT,
                       _slang_gen_operation(A, &oper->children[0]),
                       _slang_gen_operation(A, &oper->children[1]));
-   case slang_oper_lessequal:
-      /* child[0] <= child[1]  ---->   child[1] >= child[0] */
+   case SLANG_OPER_GREATEREQUAL:
       return new_node2(IR_SGE,
-                      _slang_gen_operation(A, &oper->children[1]),
-                      _slang_gen_operation(A, &oper->children[0]));
-   case slang_oper_add:
+                      _slang_gen_operation(A, &oper->children[0]),
+                      _slang_gen_operation(A, &oper->children[1]));
+   case SLANG_OPER_LESSEQUAL:
+      return new_node2(IR_SLE,
+                       _slang_gen_operation(A, &oper->children[0]),
+                       _slang_gen_operation(A, &oper->children[1]));
+   case SLANG_OPER_ADD:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
         n = _slang_gen_function_call_name(A, "+", oper, NULL);
         return n;
       }
-   case slang_oper_subtract:
+   case SLANG_OPER_SUBTRACT:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
         n = _slang_gen_function_call_name(A, "-", oper, NULL);
         return n;
       }
-   case slang_oper_multiply:
+   case SLANG_OPER_MULTIPLY:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
+         _slang_check_matmul_optimization(A, oper);
          n = _slang_gen_function_call_name(A, "*", oper, NULL);
         return n;
       }
-   case slang_oper_divide:
+   case SLANG_OPER_DIVIDE:
       {
          slang_ir_node *n;
          assert(oper->num_children == 2);
         n = _slang_gen_function_call_name(A, "/", oper, NULL);
         return n;
       }
-   case slang_oper_minus:
+   case SLANG_OPER_MINUS:
       {
          slang_ir_node *n;
          assert(oper->num_children == 1);
         n = _slang_gen_function_call_name(A, "-", oper, NULL);
         return n;
       }
-   case slang_oper_plus:
+   case SLANG_OPER_PLUS:
       /* +expr   --> do nothing */
       return _slang_gen_operation(A, &oper->children[0]);
-   case slang_oper_variable_decl:
+   case SLANG_OPER_VARIABLE_DECL:
       return _slang_gen_declaration(A, oper);
-   case slang_oper_assign:
+   case SLANG_OPER_ASSIGN:
       return _slang_gen_assignment(A, oper);
-   case slang_oper_addassign:
+   case SLANG_OPER_ADDASSIGN:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
         n = _slang_gen_function_call_name(A, "+=", oper, &oper->children[0]);
         return n;
       }
-   case slang_oper_subassign:
+   case SLANG_OPER_SUBASSIGN:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
@@ -2546,50 +2629,39 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
         return n;
       }
       break;
-   case slang_oper_mulassign:
+   case SLANG_OPER_MULASSIGN:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
         n = _slang_gen_function_call_name(A, "*=", oper, &oper->children[0]);
         return n;
       }
-   case slang_oper_divassign:
+   case SLANG_OPER_DIVASSIGN:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
         n = _slang_gen_function_call_name(A, "/=", oper, &oper->children[0]);
         return n;
       }
-   case slang_oper_logicaland:
+   case SLANG_OPER_LOGICALAND:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
         n = _slang_gen_logical_and(A, oper);
         return n;
       }
-   case slang_oper_logicalor:
+   case SLANG_OPER_LOGICALOR:
       {
         slang_ir_node *n;
          assert(oper->num_children == 2);
         n = _slang_gen_logical_or(A, oper);
         return n;
       }
-   case slang_oper_logicalxor:
-      {
-        slang_ir_node *n;
-         assert(oper->num_children == 2);
-        n = _slang_gen_function_call_name(A, "__logicalXor", oper, NULL);
-        return n;
-      }
-   case slang_oper_not:
-      {
-        slang_ir_node *n;
-         assert(oper->num_children == 1);
-        n = _slang_gen_function_call_name(A, "__logicalNot", oper, NULL);
-        return n;
-      }
-
-   case slang_oper_select:  /* b ? x : y */
+   case SLANG_OPER_LOGICALXOR:
+      return _slang_gen_xor(A, oper);
+   case SLANG_OPER_NOT:
+      return _slang_gen_not(A, oper);
+   case SLANG_OPER_SELECT:  /* b ? x : y */
       {
         slang_ir_node *n;
          assert(oper->num_children == 3);
@@ -2597,61 +2669,52 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
         return n;
       }
 
-   case slang_oper_asm:
+   case SLANG_OPER_ASM:
       return _slang_gen_asm(A, oper, NULL);
-   case slang_oper_call:
+   case SLANG_OPER_CALL:
       return _slang_gen_function_call_name(A, (const char *) oper->a_id,
                                            oper, NULL);
-   case slang_oper_return:
+   case SLANG_OPER_RETURN:
       return _slang_gen_return(A, oper);
-   case slang_oper_goto:
-      return new_jump((char*) oper->a_id);
-   case slang_oper_label:
-      return new_label((char*) oper->a_id);
-   case slang_oper_identifier:
+   case SLANG_OPER_LABEL:
+      return new_label(oper->label);
+   case SLANG_OPER_IDENTIFIER:
       return _slang_gen_variable(A, oper);
-   case slang_oper_if:
-      if (A->program->Target == GL_FRAGMENT_PROGRAM_ARB
-          && UseHighLevelInstructions) {
-         return _slang_gen_hl_if(A, oper);
-      }
-      else {
-         /* XXX update tnl executor */
-         return _slang_gen_if(A, oper);
-      }
-   case slang_oper_field:
+   case SLANG_OPER_IF:
+      return _slang_gen_if(A, oper);
+   case SLANG_OPER_FIELD:
       return _slang_gen_field(A, oper);
-   case slang_oper_subscript:
+   case SLANG_OPER_SUBSCRIPT:
       return _slang_gen_subscript(A, oper);
-   case slang_oper_literal_float:
+   case SLANG_OPER_LITERAL_FLOAT:
       /* fall-through */
-   case slang_oper_literal_int:
+   case SLANG_OPER_LITERAL_INT:
       /* fall-through */
-   case slang_oper_literal_bool:
-      return new_float_literal(oper->literal);
+   case SLANG_OPER_LITERAL_BOOL:
+      return new_float_literal(oper->literal, oper->literal_size);
 
-   case slang_oper_postincrement:   /* var++ */
+   case SLANG_OPER_POSTINCREMENT:   /* var++ */
       {
         slang_ir_node *n;
          assert(oper->num_children == 1);
         n = _slang_gen_function_call_name(A, "__postIncr", oper, NULL);
         return n;
       }
-   case slang_oper_postdecrement:   /* var-- */
+   case SLANG_OPER_POSTDECREMENT:   /* var-- */
       {
         slang_ir_node *n;
          assert(oper->num_children == 1);
         n = _slang_gen_function_call_name(A, "__postDecr", oper, NULL);
         return n;
       }
-   case slang_oper_preincrement:   /* ++var */
+   case SLANG_OPER_PREINCREMENT:   /* ++var */
       {
         slang_ir_node *n;
          assert(oper->num_children == 1);
         n = _slang_gen_function_call_name(A, "++", oper, NULL);
         return n;
       }
-   case slang_oper_predecrement:   /* --var */
+   case SLANG_OPER_PREDECREMENT:   /* --var */
       {
         slang_ir_node *n;
          assert(oper->num_children == 1);
@@ -2659,7 +2722,8 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
         return n;
       }
 
-   case slang_oper_sequence:
+   case SLANG_OPER_INLINED_CALL:
+   case SLANG_OPER_SEQUENCE:
       {
          slang_ir_node *tree = NULL;
          GLuint i;
@@ -2667,20 +2731,23 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
             slang_ir_node *n = _slang_gen_operation(A, &oper->children[i]);
             tree = tree ? new_seq(tree, n) : n;
          }
+         if (oper->type == SLANG_OPER_INLINED_CALL) {
+            tree = new_inlined_function_call(tree, oper->label);
+         }
          return tree;
       }
 
-   case slang_oper_none:
-      return NULL;
-   case slang_oper_void:
-      return NULL;
+   case SLANG_OPER_NONE:
+   case SLANG_OPER_VOID:
+      /* returning NULL here would generate an error */
+      return new_node0(IR_NOP);
 
    default:
-      printf("Unhandled node type %d\n", oper->type);
-      abort();
+      _mesa_problem(NULL, "bad node type %d in _slang_gen_operation",
+                    oper->type);
       return new_node0(IR_NOP);
    }
-      abort();
+
    return NULL;
 }
 
@@ -2722,9 +2789,10 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
       store = _slang_new_ir_storage(PROGRAM_SAMPLER, samplerUniform, texIndex);
       if (dbg) printf("SAMPLER ");
    }
-   else if (var->type.qualifier == slang_qual_uniform) {
+   else if (var->type.qualifier == SLANG_QUAL_UNIFORM) {
       /* Uniform variable */
-      const GLint size = _slang_sizeof_type_specifier(&var->type.specifier);
+      const GLint size = _slang_sizeof_type_specifier(&var->type.specifier)
+                         * MAX2(var->array_len, 1);
       if (prog) {
          /* user-defined uniform */
          GLint uniformLoc = _mesa_add_uniform(prog->Parameters, varName, size);
@@ -2737,9 +2805,9 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
           */
          store = _slang_new_ir_storage(PROGRAM_STATE_VAR, -1, size);
       }
-      if (dbg) printf("UNIFORM ");
+      if (dbg) printf("UNIFORM (sz %d) ", size);
    }
-   else if (var->type.qualifier == slang_qual_varying) {
+   else if (var->type.qualifier == SLANG_QUAL_VARYING) {
       const GLint size = 4; /* XXX fix */
       if (prog) {
          /* user-defined varying */
@@ -2748,16 +2816,19 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
       }
       else {
          /* pre-defined varying, like gl_Color or gl_TexCoord */
-         if (type == slang_unit_fragment_builtin) {
-            GLint index = _slang_input_index(varName, GL_FRAGMENT_PROGRAM_ARB);
+         if (type == SLANG_UNIT_FRAGMENT_BUILTIN) {
+            GLuint swizzle;
+            GLint index = _slang_input_index(varName, GL_FRAGMENT_PROGRAM_ARB,
+                                             &swizzle);
             assert(index >= 0);
             store = _slang_new_ir_storage(PROGRAM_INPUT, index, size);
+            store->Swizzle = swizzle;
             assert(index < FRAG_ATTRIB_MAX);
          }
          else {
             GLint index = _slang_output_index(varName, GL_VERTEX_PROGRAM_ARB);
             assert(index >= 0);
-            assert(type == slang_unit_vertex_builtin);
+            assert(type == SLANG_UNIT_VERTEX_BUILTIN);
             store = _slang_new_ir_storage(PROGRAM_OUTPUT, index, size);
             assert(index < VERT_RESULT_MAX);
          }
@@ -2765,7 +2836,7 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
       }
       if (dbg) printf("VARYING ");
    }
-   else if (var->type.qualifier == slang_qual_attribute) {
+   else if (var->type.qualifier == SLANG_QUAL_ATTRIBUTE) {
       if (prog) {
          /* user-defined vertex attribute */
          const GLint size = _slang_sizeof_type_specifier(&var->type.specifier);
@@ -2778,34 +2849,40 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
       }
       else {
          /* pre-defined vertex attrib */
-         GLint index = _slang_input_index(varName, GL_VERTEX_PROGRAM_ARB);
+         GLuint swizzle;
+         GLint index = _slang_input_index(varName, GL_VERTEX_PROGRAM_ARB,
+                                          &swizzle);
          GLint size = 4; /* XXX? */
          assert(index >= 0);
          store = _slang_new_ir_storage(PROGRAM_INPUT, index, size);
+         store->Swizzle = swizzle;
       }
       if (dbg) printf("ATTRIB ");
    }
-   else if (var->type.qualifier == slang_qual_fixedinput) {
-      GLint index = _slang_input_index(varName, GL_FRAGMENT_PROGRAM_ARB);
+   else if (var->type.qualifier == SLANG_QUAL_FIXEDINPUT) {
+      GLuint swizzle;
+      GLint index = _slang_input_index(varName, GL_FRAGMENT_PROGRAM_ARB,
+                                       &swizzle);
       GLint size = 4; /* XXX? */
       store = _slang_new_ir_storage(PROGRAM_INPUT, index, size);
+      store->Swizzle = swizzle;
       if (dbg) printf("INPUT ");
    }
-   else if (var->type.qualifier == slang_qual_fixedoutput) {
-      if (type == slang_unit_vertex_builtin) {
+   else if (var->type.qualifier == SLANG_QUAL_FIXEDOUTPUT) {
+      if (type == SLANG_UNIT_VERTEX_BUILTIN) {
          GLint index = _slang_output_index(varName, GL_VERTEX_PROGRAM_ARB);
          GLint size = 4; /* XXX? */
          store = _slang_new_ir_storage(PROGRAM_OUTPUT, index, size);
       }
       else {
-         assert(type == slang_unit_fragment_builtin);
+         assert(type == SLANG_UNIT_FRAGMENT_BUILTIN);
          GLint index = _slang_output_index(varName, GL_FRAGMENT_PROGRAM_ARB);
          GLint size = 4; /* XXX? */
          store = _slang_new_ir_storage(PROGRAM_OUTPUT, index, size);
       }
       if (dbg) printf("OUTPUT ");
    }
-   else if (var->type.qualifier == slang_qual_const && !prog) {
+   else if (var->type.qualifier == SLANG_QUAL_CONST && !prog) {
       /* pre-defined global constant, like gl_MaxLights */
       const GLint size = _slang_sizeof_type_specifier(&var->type.specifier);
       store = _slang_new_ir_storage(PROGRAM_CONSTANT, -1, size);
@@ -2836,7 +2913,7 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
          n = new_seq(n, init);
       }
 
-      success = _slang_emit_code(n, A->vartable, A->program, GL_FALSE);
+      success = _slang_emit_code(n, A->vartable, A->program, GL_FALSE, A->log);
 
       _slang_free_ir_tree(n);
    }
@@ -2859,20 +2936,35 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
 GLboolean
 _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun)
 {
-   slang_ir_node *n, *endLabel;
+   slang_ir_node *n;
    GLboolean success = GL_TRUE;
 
    if (_mesa_strcmp((char *) fun->header.a_name, "main") != 0) {
       /* we only really generate code for main, all other functions get
        * inlined.
        */
+#if 0
+      /* do some basic error checking though */
+      if (fun->header.type.specifier.type != SLANG_SPEC_VOID) {
+         /* check that non-void functions actually return something */
+         slang_operation *op
+            = _slang_find_node_type(fun->body, SLANG_OPER_RETURN);
+         if (!op) {
+            slang_info_log_error(A->log,
+                                 "function \"%s\" has no return statement",
+                                 (char *) fun->header.a_name);
+            printf(
+                   "function \"%s\" has no return statement\n",
+                   (char *) fun->header.a_name);
+            return GL_FALSE;
+         }
+      }
+#endif
       return GL_TRUE;  /* not an error */
    }
 
-#if 1
-   printf("\n*********** codegen_function %s\n", (char *) fun->header.a_name);
-#endif
 #if 0
+   printf("\n*********** codegen_function %s\n", (char *) fun->header.a_name);
    slang_print_function(fun, 1);
 #endif
 
@@ -2880,15 +2972,19 @@ _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun)
    assert(A->program->Parameters );
    assert(A->program->Varying);
    assert(A->vartable);
+   A->CurLoop = NULL;
+   A->CurFunction = fun;
 
    /* fold constant expressions, etc. */
    _slang_simplify(fun->body, &A->space, A->atoms);
 
-   A->CurFunction = fun;
+#if 0
+   printf("\n*********** simplified %s\n", (char *) fun->header.a_name);
+   slang_print_function(fun, 1);
+#endif
 
    /* Create an end-of-function label */
-   if (!A->CurFunction->end_label)
-      A->CurFunction->end_label = slang_atom_pool_gen(A->atoms, "__endOfFunc_main_");
+   A->curFuncEndLabel = _slang_label_new("__endOfFunc__main");
 
    /* push new vartable scope */
    _slang_push_var_table(A->vartable);
@@ -2907,10 +3003,10 @@ _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun)
    }
 
    /* append an end-of-function-label to IR tree */
-   endLabel = new_label(fun->end_label);
-   n = new_seq(n, endLabel);
+   n = new_seq(n, new_label(A->curFuncEndLabel));
 
-   A->CurFunction = NULL;
+   /*_slang_label_delete(A->curFuncEndLabel);*/
+   A->curFuncEndLabel = NULL;
 
 #if 0
    printf("************* New AST for %s *****\n", (char*)fun->header.a_name);
@@ -2918,14 +3014,14 @@ _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun)
 #endif
 #if 0
    printf("************* IR for %s *******\n", (char*)fun->header.a_name);
-   slang_print_ir(n, 0);
+   _slang_print_ir_tree(n, 0);
 #endif
-#if 1
+#if 0
    printf("************* End codegen function ************\n\n");
 #endif
 
    /* Emit program instructions */
-   success = _slang_emit_code(n, A->vartable, A->program, GL_TRUE);
+   success = _slang_emit_code(n, A->vartable, A->program, GL_TRUE, A->log);
    _slang_free_ir_tree(n);
 
    /* free codegen context */