xref: /aosp_15_r20/external/mesa3d/src/compiler/glsl/opt_minmax.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1*61046927SAndroid Build Coastguard Worker /*
2*61046927SAndroid Build Coastguard Worker  * Copyright © 2014 Intel Corporation
3*61046927SAndroid Build Coastguard Worker  *
4*61046927SAndroid Build Coastguard Worker  * Permission is hereby granted, free of charge, to any person obtaining a
5*61046927SAndroid Build Coastguard Worker  * copy of this software and associated documentation files (the "Software"),
6*61046927SAndroid Build Coastguard Worker  * to deal in the Software without restriction, including without limitation
7*61046927SAndroid Build Coastguard Worker  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*61046927SAndroid Build Coastguard Worker  * and/or sell copies of the Software, and to permit persons to whom the
9*61046927SAndroid Build Coastguard Worker  * Software is furnished to do so, subject to the following conditions:
10*61046927SAndroid Build Coastguard Worker  *
11*61046927SAndroid Build Coastguard Worker  * The above copyright notice and this permission notice (including the next
12*61046927SAndroid Build Coastguard Worker  * paragraph) shall be included in all copies or substantial portions of the
13*61046927SAndroid Build Coastguard Worker  * Software.
14*61046927SAndroid Build Coastguard Worker  *
15*61046927SAndroid Build Coastguard Worker  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*61046927SAndroid Build Coastguard Worker  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*61046927SAndroid Build Coastguard Worker  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18*61046927SAndroid Build Coastguard Worker  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*61046927SAndroid Build Coastguard Worker  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20*61046927SAndroid Build Coastguard Worker  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21*61046927SAndroid Build Coastguard Worker  * DEALINGS IN THE SOFTWARE.
22*61046927SAndroid Build Coastguard Worker  */
23*61046927SAndroid Build Coastguard Worker 
24*61046927SAndroid Build Coastguard Worker /**
25*61046927SAndroid Build Coastguard Worker  * \file opt_minmax.cpp
26*61046927SAndroid Build Coastguard Worker  *
27*61046927SAndroid Build Coastguard Worker  * Drop operands from an expression tree of only min/max operations if they
28*61046927SAndroid Build Coastguard Worker  * can be proven to not contribute to the final result.
29*61046927SAndroid Build Coastguard Worker  *
30*61046927SAndroid Build Coastguard Worker  * The algorithm is similar to alpha-beta pruning on a minmax search.
31*61046927SAndroid Build Coastguard Worker  */
32*61046927SAndroid Build Coastguard Worker 
33*61046927SAndroid Build Coastguard Worker #include "ir.h"
34*61046927SAndroid Build Coastguard Worker #include "ir_visitor.h"
35*61046927SAndroid Build Coastguard Worker #include "ir_rvalue_visitor.h"
36*61046927SAndroid Build Coastguard Worker #include "ir_optimization.h"
37*61046927SAndroid Build Coastguard Worker #include "ir_builder.h"
38*61046927SAndroid Build Coastguard Worker #include "program/prog_instruction.h"
39*61046927SAndroid Build Coastguard Worker #include "compiler/glsl_types.h"
40*61046927SAndroid Build Coastguard Worker #include "main/macros.h"
41*61046927SAndroid Build Coastguard Worker #include "util/half_float.h"
42*61046927SAndroid Build Coastguard Worker 
43*61046927SAndroid Build Coastguard Worker using namespace ir_builder;
44*61046927SAndroid Build Coastguard Worker 
45*61046927SAndroid Build Coastguard Worker namespace {
46*61046927SAndroid Build Coastguard Worker 
47*61046927SAndroid Build Coastguard Worker enum compare_components_result {
48*61046927SAndroid Build Coastguard Worker    LESS,
49*61046927SAndroid Build Coastguard Worker    LESS_OR_EQUAL,
50*61046927SAndroid Build Coastguard Worker    EQUAL,
51*61046927SAndroid Build Coastguard Worker    GREATER_OR_EQUAL,
52*61046927SAndroid Build Coastguard Worker    GREATER,
53*61046927SAndroid Build Coastguard Worker    MIXED
54*61046927SAndroid Build Coastguard Worker };
55*61046927SAndroid Build Coastguard Worker 
56*61046927SAndroid Build Coastguard Worker class minmax_range {
57*61046927SAndroid Build Coastguard Worker public:
minmax_range(ir_constant * low=NULL,ir_constant * high=NULL)58*61046927SAndroid Build Coastguard Worker    minmax_range(ir_constant *low = NULL, ir_constant *high = NULL)
59*61046927SAndroid Build Coastguard Worker    {
60*61046927SAndroid Build Coastguard Worker       this->low = low;
61*61046927SAndroid Build Coastguard Worker       this->high = high;
62*61046927SAndroid Build Coastguard Worker    }
63*61046927SAndroid Build Coastguard Worker 
64*61046927SAndroid Build Coastguard Worker    /* low is the lower limit of the range, high is the higher limit. NULL on
65*61046927SAndroid Build Coastguard Worker     * low means negative infinity (unlimited) and on high positive infinity
66*61046927SAndroid Build Coastguard Worker     * (unlimited). Because of the two interpretations of the value NULL,
67*61046927SAndroid Build Coastguard Worker     * arbitrary comparison between ir_constants is impossible.
68*61046927SAndroid Build Coastguard Worker     */
69*61046927SAndroid Build Coastguard Worker    ir_constant *low;
70*61046927SAndroid Build Coastguard Worker    ir_constant *high;
71*61046927SAndroid Build Coastguard Worker };
72*61046927SAndroid Build Coastguard Worker 
73*61046927SAndroid Build Coastguard Worker class ir_minmax_visitor : public ir_rvalue_enter_visitor {
74*61046927SAndroid Build Coastguard Worker public:
ir_minmax_visitor()75*61046927SAndroid Build Coastguard Worker    ir_minmax_visitor()
76*61046927SAndroid Build Coastguard Worker       : progress(false)
77*61046927SAndroid Build Coastguard Worker    {
78*61046927SAndroid Build Coastguard Worker    }
79*61046927SAndroid Build Coastguard Worker 
80*61046927SAndroid Build Coastguard Worker    ir_rvalue *prune_expression(ir_expression *expr, minmax_range baserange);
81*61046927SAndroid Build Coastguard Worker 
82*61046927SAndroid Build Coastguard Worker    void handle_rvalue(ir_rvalue **rvalue);
83*61046927SAndroid Build Coastguard Worker 
84*61046927SAndroid Build Coastguard Worker    bool progress;
85*61046927SAndroid Build Coastguard Worker };
86*61046927SAndroid Build Coastguard Worker 
87*61046927SAndroid Build Coastguard Worker /*
88*61046927SAndroid Build Coastguard Worker  * Returns LESS if all vector components of `a' are strictly lower than of `b',
89*61046927SAndroid Build Coastguard Worker  * GREATER if all vector components of `a' are strictly greater than of `b',
90*61046927SAndroid Build Coastguard Worker  * MIXED if some vector components of `a' are strictly lower than of `b' while
91*61046927SAndroid Build Coastguard Worker  * others are strictly greater, or EQUAL otherwise.
92*61046927SAndroid Build Coastguard Worker  */
93*61046927SAndroid Build Coastguard Worker static enum compare_components_result
compare_components(ir_constant * a,ir_constant * b)94*61046927SAndroid Build Coastguard Worker compare_components(ir_constant *a, ir_constant *b)
95*61046927SAndroid Build Coastguard Worker {
96*61046927SAndroid Build Coastguard Worker    assert(a != NULL);
97*61046927SAndroid Build Coastguard Worker    assert(b != NULL);
98*61046927SAndroid Build Coastguard Worker 
99*61046927SAndroid Build Coastguard Worker    assert(a->type->base_type == b->type->base_type);
100*61046927SAndroid Build Coastguard Worker 
101*61046927SAndroid Build Coastguard Worker    unsigned a_inc = glsl_type_is_scalar(a->type) ? 0 : 1;
102*61046927SAndroid Build Coastguard Worker    unsigned b_inc = glsl_type_is_scalar(b->type) ? 0 : 1;
103*61046927SAndroid Build Coastguard Worker    unsigned components = MAX2(glsl_get_components(a->type), glsl_get_components(b->type));
104*61046927SAndroid Build Coastguard Worker 
105*61046927SAndroid Build Coastguard Worker    bool foundless = false;
106*61046927SAndroid Build Coastguard Worker    bool foundgreater = false;
107*61046927SAndroid Build Coastguard Worker    bool foundequal = false;
108*61046927SAndroid Build Coastguard Worker 
109*61046927SAndroid Build Coastguard Worker    for (unsigned i = 0, c0 = 0, c1 = 0;
110*61046927SAndroid Build Coastguard Worker         i < components;
111*61046927SAndroid Build Coastguard Worker         c0 += a_inc, c1 += b_inc, ++i) {
112*61046927SAndroid Build Coastguard Worker       switch (a->type->base_type) {
113*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_UINT16:
114*61046927SAndroid Build Coastguard Worker          if (a->value.u16[c0] < b->value.u16[c1])
115*61046927SAndroid Build Coastguard Worker             foundless = true;
116*61046927SAndroid Build Coastguard Worker          else if (a->value.u16[c0] > b->value.u16[c1])
117*61046927SAndroid Build Coastguard Worker             foundgreater = true;
118*61046927SAndroid Build Coastguard Worker          else
119*61046927SAndroid Build Coastguard Worker             foundequal = true;
120*61046927SAndroid Build Coastguard Worker          break;
121*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_INT16:
122*61046927SAndroid Build Coastguard Worker          if (a->value.i16[c0] < b->value.i16[c1])
123*61046927SAndroid Build Coastguard Worker             foundless = true;
124*61046927SAndroid Build Coastguard Worker          else if (a->value.i16[c0] > b->value.i16[c1])
125*61046927SAndroid Build Coastguard Worker             foundgreater = true;
126*61046927SAndroid Build Coastguard Worker          else
127*61046927SAndroid Build Coastguard Worker             foundequal = true;
128*61046927SAndroid Build Coastguard Worker          break;
129*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_UINT:
130*61046927SAndroid Build Coastguard Worker          if (a->value.u[c0] < b->value.u[c1])
131*61046927SAndroid Build Coastguard Worker             foundless = true;
132*61046927SAndroid Build Coastguard Worker          else if (a->value.u[c0] > b->value.u[c1])
133*61046927SAndroid Build Coastguard Worker             foundgreater = true;
134*61046927SAndroid Build Coastguard Worker          else
135*61046927SAndroid Build Coastguard Worker             foundequal = true;
136*61046927SAndroid Build Coastguard Worker          break;
137*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_INT:
138*61046927SAndroid Build Coastguard Worker          if (a->value.i[c0] < b->value.i[c1])
139*61046927SAndroid Build Coastguard Worker             foundless = true;
140*61046927SAndroid Build Coastguard Worker          else if (a->value.i[c0] > b->value.i[c1])
141*61046927SAndroid Build Coastguard Worker             foundgreater = true;
142*61046927SAndroid Build Coastguard Worker          else
143*61046927SAndroid Build Coastguard Worker             foundequal = true;
144*61046927SAndroid Build Coastguard Worker          break;
145*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_FLOAT16: {
146*61046927SAndroid Build Coastguard Worker          float af = _mesa_half_to_float(a->value.f16[c0]);
147*61046927SAndroid Build Coastguard Worker          float bf = _mesa_half_to_float(b->value.f16[c1]);
148*61046927SAndroid Build Coastguard Worker          if (af < bf)
149*61046927SAndroid Build Coastguard Worker             foundless = true;
150*61046927SAndroid Build Coastguard Worker          else if (af > bf)
151*61046927SAndroid Build Coastguard Worker             foundgreater = true;
152*61046927SAndroid Build Coastguard Worker          else
153*61046927SAndroid Build Coastguard Worker             foundequal = true;
154*61046927SAndroid Build Coastguard Worker          break;
155*61046927SAndroid Build Coastguard Worker       }
156*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_FLOAT:
157*61046927SAndroid Build Coastguard Worker          if (a->value.f[c0] < b->value.f[c1])
158*61046927SAndroid Build Coastguard Worker             foundless = true;
159*61046927SAndroid Build Coastguard Worker          else if (a->value.f[c0] > b->value.f[c1])
160*61046927SAndroid Build Coastguard Worker             foundgreater = true;
161*61046927SAndroid Build Coastguard Worker          else
162*61046927SAndroid Build Coastguard Worker             foundequal = true;
163*61046927SAndroid Build Coastguard Worker          break;
164*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_DOUBLE:
165*61046927SAndroid Build Coastguard Worker          if (a->value.d[c0] < b->value.d[c1])
166*61046927SAndroid Build Coastguard Worker             foundless = true;
167*61046927SAndroid Build Coastguard Worker          else if (a->value.d[c0] > b->value.d[c1])
168*61046927SAndroid Build Coastguard Worker             foundgreater = true;
169*61046927SAndroid Build Coastguard Worker          else
170*61046927SAndroid Build Coastguard Worker             foundequal = true;
171*61046927SAndroid Build Coastguard Worker          break;
172*61046927SAndroid Build Coastguard Worker       default:
173*61046927SAndroid Build Coastguard Worker          unreachable("not reached");
174*61046927SAndroid Build Coastguard Worker       }
175*61046927SAndroid Build Coastguard Worker    }
176*61046927SAndroid Build Coastguard Worker 
177*61046927SAndroid Build Coastguard Worker    if (foundless && foundgreater) {
178*61046927SAndroid Build Coastguard Worker       /* Some components are strictly lower, others are strictly greater */
179*61046927SAndroid Build Coastguard Worker       return MIXED;
180*61046927SAndroid Build Coastguard Worker    }
181*61046927SAndroid Build Coastguard Worker 
182*61046927SAndroid Build Coastguard Worker    if (foundequal) {
183*61046927SAndroid Build Coastguard Worker        /* It is not mixed, but it is not strictly lower or greater */
184*61046927SAndroid Build Coastguard Worker       if (foundless)
185*61046927SAndroid Build Coastguard Worker          return LESS_OR_EQUAL;
186*61046927SAndroid Build Coastguard Worker       if (foundgreater)
187*61046927SAndroid Build Coastguard Worker          return GREATER_OR_EQUAL;
188*61046927SAndroid Build Coastguard Worker       return EQUAL;
189*61046927SAndroid Build Coastguard Worker    }
190*61046927SAndroid Build Coastguard Worker 
191*61046927SAndroid Build Coastguard Worker    /* All components are strictly lower or strictly greater */
192*61046927SAndroid Build Coastguard Worker    return foundless ? LESS : GREATER;
193*61046927SAndroid Build Coastguard Worker }
194*61046927SAndroid Build Coastguard Worker 
195*61046927SAndroid Build Coastguard Worker static ir_constant *
combine_constant(bool ismin,ir_constant * a,ir_constant * b)196*61046927SAndroid Build Coastguard Worker combine_constant(bool ismin, ir_constant *a, ir_constant *b)
197*61046927SAndroid Build Coastguard Worker {
198*61046927SAndroid Build Coastguard Worker    void *mem_ctx = ralloc_parent(a);
199*61046927SAndroid Build Coastguard Worker    ir_constant *c = a->clone(mem_ctx, NULL);
200*61046927SAndroid Build Coastguard Worker    for (unsigned i = 0; i < glsl_get_components(c->type); i++) {
201*61046927SAndroid Build Coastguard Worker       switch (c->type->base_type) {
202*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_UINT16:
203*61046927SAndroid Build Coastguard Worker          if ((ismin && b->value.u16[i] < c->value.u16[i]) ||
204*61046927SAndroid Build Coastguard Worker              (!ismin && b->value.u16[i] > c->value.u16[i]))
205*61046927SAndroid Build Coastguard Worker             c->value.u16[i] = b->value.u16[i];
206*61046927SAndroid Build Coastguard Worker          break;
207*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_INT16:
208*61046927SAndroid Build Coastguard Worker          if ((ismin && b->value.i16[i] < c->value.i16[i]) ||
209*61046927SAndroid Build Coastguard Worker              (!ismin && b->value.i16[i] > c->value.i16[i]))
210*61046927SAndroid Build Coastguard Worker             c->value.i16[i] = b->value.i16[i];
211*61046927SAndroid Build Coastguard Worker          break;
212*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_UINT:
213*61046927SAndroid Build Coastguard Worker          if ((ismin && b->value.u[i] < c->value.u[i]) ||
214*61046927SAndroid Build Coastguard Worker              (!ismin && b->value.u[i] > c->value.u[i]))
215*61046927SAndroid Build Coastguard Worker             c->value.u[i] = b->value.u[i];
216*61046927SAndroid Build Coastguard Worker          break;
217*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_INT:
218*61046927SAndroid Build Coastguard Worker          if ((ismin && b->value.i[i] < c->value.i[i]) ||
219*61046927SAndroid Build Coastguard Worker              (!ismin && b->value.i[i] > c->value.i[i]))
220*61046927SAndroid Build Coastguard Worker             c->value.i[i] = b->value.i[i];
221*61046927SAndroid Build Coastguard Worker          break;
222*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_FLOAT16: {
223*61046927SAndroid Build Coastguard Worker          float bf = _mesa_half_to_float(b->value.f16[i]);
224*61046927SAndroid Build Coastguard Worker          float cf = _mesa_half_to_float(c->value.f16[i]);
225*61046927SAndroid Build Coastguard Worker          if ((ismin && bf < cf) || (!ismin && bf > cf))
226*61046927SAndroid Build Coastguard Worker             c->value.f16[i] = b->value.f16[i];
227*61046927SAndroid Build Coastguard Worker          break;
228*61046927SAndroid Build Coastguard Worker       }
229*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_FLOAT:
230*61046927SAndroid Build Coastguard Worker          if ((ismin && b->value.f[i] < c->value.f[i]) ||
231*61046927SAndroid Build Coastguard Worker              (!ismin && b->value.f[i] > c->value.f[i]))
232*61046927SAndroid Build Coastguard Worker             c->value.f[i] = b->value.f[i];
233*61046927SAndroid Build Coastguard Worker          break;
234*61046927SAndroid Build Coastguard Worker       case GLSL_TYPE_DOUBLE:
235*61046927SAndroid Build Coastguard Worker          if ((ismin && b->value.d[i] < c->value.d[i]) ||
236*61046927SAndroid Build Coastguard Worker              (!ismin && b->value.d[i] > c->value.d[i]))
237*61046927SAndroid Build Coastguard Worker             c->value.d[i] = b->value.d[i];
238*61046927SAndroid Build Coastguard Worker          break;
239*61046927SAndroid Build Coastguard Worker       default:
240*61046927SAndroid Build Coastguard Worker          assert(!"not reached");
241*61046927SAndroid Build Coastguard Worker       }
242*61046927SAndroid Build Coastguard Worker    }
243*61046927SAndroid Build Coastguard Worker    return c;
244*61046927SAndroid Build Coastguard Worker }
245*61046927SAndroid Build Coastguard Worker 
246*61046927SAndroid Build Coastguard Worker static ir_constant *
smaller_constant(ir_constant * a,ir_constant * b)247*61046927SAndroid Build Coastguard Worker smaller_constant(ir_constant *a, ir_constant *b)
248*61046927SAndroid Build Coastguard Worker {
249*61046927SAndroid Build Coastguard Worker    assert(a != NULL);
250*61046927SAndroid Build Coastguard Worker    assert(b != NULL);
251*61046927SAndroid Build Coastguard Worker 
252*61046927SAndroid Build Coastguard Worker    enum compare_components_result ret = compare_components(a, b);
253*61046927SAndroid Build Coastguard Worker    if (ret == MIXED)
254*61046927SAndroid Build Coastguard Worker       return combine_constant(true, a, b);
255*61046927SAndroid Build Coastguard Worker    else if (ret < EQUAL)
256*61046927SAndroid Build Coastguard Worker       return a;
257*61046927SAndroid Build Coastguard Worker    else
258*61046927SAndroid Build Coastguard Worker       return b;
259*61046927SAndroid Build Coastguard Worker }
260*61046927SAndroid Build Coastguard Worker 
261*61046927SAndroid Build Coastguard Worker static ir_constant *
larger_constant(ir_constant * a,ir_constant * b)262*61046927SAndroid Build Coastguard Worker larger_constant(ir_constant *a, ir_constant *b)
263*61046927SAndroid Build Coastguard Worker {
264*61046927SAndroid Build Coastguard Worker    assert(a != NULL);
265*61046927SAndroid Build Coastguard Worker    assert(b != NULL);
266*61046927SAndroid Build Coastguard Worker 
267*61046927SAndroid Build Coastguard Worker    enum compare_components_result ret = compare_components(a, b);
268*61046927SAndroid Build Coastguard Worker    if (ret == MIXED)
269*61046927SAndroid Build Coastguard Worker       return combine_constant(false, a, b);
270*61046927SAndroid Build Coastguard Worker    else if (ret < EQUAL)
271*61046927SAndroid Build Coastguard Worker       return b;
272*61046927SAndroid Build Coastguard Worker    else
273*61046927SAndroid Build Coastguard Worker       return a;
274*61046927SAndroid Build Coastguard Worker }
275*61046927SAndroid Build Coastguard Worker 
276*61046927SAndroid Build Coastguard Worker /* Combines two ranges by doing an element-wise min() / max() depending on the
277*61046927SAndroid Build Coastguard Worker  * operation.
278*61046927SAndroid Build Coastguard Worker  */
279*61046927SAndroid Build Coastguard Worker static minmax_range
combine_range(minmax_range r0,minmax_range r1,bool ismin)280*61046927SAndroid Build Coastguard Worker combine_range(minmax_range r0, minmax_range r1, bool ismin)
281*61046927SAndroid Build Coastguard Worker {
282*61046927SAndroid Build Coastguard Worker    minmax_range ret;
283*61046927SAndroid Build Coastguard Worker 
284*61046927SAndroid Build Coastguard Worker    if (!r0.low) {
285*61046927SAndroid Build Coastguard Worker       ret.low = ismin ? r0.low : r1.low;
286*61046927SAndroid Build Coastguard Worker    } else if (!r1.low) {
287*61046927SAndroid Build Coastguard Worker       ret.low = ismin ? r1.low : r0.low;
288*61046927SAndroid Build Coastguard Worker    } else {
289*61046927SAndroid Build Coastguard Worker       ret.low = ismin ? smaller_constant(r0.low, r1.low) :
290*61046927SAndroid Build Coastguard Worker          larger_constant(r0.low, r1.low);
291*61046927SAndroid Build Coastguard Worker    }
292*61046927SAndroid Build Coastguard Worker 
293*61046927SAndroid Build Coastguard Worker    if (!r0.high) {
294*61046927SAndroid Build Coastguard Worker       ret.high = ismin ? r1.high : r0.high;
295*61046927SAndroid Build Coastguard Worker    } else if (!r1.high) {
296*61046927SAndroid Build Coastguard Worker       ret.high = ismin ? r0.high : r1.high;
297*61046927SAndroid Build Coastguard Worker    } else {
298*61046927SAndroid Build Coastguard Worker       ret.high = ismin ? smaller_constant(r0.high, r1.high) :
299*61046927SAndroid Build Coastguard Worker          larger_constant(r0.high, r1.high);
300*61046927SAndroid Build Coastguard Worker    }
301*61046927SAndroid Build Coastguard Worker 
302*61046927SAndroid Build Coastguard Worker    return ret;
303*61046927SAndroid Build Coastguard Worker }
304*61046927SAndroid Build Coastguard Worker 
305*61046927SAndroid Build Coastguard Worker /* Returns a range so that lower limit is the larger of the two lower limits,
306*61046927SAndroid Build Coastguard Worker  * and higher limit is the smaller of the two higher limits.
307*61046927SAndroid Build Coastguard Worker  */
308*61046927SAndroid Build Coastguard Worker static minmax_range
range_intersection(minmax_range r0,minmax_range r1)309*61046927SAndroid Build Coastguard Worker range_intersection(minmax_range r0, minmax_range r1)
310*61046927SAndroid Build Coastguard Worker {
311*61046927SAndroid Build Coastguard Worker    minmax_range ret;
312*61046927SAndroid Build Coastguard Worker 
313*61046927SAndroid Build Coastguard Worker    if (!r0.low)
314*61046927SAndroid Build Coastguard Worker       ret.low = r1.low;
315*61046927SAndroid Build Coastguard Worker    else if (!r1.low)
316*61046927SAndroid Build Coastguard Worker       ret.low = r0.low;
317*61046927SAndroid Build Coastguard Worker    else
318*61046927SAndroid Build Coastguard Worker       ret.low = larger_constant(r0.low, r1.low);
319*61046927SAndroid Build Coastguard Worker 
320*61046927SAndroid Build Coastguard Worker    if (!r0.high)
321*61046927SAndroid Build Coastguard Worker       ret.high = r1.high;
322*61046927SAndroid Build Coastguard Worker    else if (!r1.high)
323*61046927SAndroid Build Coastguard Worker       ret.high = r0.high;
324*61046927SAndroid Build Coastguard Worker    else
325*61046927SAndroid Build Coastguard Worker       ret.high = smaller_constant(r0.high, r1.high);
326*61046927SAndroid Build Coastguard Worker 
327*61046927SAndroid Build Coastguard Worker    return ret;
328*61046927SAndroid Build Coastguard Worker }
329*61046927SAndroid Build Coastguard Worker 
330*61046927SAndroid Build Coastguard Worker static minmax_range
get_range(ir_rvalue * rval)331*61046927SAndroid Build Coastguard Worker get_range(ir_rvalue *rval)
332*61046927SAndroid Build Coastguard Worker {
333*61046927SAndroid Build Coastguard Worker    ir_expression *expr = rval->as_expression();
334*61046927SAndroid Build Coastguard Worker    if (expr && (expr->operation == ir_binop_min ||
335*61046927SAndroid Build Coastguard Worker                 expr->operation == ir_binop_max)) {
336*61046927SAndroid Build Coastguard Worker       minmax_range r0 = get_range(expr->operands[0]);
337*61046927SAndroid Build Coastguard Worker       minmax_range r1 = get_range(expr->operands[1]);
338*61046927SAndroid Build Coastguard Worker       return combine_range(r0, r1, expr->operation == ir_binop_min);
339*61046927SAndroid Build Coastguard Worker    }
340*61046927SAndroid Build Coastguard Worker 
341*61046927SAndroid Build Coastguard Worker    ir_constant *c = rval->as_constant();
342*61046927SAndroid Build Coastguard Worker    if (c) {
343*61046927SAndroid Build Coastguard Worker       return minmax_range(c, c);
344*61046927SAndroid Build Coastguard Worker    }
345*61046927SAndroid Build Coastguard Worker 
346*61046927SAndroid Build Coastguard Worker    return minmax_range();
347*61046927SAndroid Build Coastguard Worker }
348*61046927SAndroid Build Coastguard Worker 
349*61046927SAndroid Build Coastguard Worker /**
350*61046927SAndroid Build Coastguard Worker  * Prunes a min/max expression considering the base range of the parent
351*61046927SAndroid Build Coastguard Worker  * min/max expression.
352*61046927SAndroid Build Coastguard Worker  *
353*61046927SAndroid Build Coastguard Worker  * @param baserange the range that the parents of this min/max expression
354*61046927SAndroid Build Coastguard Worker  * in the min/max tree will clamp its value to.
355*61046927SAndroid Build Coastguard Worker  */
356*61046927SAndroid Build Coastguard Worker ir_rvalue *
prune_expression(ir_expression * expr,minmax_range baserange)357*61046927SAndroid Build Coastguard Worker ir_minmax_visitor::prune_expression(ir_expression *expr, minmax_range baserange)
358*61046927SAndroid Build Coastguard Worker {
359*61046927SAndroid Build Coastguard Worker    assert(expr->operation == ir_binop_min ||
360*61046927SAndroid Build Coastguard Worker           expr->operation == ir_binop_max);
361*61046927SAndroid Build Coastguard Worker 
362*61046927SAndroid Build Coastguard Worker    bool ismin = expr->operation == ir_binop_min;
363*61046927SAndroid Build Coastguard Worker    minmax_range limits[2];
364*61046927SAndroid Build Coastguard Worker 
365*61046927SAndroid Build Coastguard Worker    /* Recurse to get the ranges for each of the subtrees of this
366*61046927SAndroid Build Coastguard Worker     * expression. We need to do this as a separate step because we need to
367*61046927SAndroid Build Coastguard Worker     * know the ranges of each of the subtrees before we prune either one.
368*61046927SAndroid Build Coastguard Worker     * Consider something like this:
369*61046927SAndroid Build Coastguard Worker     *
370*61046927SAndroid Build Coastguard Worker     *        max
371*61046927SAndroid Build Coastguard Worker     *     /       \
372*61046927SAndroid Build Coastguard Worker     *    max     max
373*61046927SAndroid Build Coastguard Worker     *   /   \   /   \
374*61046927SAndroid Build Coastguard Worker     *  3    a   b    2
375*61046927SAndroid Build Coastguard Worker     *
376*61046927SAndroid Build Coastguard Worker     * We would like to prune away the max on the bottom-right, but to do so
377*61046927SAndroid Build Coastguard Worker     * we need to know the range of the expression on the left beforehand,
378*61046927SAndroid Build Coastguard Worker     * and there's no guarantee that we will visit either subtree in a
379*61046927SAndroid Build Coastguard Worker     * particular order.
380*61046927SAndroid Build Coastguard Worker     */
381*61046927SAndroid Build Coastguard Worker    for (unsigned i = 0; i < 2; ++i)
382*61046927SAndroid Build Coastguard Worker       limits[i] = get_range(expr->operands[i]);
383*61046927SAndroid Build Coastguard Worker 
384*61046927SAndroid Build Coastguard Worker    for (unsigned i = 0; i < 2; ++i) {
385*61046927SAndroid Build Coastguard Worker       bool is_redundant = false;
386*61046927SAndroid Build Coastguard Worker 
387*61046927SAndroid Build Coastguard Worker       enum compare_components_result cr = LESS;
388*61046927SAndroid Build Coastguard Worker       if (ismin) {
389*61046927SAndroid Build Coastguard Worker          /* If this operand will always be greater than the other one, it's
390*61046927SAndroid Build Coastguard Worker           * redundant.
391*61046927SAndroid Build Coastguard Worker           */
392*61046927SAndroid Build Coastguard Worker          if (limits[i].low && limits[1 - i].high) {
393*61046927SAndroid Build Coastguard Worker                cr = compare_components(limits[i].low, limits[1 - i].high);
394*61046927SAndroid Build Coastguard Worker             if (cr >= EQUAL && cr != MIXED)
395*61046927SAndroid Build Coastguard Worker                is_redundant = true;
396*61046927SAndroid Build Coastguard Worker          }
397*61046927SAndroid Build Coastguard Worker          /* If this operand is always greater than baserange, then even if
398*61046927SAndroid Build Coastguard Worker           * it's smaller than the other one it'll get clamped, so it's
399*61046927SAndroid Build Coastguard Worker           * redundant.
400*61046927SAndroid Build Coastguard Worker           */
401*61046927SAndroid Build Coastguard Worker          if (!is_redundant && limits[i].low && baserange.high) {
402*61046927SAndroid Build Coastguard Worker             cr = compare_components(limits[i].low, baserange.high);
403*61046927SAndroid Build Coastguard Worker             if (cr > EQUAL && cr != MIXED)
404*61046927SAndroid Build Coastguard Worker                is_redundant = true;
405*61046927SAndroid Build Coastguard Worker          }
406*61046927SAndroid Build Coastguard Worker       } else {
407*61046927SAndroid Build Coastguard Worker          /* If this operand will always be lower than the other one, it's
408*61046927SAndroid Build Coastguard Worker           * redundant.
409*61046927SAndroid Build Coastguard Worker           */
410*61046927SAndroid Build Coastguard Worker          if (limits[i].high && limits[1 - i].low) {
411*61046927SAndroid Build Coastguard Worker             cr = compare_components(limits[i].high, limits[1 - i].low);
412*61046927SAndroid Build Coastguard Worker             if (cr <= EQUAL)
413*61046927SAndroid Build Coastguard Worker                is_redundant = true;
414*61046927SAndroid Build Coastguard Worker          }
415*61046927SAndroid Build Coastguard Worker          /* If this operand is always lower than baserange, then even if
416*61046927SAndroid Build Coastguard Worker           * it's greater than the other one it'll get clamped, so it's
417*61046927SAndroid Build Coastguard Worker           * redundant.
418*61046927SAndroid Build Coastguard Worker           */
419*61046927SAndroid Build Coastguard Worker          if (!is_redundant && limits[i].high && baserange.low) {
420*61046927SAndroid Build Coastguard Worker             cr = compare_components(limits[i].high, baserange.low);
421*61046927SAndroid Build Coastguard Worker             if (cr < EQUAL)
422*61046927SAndroid Build Coastguard Worker                is_redundant = true;
423*61046927SAndroid Build Coastguard Worker          }
424*61046927SAndroid Build Coastguard Worker       }
425*61046927SAndroid Build Coastguard Worker 
426*61046927SAndroid Build Coastguard Worker       if (is_redundant) {
427*61046927SAndroid Build Coastguard Worker          progress = true;
428*61046927SAndroid Build Coastguard Worker 
429*61046927SAndroid Build Coastguard Worker          /* Recurse if necessary. */
430*61046927SAndroid Build Coastguard Worker          ir_expression *op_expr = expr->operands[1 - i]->as_expression();
431*61046927SAndroid Build Coastguard Worker          if (op_expr && (op_expr->operation == ir_binop_min ||
432*61046927SAndroid Build Coastguard Worker                          op_expr->operation == ir_binop_max)) {
433*61046927SAndroid Build Coastguard Worker             return prune_expression(op_expr, baserange);
434*61046927SAndroid Build Coastguard Worker          }
435*61046927SAndroid Build Coastguard Worker 
436*61046927SAndroid Build Coastguard Worker          return expr->operands[1 - i];
437*61046927SAndroid Build Coastguard Worker       } else if (cr == MIXED) {
438*61046927SAndroid Build Coastguard Worker          /* If we have mixed vector operands, we can try to resolve the minmax
439*61046927SAndroid Build Coastguard Worker           * expression by doing a component-wise minmax:
440*61046927SAndroid Build Coastguard Worker           *
441*61046927SAndroid Build Coastguard Worker           *             min                          min
442*61046927SAndroid Build Coastguard Worker           *           /    \                       /    \
443*61046927SAndroid Build Coastguard Worker           *         min     a       ===>        [1,1]    a
444*61046927SAndroid Build Coastguard Worker           *       /    \
445*61046927SAndroid Build Coastguard Worker           *    [1,3]   [3,1]
446*61046927SAndroid Build Coastguard Worker           *
447*61046927SAndroid Build Coastguard Worker           */
448*61046927SAndroid Build Coastguard Worker          ir_constant *a = expr->operands[0]->as_constant();
449*61046927SAndroid Build Coastguard Worker          ir_constant *b = expr->operands[1]->as_constant();
450*61046927SAndroid Build Coastguard Worker          if (a && b)
451*61046927SAndroid Build Coastguard Worker             return combine_constant(ismin, a, b);
452*61046927SAndroid Build Coastguard Worker       }
453*61046927SAndroid Build Coastguard Worker    }
454*61046927SAndroid Build Coastguard Worker 
455*61046927SAndroid Build Coastguard Worker    /* Now recurse to operands giving them the proper baserange. The baserange
456*61046927SAndroid Build Coastguard Worker     * to pass is the intersection of our baserange and the other operand's
457*61046927SAndroid Build Coastguard Worker     * limit with one of the ranges unlimited. If we can't compute a valid
458*61046927SAndroid Build Coastguard Worker     * intersection, we use the current baserange.
459*61046927SAndroid Build Coastguard Worker     */
460*61046927SAndroid Build Coastguard Worker    for (unsigned i = 0; i < 2; ++i) {
461*61046927SAndroid Build Coastguard Worker       ir_expression *op_expr = expr->operands[i]->as_expression();
462*61046927SAndroid Build Coastguard Worker       if (op_expr && (op_expr->operation == ir_binop_min ||
463*61046927SAndroid Build Coastguard Worker                       op_expr->operation == ir_binop_max)) {
464*61046927SAndroid Build Coastguard Worker          /* We can only compute a new baserange for this operand if we managed
465*61046927SAndroid Build Coastguard Worker           * to compute a valid range for the other operand.
466*61046927SAndroid Build Coastguard Worker           */
467*61046927SAndroid Build Coastguard Worker          if (ismin)
468*61046927SAndroid Build Coastguard Worker             limits[1 - i].low = NULL;
469*61046927SAndroid Build Coastguard Worker          else
470*61046927SAndroid Build Coastguard Worker             limits[1 - i].high = NULL;
471*61046927SAndroid Build Coastguard Worker          minmax_range base = range_intersection(limits[1 - i], baserange);
472*61046927SAndroid Build Coastguard Worker          expr->operands[i] = prune_expression(op_expr, base);
473*61046927SAndroid Build Coastguard Worker       }
474*61046927SAndroid Build Coastguard Worker    }
475*61046927SAndroid Build Coastguard Worker 
476*61046927SAndroid Build Coastguard Worker    /* If we got here we could not discard any of the operands of the minmax
477*61046927SAndroid Build Coastguard Worker     * expression, but we can still try to resolve the expression if both
478*61046927SAndroid Build Coastguard Worker     * operands are constant. We do this after the loop above, to make sure
479*61046927SAndroid Build Coastguard Worker     * that if our operands are minmax expressions we have tried to prune them
480*61046927SAndroid Build Coastguard Worker     * first (hopefully reducing them to constants).
481*61046927SAndroid Build Coastguard Worker     */
482*61046927SAndroid Build Coastguard Worker    ir_constant *a = expr->operands[0]->as_constant();
483*61046927SAndroid Build Coastguard Worker    ir_constant *b = expr->operands[1]->as_constant();
484*61046927SAndroid Build Coastguard Worker    if (a && b)
485*61046927SAndroid Build Coastguard Worker       return combine_constant(ismin, a, b);
486*61046927SAndroid Build Coastguard Worker 
487*61046927SAndroid Build Coastguard Worker    return expr;
488*61046927SAndroid Build Coastguard Worker }
489*61046927SAndroid Build Coastguard Worker 
490*61046927SAndroid Build Coastguard Worker static ir_rvalue *
swizzle_if_required(ir_expression * expr,ir_rvalue * rval)491*61046927SAndroid Build Coastguard Worker swizzle_if_required(ir_expression *expr, ir_rvalue *rval)
492*61046927SAndroid Build Coastguard Worker {
493*61046927SAndroid Build Coastguard Worker    if (glsl_type_is_vector(expr->type) && glsl_type_is_scalar(rval->type)) {
494*61046927SAndroid Build Coastguard Worker       return swizzle(rval, SWIZZLE_XXXX, expr->type->vector_elements);
495*61046927SAndroid Build Coastguard Worker    } else {
496*61046927SAndroid Build Coastguard Worker       return rval;
497*61046927SAndroid Build Coastguard Worker    }
498*61046927SAndroid Build Coastguard Worker }
499*61046927SAndroid Build Coastguard Worker 
500*61046927SAndroid Build Coastguard Worker void
handle_rvalue(ir_rvalue ** rvalue)501*61046927SAndroid Build Coastguard Worker ir_minmax_visitor::handle_rvalue(ir_rvalue **rvalue)
502*61046927SAndroid Build Coastguard Worker {
503*61046927SAndroid Build Coastguard Worker    if (!*rvalue)
504*61046927SAndroid Build Coastguard Worker       return;
505*61046927SAndroid Build Coastguard Worker 
506*61046927SAndroid Build Coastguard Worker    ir_expression *expr = (*rvalue)->as_expression();
507*61046927SAndroid Build Coastguard Worker    if (!expr || (expr->operation != ir_binop_min &&
508*61046927SAndroid Build Coastguard Worker                  expr->operation != ir_binop_max))
509*61046927SAndroid Build Coastguard Worker       return;
510*61046927SAndroid Build Coastguard Worker 
511*61046927SAndroid Build Coastguard Worker    ir_rvalue *new_rvalue = prune_expression(expr, minmax_range());
512*61046927SAndroid Build Coastguard Worker    if (new_rvalue == *rvalue)
513*61046927SAndroid Build Coastguard Worker       return;
514*61046927SAndroid Build Coastguard Worker 
515*61046927SAndroid Build Coastguard Worker    /* If the expression type is a vector and the optimization leaves a scalar
516*61046927SAndroid Build Coastguard Worker     * as the result, we need to turn it into a vector.
517*61046927SAndroid Build Coastguard Worker     */
518*61046927SAndroid Build Coastguard Worker    *rvalue = swizzle_if_required(expr, new_rvalue);
519*61046927SAndroid Build Coastguard Worker 
520*61046927SAndroid Build Coastguard Worker    progress = true;
521*61046927SAndroid Build Coastguard Worker }
522*61046927SAndroid Build Coastguard Worker 
523*61046927SAndroid Build Coastguard Worker }
524*61046927SAndroid Build Coastguard Worker 
525*61046927SAndroid Build Coastguard Worker bool
do_minmax_prune(exec_list * instructions)526*61046927SAndroid Build Coastguard Worker do_minmax_prune(exec_list *instructions)
527*61046927SAndroid Build Coastguard Worker {
528*61046927SAndroid Build Coastguard Worker    ir_minmax_visitor v;
529*61046927SAndroid Build Coastguard Worker 
530*61046927SAndroid Build Coastguard Worker    visit_list_elements(&v, instructions);
531*61046927SAndroid Build Coastguard Worker 
532*61046927SAndroid Build Coastguard Worker    return v.progress;
533*61046927SAndroid Build Coastguard Worker }
534