101e04c3fSmrg/*
201e04c3fSmrg * Copyright © 2010 Intel Corporation
301e04c3fSmrg *
401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
501e04c3fSmrg * copy of this software and associated documentation files (the "Software"),
601e04c3fSmrg * to deal in the Software without restriction, including without limitation
701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the
901e04c3fSmrg * Software is furnished to do so, subject to the following conditions:
1001e04c3fSmrg *
1101e04c3fSmrg * The above copyright notice and this permission notice (including the next
1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the
1301e04c3fSmrg * Software.
1401e04c3fSmrg *
1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2101e04c3fSmrg * DEALINGS IN THE SOFTWARE.
2201e04c3fSmrg */
2301e04c3fSmrg
2401e04c3fSmrg/**
2501e04c3fSmrg * \file lower_discard.cpp
2601e04c3fSmrg *
2701e04c3fSmrg * This pass moves discards out of if-statements.
2801e04c3fSmrg *
2901e04c3fSmrg * Case 1: The "then" branch contains a conditional discard:
3001e04c3fSmrg * ---------------------------------------------------------
3101e04c3fSmrg *
3201e04c3fSmrg *    if (cond1) {
3301e04c3fSmrg *	 s1;
3401e04c3fSmrg *	 discard cond2;
3501e04c3fSmrg *	 s2;
3601e04c3fSmrg *    } else {
3701e04c3fSmrg *	 s3;
3801e04c3fSmrg *    }
3901e04c3fSmrg *
4001e04c3fSmrg * becomes:
4101e04c3fSmrg *
4201e04c3fSmrg *    temp = false;
4301e04c3fSmrg *    if (cond1) {
4401e04c3fSmrg *	 s1;
4501e04c3fSmrg *	 temp = cond2;
4601e04c3fSmrg *	 s2;
4701e04c3fSmrg *    } else {
4801e04c3fSmrg *	 s3;
4901e04c3fSmrg *    }
5001e04c3fSmrg *    discard temp;
5101e04c3fSmrg *
5201e04c3fSmrg * Case 2: The "else" branch contains a conditional discard:
5301e04c3fSmrg * ---------------------------------------------------------
5401e04c3fSmrg *
5501e04c3fSmrg *    if (cond1) {
5601e04c3fSmrg *	 s1;
5701e04c3fSmrg *    } else {
5801e04c3fSmrg *	 s2;
5901e04c3fSmrg *	 discard cond2;
6001e04c3fSmrg *	 s3;
6101e04c3fSmrg *    }
6201e04c3fSmrg *
6301e04c3fSmrg * becomes:
6401e04c3fSmrg *
6501e04c3fSmrg *    temp = false;
6601e04c3fSmrg *    if (cond1) {
6701e04c3fSmrg *	 s1;
6801e04c3fSmrg *    } else {
6901e04c3fSmrg *	 s2;
7001e04c3fSmrg *	 temp = cond2;
7101e04c3fSmrg *	 s3;
7201e04c3fSmrg *    }
7301e04c3fSmrg *    discard temp;
7401e04c3fSmrg *
7501e04c3fSmrg * Case 3: Both branches contain a conditional discard:
7601e04c3fSmrg * ----------------------------------------------------
7701e04c3fSmrg *
7801e04c3fSmrg *    if (cond1) {
7901e04c3fSmrg *	 s1;
8001e04c3fSmrg *	 discard cond2;
8101e04c3fSmrg *	 s2;
8201e04c3fSmrg *    } else {
8301e04c3fSmrg *	 s3;
8401e04c3fSmrg *	 discard cond3;
8501e04c3fSmrg *	 s4;
8601e04c3fSmrg *    }
8701e04c3fSmrg *
8801e04c3fSmrg * becomes:
8901e04c3fSmrg *
9001e04c3fSmrg *    temp = false;
9101e04c3fSmrg *    if (cond1) {
9201e04c3fSmrg *	 s1;
9301e04c3fSmrg *	 temp = cond2;
9401e04c3fSmrg *	 s2;
9501e04c3fSmrg *    } else {
9601e04c3fSmrg *	 s3;
9701e04c3fSmrg *	 temp = cond3;
9801e04c3fSmrg *	 s4;
9901e04c3fSmrg *    }
10001e04c3fSmrg *    discard temp;
10101e04c3fSmrg *
10201e04c3fSmrg * If there are multiple conditional discards, we need only deal with one of
10301e04c3fSmrg * them.  Repeatedly applying this pass will take care of the others.
10401e04c3fSmrg *
10501e04c3fSmrg * Unconditional discards are treated as having a condition of "true".
10601e04c3fSmrg */
10701e04c3fSmrg
10801e04c3fSmrg#include "compiler/glsl_types.h"
10901e04c3fSmrg#include "ir.h"
11001e04c3fSmrg
11101e04c3fSmrgnamespace {
11201e04c3fSmrg
11301e04c3fSmrgclass lower_discard_visitor : public ir_hierarchical_visitor {
11401e04c3fSmrgpublic:
11501e04c3fSmrg   lower_discard_visitor()
11601e04c3fSmrg   {
11701e04c3fSmrg      this->progress = false;
11801e04c3fSmrg   }
11901e04c3fSmrg
12001e04c3fSmrg   ir_visitor_status visit_leave(ir_if *);
12101e04c3fSmrg
12201e04c3fSmrg   bool progress;
12301e04c3fSmrg};
12401e04c3fSmrg
12501e04c3fSmrg} /* anonymous namespace */
12601e04c3fSmrg
12701e04c3fSmrgbool
12801e04c3fSmrglower_discard(exec_list *instructions)
12901e04c3fSmrg{
13001e04c3fSmrg   lower_discard_visitor v;
13101e04c3fSmrg
13201e04c3fSmrg   visit_list_elements(&v, instructions);
13301e04c3fSmrg
13401e04c3fSmrg   return v.progress;
13501e04c3fSmrg}
13601e04c3fSmrg
13701e04c3fSmrg
13801e04c3fSmrgstatic ir_discard *
13901e04c3fSmrgfind_discard(exec_list &instructions)
14001e04c3fSmrg{
14101e04c3fSmrg   foreach_in_list(ir_instruction, node, &instructions) {
14201e04c3fSmrg      ir_discard *ir = node->as_discard();
14301e04c3fSmrg      if (ir != NULL)
14401e04c3fSmrg	 return ir;
14501e04c3fSmrg   }
14601e04c3fSmrg   return NULL;
14701e04c3fSmrg}
14801e04c3fSmrg
14901e04c3fSmrg
15001e04c3fSmrgstatic void
15101e04c3fSmrgreplace_discard(void *mem_ctx, ir_variable *var, ir_discard *ir)
15201e04c3fSmrg{
15301e04c3fSmrg   ir_rvalue *condition = ir->condition;
15401e04c3fSmrg
15501e04c3fSmrg   /* For unconditional discards, use "true" as the condition. */
15601e04c3fSmrg   if (condition == NULL)
15701e04c3fSmrg      condition = new(mem_ctx) ir_constant(true);
15801e04c3fSmrg
15901e04c3fSmrg   ir_assignment *assignment =
16001e04c3fSmrg      new(mem_ctx) ir_assignment(new(mem_ctx) ir_dereference_variable(var),
16101e04c3fSmrg                                 condition);
16201e04c3fSmrg
16301e04c3fSmrg   ir->replace_with(assignment);
16401e04c3fSmrg}
16501e04c3fSmrg
16601e04c3fSmrg
16701e04c3fSmrgir_visitor_status
16801e04c3fSmrglower_discard_visitor::visit_leave(ir_if *ir)
16901e04c3fSmrg{
17001e04c3fSmrg   ir_discard *then_discard = find_discard(ir->then_instructions);
17101e04c3fSmrg   ir_discard *else_discard = find_discard(ir->else_instructions);
17201e04c3fSmrg
17301e04c3fSmrg   if (then_discard == NULL && else_discard == NULL)
17401e04c3fSmrg      return visit_continue;
17501e04c3fSmrg
17601e04c3fSmrg   void *mem_ctx = ralloc_parent(ir);
17701e04c3fSmrg
17801e04c3fSmrg   ir_variable *temp = new(mem_ctx) ir_variable(glsl_type::bool_type,
17901e04c3fSmrg						"discard_cond_temp",
18001e04c3fSmrg						ir_var_temporary);
18101e04c3fSmrg   ir_assignment *temp_initializer =
18201e04c3fSmrg      new(mem_ctx) ir_assignment(new(mem_ctx) ir_dereference_variable(temp),
18301e04c3fSmrg                                 new(mem_ctx) ir_constant(false));
18401e04c3fSmrg
18501e04c3fSmrg   ir->insert_before(temp);
18601e04c3fSmrg   ir->insert_before(temp_initializer);
18701e04c3fSmrg
18801e04c3fSmrg   if (then_discard != NULL)
18901e04c3fSmrg      replace_discard(mem_ctx, temp, then_discard);
19001e04c3fSmrg
19101e04c3fSmrg   if (else_discard != NULL)
19201e04c3fSmrg      replace_discard(mem_ctx, temp, else_discard);
19301e04c3fSmrg
19401e04c3fSmrg   ir_discard *discard = then_discard != NULL ? then_discard : else_discard;
19501e04c3fSmrg   discard->condition = new(mem_ctx) ir_dereference_variable(temp);
19601e04c3fSmrg   ir->insert_after(discard);
19701e04c3fSmrg
19801e04c3fSmrg   this->progress = true;
19901e04c3fSmrg
20001e04c3fSmrg   return visit_continue;
20101e04c3fSmrg}
202