mesa: set up gl_vert_result and gl_frag_attrib values for gl_ClipDistance.
[mesa.git] / src / glsl / ast_function.cpp
index 60a2c617f702c6f75ac01e833c11eba71be65df8..ca45934a478ec311ab740545ff3bd3a6a51f754e 100644 (file)
@@ -62,8 +62,10 @@ process_parameters(exec_list *instructions, exec_list *actual_parameters,
  *
  * \param return_type Return type of the function.  May be \c NULL.
  * \param name        Name of the function.
- * \param parameters  Parameter list for the function.  This may be either a
- *                    formal or actual parameter list.  Only the type is used.
+ * \param parameters  List of \c ir_instruction nodes representing the
+ *                    parameter list for the function.  This may be either a
+ *                    formal (\c ir_variable) or actual (\c ir_rvalue)
+ *                    parameter list.  Only the type is used.
  *
  * \return
  * A ralloced string representing the prototype of the function.
@@ -123,7 +125,7 @@ match_function_by_name(exec_list *instructions, const char *name,
            if (f == NULL) {
               f = new(ctx) ir_function(name);
               state->symbols->add_global_function(f);
-              emit_function(state, instructions, f);
+              emit_function(state, f);
            }
 
            f->add_signature(sig->clone_prototype(f, NULL));
@@ -132,6 +134,8 @@ match_function_by_name(exec_list *instructions, const char *name,
       }
    }
 
+   exec_list post_call_conversions;
+
    if (sig != NULL) {
       /* Verify that 'out' and 'inout' actual parameters are lvalues.  This
        * isn't done in ir_function::matching_signature because that function
@@ -139,6 +143,12 @@ match_function_by_name(exec_list *instructions, const char *name,
        *
        * Also, validate that 'const_in' formal parameters (an extension of our
        * IR) correspond to ir_constant actual parameters.
+       *
+       * Also, perform implicit conversion of arguments.  Note: to implicitly
+       * convert out parameters, we need to place them in a temporary
+       * variable, and do the conversion after the call takes place.  Since we
+       * haven't emitted the call yet, we'll place the post-call conversions
+       * in a temporary exec_list, and emit them later.
        */
       exec_list_iterator actual_iter = actual_parameters->iterator();
       exec_list_iterator formal_iter = sig->parameters.iterator();
@@ -154,6 +164,7 @@ match_function_by_name(exec_list *instructions, const char *name,
            _mesa_glsl_error(loc, state,
                             "parameter `%s' must be a constant expression",
                             formal->name);
+           return ir_call::get_error_instruction(ctx);
         }
 
         if ((formal->mode == ir_var_out)
@@ -183,8 +194,64 @@ match_function_by_name(exec_list *instructions, const char *name,
         }
 
         if (formal->type->is_numeric() || formal->type->is_boolean()) {
-           ir_rvalue *converted = convert_component(actual, formal->type);
-           actual->replace_with(converted);
+            switch (formal->mode) {
+            case ir_var_const_in:
+            case ir_var_in: {
+               ir_rvalue *converted
+                  = convert_component(actual, formal->type);
+               actual->replace_with(converted);
+               break;
+            }
+            case ir_var_out:
+               if (actual->type != formal->type) {
+                  /* To convert an out parameter, we need to create a
+                   * temporary variable to hold the value before conversion,
+                   * and then perform the conversion after the function call
+                   * returns.
+                   *
+                   * This has the effect of transforming code like this:
+                   *
+                   *   void f(out int x);
+                   *   float value;
+                   *   f(value);
+                   *
+                   * Into IR that's equivalent to this:
+                   *
+                   *   void f(out int x);
+                   *   float value;
+                   *   int out_parameter_conversion;
+                   *   f(out_parameter_conversion);
+                   *   value = float(out_parameter_conversion);
+                   */
+                  ir_variable *tmp =
+                     new(ctx) ir_variable(formal->type,
+                                          "out_parameter_conversion",
+                                          ir_var_temporary);
+                  instructions->push_tail(tmp);
+                  ir_dereference_variable *deref_tmp_1
+                     = new(ctx) ir_dereference_variable(tmp);
+                  ir_dereference_variable *deref_tmp_2
+                     = new(ctx) ir_dereference_variable(tmp);
+                  ir_rvalue *converted_tmp
+                     = convert_component(deref_tmp_1, actual->type);
+                  ir_assignment *assignment
+                     = new(ctx) ir_assignment(actual, converted_tmp);
+                  post_call_conversions.push_tail(assignment);
+                  actual->replace_with(deref_tmp_2);
+               }
+               break;
+            case ir_var_inout:
+               /* Inout parameters should never require conversion, since that
+                * would require an implicit conversion to exist both to and
+                * from the formal parameter type, and there are no
+                * bidirectional implicit conversions.
+                */
+               assert (actual->type == formal->type);
+               break;
+            default:
+               assert (!"Illegal formal parameter mode");
+               break;
+            }
         }
 
         actual_iter.next();
@@ -194,11 +261,27 @@ match_function_by_name(exec_list *instructions, const char *name,
       /* Always insert the call in the instruction stream, and return a deref
        * of its return val if it returns a value, since we don't know if
        * the rvalue is going to be assigned to anything or not.
+       *
+       * Also insert any out parameter conversions after the call.
        */
       ir_call *call = new(ctx) ir_call(sig, actual_parameters);
+      ir_dereference_variable *deref;
       if (!sig->return_type->is_void()) {
+         /* If the function call is a constant expression, don't
+          * generate the instructions to call it; just generate an
+          * ir_constant representing the constant value.
+          *
+          * Function calls can only be constant expressions starting
+          * in GLSL 1.20.
+          */
+         if (state->language_version >= 120) {
+            ir_constant *const_val = call->constant_expression_value();
+            if (const_val) {
+               return const_val;
+            }
+         }
+
         ir_variable *var;
-        ir_dereference_variable *deref;
 
         var = new(ctx) ir_variable(sig->return_type,
                                    ralloc_asprintf(ctx, "%s_retval",
@@ -209,15 +292,14 @@ match_function_by_name(exec_list *instructions, const char *name,
         deref = new(ctx) ir_dereference_variable(var);
         ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL);
         instructions->push_tail(assign);
-        if (state->language_version >= 120)
-           var->constant_value = call->constant_expression_value();
 
         deref = new(ctx) ir_dereference_variable(var);
-        return deref;
       } else {
         instructions->push_tail(call);
-        return NULL;
+        deref = NULL;
       }
+      instructions->append_list(&post_call_conversions);
+      return deref;
    } else {
       char *str = prototype_string(NULL, name, actual_parameters);
 
@@ -440,13 +522,21 @@ process_array_constructor(exec_list *instructions,
       ir_rvalue *ir = (ir_rvalue *) n;
       ir_rvalue *result = ir;
 
-      /* Apply implicit conversions (not the scalar constructor rules!) */
+      /* Apply implicit conversions (not the scalar constructor rules!). See
+       * the spec quote above. */
       if (constructor_type->element_type()->is_float()) {
         const glsl_type *desired_type =
            glsl_type::get_instance(GLSL_TYPE_FLOAT,
                                    ir->type->vector_elements,
                                    ir->type->matrix_columns);
-        result = convert_component(ir, desired_type);
+        if (result->type->can_implicitly_convert_to(desired_type)) {
+           /* Even though convert_component() implements the constructor
+            * conversion rules (not the implicit conversion rules), its safe
+            * to use it here because we already checked that the implicit
+            * conversion is legal.
+            */
+           result = convert_component(ir, desired_type);
+        }
       }
 
       if (result->type != constructor_type->element_type()) {