glsl: Implement constant expr evaluation for bit-shift ops
[mesa.git] / src / glsl / ir_constant_expression.cpp
index 580ef1d4faec445bc466aee646a2cb78067daddf..b250a164a1a31f441e3d266e74853ae30118d8d1 100644 (file)
@@ -34,7 +34,7 @@
  */
 
 #include <math.h>
-#include "main/macros.h"
+#include "main/core.h" /* for MAX2, MIN2, CLAMP */
 #include "ir.h"
 #include "ir_visitor.h"
 #include "glsl_types.h"
@@ -89,9 +89,9 @@ ir_expression::constant_expression_value()
    if (op[0]->type->is_array()) {
       assert(op[1] != NULL && op[1]->type->is_array());
       switch (this->operation) {
-      case ir_binop_equal:
+      case ir_binop_all_equal:
         return new(ctx) ir_constant(op[0]->has_value(op[1]));
-      case ir_binop_nequal:
+      case ir_binop_any_nequal:
         return new(ctx) ir_constant(!op[0]->has_value(op[1]));
       default:
         break;
@@ -100,6 +100,21 @@ ir_expression::constant_expression_value()
    }
 
    switch (this->operation) {
+   case ir_unop_bit_not:
+       switch (op[0]->type->base_type) {
+       case GLSL_TYPE_INT:
+           for (unsigned c = 0; c < components; c++)
+               data.i[c] = ~ op[0]->value.i[c];
+           break;
+       case GLSL_TYPE_UINT:
+           for (unsigned c = 0; c < components; c++)
+               data.u[c] = ~ op[0]->value.u[c];
+           break;
+       default:
+           assert(0);
+       }
+       break;
+
    case ir_unop_logic_not:
       assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
       for (unsigned c = 0; c < op[0]->type->components(); c++)
@@ -149,6 +164,15 @@ ir_expression::constant_expression_value()
       }
       break;
 
+   case ir_unop_any:
+      assert(op[0]->type->is_boolean());
+      data.b[0] = false;
+      for (unsigned c = 0; c < op[0]->type->components(); c++) {
+        if (op[0]->value.b[c])
+           data.b[0] = true;
+      }
+      break;
+
    case ir_unop_trunc:
       assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
       for (unsigned c = 0; c < op[0]->type->components(); c++) {
@@ -613,14 +637,97 @@ ir_expression::constant_expression_value()
         assert(0);
       }
       break;
-
    case ir_binop_equal:
-      data.b[0] = op[0]->has_value(op[1]);
+      assert(op[0]->type == op[1]->type);
+      for (unsigned c = 0; c < components; c++) {
+        switch (op[0]->type->base_type) {
+        case GLSL_TYPE_UINT:
+           data.b[c] = op[0]->value.u[c] == op[1]->value.u[c];
+           break;
+        case GLSL_TYPE_INT:
+           data.b[c] = op[0]->value.i[c] == op[1]->value.i[c];
+           break;
+        case GLSL_TYPE_FLOAT:
+           data.b[c] = op[0]->value.f[c] == op[1]->value.f[c];
+           break;
+        default:
+           assert(0);
+        }
+      }
       break;
    case ir_binop_nequal:
+      assert(op[0]->type != op[1]->type);
+      for (unsigned c = 0; c < components; c++) {
+        switch (op[0]->type->base_type) {
+        case GLSL_TYPE_UINT:
+           data.b[c] = op[0]->value.u[c] != op[1]->value.u[c];
+           break;
+        case GLSL_TYPE_INT:
+           data.b[c] = op[0]->value.i[c] != op[1]->value.i[c];
+           break;
+        case GLSL_TYPE_FLOAT:
+           data.b[c] = op[0]->value.f[c] != op[1]->value.f[c];
+           break;
+        default:
+           assert(0);
+        }
+      }
+      break;
+   case ir_binop_all_equal:
+      data.b[0] = op[0]->has_value(op[1]);
+      break;
+   case ir_binop_any_nequal:
       data.b[0] = !op[0]->has_value(op[1]);
       break;
 
+   case ir_binop_lshift:
+      for (unsigned c = 0, c0 = 0, c1 = 0;
+           c < components;
+           c0 += c0_inc, c1 += c1_inc, c++) {
+
+          if (op[0]->type->base_type == GLSL_TYPE_INT &&
+              op[1]->type->base_type == GLSL_TYPE_INT) {
+              data.i[c] = op[0]->value.i[c0] << op[1]->value.i[c1];
+
+          } else if (op[0]->type->base_type == GLSL_TYPE_INT &&
+                     op[1]->type->base_type == GLSL_TYPE_UINT) {
+              data.i[c] = op[0]->value.i[c0] << op[1]->value.u[c1];
+
+          } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+                     op[1]->type->base_type == GLSL_TYPE_INT) {
+              data.u[c] = op[0]->value.u[c0] << op[1]->value.i[c1];
+
+          } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+                     op[1]->type->base_type == GLSL_TYPE_UINT) {
+              data.u[c] = op[0]->value.u[c0] << op[1]->value.u[c1];
+          }
+      }
+      break;
+
+   case ir_binop_rshift:
+       for (unsigned c = 0, c0 = 0, c1 = 0;
+            c < components;
+            c0 += c0_inc, c1 += c1_inc, c++) {
+
+           if (op[0]->type->base_type == GLSL_TYPE_INT &&
+               op[1]->type->base_type == GLSL_TYPE_INT) {
+               data.i[c] = op[0]->value.i[c0] >> op[1]->value.i[c1];
+
+           } else if (op[0]->type->base_type == GLSL_TYPE_INT &&
+                      op[1]->type->base_type == GLSL_TYPE_UINT) {
+               data.i[c] = op[0]->value.i[c0] >> op[1]->value.u[c1];
+
+           } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+                      op[1]->type->base_type == GLSL_TYPE_INT) {
+               data.u[c] = op[0]->value.u[c0] >> op[1]->value.i[c1];
+
+           } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+                      op[1]->type->base_type == GLSL_TYPE_UINT) {
+               data.u[c] = op[0]->value.u[c0] >> op[1]->value.u[c1];
+           }
+       }
+       break;
+
    default:
       /* FINISHME: Should handle all expression types. */
       return NULL;
@@ -644,7 +751,7 @@ ir_swizzle::constant_expression_value()
    ir_constant *v = this->val->constant_expression_value();
 
    if (v != NULL) {
-      ir_constant_data data;
+      ir_constant_data data = { { 0 } };
 
       const unsigned swiz_idx[4] = {
         this->mask.x, this->mask.y, this->mask.z, this->mask.w
@@ -674,18 +781,27 @@ ir_dereference_variable::constant_expression_value()
    if (!var)
       return NULL;
 
-   return var->constant_value ? var->constant_value->clone(NULL) : NULL;
+   /* The constant_value of a uniform variable is its initializer,
+    * not the lifetime constant value of the uniform.
+    */
+   if (var->mode == ir_var_uniform)
+      return NULL;
+
+   if (!var->constant_value)
+      return NULL;
+
+   return var->constant_value->clone(talloc_parent(var), NULL);
 }
 
 
 ir_constant *
 ir_dereference_array::constant_expression_value()
 {
-   void *ctx = talloc_parent(this);
    ir_constant *array = this->array->constant_expression_value();
    ir_constant *idx = this->array_index->constant_expression_value();
 
    if ((array != NULL) && (idx != NULL)) {
+      void *ctx = talloc_parent(this);
       if (array->type->is_matrix()) {
         /* Array access of a matrix results in a vector.
          */
@@ -698,7 +814,7 @@ ir_dereference_array::constant_expression_value()
          */
         const unsigned mat_idx = column * column_type->vector_elements;
 
-        ir_constant_data data;
+        ir_constant_data data = { { 0 } };
 
         switch (column_type->base_type) {
         case GLSL_TYPE_UINT:
@@ -726,7 +842,7 @@ ir_dereference_array::constant_expression_value()
         return new(ctx) ir_constant(array, component);
       } else {
         const unsigned index = idx->value.u[0];
-        return array->get_array_element(index)->clone(NULL);
+        return array->get_array_element(index)->clone(ctx, NULL);
       }
    }
    return NULL;
@@ -767,7 +883,7 @@ ir_call::constant_expression_value()
     * "Function calls to user-defined functions (non-built-in functions)
     *  cannot be used to form constant expressions."
     */
-   if (!this->callee->is_built_in)
+   if (!this->callee->is_builtin)
       return NULL;
 
    unsigned num_parameters = 0;
@@ -836,7 +952,30 @@ ir_call::constant_expression_value()
    } else if (strcmp(callee, "ceil") == 0) {
       expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL);
    } else if (strcmp(callee, "clamp") == 0) {
-      return NULL; /* FINISHME: implement this */
+      assert(num_parameters == 3);
+      unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1;
+      unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1;
+      for (unsigned c = 0, c1 = 0, c2 = 0;
+          c < op[0]->type->components();
+          c1 += c1_inc, c2 += c2_inc, c++) {
+
+        switch (op[0]->type->base_type) {
+        case GLSL_TYPE_UINT:
+           data.u[c] = CLAMP(op[0]->value.u[c], op[1]->value.u[c1],
+                             op[2]->value.u[c2]);
+           break;
+        case GLSL_TYPE_INT:
+           data.i[c] = CLAMP(op[0]->value.i[c], op[1]->value.i[c1],
+                             op[2]->value.i[c2]);
+           break;
+        case GLSL_TYPE_FLOAT:
+           data.f[c] = CLAMP(op[0]->value.f[c], op[1]->value.f[c1],
+                             op[2]->value.f[c2]);
+           break;
+        default:
+           assert(!"Should not get here.");
+        }
+      }
    } else if (strcmp(callee, "cos") == 0) {
       expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL);
    } else if (strcmp(callee, "cosh") == 0) {
@@ -872,6 +1011,9 @@ ir_call::constant_expression_value()
         case GLSL_TYPE_FLOAT:
            data.b[c] = op[0]->value.f[c] == op[1]->value.f[c];
            break;
+        case GLSL_TYPE_BOOL:
+           data.b[c] = op[0]->value.b[c] == op[1]->value.b[c];
+           break;
         default:
            assert(!"Should not get here.");
         }
@@ -881,7 +1023,10 @@ ir_call::constant_expression_value()
    } else if (strcmp(callee, "exp2") == 0) {
       expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL);
    } else if (strcmp(callee, "faceforward") == 0) {
-      return NULL; /* FINISHME: implement this */
+      if (dot(op[2], op[1]) < 0)
+        return op[0];
+      for (unsigned c = 0; c < op[0]->type->components(); c++)
+        data.f[c] = -op[0]->value.f[c];
    } else if (strcmp(callee, "floor") == 0) {
       expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL);
    } else if (strcmp(callee, "fract") == 0) {
@@ -973,7 +1118,19 @@ ir_call::constant_expression_value()
    } else if (strcmp(callee, "min") == 0) {
       expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]);
    } else if (strcmp(callee, "mix") == 0) {
-      return NULL; /* FINISHME: implement this */
+      assert(op[0]->type->is_float() && op[1]->type->is_float());
+      if (op[2]->type->is_float()) {
+        unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1;
+        unsigned components = op[0]->type->components();
+        for (unsigned c = 0, c2 = 0; c < components; c2 += c2_inc, c++) {
+           data.f[c] = op[0]->value.f[c] * (1 - op[2]->value.f[c2]) +
+                       op[1]->value.f[c] * op[2]->value.f[c2];
+        }
+      } else {
+        assert(op[2]->type->is_boolean());
+        for (unsigned c = 0; c < op[0]->type->components(); c++)
+           data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c];
+      }
    } else if (strcmp(callee, "mod") == 0) {
       expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]);
    } else if (strcmp(callee, "normalize") == 0) {
@@ -1000,12 +1157,22 @@ ir_call::constant_expression_value()
         case GLSL_TYPE_FLOAT:
            data.b[c] = op[0]->value.f[c] != op[1]->value.f[c];
            break;
+        case GLSL_TYPE_BOOL:
+           data.b[c] = op[0]->value.b[c] != op[1]->value.b[c];
+           break;
         default:
            assert(!"Should not get here.");
         }
       }
    } else if (strcmp(callee, "outerProduct") == 0) {
-      return NULL; /* FINISHME: implement this */
+      assert(op[0]->type->is_vector() && op[1]->type->is_vector());
+      const unsigned m = op[0]->type->vector_elements;
+      const unsigned n = op[1]->type->vector_elements;
+      for (unsigned j = 0; j < n; j++) {
+        for (unsigned i = 0; i < m; i++) {
+           data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j];
+        }
+      }
    } else if (strcmp(callee, "pow") == 0) {
       expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]);
    } else if (strcmp(callee, "radians") == 0) {
@@ -1038,11 +1205,29 @@ ir_call::constant_expression_value()
       for (unsigned c = 0; c < op[0]->type->components(); c++)
         data.f[c] = sinhf(op[0]->value.f[c]);
    } else if (strcmp(callee, "smoothstep") == 0) {
-      return NULL; /* FINISHME: implement this */
+      assert(num_parameters == 3);
+      assert(op[1]->type == op[0]->type);
+      unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1;
+      for (unsigned c = 0, e = 0; c < type->components(); e += edge_inc, c++) {
+        const float edge0 = op[0]->value.f[e];
+        const float edge1 = op[1]->value.f[e];
+        if (edge0 == edge1) {
+           data.f[c] = 0.0; /* Avoid a crash - results are undefined anyway */
+        } else {
+           const float numerator = op[2]->value.f[c] - edge0;
+           const float denominator = edge1 - edge0;
+           const float t = CLAMP(numerator/denominator, 0, 1);
+           data.f[c] = t * t * (3 - 2 * t);
+        }
+      }
    } else if (strcmp(callee, "sqrt") == 0) {
       expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL);
    } else if (strcmp(callee, "step") == 0) {
-      return NULL; /* FINISHME: implement this */
+      assert(op[0]->type->is_float() && op[1]->type->is_float());
+      /* op[0] (edge) may be either a scalar or a vector */
+      const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1;
+      for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++)
+        data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0 : 1.0;
    } else if (strcmp(callee, "tan") == 0) {
       assert(op[0]->type->is_float());
       for (unsigned c = 0; c < op[0]->type->components(); c++)
@@ -1052,7 +1237,14 @@ ir_call::constant_expression_value()
       for (unsigned c = 0; c < op[0]->type->components(); c++)
         data.f[c] = tanhf(op[0]->value.f[c]);
    } else if (strcmp(callee, "transpose") == 0) {
-      return NULL; /* FINISHME: implement this */
+      assert(op[0]->type->is_matrix());
+      const unsigned n = op[0]->type->vector_elements;
+      const unsigned m = op[0]->type->matrix_columns;
+      for (unsigned j = 0; j < m; j++) {
+        for (unsigned i = 0; i < n; i++) {
+           data.f[m*i+j] += op[0]->value.f[i+n*j];
+        }
+      }
    } else {
       /* Unsupported builtin - some are not allowed in constant expressions. */
       return NULL;