ir_visitor_status ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
{
	ir_expression *orig_expr = orig_assign->rhs->as_expression();
	unsigned int i, matrix_columns = 1;
	ir_dereference *op[2];

	if (!orig_expr)
		return visit_continue;

	if (!has_matrix_operand(orig_expr, matrix_columns))
		return visit_continue;

	check(orig_expr->get_num_operands() <= 2);

	mem_ctx = ralloc_parent(orig_assign);

	ir_dereference_variable *result =
		orig_assign->lhs->as_dereference_variable();
	check(result);

	/* Store the expression operands in temps so we can use them
	* multiple times.
	*/
	for (i = 0; i < orig_expr->get_num_operands(); i++)
	{
		ir_assignment *assign;
		ir_dereference *deref = orig_expr->operands[i]->as_dereference();

		/* Avoid making a temporary if we don't need to to avoid aliasing. */
		if (deref &&
			deref->variable_referenced() != result->variable_referenced())
		{
			op[i] = deref;
			continue;
		}

		/* Otherwise, store the operand in a temporary generally if it's
		* not a dereference.
		*/
		ir_variable *var = new(mem_ctx)ir_variable(orig_expr->operands[i]->type,
			"mat_op_to_vec",
			ir_var_temporary);
		base_ir->insert_before(var);

		/* Note that we use this dereference for the assignment.  That means
		* that others that want to use op[i] have to clone the deref.
		*/
		op[i] = new(mem_ctx)ir_dereference_variable(var);
		assign = new(mem_ctx)ir_assignment(op[i], orig_expr->operands[i]);
		base_ir->insert_before(assign);
	}

	/* OK, time to break down this matrix operation. */
	switch (orig_expr->operation)
	{
	case ir_unop_neg:
	{
		/* Apply the operation to each column.*/
		for (i = 0; i < matrix_columns; i++)
		{
			ir_expression *column_expr;
			ir_assignment *column_assign;

			check(op[0]);
			column_expr = new(mem_ctx)ir_expression(orig_expr->operation,
				get_column(op[0], i));

			column_assign = new(mem_ctx)ir_assignment(get_column(result, i),
				column_expr);
			check(column_assign->write_mask != 0);
			base_ir->insert_before(column_assign);
		}
		break;
	}
	case ir_binop_add:
	case ir_binop_sub:
	case ir_binop_div:
	case ir_binop_mod:
	{
		/* For most operations, the matrix version is just going
		* column-wise through and applying the operation to each column
		* if available.
		*/
		for (i = 0; i < matrix_columns; i++)
		{
			ir_expression *column_expr;
			ir_assignment *column_assign;

			check(op[0] && op[1]);
			column_expr = new(mem_ctx)ir_expression(orig_expr->operation,
				get_column(op[0], i),
				get_column(op[1], i));

			column_assign = new(mem_ctx)ir_assignment(get_column(result, i),
				column_expr);
			check(column_assign->write_mask != 0);
			base_ir->insert_before(column_assign);
		}
		break;
	}
	case ir_binop_mul:
		check(op[0] && op[1]);
		if (op[0]->type->is_matrix())
		{
			if (op[1]->type->is_matrix())
			{
				do_mul_mat_mat(result, op[0], op[1]);
			}
			else if (op[1]->type->is_vector())
			{
				do_mul_mat_vec(result, op[0], op[1]);
			}
			else
			{
				check(op[1]->type->is_scalar());
				do_mul_mat_scalar(result, op[0], op[1]);
			}
		}
		else
		{
			check(op[1]->type->is_matrix());
			if (op[0]->type->is_vector())
			{
				do_mul_vec_mat(result, op[0], op[1]);
			}
			else
			{
				check(op[0]->type->is_scalar());
				do_mul_mat_scalar(result, op[1], op[0]);
			}
		}
		break;

	case ir_binop_all_equal:
	case ir_binop_any_nequal:
		check(op[0] && op[1]);
		do_equal_mat_mat(result, op[1], op[0],
			(orig_expr->operation == ir_binop_all_equal));
		break;

	default:
		printf("FINISHME: Handle matrix operation for %s\n",
			orig_expr->operator_string());
		abort();
	}
	orig_assign->remove();
	this->made_progress = true;

	return visit_continue;
}
ir_visitor_status
ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
{
   ir_expression *orig_expr = orig_assign->rhs->as_expression();
   unsigned int i, matrix_columns = 1;
   ir_variable *op_var[2];

   if (!orig_expr)
      return visit_continue;

   if (!has_matrix_operand(orig_expr, matrix_columns))
      return visit_continue;

   assert(orig_expr->get_num_operands() <= 2);

   mem_ctx = ralloc_parent(orig_assign);

   ir_dereference_variable *lhs_deref =
      orig_assign->lhs->as_dereference_variable();
   assert(lhs_deref);

   ir_variable *result_var = lhs_deref->var;

   /* Store the expression operands in temps so we can use them
    * multiple times.
    */
   for (i = 0; i < orig_expr->get_num_operands(); i++) {
      ir_assignment *assign;

      op_var[i] = new(mem_ctx) ir_variable(orig_expr->operands[i]->type,
					   "mat_op_to_vec",
					   ir_var_temporary);
      base_ir->insert_before(op_var[i]);

      lhs_deref = new(mem_ctx) ir_dereference_variable(op_var[i]);
      assign = new(mem_ctx) ir_assignment(lhs_deref,
					  orig_expr->operands[i],
					  NULL);
      base_ir->insert_before(assign);
   }

   /* OK, time to break down this matrix operation. */
   switch (orig_expr->operation) {
   case ir_unop_neg: {
      const unsigned mask = (1U << result_var->type->vector_elements) - 1;

      /* Apply the operation to each column.*/
      for (i = 0; i < matrix_columns; i++) {
	 ir_rvalue *op0 = get_column(op_var[0], i);
	 ir_dereference *result = get_column(result_var, i);
	 ir_expression *column_expr;
	 ir_assignment *column_assign;

	 column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
						  result->type,
						  op0,
						  NULL);

	 column_assign = new(mem_ctx) ir_assignment(result,
						    column_expr,
						    NULL,
						    mask);
	 assert(column_assign->write_mask != 0);
	 base_ir->insert_before(column_assign);
      }
      break;
   }
   case ir_binop_add:
   case ir_binop_sub:
   case ir_binop_div:
   case ir_binop_mod: {
      const unsigned mask = (1U << result_var->type->vector_elements) - 1;

      /* For most operations, the matrix version is just going
       * column-wise through and applying the operation to each column
       * if available.
       */
      for (i = 0; i < matrix_columns; i++) {
	 ir_rvalue *op0 = get_column(op_var[0], i);
	 ir_rvalue *op1 = get_column(op_var[1], i);
	 ir_dereference *result = get_column(result_var, i);
	 ir_expression *column_expr;
	 ir_assignment *column_assign;

	 column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
						  result->type,
						  op0,
						  op1);

	 column_assign = new(mem_ctx) ir_assignment(result,
						    column_expr,
						    NULL,
						    mask);
	 assert(column_assign->write_mask != 0);
	 base_ir->insert_before(column_assign);
      }
      break;
   }
   case ir_binop_mul:
      if (op_var[0]->type->is_matrix()) {
	 if (op_var[1]->type->is_matrix()) {
	    do_mul_mat_mat(result_var, op_var[0], op_var[1]);
	 } else if (op_var[1]->type->is_vector()) {
	    do_mul_mat_vec(result_var, op_var[0], op_var[1]);
	 } else {
	    assert(op_var[1]->type->is_scalar());
	    do_mul_mat_scalar(result_var, op_var[0], op_var[1]);
	 }
      } else {
	 assert(op_var[1]->type->is_matrix());
	 if (op_var[0]->type->is_vector()) {
	    do_mul_vec_mat(result_var, op_var[0], op_var[1]);
	 } else {
	    assert(op_var[0]->type->is_scalar());
	    do_mul_mat_scalar(result_var, op_var[1], op_var[0]);
	 }
      }
      break;

   case ir_binop_all_equal:
   case ir_binop_any_nequal:
      do_equal_mat_mat(result_var, op_var[1], op_var[0],
		       (orig_expr->operation == ir_binop_all_equal));
      break;

   default:
      printf("FINISHME: Handle matrix operation for %s\n",
	     orig_expr->operator_string());
      abort();
   }
   orig_assign->remove();
   this->made_progress = true;

   return visit_continue;
}