1/*
2 * Copyright © 2010 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24/**
25 * \file lower_discard.cpp
26 *
27 * This pass moves discards out of if-statements.
28 *
29 * Case 1: The "then" branch contains a conditional discard:
30 * ---------------------------------------------------------
31 *
32 *    if (cond1) {
33 *	 s1;
34 *	 discard cond2;
35 *	 s2;
36 *    } else {
37 *	 s3;
38 *    }
39 *
40 * becomes:
41 *
42 *    temp = false;
43 *    if (cond1) {
44 *	 s1;
45 *	 temp = cond2;
46 *	 s2;
47 *    } else {
48 *	 s3;
49 *    }
50 *    discard temp;
51 *
52 * Case 2: The "else" branch contains a conditional discard:
53 * ---------------------------------------------------------
54 *
55 *    if (cond1) {
56 *	 s1;
57 *    } else {
58 *	 s2;
59 *	 discard cond2;
60 *	 s3;
61 *    }
62 *
63 * becomes:
64 *
65 *    temp = false;
66 *    if (cond1) {
67 *	 s1;
68 *    } else {
69 *	 s2;
70 *	 temp = cond2;
71 *	 s3;
72 *    }
73 *    discard temp;
74 *
75 * Case 3: Both branches contain a conditional discard:
76 * ----------------------------------------------------
77 *
78 *    if (cond1) {
79 *	 s1;
80 *	 discard cond2;
81 *	 s2;
82 *    } else {
83 *	 s3;
84 *	 discard cond3;
85 *	 s4;
86 *    }
87 *
88 * becomes:
89 *
90 *    temp = false;
91 *    if (cond1) {
92 *	 s1;
93 *	 temp = cond2;
94 *	 s2;
95 *    } else {
96 *	 s3;
97 *	 temp = cond3;
98 *	 s4;
99 *    }
100 *    discard temp;
101 *
102 * If there are multiple conditional discards, we need only deal with one of
103 * them.  Repeatedly applying this pass will take care of the others.
104 *
105 * Unconditional discards are treated as having a condition of "true".
106 */
107
108#include "glsl_types.h"
109#include "ir.h"
110
111class lower_discard_visitor : public ir_hierarchical_visitor {
112public:
113   lower_discard_visitor()
114   {
115      this->progress = false;
116   }
117
118   ir_visitor_status visit_leave(ir_if *);
119
120   bool progress;
121};
122
123
124bool
125lower_discard(exec_list *instructions)
126{
127   lower_discard_visitor v;
128
129   visit_list_elements(&v, instructions);
130
131   return v.progress;
132}
133
134
135static ir_discard *
136find_discard(exec_list &instructions)
137{
138   foreach_list(n, &instructions) {
139      ir_discard *ir = ((ir_instruction *) n)->as_discard();
140      if (ir != NULL)
141	 return ir;
142   }
143   return NULL;
144}
145
146
147static void
148replace_discard(void *mem_ctx, ir_variable *var, ir_discard *ir)
149{
150   ir_rvalue *condition = ir->condition;
151
152   /* For unconditional discards, use "true" as the condition. */
153   if (condition == NULL)
154      condition = new(mem_ctx) ir_constant(true);
155
156   ir_assignment *assignment =
157      new(mem_ctx) ir_assignment(new(mem_ctx) ir_dereference_variable(var),
158				 condition, NULL);
159
160   ir->replace_with(assignment);
161}
162
163
164ir_visitor_status
165lower_discard_visitor::visit_leave(ir_if *ir)
166{
167   ir_discard *then_discard = find_discard(ir->then_instructions);
168   ir_discard *else_discard = find_discard(ir->else_instructions);
169
170   if (then_discard == NULL && else_discard == NULL)
171      return visit_continue;
172
173   void *mem_ctx = ralloc_parent(ir);
174
175   ir_variable *temp = new(mem_ctx) ir_variable(glsl_type::bool_type,
176						"discard_cond_temp",
177						ir_var_temporary);
178   ir_assignment *temp_initializer =
179      new(mem_ctx) ir_assignment(new(mem_ctx) ir_dereference_variable(temp),
180				 new(mem_ctx) ir_constant(false), NULL);
181
182   ir->insert_before(temp);
183   ir->insert_before(temp_initializer);
184
185   if (then_discard != NULL)
186      replace_discard(mem_ctx, temp, then_discard);
187
188   if (else_discard != NULL)
189      replace_discard(mem_ctx, temp, else_discard);
190
191   ir_discard *discard = then_discard != NULL ? then_discard : else_discard;
192   discard->condition = new(mem_ctx) ir_dereference_variable(temp);
193   ir->insert_after(discard);
194
195   this->progress = true;
196
197   return visit_continue;
198}
199