glsl: Change built-in constant expression evaluation to run the IR.
authorOlivier Galibert <galibert@pobox.com>
Wed, 2 May 2012 21:11:42 +0000 (23:11 +0200)
committerKenneth Graunke <kenneth@whitecape.org>
Tue, 8 May 2012 19:55:52 +0000 (12:55 -0700)
This removes code duplication with
ir_expression::constant_expression_value and builtins/ir/*.

Signed-off-by: Olivier Galibert <galibert@pobox.com>
Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
src/glsl/ir.h
src/glsl/ir_constant_expression.cpp

index 92be063747f76791677f540dd809ed1c9f9751a6..9c7961ab92c8642bb2b481cce3177767e1717ca8 100644 (file)
@@ -576,6 +576,21 @@ private:
    const ir_function_signature *origin;
 
    friend class ir_function;
+
+   /**
+    * Helper function to run a list of instructions for constant
+    * expression evaluation.
+    *
+    * The hash table represents the values of the visible variables.
+    * There are no scoping issues because the table is indexed on
+    * ir_variable pointers, not variable names.
+    *
+    * Returns false if the expression is not constant, true otherwise,
+    * and the value in *result if result is non-NULL.
+    */
+   bool constant_expression_evaluate_expression_list(const struct exec_list &body,
+                                                    struct hash_table *variable_context,
+                                                    ir_constant **result);
 };
 
 
index 418019abb632ef8d92da92a016fea439e422e93b..08a33285b3a065253fcb0f6fd8bc3ad058663284 100644 (file)
@@ -1128,6 +1128,115 @@ ir_call::constant_expression_value(struct hash_table *variable_context)
 }
 
 
+bool ir_function_signature::constant_expression_evaluate_expression_list(const struct exec_list &body,
+                                                                        struct hash_table *variable_context,
+                                                                        ir_constant **result)
+{
+   foreach_list(n, &body) {
+      ir_instruction *inst = (ir_instruction *)n;
+      switch(inst->ir_type) {
+
+        /* (declare () type symbol) */
+      case ir_type_variable: {
+        ir_variable *var = inst->as_variable();
+        hash_table_insert(variable_context, ir_constant::zero(this, var->type), var);
+        break;
+      }
+
+        /* (assign [condition] (write-mask) (ref) (value)) */
+      case ir_type_assignment: {
+        ir_assignment *asg = inst->as_assignment();
+        if (asg->condition) {
+           ir_constant *cond = asg->condition->constant_expression_value(variable_context);
+           if (!cond)
+              return false;
+           if (!cond->get_bool_component(0))
+              break;
+        }
+
+        ir_constant *store = NULL;
+        int offset = 0;
+        asg->lhs->constant_referenced(variable_context, store, offset);
+
+        if (!store)
+           return false;
+
+        ir_constant *value = asg->rhs->constant_expression_value(variable_context);
+
+        if (!value)
+           return false;
+
+        store->copy_masked_offset(value, offset, asg->write_mask);
+        break;
+      }
+
+        /* (return (expression)) */
+      case ir_type_return:
+        assert (result);
+        *result = inst->as_return()->value->constant_expression_value(variable_context);
+        return *result != NULL;
+
+        /* (call name (ref) (params))*/
+      case ir_type_call: {
+        ir_call *call = inst->as_call();
+
+        /* Just say no to void functions in constant expressions.  We
+         * don't need them at that point.
+         */
+
+        if (!call->return_deref)
+           return false;
+
+        ir_constant *store = NULL;
+        int offset = 0;
+        call->return_deref->constant_referenced(variable_context, store, offset);
+
+        if (!store)
+           return false;
+
+        ir_constant *value = call->constant_expression_value(variable_context);
+
+        if(!value)
+           return false;
+
+        store->copy_offset(value, offset);
+        break;
+      }
+
+        /* (if condition (then-instructions) (else-instructions)) */
+      case ir_type_if: {
+        ir_if *iif = inst->as_if();
+
+        ir_constant *cond = iif->condition->constant_expression_value(variable_context);
+        if (!cond || !cond->type->is_boolean())
+           return false;
+
+        exec_list &branch = cond->get_bool_component(0) ? iif->then_instructions : iif->else_instructions;
+
+        *result = NULL;
+        if (!constant_expression_evaluate_expression_list(branch, variable_context, result))
+           return false;
+
+        /* If there was a return in the branch chosen, drop out now. */
+        if (*result)
+           return true;
+
+        break;
+      }
+
+        /* Every other expression type, we drop out. */
+      default:
+        return false;
+      }
+   }
+
+   /* Reaching the end of the block is not an error condition */
+   if (result)
+      *result = NULL;
+
+   return true;
+}
+
 ir_constant *
 ir_function_signature::constant_expression_value(exec_list *actual_parameters, struct hash_table *variable_context)
 {
@@ -1142,396 +1251,48 @@ ir_function_signature::constant_expression_value(exec_list *actual_parameters, s
    if (!this->is_builtin)
       return NULL;
 
-   unsigned num_parameters = 0;
+   /*
+    * Of the builtin functions, only the texture lookups and the noise
+    * ones must not be used in constant expressions.  They all include
+    * specific opcodes so they don't need to be special-cased at this
+    * point.
+    */
+
+   /* Initialize the table of dereferencable names with the function
+    * parameters.  Verify their const-ness on the way.
+    *
+    * We expect the correctness of the number of parameters to have
+    * been checked earlier.
+    */
+   hash_table *deref_hash = hash_table_ctor(8, hash_table_pointer_hash,
+                                           hash_table_pointer_compare);
+
+   /* If "origin" is non-NULL, then the function body is there.  So we
+    * have to use the variable objects from the object with the body,
+    * but the parameter instanciation on the current object.
+    */
+   const exec_node *parameter_info = origin ? origin->parameters.head : parameters.head;
 
-   /* Check if all parameters are constant */
-   ir_constant *op[3];
    foreach_list(n, actual_parameters) {
       ir_constant *constant = ((ir_rvalue *) n)->constant_expression_value(variable_context);
       if (constant == NULL)
         return NULL;
 
-      op[num_parameters] = constant;
+      ir_variable *var = (ir_variable *)parameter_info;
+      hash_table_insert(deref_hash, constant, var);
 
-      assert(num_parameters < 3);
-      num_parameters++;
+      parameter_info = parameter_info->next;
    }
 
-   /* Individual cases below can either:
-    * - Assign "expr" a new ir_expression to evaluate (for basic opcodes)
-    * - Fill "data" with appopriate constant data
-    * - Return an ir_constant directly.
-    */
-   void *mem_ctx = ralloc_parent(this);
-   ir_expression *expr = NULL;
-
-   ir_constant_data data;
-   memset(&data, 0, sizeof(data));
-
-   const char *callee = this->function_name();
-   if (strcmp(callee, "abs") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL);
-   } else if (strcmp(callee, "all") == 0) {
-      assert(op[0]->type->is_boolean());
-      for (unsigned c = 0; c < op[0]->type->components(); c++) {
-        if (!op[0]->value.b[c])
-           return new(mem_ctx) ir_constant(false);
-      }
-      return new(mem_ctx) ir_constant(true);
-   } else if (strcmp(callee, "any") == 0) {
-      assert(op[0]->type->is_boolean());
-      for (unsigned c = 0; c < op[0]->type->components(); c++) {
-        if (op[0]->value.b[c])
-           return new(mem_ctx) ir_constant(true);
-      }
-      return new(mem_ctx) ir_constant(false);
-   } else if (strcmp(callee, "acos") == 0) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = acosf(op[0]->value.f[c]);
-   } else if (strcmp(callee, "acosh") == 0) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = acoshf(op[0]->value.f[c]);
-   } else if (strcmp(callee, "asin") == 0) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = asinf(op[0]->value.f[c]);
-   } else if (strcmp(callee, "asinh") == 0) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = asinhf(op[0]->value.f[c]);
-   } else if (strcmp(callee, "atan") == 0) {
-      assert(op[0]->type->is_float());
-      if (num_parameters == 2) {
-        assert(op[1]->type->is_float());
-        for (unsigned c = 0; c < op[0]->type->components(); c++)
-           data.f[c] = atan2f(op[0]->value.f[c], op[1]->value.f[c]);
-      } else {
-        for (unsigned c = 0; c < op[0]->type->components(); c++)
-           data.f[c] = atanf(op[0]->value.f[c]);
-      }
-   } else if (strcmp(callee, "atanh") == 0) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = atanhf(op[0]->value.f[c]);
-   } else if (strcmp(callee, "dFdx") == 0 || strcmp(callee, "dFdy") == 0) {
-      return ir_constant::zero(mem_ctx, type);
-   } 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) {
-      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++) {
+   ir_constant *result = NULL;
 
-        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) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = coshf(op[0]->value.f[c]);
-   } else if (strcmp(callee, "cross") == 0) {
-      assert(op[0]->type == glsl_type::vec3_type);
-      assert(op[1]->type == glsl_type::vec3_type);
-      data.f[0] = (op[0]->value.f[1] * op[1]->value.f[2] -
-                  op[1]->value.f[1] * op[0]->value.f[2]);
-      data.f[1] = (op[0]->value.f[2] * op[1]->value.f[0] -
-                  op[1]->value.f[2] * op[0]->value.f[0]);
-      data.f[2] = (op[0]->value.f[0] * op[1]->value.f[1] -
-                  op[1]->value.f[0] * op[0]->value.f[1]);
-   } else if (strcmp(callee, "degrees") == 0) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = 180.0F / M_PI * op[0]->value.f[c];
-   } else if (strcmp(callee, "distance") == 0) {
-      assert(op[0]->type->is_float() && op[1]->type->is_float());
-      float length_squared = 0.0;
-      for (unsigned c = 0; c < op[0]->type->components(); c++) {
-        float t = op[0]->value.f[c] - op[1]->value.f[c];
-        length_squared += t * t;
-      }
-      return new(mem_ctx) ir_constant(sqrtf(length_squared));
-   } else if (strcmp(callee, "dot") == 0) {
-      return new(mem_ctx) ir_constant(dot(op[0], op[1]));
-   } else if (strcmp(callee, "equal") == 0) {
-      assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
-      for (unsigned c = 0; c < op[0]->type->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;
-        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, "exp") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL);
-   } 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) {
-      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) {
-      expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL);
-   } else if (strcmp(callee, "fwidth") == 0) {
-      return ir_constant::zero(mem_ctx, type);
-   } else if (strcmp(callee, "greaterThan") == 0) {
-      assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
-      for (unsigned c = 0; c < op[0]->type->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(!"Should not get here.");
-        }
-      }
-   } else if (strcmp(callee, "greaterThanEqual") == 0) {
-      assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
-      for (unsigned c = 0; c < op[0]->type->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(!"Should not get here.");
-        }
-      }
-   } else if (strcmp(callee, "inversesqrt") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL);
-   } else if (strcmp(callee, "length") == 0) {
-      return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0])));
-   } else if (strcmp(callee, "lessThan") == 0) {
-      assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
-      for (unsigned c = 0; c < op[0]->type->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(!"Should not get here.");
-        }
-      }
-   } else if (strcmp(callee, "lessThanEqual") == 0) {
-      assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
-      for (unsigned c = 0; c < op[0]->type->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(!"Should not get here.");
-        }
-      }
-   } else if (strcmp(callee, "log") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL);
-   } else if (strcmp(callee, "log2") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL);
-   } else if (strcmp(callee, "matrixCompMult") == 0) {
-      assert(op[0]->type->is_float() && op[1]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = op[0]->value.f[c] * op[1]->value.f[c];
-   } else if (strcmp(callee, "max") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]);
-   } 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) {
-      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) {
-      assert(op[0]->type->is_float());
-      float length = sqrtf(dot(op[0], op[0]));
-
-      if (length == 0)
-        return ir_constant::zero(mem_ctx, type);
-
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = op[0]->value.f[c] / length;
-   } else if (strcmp(callee, "not") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL);
-   } else if (strcmp(callee, "notEqual") == 0) {
-      assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
-      for (unsigned c = 0; c < op[0]->type->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;
-        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) {
-      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) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = M_PI / 180.0F * op[0]->value.f[c];
-   } else if (strcmp(callee, "reflect") == 0) {
-      assert(op[0]->type->is_float());
-      float dot_NI = dot(op[1], op[0]);
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c];
-   } else if (strcmp(callee, "refract") == 0) {
-      const float eta = op[2]->value.f[0];
-      const float dot_NI = dot(op[1], op[0]);
-      const float k = 1.0F - eta * eta * (1.0F - dot_NI * dot_NI);
-      if (k < 0.0) {
-        return ir_constant::zero(mem_ctx, type);
-      } else {
-        for (unsigned c = 0; c < type->components(); c++) {
-           data.f[c] = eta * op[0]->value.f[c] - (eta * dot_NI + sqrtf(k))
-                           * op[1]->value.f[c];
-        }
-      }
-   } else if (strcmp(callee, "round") == 0 ||
-             strcmp(callee, "roundEven") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_round_even, op[0]);
-   } else if (strcmp(callee, "sign") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL);
-   } else if (strcmp(callee, "sin") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL);
-   } else if (strcmp(callee, "sinh") == 0) {
-      assert(op[0]->type->is_float());
-      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) {
-      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) {
-      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.0F : 1.0F;
-   } else if (strcmp(callee, "tan") == 0) {
-      assert(op[0]->type->is_float());
-      for (unsigned c = 0; c < op[0]->type->components(); c++)
-        data.f[c] = tanf(op[0]->value.f[c]);
-   } else if (strcmp(callee, "tanh") == 0) {
-      assert(op[0]->type->is_float());
-      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) {
-      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 if (strcmp(callee, "trunc") == 0) {
-      expr = new(mem_ctx) ir_expression(ir_unop_trunc, op[0]);
-   } else {
-      /* Unsupported builtin - some are not allowed in constant expressions. */
-      return NULL;
-   }
+   /* Now run the builtin function until something non-constant
+    * happens or we get the result.
+    */
+   if (constant_expression_evaluate_expression_list(origin ? origin->body : body, deref_hash, &result) && result)
+      result = result->clone(ralloc_parent(this), NULL);
 
-   if (expr != NULL)
-      return expr->constant_expression_value();
+   hash_table_dtor(deref_hash);
 
-   return new(mem_ctx) ir_constant(type, &data);
+   return result;
 }