Merge branch 'master' of git+ssh://znh@git.freedesktop.org/git/mesa/mesa into 965...
[mesa.git] / src / mesa / shader / slang / slang_codegen.c
index c2a33778c6de79582433766f8a9e37058cbc1251..eae12f2ae9c59999252e77d7d680e039fab131e8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Mesa 3-D graphics library
- * Version:  6.5.3
+ * Version:  7.1
  *
  * Copyright (C) 2005-2007  Brian Paul   All Rights Reserved.
  *
 
 
 
-#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 "main/imports.h"
+#include "main/macros.h"
+#include "main/mtypes.h"
+#include "shader/program.h"
+#include "shader/prog_instruction.h"
+#include "shader/prog_parameter.h"
+#include "shader/prog_statevars.h"
 #include "slang_typeinfo.h"
 #include "slang_codegen.h"
 #include "slang_compile.h"
 #include "slang_label.h"
+#include "slang_mem.h"
 #include "slang_simplify.h"
 #include "slang_emit.h"
 #include "slang_vartable.h"
@@ -107,42 +108,84 @@ _slang_field_offset(const slang_type_specifier *spec, slang_atom field)
 }
 
 
+/**
+ * Return the size (in floats) of the given type specifier.
+ * If the size is greater than 4, the size should be a multiple of 4
+ * so that the correct number of 4-float registers are allocated.
+ * For example, a mat3x2 is size 12 because we want to store the
+ * 3 columns in 3 float[4] registers.
+ */
 GLuint
 _slang_sizeof_type_specifier(const slang_type_specifier *spec)
 {
+   GLuint sz;
    switch (spec->type) {
    case SLANG_SPEC_VOID:
-      return 0;
+      sz = 0;
+      break;
    case SLANG_SPEC_BOOL:
-      return 1;
+      sz = 1;
+      break;
    case SLANG_SPEC_BVEC2:
-      return 2;
+      sz = 2;
+      break;
    case SLANG_SPEC_BVEC3:
-      return 3;
+      sz = 3;
+      break;
    case SLANG_SPEC_BVEC4:
-      return 4;
+      sz = 4;
+      break;
    case SLANG_SPEC_INT:
-      return 1;
+      sz = 1;
+      break;
    case SLANG_SPEC_IVEC2:
-      return 2;
+      sz = 2;
+      break;
    case SLANG_SPEC_IVEC3:
-      return 3;
+      sz = 3;
+      break;
    case SLANG_SPEC_IVEC4:
-      return 4;
+      sz = 4;
+      break;
    case SLANG_SPEC_FLOAT:
-      return 1;
+      sz = 1;
+      break;
    case SLANG_SPEC_VEC2:
-      return 2;
+      sz = 2;
+      break;
    case SLANG_SPEC_VEC3:
-      return 3;
+      sz = 3;
+      break;
    case SLANG_SPEC_VEC4:
-      return 4;
+      sz = 4;
+      break;
    case SLANG_SPEC_MAT2:
-      return 2 * 2;
+      sz = 2 * 4; /* 2 columns (regs) */
+      break;
    case SLANG_SPEC_MAT3:
-      return 3 * 3;
+      sz = 3 * 4;
+      break;
    case SLANG_SPEC_MAT4:
-      return 4 * 4;
+      sz = 4 * 4;
+      break;
+   case SLANG_SPEC_MAT23:
+      sz = 2 * 4; /* 2 columns (regs) */
+      break;
+   case SLANG_SPEC_MAT32:
+      sz = 3 * 4; /* 3 columns (regs) */
+      break;
+   case SLANG_SPEC_MAT24:
+      sz = 2 * 4;
+      break;
+   case SLANG_SPEC_MAT42:
+      sz = 4 * 4; /* 4 columns (regs) */
+      break;
+   case SLANG_SPEC_MAT34:
+      sz = 3 * 4;
+      break;
+   case SLANG_SPEC_MAT43:
+      sz = 4 * 4; /* 4 columns (regs) */
+      break;
    case SLANG_SPEC_SAMPLER1D:
    case SLANG_SPEC_SAMPLER2D:
    case SLANG_SPEC_SAMPLER3D:
@@ -151,16 +194,27 @@ _slang_sizeof_type_specifier(const slang_type_specifier *spec)
    case SLANG_SPEC_SAMPLER2DSHADOW:
    case SLANG_SPEC_SAMPLER2DRECT:
    case SLANG_SPEC_SAMPLER2DRECTSHADOW:
-      return 1; /* a sampler is basically just an integer index */
+      sz = 1; /* a sampler is basically just an integer index */
+      break;
    case SLANG_SPEC_STRUCT:
-      return _slang_field_offset(spec, 0); /* special use */
+      sz = _slang_field_offset(spec, 0); /* special use */
+      if (sz > 4) {
+         sz = (sz + 3) & ~0x3; /* round up to multiple of four */
+      }
+      break;
    case SLANG_SPEC_ARRAY:
-      return _slang_sizeof_type_specifier(spec->_array);
+      sz = _slang_sizeof_type_specifier(spec->_array);
+      break;
    default:
       _mesa_problem(NULL, "Unexpected type in _slang_sizeof_type_specifier()");
-      return 0;
+      sz = 0;
    }
-   return 0;
+
+   if (sz > 4) {
+      /* if size is > 4, it should be a multiple of four */
+      assert((sz & 0x3) == 0);
+   }
+   return sz;
 }
 
 
@@ -406,7 +460,7 @@ static slang_ir_node *
 new_node3(slang_ir_opcode op,
           slang_ir_node *c0, slang_ir_node *c1, slang_ir_node *c2)
 {
-   slang_ir_node *n = (slang_ir_node *) calloc(1, sizeof(slang_ir_node));
+   slang_ir_node *n = (slang_ir_node *) _slang_alloc(sizeof(slang_ir_node));
    if (n) {
       n->Opcode = op;
       n->Children[0] = c0;
@@ -468,13 +522,35 @@ new_float_literal(const float v[4], GLuint size)
    return n;
 }
 
+
+static slang_ir_node *
+new_not(slang_ir_node *n)
+{
+   return new_node1(IR_NOT, n);
+}
+
+
+/**
+ * Inlined subroutine.
+ */
+static slang_ir_node *
+new_inlined_function_call(slang_ir_node *code, slang_label *name)
+{
+   slang_ir_node *n = new_node1(IR_FUNC, code);
+   assert(name);
+   if (n)
+      n->Label = name;
+   return n;
+}
+
+
 /**
  * Unconditional jump.
  */
 static slang_ir_node *
-new_jump(slang_label *dest)
+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->Label = dest;
@@ -505,15 +581,15 @@ new_break(slang_ir_node *loopNode)
 
 
 /**
- * Make new IR_BREAK_IF_TRUE or IR_BREAK_IF_FALSE node.
+ * Make new IR_BREAK_IF_TRUE.
  */
 static slang_ir_node *
-new_break_if(slang_ir_node *loopNode, slang_ir_node *cond, GLboolean breakTrue)
+new_break_if_true(slang_ir_node *loopNode, slang_ir_node *cond)
 {
    slang_ir_node *n;
    assert(loopNode);
    assert(loopNode->Opcode == IR_LOOP);
-   n = new_node1(breakTrue ? IR_BREAK_IF_TRUE : IR_BREAK_IF_FALSE, cond);
+   n = new_node1(IR_BREAK_IF_TRUE, cond);
    if (n) {
       /* insert this node at head of linked list */
       n->List = loopNode->List;
@@ -524,15 +600,15 @@ new_break_if(slang_ir_node *loopNode, slang_ir_node *cond, GLboolean breakTrue)
 
 
 /**
- * Make new IR_CONT_IF_TRUE or IR_CONT_IF_FALSE node.
+ * Make new IR_CONT_IF_TRUE node.
  */
 static slang_ir_node *
-new_cont_if(slang_ir_node *loopNode, slang_ir_node *cond, GLboolean contTrue)
+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(contTrue ? IR_CONT_IF_TRUE : IR_CONT_IF_FALSE, cond);
+   n = new_node1(IR_CONT_IF_TRUE, cond);
    if (n) {
       /* insert this node at head of linked list */
       n->List = loopNode->List;
@@ -607,33 +683,20 @@ _slang_is_noop(const slang_operation *oper)
 
 
 /**
- * Produce inline code for a call to an assembly instruction.
- * XXX Note: children are passed as asm args in-order, not by name!
+ * Recursively search tree for a node of the given type.
  */
 static slang_operation *
-slang_inline_asm_function(slang_assemble_ctx *A,
-                          slang_function *fun, slang_operation *oper)
+_slang_find_node_type(slang_operation *oper, slang_operation_type type)
 {
-   const GLuint numArgs = oper->num_children;
-   const slang_operation *args = oper->children;
    GLuint i;
-   slang_operation *inlined = slang_operation_new(1);
-
-   /*assert(oper->type == SLANG_OPER_CALL);  or vec4_add, etc */
-   /*
-   printf("Inline asm %s\n", (char*) fun->header.a_name);
-   */
-   inlined->type = fun->body->children[0].type;
-   inlined->a_id = fun->body->children[0].a_id;
-   inlined->num_children = numArgs;
-   inlined->children = slang_operation_new(numArgs);
-   inlined->locals->outer_scope = oper->locals->outer_scope;
-
-   for (i = 0; i < numArgs; i++) {
-      slang_operation_copy(inlined->children + i, args + 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 inlined;
+   return NULL;
 }
 
 
@@ -749,7 +812,7 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
          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 */
@@ -797,6 +860,70 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper,
 
 
 
+/**
+ * Produce inline code for a call to an assembly instruction.
+ * This is typically used to compile a call to a built-in function like this:
+ *
+ * vec4 mix(const vec4 x, const vec4 y, const vec4 a)
+ * {
+ *    __asm vec4_lrp __retVal, a, y, x;
+ * }
+ *
+ * We basically translate a SLANG_OPER_CALL into a SLANG_OPER_ASM.
+ */
+static slang_operation *
+slang_inline_asm_function(slang_assemble_ctx *A,
+                          slang_function *fun, slang_operation *oper)
+{
+   const GLuint numArgs = oper->num_children;
+   GLuint i;
+   slang_operation *inlined;
+   const GLboolean haveRetValue = _slang_function_has_return_value(fun);
+   slang_variable **substOld;
+   slang_operation **substNew;
+
+   ASSERT(slang_is_asm_function(fun));
+   ASSERT(fun->param_count == numArgs + haveRetValue);
+
+   /*
+   printf("Inline %s as %s\n",
+          (char*) fun->header.a_name,
+          (char*) fun->body->children[0].a_id);
+   */
+
+   /*
+    * We'll substitute formal params with actual args in the asm call.
+    */
+   substOld = (slang_variable **)
+      _slang_alloc(numArgs * sizeof(slang_variable *));
+   substNew = (slang_operation **)
+      _slang_alloc(numArgs * sizeof(slang_operation *));
+   for (i = 0; i < numArgs; i++) {
+      substOld[i] = fun->parameters->variables[i];
+      substNew[i] = oper->children + i;
+   }
+
+   /* make a copy of the code to inline */
+   inlined = slang_operation_new(1);
+   slang_operation_copy(inlined, &fun->body->children[0]);
+   if (haveRetValue) {
+      /* get rid of the __retVal child */
+      for (i = 0; i < numArgs; i++) {
+         inlined->children[i] = inlined->children[i + 1];
+      }
+      inlined->num_children--;
+   }
+
+   /* now do formal->actual substitutions */
+   slang_substitute(A, inlined, numArgs, substOld, substNew, GL_FALSE);
+
+   _slang_free(substOld);
+   _slang_free(substNew);
+
+   return inlined;
+}
+
+
 /**
  * Inline the given function call operation.
  * Return a new slang_operation that corresponds to the inlined code.
@@ -830,11 +957,11 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
 
    /* allocate temporary arrays */
    paramMode = (ParamMode *)
-      _mesa_calloc(totalArgs * sizeof(ParamMode));
+      _slang_alloc(totalArgs * sizeof(ParamMode));
    substOld = (slang_variable **)
-      _mesa_calloc(totalArgs * sizeof(slang_variable *));
+      _slang_alloc(totalArgs * sizeof(slang_variable *));
    substNew = (slang_operation **)
-      _mesa_calloc(totalArgs * sizeof(slang_operation *));
+      _slang_alloc(totalArgs * sizeof(slang_operation *));
 
 #if 0
    printf("Inline call to %s  (total vars=%d  nparams=%d)\n",
@@ -1035,9 +1162,9 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun,
       }
    }
 
-   _mesa_free(paramMode);
-   _mesa_free(substOld);
-   _mesa_free(substNew);
+   _slang_free(paramMode);
+   _slang_free(substOld);
+   _slang_free(substNew);
 
 #if 0
    printf("Done Inline call to %s  (total vars=%d  nparams=%d)\n",
@@ -1074,12 +1201,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 */
    slang_operation_destruct(oper);
    *oper = *inlined;
-   /* XXX slang_operation_destruct(inlined) ??? */
+   _slang_free(inlined);
 
 #if 0
    assert(inlined->locals);
@@ -1093,7 +1236,6 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun,
 
    /*_slang_label_delete(A->curFuncEndLabel);*/
    A->curFuncEndLabel = prevFuncEndLabel;
-   assert(A->curFuncEndLabel);
 
    return n;
 }
@@ -1119,15 +1261,23 @@ make_writemask(const char *field)
    while (*field) {
       switch (*field) {
       case 'x':
+      case 's':
+      case 'r':
          mask |= WRITEMASK_X;
          break;
       case 'y':
+      case 't':
+      case 'g':
          mask |= WRITEMASK_Y;
          break;
       case 'z':
+      case 'p':
+      case 'b':
          mask |= WRITEMASK_Z;
          break;
       case 'w':
+      case 'q':
+      case 'a':
          mask |= WRITEMASK_W;
          break;
       default:
@@ -1210,7 +1360,7 @@ _slang_gen_asm(slang_assemble_ctx *A, slang_operation *oper,
       n->Store = n0->Store;
       n->Writemask = writemask;
 
-      free(n0);
+      _slang_free(n0);
    }
 
    return n;
@@ -1283,7 +1433,7 @@ _slang_gen_function_call_name(slang_assemble_ctx *A, const char *name,
        * Try adapting the parameters.
        */
       fun = _slang_first_function(A->space.funcs, name);
-      if (!_slang_adapt_call(oper, fun, &A->space, A->atoms, A->log)) {
+      if (!fun || !_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;
       }
@@ -1314,6 +1464,22 @@ _slang_is_constant_cond(const slang_operation *oper, GLboolean *value)
 }
 
 
+/**
+ * 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;
+
+   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 loop code using high-level IR_LOOP instruction
@@ -1326,9 +1492,15 @@ _slang_gen_while(slang_assemble_ctx * A, const slang_operation *oper)
     *    BREAK if !expr (child[0])
     *    body code (child[1])
     */
-   slang_ir_node *prevLoop, *loop, *cond, *breakIf, *body;
+   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);
 
@@ -1343,13 +1515,14 @@ _slang_gen_while(slang_assemble_ctx * A, const slang_operation *oper)
    prevLoop = A->CurLoop;
    A->CurLoop = loop;
 
-   cond = new_cond(_slang_gen_operation(A, &oper->children[0]));
    if (isConst && constTrue) {
       /* while(nonzero constant), no conditional break */
       breakIf = NULL;
    }
    else {
-      breakIf = new_break_if(A->CurLoop, cond, GL_FALSE);
+      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);
@@ -1382,9 +1555,15 @@ _slang_gen_do(slang_assemble_ctx * A, const slang_operation *oper)
     *    tail code:
     *       BREAK if !expr (child[1])
     */
-   slang_ir_node *prevLoop, *loop, *cond;
+   slang_ir_node *prevLoop, *loop;
    GLboolean isConst, constTrue;
 
+   /* 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;
+   }
+
    loop = new_loop(NULL);
 
    /* save old, push new loop */
@@ -1401,8 +1580,9 @@ _slang_gen_do(slang_assemble_ctx * A, const slang_operation *oper)
       loop->Children[1] = NULL; /* no tail code */
    }
    else {
-      cond = new_cond(_slang_gen_operation(A, &oper->children[1]));
-      loop->Children[1] = new_break_if(A->CurLoop, cond, GL_FALSE);
+      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);
    }
 
    /* XXX we should do infinite loop detection, as above */
@@ -1437,8 +1617,8 @@ _slang_gen_for(slang_assemble_ctx * A, const slang_operation *oper)
    prevLoop = A->CurLoop;
    A->CurLoop = loop;
 
-   cond = new_cond(_slang_gen_operation(A, &oper->children[1]));
-   breakIf = new_break_if(A->CurLoop, cond, GL_FALSE);
+   cond = new_cond(new_not(_slang_gen_operation(A, &oper->children[1])));
+   breakIf = new_break_if_true(A->CurLoop, cond);
    body = _slang_gen_operation(A, &oper->children[3]);
    incr = _slang_gen_operation(A, &oper->children[2]);
 
@@ -1475,7 +1655,7 @@ _slang_gen_continue(slang_assemble_ctx * A, const slang_operation *oper)
  * Determine if the given operation is of a specific type.
  */
 static GLboolean
-is_operation_type(const const slang_operation *oper, slang_operation_type type)
+is_operation_type(const slang_operation *oper, slang_operation_type type)
 {
    if (oper->type == type)
       return GL_TRUE;
@@ -1507,6 +1687,12 @@ _slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper)
    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) {
@@ -1524,7 +1710,7 @@ _slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper)
 
    if (is_operation_type(&oper->children[1], SLANG_OPER_BREAK)) {
       /* Special case: generate a conditional break */
-      ifBody = new_break_if(A->CurLoop, cond, GL_TRUE);
+      ifBody = new_break_if_true(A->CurLoop, cond);
       if (haveElseClause) {
          elseBody = _slang_gen_operation(A, &oper->children[2]);
          return new_seq(ifBody, elseBody);
@@ -1533,7 +1719,7 @@ _slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper)
    }
    else if (is_operation_type(&oper->children[1], SLANG_OPER_CONTINUE)) {
       /* Special case: generate a conditional break */
-      ifBody = new_cont_if(A->CurLoop, cond, GL_TRUE);
+      ifBody = new_cont_if_true(A->CurLoop, cond);
       if (haveElseClause) {
          elseBody = _slang_gen_operation(A, &oper->children[2]);
          return new_seq(ifBody, elseBody);
@@ -1554,6 +1740,52 @@ _slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper)
 
 
 
+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.
  */
@@ -1561,7 +1793,7 @@ static slang_ir_node *
 _slang_gen_temporary(GLint size)
 {
    slang_ir_storage *store;
-   slang_ir_node *n;
+   slang_ir_node *n = NULL;
 
    store = _slang_new_ir_storage(PROGRAM_TEMPORARY, -1, size);
    if (store) {
@@ -1570,7 +1802,7 @@ _slang_gen_temporary(GLint size)
          n->Store = store;
       }
       else {
-         free(store);
+         _slang_free(store);
       }
    }
    return n;
@@ -1596,6 +1828,7 @@ _slang_gen_var_decl(slang_assemble_ctx *A, slang_variable *var)
 
       n->Store->File = PROGRAM_TEMPORARY;
       n->Store->Size = _slang_sizeof_type_specifier(&n->Var->type.specifier);
+      A->program->NumTemporaries++;
       assert(n->Store->Size > 0);
    }
    return n;
@@ -1680,11 +1913,6 @@ _slang_gen_logical_and(slang_assemble_ctx *A, slang_operation *oper)
    select->children[2].literal_size = 1;
 
    n = _slang_gen_select(A, select);
-
-   /* xxx wrong */
-   free(select->children);
-   free(select);
-
    return n;
 }
 
@@ -1711,11 +1939,6 @@ _slang_gen_logical_or(slang_assemble_ctx *A, slang_operation *oper)
    slang_operation_copy(&select->children[2], &oper->children[1]);
 
    n = _slang_gen_select(A, select);
-
-   /* xxx wrong */
-   free(select->children);
-   free(select);
-
    return n;
 }
 
@@ -1744,22 +1967,7 @@ _slang_gen_return(slang_assemble_ctx * A, slang_operation *oper)
    }
 
    if (!haveReturnValue) {
-      /* Convert from:
-       *   return;
-       * To:
-       *   goto __endOfFunction;
-       */
-      slang_ir_node *n;
-      slang_operation gotoOp;
-      slang_operation_construct(&gotoOp);
-      gotoOp.type = SLANG_OPER_GOTO;
-      gotoOp.label = A->curFuncEndLabel;
-      assert(gotoOp.label);
-
-      n = _slang_gen_operation(A, &gotoOp);
-      /* destroy temp code */
-      slang_operation_destruct(&gotoOp);
-      return n;
+      return new_return(A->curFuncEndLabel);
    }
    else {
       /*
@@ -1767,9 +1975,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;
 
@@ -1787,17 +1995,8 @@ _slang_gen_return(slang_assemble_ctx * A, slang_operation *oper)
       }
 #endif
 
-      block = slang_operation_new(1);
-      block->type = SLANG_OPER_BLOCK_NO_NEW_SCOPE;
-      assert(block->locals);
-      block->locals->outer_scope = oper->locals->outer_scope;
-      block->num_children = 2;
-      block->children = slang_operation_new(2);
-
-      /* child[0]: __retVal = expr; */
-      assign = &block->children[0];
+      assign = slang_operation_new(1);
       assign->type = SLANG_OPER_ASSIGN;
-      assign->locals->outer_scope = block->locals;
       assign->num_children = 2;
       assign->children = slang_operation_new(2);
       /* lhs (__retVal) */
@@ -1808,22 +2007,11 @@ _slang_gen_return(slang_assemble_ctx * A, slang_operation *oper)
       /* 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->curFuncEndLabel);
-      /* XXX don't call function? */
-      jump->label = A->curFuncEndLabel;
-      assert(jump->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;
    }
 }
@@ -2037,6 +2225,26 @@ _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) {
+      /* 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)
@@ -2061,7 +2269,8 @@ _slang_gen_assignment(slang_assemble_ctx * A, slang_operation *oper)
              lhs->Store->File != PROGRAM_TEMPORARY &&
              lhs->Store->File != PROGRAM_VARYING &&
              lhs->Store->File != PROGRAM_UNDEFINED) {
-            slang_info_log_error(A->log, "Assignment to read-only variable");
+            slang_info_log_error(A->log,
+                                 "illegal assignment to read-only l-value");
             return NULL;
          }
       }
@@ -2120,7 +2329,8 @@ _slang_gen_field(slang_assemble_ctx * A, slang_operation *oper)
          n = _slang_gen_swizzle(n, swizzle);
       return n;
    }
-   else if (ti.spec.type == SLANG_SPEC_FLOAT) {
+   else if (   ti.spec.type == SLANG_SPEC_FLOAT
+            || ti.spec.type == SLANG_SPEC_INT) {
       const GLuint rows = 1;
       slang_swizzle swz;
       slang_ir_node *n;
@@ -2143,15 +2353,16 @@ _slang_gen_field(slang_assemble_ctx * A, slang_operation *oper)
       /* oper->a_id is the field name */
       slang_ir_node *base, *n;
       slang_typeinfo field_ti;
-      GLint fieldSize, fieldOffset;
+      GLint fieldSize, fieldOffset = -1;
       /* 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 (fieldSize > 0)
+         fieldOffset = _slang_field_offset(&ti.spec, oper->a_id);
 
-      if (fieldOffset < 0) {
+      if (fieldSize == 0 || fieldOffset < 0) {
          slang_info_log_error(A->log,
                               "\"%s\" is not a member of struct \"%s\"",
                               (char *) oper->a_id,
@@ -2402,11 +2613,13 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
    case SLANG_OPER_BREAK:
       if (!A->CurLoop) {
          slang_info_log_error(A->log, "'break' not in loop");
+         return NULL;
       }
       return new_break(A->CurLoop);
    case SLANG_OPER_CONTINUE:
       if (!A->CurLoop) {
          slang_info_log_error(A->log, "'continue' not in loop");
+         return NULL;
       }
       return _slang_gen_continue(A, oper);
    case SLANG_OPER_DISCARD:
@@ -2523,20 +2736,9 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *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;
-      }
+      return _slang_gen_xor(A, oper);
    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;
-      }
-
+      return _slang_gen_not(A, oper);
    case SLANG_OPER_SELECT:  /* b ? x : y */
       {
         slang_ir_node *n;
@@ -2552,8 +2754,6 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
                                            oper, NULL);
    case SLANG_OPER_RETURN:
       return _slang_gen_return(A, oper);
-   case SLANG_OPER_GOTO:
-      return new_jump(oper->label);
    case SLANG_OPER_LABEL:
       return new_label(oper->label);
    case SLANG_OPER_IDENTIFIER:
@@ -2600,6 +2800,7 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper)
         return n;
       }
 
+   case SLANG_OPER_INLINED_CALL:
    case SLANG_OPER_SEQUENCE:
       {
          slang_ir_node *tree = NULL;
@@ -2608,6 +2809,9 @@ _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;
       }
 
@@ -2647,11 +2851,10 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
    struct gl_program *prog = A->program;
    const char *varName = (char *) var->a_name;
    GLboolean success = GL_TRUE;
-   GLint texIndex;
    slang_ir_storage *store = NULL;
    int dbg = 0;
-
-   texIndex = sampler_to_texture_index(var->type.specifier.type);
+   const GLenum datatype = _slang_gltype_from_specifier(&var->type.specifier);
+   const GLint texIndex = sampler_to_texture_index(var->type.specifier.type);
 
    if (texIndex != -1) {
       /* Texture sampler:
@@ -2659,7 +2862,8 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
        * store->Index = sampler uniform location
        * store->Size = texture type index (1D, 2D, 3D, cube, etc)
        */
-      GLint samplerUniform = _mesa_add_sampler(prog->Parameters, varName);
+      GLint samplerUniform
+         = _mesa_add_sampler(prog->Parameters, varName, datatype);
       store = _slang_new_ir_storage(PROGRAM_SAMPLER, samplerUniform, texIndex);
       if (dbg) printf("SAMPLER ");
    }
@@ -2669,8 +2873,35 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
                          * MAX2(var->array_len, 1);
       if (prog) {
          /* user-defined uniform */
-         GLint uniformLoc = _mesa_add_uniform(prog->Parameters, varName, size);
-         store = _slang_new_ir_storage(PROGRAM_UNIFORM, uniformLoc, size);
+         if (datatype == GL_NONE) {
+            if (var->type.specifier.type == SLANG_SPEC_STRUCT) {
+               _mesa_problem(NULL, "user-declared uniform structs not supported yet");
+               /* XXX what we need to do is unroll the struct into its
+                * basic types, creating a uniform variable for each.
+                * For example:
+                * struct foo {
+                *   vec3 a;
+                *   vec4 b;
+                * };
+                * uniform foo f;
+                *
+                * Should produce uniforms:
+                * "f.a"  (GL_FLOAT_VEC3)
+                * "f.b"  (GL_FLOAT_VEC4)
+                */
+            }
+            else {
+               slang_info_log_error(A->log,
+                                    "invalid datatype for uniform variable %s",
+                                    (char *) var->a_name);
+            }
+            return GL_FALSE;
+         }
+         else {
+            GLint uniformLoc = _mesa_add_uniform(prog->Parameters, varName,
+                                                 size, datatype);
+            store = _slang_new_ir_storage(PROGRAM_UNIFORM, uniformLoc, size);
+         }
       }
       else {
          /* pre-defined uniform, like gl_ModelviewMatrix */
@@ -2734,7 +2965,7 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
       if (dbg) printf("ATTRIB ");
    }
    else if (var->type.qualifier == SLANG_QUAL_FIXEDINPUT) {
-      GLuint swizzle;
+      GLuint swizzle = SWIZZLE_XYZW; /* silence compiler warning */
       GLint index = _slang_input_index(varName, GL_FRAGMENT_PROGRAM_ARB,
                                        &swizzle);
       GLint size = 4; /* XXX? */
@@ -2749,9 +2980,9 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var,
          store = _slang_new_ir_storage(PROGRAM_OUTPUT, index, size);
       }
       else {
-         assert(type == SLANG_UNIT_FRAGMENT_BUILTIN);
          GLint index = _slang_output_index(varName, GL_FRAGMENT_PROGRAM_ARB);
          GLint size = 4; /* XXX? */
+         assert(type == SLANG_UNIT_FRAGMENT_BUILTIN);
          store = _slang_new_ir_storage(PROGRAM_OUTPUT, index, size);
       }
       if (dbg) printf("OUTPUT ");
@@ -2817,6 +3048,23 @@ _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun)
       /* 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 */
    }