1*61046927SAndroid Build Coastguard Worker /*
2*61046927SAndroid Build Coastguard Worker * Copyright © 2014 Rob Clark <[email protected]>
3*61046927SAndroid Build Coastguard Worker * SPDX-License-Identifier: MIT
4*61046927SAndroid Build Coastguard Worker *
5*61046927SAndroid Build Coastguard Worker * Authors:
6*61046927SAndroid Build Coastguard Worker * Rob Clark <[email protected]>
7*61046927SAndroid Build Coastguard Worker */
8*61046927SAndroid Build Coastguard Worker
9*61046927SAndroid Build Coastguard Worker #include <math.h>
10*61046927SAndroid Build Coastguard Worker #include "util/half_float.h"
11*61046927SAndroid Build Coastguard Worker #include "util/u_math.h"
12*61046927SAndroid Build Coastguard Worker
13*61046927SAndroid Build Coastguard Worker #include "ir3.h"
14*61046927SAndroid Build Coastguard Worker #include "ir3_compiler.h"
15*61046927SAndroid Build Coastguard Worker #include "ir3_shader.h"
16*61046927SAndroid Build Coastguard Worker
17*61046927SAndroid Build Coastguard Worker #define swap(a, b) \
18*61046927SAndroid Build Coastguard Worker do { \
19*61046927SAndroid Build Coastguard Worker __typeof(a) __tmp = (a); \
20*61046927SAndroid Build Coastguard Worker (a) = (b); \
21*61046927SAndroid Build Coastguard Worker (b) = __tmp; \
22*61046927SAndroid Build Coastguard Worker } while (0)
23*61046927SAndroid Build Coastguard Worker
24*61046927SAndroid Build Coastguard Worker /*
25*61046927SAndroid Build Coastguard Worker * Copy Propagate:
26*61046927SAndroid Build Coastguard Worker */
27*61046927SAndroid Build Coastguard Worker
28*61046927SAndroid Build Coastguard Worker struct ir3_cp_ctx {
29*61046927SAndroid Build Coastguard Worker struct ir3 *shader;
30*61046927SAndroid Build Coastguard Worker struct ir3_shader_variant *so;
31*61046927SAndroid Build Coastguard Worker bool progress;
32*61046927SAndroid Build Coastguard Worker };
33*61046927SAndroid Build Coastguard Worker
34*61046927SAndroid Build Coastguard Worker /* is it a type preserving mov, with ok flags?
35*61046927SAndroid Build Coastguard Worker *
36*61046927SAndroid Build Coastguard Worker * @instr: the mov to consider removing
37*61046927SAndroid Build Coastguard Worker * @dst_instr: the instruction consuming the mov (instr)
38*61046927SAndroid Build Coastguard Worker *
39*61046927SAndroid Build Coastguard Worker * TODO maybe drop allow_flags since this is only false when dst is
40*61046927SAndroid Build Coastguard Worker * NULL (ie. outputs)
41*61046927SAndroid Build Coastguard Worker */
42*61046927SAndroid Build Coastguard Worker static bool
is_eligible_mov(struct ir3_instruction * instr,struct ir3_instruction * dst_instr,bool allow_flags)43*61046927SAndroid Build Coastguard Worker is_eligible_mov(struct ir3_instruction *instr,
44*61046927SAndroid Build Coastguard Worker struct ir3_instruction *dst_instr, bool allow_flags)
45*61046927SAndroid Build Coastguard Worker {
46*61046927SAndroid Build Coastguard Worker if (is_same_type_mov(instr)) {
47*61046927SAndroid Build Coastguard Worker struct ir3_register *dst = instr->dsts[0];
48*61046927SAndroid Build Coastguard Worker struct ir3_register *src = instr->srcs[0];
49*61046927SAndroid Build Coastguard Worker struct ir3_instruction *src_instr = ssa(src);
50*61046927SAndroid Build Coastguard Worker
51*61046927SAndroid Build Coastguard Worker /* only if mov src is SSA (not const/immed): */
52*61046927SAndroid Build Coastguard Worker if (!src_instr)
53*61046927SAndroid Build Coastguard Worker return false;
54*61046927SAndroid Build Coastguard Worker
55*61046927SAndroid Build Coastguard Worker /* no indirect: */
56*61046927SAndroid Build Coastguard Worker if (dst->flags & IR3_REG_RELATIV)
57*61046927SAndroid Build Coastguard Worker return false;
58*61046927SAndroid Build Coastguard Worker if (src->flags & IR3_REG_RELATIV)
59*61046927SAndroid Build Coastguard Worker return false;
60*61046927SAndroid Build Coastguard Worker
61*61046927SAndroid Build Coastguard Worker if (src->flags & IR3_REG_ARRAY)
62*61046927SAndroid Build Coastguard Worker return false;
63*61046927SAndroid Build Coastguard Worker
64*61046927SAndroid Build Coastguard Worker if (!allow_flags)
65*61046927SAndroid Build Coastguard Worker if (src->flags & (IR3_REG_FABS | IR3_REG_FNEG | IR3_REG_SABS |
66*61046927SAndroid Build Coastguard Worker IR3_REG_SNEG | IR3_REG_BNOT))
67*61046927SAndroid Build Coastguard Worker return false;
68*61046927SAndroid Build Coastguard Worker
69*61046927SAndroid Build Coastguard Worker return true;
70*61046927SAndroid Build Coastguard Worker }
71*61046927SAndroid Build Coastguard Worker return false;
72*61046927SAndroid Build Coastguard Worker }
73*61046927SAndroid Build Coastguard Worker
74*61046927SAndroid Build Coastguard Worker /* propagate register flags from src to dst.. negates need special
75*61046927SAndroid Build Coastguard Worker * handling to cancel each other out.
76*61046927SAndroid Build Coastguard Worker */
77*61046927SAndroid Build Coastguard Worker static void
combine_flags(unsigned * dstflags,struct ir3_instruction * src)78*61046927SAndroid Build Coastguard Worker combine_flags(unsigned *dstflags, struct ir3_instruction *src)
79*61046927SAndroid Build Coastguard Worker {
80*61046927SAndroid Build Coastguard Worker unsigned srcflags = src->srcs[0]->flags;
81*61046927SAndroid Build Coastguard Worker
82*61046927SAndroid Build Coastguard Worker /* if what we are combining into already has (abs) flags,
83*61046927SAndroid Build Coastguard Worker * we can drop (neg) from src:
84*61046927SAndroid Build Coastguard Worker */
85*61046927SAndroid Build Coastguard Worker if (*dstflags & IR3_REG_FABS)
86*61046927SAndroid Build Coastguard Worker srcflags &= ~IR3_REG_FNEG;
87*61046927SAndroid Build Coastguard Worker if (*dstflags & IR3_REG_SABS)
88*61046927SAndroid Build Coastguard Worker srcflags &= ~IR3_REG_SNEG;
89*61046927SAndroid Build Coastguard Worker
90*61046927SAndroid Build Coastguard Worker if (srcflags & IR3_REG_FABS)
91*61046927SAndroid Build Coastguard Worker *dstflags |= IR3_REG_FABS;
92*61046927SAndroid Build Coastguard Worker if (srcflags & IR3_REG_SABS)
93*61046927SAndroid Build Coastguard Worker *dstflags |= IR3_REG_SABS;
94*61046927SAndroid Build Coastguard Worker if (srcflags & IR3_REG_FNEG)
95*61046927SAndroid Build Coastguard Worker *dstflags ^= IR3_REG_FNEG;
96*61046927SAndroid Build Coastguard Worker if (srcflags & IR3_REG_SNEG)
97*61046927SAndroid Build Coastguard Worker *dstflags ^= IR3_REG_SNEG;
98*61046927SAndroid Build Coastguard Worker if (srcflags & IR3_REG_BNOT)
99*61046927SAndroid Build Coastguard Worker *dstflags ^= IR3_REG_BNOT;
100*61046927SAndroid Build Coastguard Worker
101*61046927SAndroid Build Coastguard Worker *dstflags &= ~(IR3_REG_SSA | IR3_REG_SHARED);
102*61046927SAndroid Build Coastguard Worker *dstflags |= srcflags & IR3_REG_SSA;
103*61046927SAndroid Build Coastguard Worker *dstflags |= srcflags & IR3_REG_CONST;
104*61046927SAndroid Build Coastguard Worker *dstflags |= srcflags & IR3_REG_IMMED;
105*61046927SAndroid Build Coastguard Worker *dstflags |= srcflags & IR3_REG_RELATIV;
106*61046927SAndroid Build Coastguard Worker *dstflags |= srcflags & IR3_REG_ARRAY;
107*61046927SAndroid Build Coastguard Worker *dstflags |= srcflags & IR3_REG_SHARED;
108*61046927SAndroid Build Coastguard Worker
109*61046927SAndroid Build Coastguard Worker /* if src of the src is boolean we can drop the (abs) since we know
110*61046927SAndroid Build Coastguard Worker * the source value is already a postitive integer. This cleans
111*61046927SAndroid Build Coastguard Worker * up the absnegs that get inserted when converting between nir and
112*61046927SAndroid Build Coastguard Worker * native boolean (see ir3_b2n/n2b)
113*61046927SAndroid Build Coastguard Worker */
114*61046927SAndroid Build Coastguard Worker struct ir3_instruction *srcsrc = ssa(src->srcs[0]);
115*61046927SAndroid Build Coastguard Worker if (srcsrc && is_bool(srcsrc))
116*61046927SAndroid Build Coastguard Worker *dstflags &= ~IR3_REG_SABS;
117*61046927SAndroid Build Coastguard Worker }
118*61046927SAndroid Build Coastguard Worker
119*61046927SAndroid Build Coastguard Worker /* Tries lowering an immediate register argument to a const buffer access by
120*61046927SAndroid Build Coastguard Worker * adding to the list of immediates to be pushed to the const buffer when
121*61046927SAndroid Build Coastguard Worker * switching to this shader.
122*61046927SAndroid Build Coastguard Worker */
123*61046927SAndroid Build Coastguard Worker static bool
lower_immed(struct ir3_cp_ctx * ctx,struct ir3_instruction * instr,unsigned n,struct ir3_register * reg,unsigned new_flags)124*61046927SAndroid Build Coastguard Worker lower_immed(struct ir3_cp_ctx *ctx, struct ir3_instruction *instr, unsigned n,
125*61046927SAndroid Build Coastguard Worker struct ir3_register *reg, unsigned new_flags)
126*61046927SAndroid Build Coastguard Worker {
127*61046927SAndroid Build Coastguard Worker if (ctx->shader->compiler->load_shader_consts_via_preamble)
128*61046927SAndroid Build Coastguard Worker return false;
129*61046927SAndroid Build Coastguard Worker
130*61046927SAndroid Build Coastguard Worker if (!(new_flags & IR3_REG_IMMED))
131*61046927SAndroid Build Coastguard Worker return false;
132*61046927SAndroid Build Coastguard Worker
133*61046927SAndroid Build Coastguard Worker new_flags &= ~IR3_REG_IMMED;
134*61046927SAndroid Build Coastguard Worker new_flags |= IR3_REG_CONST;
135*61046927SAndroid Build Coastguard Worker
136*61046927SAndroid Build Coastguard Worker if (!ir3_valid_flags(instr, n, new_flags))
137*61046927SAndroid Build Coastguard Worker return false;
138*61046927SAndroid Build Coastguard Worker
139*61046927SAndroid Build Coastguard Worker reg = ir3_reg_clone(ctx->shader, reg);
140*61046927SAndroid Build Coastguard Worker
141*61046927SAndroid Build Coastguard Worker /* Half constant registers seems to handle only 32-bit values
142*61046927SAndroid Build Coastguard Worker * within floating-point opcodes. So convert back to 32-bit values.
143*61046927SAndroid Build Coastguard Worker */
144*61046927SAndroid Build Coastguard Worker bool f_opcode =
145*61046927SAndroid Build Coastguard Worker (is_cat2_float(instr->opc) || is_cat3_float(instr->opc)) ? true : false;
146*61046927SAndroid Build Coastguard Worker if (f_opcode && (new_flags & IR3_REG_HALF))
147*61046927SAndroid Build Coastguard Worker reg->uim_val = fui(_mesa_half_to_float(reg->uim_val));
148*61046927SAndroid Build Coastguard Worker
149*61046927SAndroid Build Coastguard Worker /* in some cases, there are restrictions on (abs)/(neg) plus const..
150*61046927SAndroid Build Coastguard Worker * so just evaluate those and clear the flags:
151*61046927SAndroid Build Coastguard Worker */
152*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_SABS) {
153*61046927SAndroid Build Coastguard Worker reg->iim_val = abs(reg->iim_val);
154*61046927SAndroid Build Coastguard Worker new_flags &= ~IR3_REG_SABS;
155*61046927SAndroid Build Coastguard Worker }
156*61046927SAndroid Build Coastguard Worker
157*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_FABS) {
158*61046927SAndroid Build Coastguard Worker reg->fim_val = fabs(reg->fim_val);
159*61046927SAndroid Build Coastguard Worker new_flags &= ~IR3_REG_FABS;
160*61046927SAndroid Build Coastguard Worker }
161*61046927SAndroid Build Coastguard Worker
162*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_SNEG) {
163*61046927SAndroid Build Coastguard Worker reg->iim_val = -reg->iim_val;
164*61046927SAndroid Build Coastguard Worker new_flags &= ~IR3_REG_SNEG;
165*61046927SAndroid Build Coastguard Worker }
166*61046927SAndroid Build Coastguard Worker
167*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_FNEG) {
168*61046927SAndroid Build Coastguard Worker reg->fim_val = -reg->fim_val;
169*61046927SAndroid Build Coastguard Worker new_flags &= ~IR3_REG_FNEG;
170*61046927SAndroid Build Coastguard Worker }
171*61046927SAndroid Build Coastguard Worker
172*61046927SAndroid Build Coastguard Worker reg->num = ir3_const_find_imm(ctx->so, reg->uim_val);
173*61046927SAndroid Build Coastguard Worker
174*61046927SAndroid Build Coastguard Worker if (reg->num == INVALID_CONST_REG) {
175*61046927SAndroid Build Coastguard Worker /* Don't modify the const state for the binning variant. */
176*61046927SAndroid Build Coastguard Worker if (ctx->so->binning_pass)
177*61046927SAndroid Build Coastguard Worker return false;
178*61046927SAndroid Build Coastguard Worker
179*61046927SAndroid Build Coastguard Worker reg->num = ir3_const_add_imm(ctx->so, reg->uim_val);
180*61046927SAndroid Build Coastguard Worker
181*61046927SAndroid Build Coastguard Worker if (reg->num == INVALID_CONST_REG)
182*61046927SAndroid Build Coastguard Worker return false;
183*61046927SAndroid Build Coastguard Worker }
184*61046927SAndroid Build Coastguard Worker
185*61046927SAndroid Build Coastguard Worker reg->flags = new_flags;
186*61046927SAndroid Build Coastguard Worker
187*61046927SAndroid Build Coastguard Worker instr->srcs[n] = reg;
188*61046927SAndroid Build Coastguard Worker
189*61046927SAndroid Build Coastguard Worker return true;
190*61046927SAndroid Build Coastguard Worker }
191*61046927SAndroid Build Coastguard Worker
192*61046927SAndroid Build Coastguard Worker static void
unuse(struct ir3_instruction * instr)193*61046927SAndroid Build Coastguard Worker unuse(struct ir3_instruction *instr)
194*61046927SAndroid Build Coastguard Worker {
195*61046927SAndroid Build Coastguard Worker assert(instr->use_count > 0);
196*61046927SAndroid Build Coastguard Worker
197*61046927SAndroid Build Coastguard Worker if (--instr->use_count == 0) {
198*61046927SAndroid Build Coastguard Worker struct ir3_block *block = instr->block;
199*61046927SAndroid Build Coastguard Worker
200*61046927SAndroid Build Coastguard Worker instr->barrier_class = 0;
201*61046927SAndroid Build Coastguard Worker instr->barrier_conflict = 0;
202*61046927SAndroid Build Coastguard Worker
203*61046927SAndroid Build Coastguard Worker /* we don't want to remove anything in keeps (which could
204*61046927SAndroid Build Coastguard Worker * be things like array store's)
205*61046927SAndroid Build Coastguard Worker */
206*61046927SAndroid Build Coastguard Worker for (unsigned i = 0; i < block->keeps_count; i++) {
207*61046927SAndroid Build Coastguard Worker assert(block->keeps[i] != instr);
208*61046927SAndroid Build Coastguard Worker }
209*61046927SAndroid Build Coastguard Worker }
210*61046927SAndroid Build Coastguard Worker }
211*61046927SAndroid Build Coastguard Worker
212*61046927SAndroid Build Coastguard Worker /**
213*61046927SAndroid Build Coastguard Worker * Handles the special case of the 2nd src (n == 1) to "normal" mad
214*61046927SAndroid Build Coastguard Worker * instructions, which cannot reference a constant. See if it is
215*61046927SAndroid Build Coastguard Worker * possible to swap the 1st and 2nd sources.
216*61046927SAndroid Build Coastguard Worker */
217*61046927SAndroid Build Coastguard Worker static bool
try_swap_mad_two_srcs(struct ir3_instruction * instr,unsigned new_flags)218*61046927SAndroid Build Coastguard Worker try_swap_mad_two_srcs(struct ir3_instruction *instr, unsigned new_flags)
219*61046927SAndroid Build Coastguard Worker {
220*61046927SAndroid Build Coastguard Worker if (!is_mad(instr->opc))
221*61046927SAndroid Build Coastguard Worker return false;
222*61046927SAndroid Build Coastguard Worker
223*61046927SAndroid Build Coastguard Worker /* If we've already tried, nothing more to gain.. we will only
224*61046927SAndroid Build Coastguard Worker * have previously swapped if the original 2nd src was const or
225*61046927SAndroid Build Coastguard Worker * immed. So swapping back won't improve anything and could
226*61046927SAndroid Build Coastguard Worker * result in an infinite "progress" loop.
227*61046927SAndroid Build Coastguard Worker */
228*61046927SAndroid Build Coastguard Worker if (instr->cat3.swapped)
229*61046927SAndroid Build Coastguard Worker return false;
230*61046927SAndroid Build Coastguard Worker
231*61046927SAndroid Build Coastguard Worker /* cat3 doesn't encode immediate, but we can lower immediate
232*61046927SAndroid Build Coastguard Worker * to const if that helps:
233*61046927SAndroid Build Coastguard Worker */
234*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_IMMED) {
235*61046927SAndroid Build Coastguard Worker new_flags &= ~IR3_REG_IMMED;
236*61046927SAndroid Build Coastguard Worker new_flags |= IR3_REG_CONST;
237*61046927SAndroid Build Coastguard Worker }
238*61046927SAndroid Build Coastguard Worker
239*61046927SAndroid Build Coastguard Worker /* If the reason we couldn't fold without swapping is something
240*61046927SAndroid Build Coastguard Worker * other than const source, then swapping won't help:
241*61046927SAndroid Build Coastguard Worker */
242*61046927SAndroid Build Coastguard Worker if (!(new_flags & (IR3_REG_CONST | IR3_REG_SHARED)))
243*61046927SAndroid Build Coastguard Worker return false;
244*61046927SAndroid Build Coastguard Worker
245*61046927SAndroid Build Coastguard Worker instr->cat3.swapped = true;
246*61046927SAndroid Build Coastguard Worker
247*61046927SAndroid Build Coastguard Worker /* NOTE: pre-swap first two src's before valid_flags(),
248*61046927SAndroid Build Coastguard Worker * which might try to dereference the n'th src:
249*61046927SAndroid Build Coastguard Worker */
250*61046927SAndroid Build Coastguard Worker swap(instr->srcs[0], instr->srcs[1]);
251*61046927SAndroid Build Coastguard Worker
252*61046927SAndroid Build Coastguard Worker bool valid_swap =
253*61046927SAndroid Build Coastguard Worker /* can we propagate mov if we move 2nd src to first? */
254*61046927SAndroid Build Coastguard Worker ir3_valid_flags(instr, 0, new_flags) &&
255*61046927SAndroid Build Coastguard Worker /* and does first src fit in second slot? */
256*61046927SAndroid Build Coastguard Worker ir3_valid_flags(instr, 1, instr->srcs[1]->flags);
257*61046927SAndroid Build Coastguard Worker
258*61046927SAndroid Build Coastguard Worker if (!valid_swap) {
259*61046927SAndroid Build Coastguard Worker /* put things back the way they were: */
260*61046927SAndroid Build Coastguard Worker swap(instr->srcs[0], instr->srcs[1]);
261*61046927SAndroid Build Coastguard Worker } /* otherwise leave things swapped */
262*61046927SAndroid Build Coastguard Worker
263*61046927SAndroid Build Coastguard Worker return valid_swap;
264*61046927SAndroid Build Coastguard Worker }
265*61046927SAndroid Build Coastguard Worker
266*61046927SAndroid Build Coastguard Worker /**
267*61046927SAndroid Build Coastguard Worker * Handle cp for a given src register. This additionally handles
268*61046927SAndroid Build Coastguard Worker * the cases of collapsing immedate/const (which replace the src
269*61046927SAndroid Build Coastguard Worker * register with a non-ssa src) or collapsing mov's from relative
270*61046927SAndroid Build Coastguard Worker * src (which needs to also fixup the address src reference by the
271*61046927SAndroid Build Coastguard Worker * instruction).
272*61046927SAndroid Build Coastguard Worker */
273*61046927SAndroid Build Coastguard Worker static bool
reg_cp(struct ir3_cp_ctx * ctx,struct ir3_instruction * instr,struct ir3_register * reg,unsigned n)274*61046927SAndroid Build Coastguard Worker reg_cp(struct ir3_cp_ctx *ctx, struct ir3_instruction *instr,
275*61046927SAndroid Build Coastguard Worker struct ir3_register *reg, unsigned n)
276*61046927SAndroid Build Coastguard Worker {
277*61046927SAndroid Build Coastguard Worker struct ir3_instruction *src = ssa(reg);
278*61046927SAndroid Build Coastguard Worker
279*61046927SAndroid Build Coastguard Worker if (is_eligible_mov(src, instr, true)) {
280*61046927SAndroid Build Coastguard Worker /* simple case, no immed/const/relativ, only mov's w/ ssa src: */
281*61046927SAndroid Build Coastguard Worker struct ir3_register *src_reg = src->srcs[0];
282*61046927SAndroid Build Coastguard Worker unsigned new_flags = reg->flags;
283*61046927SAndroid Build Coastguard Worker
284*61046927SAndroid Build Coastguard Worker combine_flags(&new_flags, src);
285*61046927SAndroid Build Coastguard Worker
286*61046927SAndroid Build Coastguard Worker if (ir3_valid_flags(instr, n, new_flags)) {
287*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_ARRAY) {
288*61046927SAndroid Build Coastguard Worker assert(!(reg->flags & IR3_REG_ARRAY));
289*61046927SAndroid Build Coastguard Worker reg->array = src_reg->array;
290*61046927SAndroid Build Coastguard Worker }
291*61046927SAndroid Build Coastguard Worker reg->flags = new_flags;
292*61046927SAndroid Build Coastguard Worker reg->def = src_reg->def;
293*61046927SAndroid Build Coastguard Worker
294*61046927SAndroid Build Coastguard Worker instr->barrier_class |= src->barrier_class;
295*61046927SAndroid Build Coastguard Worker instr->barrier_conflict |= src->barrier_conflict;
296*61046927SAndroid Build Coastguard Worker
297*61046927SAndroid Build Coastguard Worker unuse(src);
298*61046927SAndroid Build Coastguard Worker reg->def->instr->use_count++;
299*61046927SAndroid Build Coastguard Worker
300*61046927SAndroid Build Coastguard Worker return true;
301*61046927SAndroid Build Coastguard Worker } else if (n == 1 && try_swap_mad_two_srcs(instr, new_flags)) {
302*61046927SAndroid Build Coastguard Worker return true;
303*61046927SAndroid Build Coastguard Worker }
304*61046927SAndroid Build Coastguard Worker } else if ((is_same_type_mov(src) || is_const_mov(src)) &&
305*61046927SAndroid Build Coastguard Worker /* cannot collapse const/immed/etc into control flow: */
306*61046927SAndroid Build Coastguard Worker opc_cat(instr->opc) != 0) {
307*61046927SAndroid Build Coastguard Worker /* immed/const/etc cases, which require some special handling: */
308*61046927SAndroid Build Coastguard Worker struct ir3_register *src_reg = src->srcs[0];
309*61046927SAndroid Build Coastguard Worker unsigned new_flags = reg->flags;
310*61046927SAndroid Build Coastguard Worker
311*61046927SAndroid Build Coastguard Worker if (src_reg->flags & IR3_REG_ARRAY)
312*61046927SAndroid Build Coastguard Worker return false;
313*61046927SAndroid Build Coastguard Worker
314*61046927SAndroid Build Coastguard Worker combine_flags(&new_flags, src);
315*61046927SAndroid Build Coastguard Worker
316*61046927SAndroid Build Coastguard Worker if (!ir3_valid_flags(instr, n, new_flags)) {
317*61046927SAndroid Build Coastguard Worker /* See if lowering an immediate to const would help. */
318*61046927SAndroid Build Coastguard Worker if (lower_immed(ctx, instr, n, src_reg, new_flags))
319*61046927SAndroid Build Coastguard Worker return true;
320*61046927SAndroid Build Coastguard Worker
321*61046927SAndroid Build Coastguard Worker /* special case for "normal" mad instructions, we can
322*61046927SAndroid Build Coastguard Worker * try swapping the first two args if that fits better.
323*61046927SAndroid Build Coastguard Worker *
324*61046927SAndroid Build Coastguard Worker * the "plain" MAD's (ie. the ones that don't shift first
325*61046927SAndroid Build Coastguard Worker * src prior to multiply) can swap their first two srcs if
326*61046927SAndroid Build Coastguard Worker * src[0] is !CONST and src[1] is CONST:
327*61046927SAndroid Build Coastguard Worker */
328*61046927SAndroid Build Coastguard Worker if ((n == 1) && try_swap_mad_two_srcs(instr, new_flags)) {
329*61046927SAndroid Build Coastguard Worker return true;
330*61046927SAndroid Build Coastguard Worker } else {
331*61046927SAndroid Build Coastguard Worker return false;
332*61046927SAndroid Build Coastguard Worker }
333*61046927SAndroid Build Coastguard Worker }
334*61046927SAndroid Build Coastguard Worker
335*61046927SAndroid Build Coastguard Worker /* Here we handle the special case of mov from
336*61046927SAndroid Build Coastguard Worker * CONST and/or RELATIV. These need to be handled
337*61046927SAndroid Build Coastguard Worker * specially, because in the case of move from CONST
338*61046927SAndroid Build Coastguard Worker * there is no src ir3_instruction so we need to
339*61046927SAndroid Build Coastguard Worker * replace the ir3_register. And in the case of
340*61046927SAndroid Build Coastguard Worker * RELATIV we need to handle the address register
341*61046927SAndroid Build Coastguard Worker * dependency.
342*61046927SAndroid Build Coastguard Worker */
343*61046927SAndroid Build Coastguard Worker if (src_reg->flags & IR3_REG_CONST) {
344*61046927SAndroid Build Coastguard Worker /* an instruction cannot reference two different
345*61046927SAndroid Build Coastguard Worker * address registers:
346*61046927SAndroid Build Coastguard Worker */
347*61046927SAndroid Build Coastguard Worker if ((src_reg->flags & IR3_REG_RELATIV) &&
348*61046927SAndroid Build Coastguard Worker conflicts(instr->address, reg->def->instr->address))
349*61046927SAndroid Build Coastguard Worker return false;
350*61046927SAndroid Build Coastguard Worker
351*61046927SAndroid Build Coastguard Worker /* These macros expand to a mov in an if statement */
352*61046927SAndroid Build Coastguard Worker if ((src_reg->flags & IR3_REG_RELATIV) &&
353*61046927SAndroid Build Coastguard Worker is_subgroup_cond_mov_macro(instr))
354*61046927SAndroid Build Coastguard Worker return false;
355*61046927SAndroid Build Coastguard Worker
356*61046927SAndroid Build Coastguard Worker /* This seems to be a hw bug, or something where the timings
357*61046927SAndroid Build Coastguard Worker * just somehow don't work out. This restriction may only
358*61046927SAndroid Build Coastguard Worker * apply if the first src is also CONST.
359*61046927SAndroid Build Coastguard Worker */
360*61046927SAndroid Build Coastguard Worker if ((opc_cat(instr->opc) == 3) && (n == 2) &&
361*61046927SAndroid Build Coastguard Worker (src_reg->flags & IR3_REG_RELATIV) && (src_reg->array.offset == 0))
362*61046927SAndroid Build Coastguard Worker return false;
363*61046927SAndroid Build Coastguard Worker
364*61046927SAndroid Build Coastguard Worker /* When narrowing constant from 32b to 16b, it seems
365*61046927SAndroid Build Coastguard Worker * to work only for float. So we should do this only with
366*61046927SAndroid Build Coastguard Worker * float opcodes.
367*61046927SAndroid Build Coastguard Worker */
368*61046927SAndroid Build Coastguard Worker if (src->cat1.dst_type == TYPE_F16) {
369*61046927SAndroid Build Coastguard Worker /* TODO: should we have a way to tell phi/collect to use a
370*61046927SAndroid Build Coastguard Worker * float move so that this is legal?
371*61046927SAndroid Build Coastguard Worker */
372*61046927SAndroid Build Coastguard Worker if (is_meta(instr))
373*61046927SAndroid Build Coastguard Worker return false;
374*61046927SAndroid Build Coastguard Worker if (instr->opc == OPC_MOV && !type_float(instr->cat1.src_type))
375*61046927SAndroid Build Coastguard Worker return false;
376*61046927SAndroid Build Coastguard Worker if (!is_cat2_float(instr->opc) && !is_cat3_float(instr->opc))
377*61046927SAndroid Build Coastguard Worker return false;
378*61046927SAndroid Build Coastguard Worker } else if (src->cat1.dst_type == TYPE_U16 || src->cat1.dst_type == TYPE_S16) {
379*61046927SAndroid Build Coastguard Worker /* Since we set CONSTANT_DEMOTION_ENABLE, a float reference of
380*61046927SAndroid Build Coastguard Worker * what was a U16 value read from the constbuf would incorrectly
381*61046927SAndroid Build Coastguard Worker * do 32f->16f conversion, when we want to read a 16f value.
382*61046927SAndroid Build Coastguard Worker */
383*61046927SAndroid Build Coastguard Worker if (is_cat2_float(instr->opc) || is_cat3_float(instr->opc))
384*61046927SAndroid Build Coastguard Worker return false;
385*61046927SAndroid Build Coastguard Worker if (instr->opc == OPC_MOV && type_float(instr->cat1.src_type))
386*61046927SAndroid Build Coastguard Worker return false;
387*61046927SAndroid Build Coastguard Worker }
388*61046927SAndroid Build Coastguard Worker
389*61046927SAndroid Build Coastguard Worker src_reg = ir3_reg_clone(instr->block->shader, src_reg);
390*61046927SAndroid Build Coastguard Worker src_reg->flags = new_flags;
391*61046927SAndroid Build Coastguard Worker instr->srcs[n] = src_reg;
392*61046927SAndroid Build Coastguard Worker
393*61046927SAndroid Build Coastguard Worker if (src_reg->flags & IR3_REG_RELATIV)
394*61046927SAndroid Build Coastguard Worker ir3_instr_set_address(instr, reg->def->instr->address->def->instr);
395*61046927SAndroid Build Coastguard Worker
396*61046927SAndroid Build Coastguard Worker return true;
397*61046927SAndroid Build Coastguard Worker }
398*61046927SAndroid Build Coastguard Worker
399*61046927SAndroid Build Coastguard Worker if (src_reg->flags & IR3_REG_IMMED) {
400*61046927SAndroid Build Coastguard Worker int32_t iim_val = src_reg->iim_val;
401*61046927SAndroid Build Coastguard Worker
402*61046927SAndroid Build Coastguard Worker assert((opc_cat(instr->opc) == 1) ||
403*61046927SAndroid Build Coastguard Worker (opc_cat(instr->opc) == 2) ||
404*61046927SAndroid Build Coastguard Worker (opc_cat(instr->opc) == 6) ||
405*61046927SAndroid Build Coastguard Worker is_meta(instr) ||
406*61046927SAndroid Build Coastguard Worker (instr->opc == OPC_ISAM && (n == 1 || n == 2)) ||
407*61046927SAndroid Build Coastguard Worker (is_mad(instr->opc) && (n == 0)));
408*61046927SAndroid Build Coastguard Worker
409*61046927SAndroid Build Coastguard Worker if ((opc_cat(instr->opc) == 2) &&
410*61046927SAndroid Build Coastguard Worker !ir3_cat2_int(instr->opc)) {
411*61046927SAndroid Build Coastguard Worker iim_val = ir3_flut(src_reg);
412*61046927SAndroid Build Coastguard Worker if (iim_val < 0) {
413*61046927SAndroid Build Coastguard Worker /* Fall back to trying to load the immediate as a const: */
414*61046927SAndroid Build Coastguard Worker return lower_immed(ctx, instr, n, src_reg, new_flags);
415*61046927SAndroid Build Coastguard Worker }
416*61046927SAndroid Build Coastguard Worker }
417*61046927SAndroid Build Coastguard Worker
418*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_SABS)
419*61046927SAndroid Build Coastguard Worker iim_val = abs(iim_val);
420*61046927SAndroid Build Coastguard Worker
421*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_SNEG)
422*61046927SAndroid Build Coastguard Worker iim_val = -iim_val;
423*61046927SAndroid Build Coastguard Worker
424*61046927SAndroid Build Coastguard Worker if (new_flags & IR3_REG_BNOT)
425*61046927SAndroid Build Coastguard Worker iim_val = ~iim_val;
426*61046927SAndroid Build Coastguard Worker
427*61046927SAndroid Build Coastguard Worker if (ir3_valid_flags(instr, n, new_flags) &&
428*61046927SAndroid Build Coastguard Worker ir3_valid_immediate(instr, iim_val)) {
429*61046927SAndroid Build Coastguard Worker new_flags &= ~(IR3_REG_SABS | IR3_REG_SNEG | IR3_REG_BNOT);
430*61046927SAndroid Build Coastguard Worker src_reg = ir3_reg_clone(instr->block->shader, src_reg);
431*61046927SAndroid Build Coastguard Worker src_reg->flags = new_flags;
432*61046927SAndroid Build Coastguard Worker src_reg->iim_val = iim_val;
433*61046927SAndroid Build Coastguard Worker instr->srcs[n] = src_reg;
434*61046927SAndroid Build Coastguard Worker
435*61046927SAndroid Build Coastguard Worker return true;
436*61046927SAndroid Build Coastguard Worker } else {
437*61046927SAndroid Build Coastguard Worker /* Fall back to trying to load the immediate as a const: */
438*61046927SAndroid Build Coastguard Worker return lower_immed(ctx, instr, n, src_reg, new_flags);
439*61046927SAndroid Build Coastguard Worker }
440*61046927SAndroid Build Coastguard Worker }
441*61046927SAndroid Build Coastguard Worker }
442*61046927SAndroid Build Coastguard Worker
443*61046927SAndroid Build Coastguard Worker return false;
444*61046927SAndroid Build Coastguard Worker }
445*61046927SAndroid Build Coastguard Worker
446*61046927SAndroid Build Coastguard Worker /* Handle special case of eliminating output mov, and similar cases where
447*61046927SAndroid Build Coastguard Worker * there isn't a normal "consuming" instruction. In this case we cannot
448*61046927SAndroid Build Coastguard Worker * collapse flags (ie. output mov from const, or w/ abs/neg flags, cannot
449*61046927SAndroid Build Coastguard Worker * be eliminated)
450*61046927SAndroid Build Coastguard Worker */
451*61046927SAndroid Build Coastguard Worker static struct ir3_instruction *
eliminate_output_mov(struct ir3_cp_ctx * ctx,struct ir3_instruction * instr)452*61046927SAndroid Build Coastguard Worker eliminate_output_mov(struct ir3_cp_ctx *ctx, struct ir3_instruction *instr)
453*61046927SAndroid Build Coastguard Worker {
454*61046927SAndroid Build Coastguard Worker if (is_eligible_mov(instr, NULL, false)) {
455*61046927SAndroid Build Coastguard Worker struct ir3_register *reg = instr->srcs[0];
456*61046927SAndroid Build Coastguard Worker if (!(reg->flags & IR3_REG_ARRAY)) {
457*61046927SAndroid Build Coastguard Worker struct ir3_instruction *src_instr = ssa(reg);
458*61046927SAndroid Build Coastguard Worker assert(src_instr);
459*61046927SAndroid Build Coastguard Worker ctx->progress = true;
460*61046927SAndroid Build Coastguard Worker return src_instr;
461*61046927SAndroid Build Coastguard Worker }
462*61046927SAndroid Build Coastguard Worker }
463*61046927SAndroid Build Coastguard Worker return instr;
464*61046927SAndroid Build Coastguard Worker }
465*61046927SAndroid Build Coastguard Worker
466*61046927SAndroid Build Coastguard Worker /**
467*61046927SAndroid Build Coastguard Worker * Find instruction src's which are mov's that can be collapsed, replacing
468*61046927SAndroid Build Coastguard Worker * the mov dst with the mov src
469*61046927SAndroid Build Coastguard Worker */
470*61046927SAndroid Build Coastguard Worker static void
instr_cp(struct ir3_cp_ctx * ctx,struct ir3_instruction * instr)471*61046927SAndroid Build Coastguard Worker instr_cp(struct ir3_cp_ctx *ctx, struct ir3_instruction *instr)
472*61046927SAndroid Build Coastguard Worker {
473*61046927SAndroid Build Coastguard Worker if (instr->srcs_count == 0)
474*61046927SAndroid Build Coastguard Worker return;
475*61046927SAndroid Build Coastguard Worker
476*61046927SAndroid Build Coastguard Worker if (ir3_instr_check_mark(instr))
477*61046927SAndroid Build Coastguard Worker return;
478*61046927SAndroid Build Coastguard Worker
479*61046927SAndroid Build Coastguard Worker /* walk down the graph from each src: */
480*61046927SAndroid Build Coastguard Worker bool progress;
481*61046927SAndroid Build Coastguard Worker do {
482*61046927SAndroid Build Coastguard Worker progress = false;
483*61046927SAndroid Build Coastguard Worker foreach_src_n (reg, n, instr) {
484*61046927SAndroid Build Coastguard Worker struct ir3_instruction *src = ssa(reg);
485*61046927SAndroid Build Coastguard Worker
486*61046927SAndroid Build Coastguard Worker if (!src)
487*61046927SAndroid Build Coastguard Worker continue;
488*61046927SAndroid Build Coastguard Worker
489*61046927SAndroid Build Coastguard Worker instr_cp(ctx, src);
490*61046927SAndroid Build Coastguard Worker
491*61046927SAndroid Build Coastguard Worker /* TODO non-indirect access we could figure out which register
492*61046927SAndroid Build Coastguard Worker * we actually want and allow cp..
493*61046927SAndroid Build Coastguard Worker */
494*61046927SAndroid Build Coastguard Worker if ((reg->flags & IR3_REG_ARRAY) && src->opc != OPC_META_PHI)
495*61046927SAndroid Build Coastguard Worker continue;
496*61046927SAndroid Build Coastguard Worker
497*61046927SAndroid Build Coastguard Worker /* Don't CP absneg into meta instructions, that won't end well: */
498*61046927SAndroid Build Coastguard Worker if (is_meta(instr) &&
499*61046927SAndroid Build Coastguard Worker (src->opc == OPC_ABSNEG_F || src->opc == OPC_ABSNEG_S))
500*61046927SAndroid Build Coastguard Worker continue;
501*61046927SAndroid Build Coastguard Worker
502*61046927SAndroid Build Coastguard Worker /* Don't CP mova and mova1 into their users */
503*61046927SAndroid Build Coastguard Worker if (writes_addr0(src) || writes_addr1(src))
504*61046927SAndroid Build Coastguard Worker continue;
505*61046927SAndroid Build Coastguard Worker
506*61046927SAndroid Build Coastguard Worker progress |= reg_cp(ctx, instr, reg, n);
507*61046927SAndroid Build Coastguard Worker ctx->progress |= progress;
508*61046927SAndroid Build Coastguard Worker }
509*61046927SAndroid Build Coastguard Worker } while (progress);
510*61046927SAndroid Build Coastguard Worker
511*61046927SAndroid Build Coastguard Worker /* After folding a mov's source we may wind up with a type-converting mov
512*61046927SAndroid Build Coastguard Worker * of an immediate. This happens e.g. with texture descriptors, since we
513*61046927SAndroid Build Coastguard Worker * narrow the descriptor (which may be a constant) to a half-reg in ir3.
514*61046927SAndroid Build Coastguard Worker * By converting the immediate in-place to the destination type, we can
515*61046927SAndroid Build Coastguard Worker * turn the mov into a same-type mov so that it can be further propagated.
516*61046927SAndroid Build Coastguard Worker */
517*61046927SAndroid Build Coastguard Worker if (instr->opc == OPC_MOV && (instr->srcs[0]->flags & IR3_REG_IMMED) &&
518*61046927SAndroid Build Coastguard Worker instr->cat1.src_type != instr->cat1.dst_type &&
519*61046927SAndroid Build Coastguard Worker /* Only do uint types for now, until we generate other types of
520*61046927SAndroid Build Coastguard Worker * mov's during instruction selection.
521*61046927SAndroid Build Coastguard Worker */
522*61046927SAndroid Build Coastguard Worker full_type(instr->cat1.src_type) == TYPE_U32 &&
523*61046927SAndroid Build Coastguard Worker full_type(instr->cat1.dst_type) == TYPE_U32) {
524*61046927SAndroid Build Coastguard Worker uint32_t uimm = instr->srcs[0]->uim_val;
525*61046927SAndroid Build Coastguard Worker if (instr->cat1.dst_type == TYPE_U16)
526*61046927SAndroid Build Coastguard Worker uimm &= 0xffff;
527*61046927SAndroid Build Coastguard Worker instr->srcs[0]->uim_val = uimm;
528*61046927SAndroid Build Coastguard Worker if (instr->dsts[0]->flags & IR3_REG_HALF)
529*61046927SAndroid Build Coastguard Worker instr->srcs[0]->flags |= IR3_REG_HALF;
530*61046927SAndroid Build Coastguard Worker else
531*61046927SAndroid Build Coastguard Worker instr->srcs[0]->flags &= ~IR3_REG_HALF;
532*61046927SAndroid Build Coastguard Worker instr->cat1.src_type = instr->cat1.dst_type;
533*61046927SAndroid Build Coastguard Worker ctx->progress = true;
534*61046927SAndroid Build Coastguard Worker }
535*61046927SAndroid Build Coastguard Worker
536*61046927SAndroid Build Coastguard Worker /* Handle converting a sam.s2en (taking samp/tex idx params via register)
537*61046927SAndroid Build Coastguard Worker * into a normal sam (encoding immediate samp/tex idx) if they are
538*61046927SAndroid Build Coastguard Worker * immediate. This saves some instructions and regs in the common case
539*61046927SAndroid Build Coastguard Worker * where we know samp/tex at compile time. This needs to be done in the
540*61046927SAndroid Build Coastguard Worker * frontend for bindless tex, though, so don't replicate it here.
541*61046927SAndroid Build Coastguard Worker */
542*61046927SAndroid Build Coastguard Worker if (is_tex(instr) && (instr->flags & IR3_INSTR_S2EN) &&
543*61046927SAndroid Build Coastguard Worker !(instr->flags & IR3_INSTR_B) &&
544*61046927SAndroid Build Coastguard Worker !(ir3_shader_debug & IR3_DBG_FORCES2EN)) {
545*61046927SAndroid Build Coastguard Worker /* The first src will be a collect, if both of it's
546*61046927SAndroid Build Coastguard Worker * two sources are mov from imm, then we can
547*61046927SAndroid Build Coastguard Worker */
548*61046927SAndroid Build Coastguard Worker struct ir3_instruction *samp_tex = ssa(instr->srcs[0]);
549*61046927SAndroid Build Coastguard Worker
550*61046927SAndroid Build Coastguard Worker assert(samp_tex->opc == OPC_META_COLLECT);
551*61046927SAndroid Build Coastguard Worker
552*61046927SAndroid Build Coastguard Worker struct ir3_register *samp = samp_tex->srcs[0];
553*61046927SAndroid Build Coastguard Worker struct ir3_register *tex = samp_tex->srcs[1];
554*61046927SAndroid Build Coastguard Worker
555*61046927SAndroid Build Coastguard Worker if ((samp->flags & IR3_REG_IMMED) && (tex->flags & IR3_REG_IMMED) &&
556*61046927SAndroid Build Coastguard Worker (samp->iim_val < 16) && (tex->iim_val < 16)) {
557*61046927SAndroid Build Coastguard Worker instr->flags &= ~IR3_INSTR_S2EN;
558*61046927SAndroid Build Coastguard Worker instr->cat5.samp = samp->iim_val;
559*61046927SAndroid Build Coastguard Worker instr->cat5.tex = tex->iim_val;
560*61046927SAndroid Build Coastguard Worker
561*61046927SAndroid Build Coastguard Worker /* shuffle around the regs to remove the first src: */
562*61046927SAndroid Build Coastguard Worker instr->srcs_count--;
563*61046927SAndroid Build Coastguard Worker for (unsigned i = 0; i < instr->srcs_count; i++) {
564*61046927SAndroid Build Coastguard Worker instr->srcs[i] = instr->srcs[i + 1];
565*61046927SAndroid Build Coastguard Worker }
566*61046927SAndroid Build Coastguard Worker
567*61046927SAndroid Build Coastguard Worker ctx->progress = true;
568*61046927SAndroid Build Coastguard Worker }
569*61046927SAndroid Build Coastguard Worker }
570*61046927SAndroid Build Coastguard Worker }
571*61046927SAndroid Build Coastguard Worker
572*61046927SAndroid Build Coastguard Worker bool
ir3_cp(struct ir3 * ir,struct ir3_shader_variant * so)573*61046927SAndroid Build Coastguard Worker ir3_cp(struct ir3 *ir, struct ir3_shader_variant *so)
574*61046927SAndroid Build Coastguard Worker {
575*61046927SAndroid Build Coastguard Worker struct ir3_cp_ctx ctx = {
576*61046927SAndroid Build Coastguard Worker .shader = ir,
577*61046927SAndroid Build Coastguard Worker .so = so,
578*61046927SAndroid Build Coastguard Worker };
579*61046927SAndroid Build Coastguard Worker
580*61046927SAndroid Build Coastguard Worker /* This is a bit annoying, and probably wouldn't be necessary if we
581*61046927SAndroid Build Coastguard Worker * tracked a reverse link from producing instruction to consumer.
582*61046927SAndroid Build Coastguard Worker * But we need to know when we've eliminated the last consumer of
583*61046927SAndroid Build Coastguard Worker * a mov, so we need to do a pass to first count consumers of a
584*61046927SAndroid Build Coastguard Worker * mov.
585*61046927SAndroid Build Coastguard Worker */
586*61046927SAndroid Build Coastguard Worker foreach_block (block, &ir->block_list) {
587*61046927SAndroid Build Coastguard Worker foreach_instr (instr, &block->instr_list) {
588*61046927SAndroid Build Coastguard Worker
589*61046927SAndroid Build Coastguard Worker /* by the way, we don't account for false-dep's, so the CP
590*61046927SAndroid Build Coastguard Worker * pass should always happen before false-dep's are inserted
591*61046927SAndroid Build Coastguard Worker */
592*61046927SAndroid Build Coastguard Worker assert(instr->deps_count == 0);
593*61046927SAndroid Build Coastguard Worker
594*61046927SAndroid Build Coastguard Worker foreach_ssa_src (src, instr) {
595*61046927SAndroid Build Coastguard Worker src->use_count++;
596*61046927SAndroid Build Coastguard Worker }
597*61046927SAndroid Build Coastguard Worker }
598*61046927SAndroid Build Coastguard Worker }
599*61046927SAndroid Build Coastguard Worker
600*61046927SAndroid Build Coastguard Worker ir3_clear_mark(ir);
601*61046927SAndroid Build Coastguard Worker
602*61046927SAndroid Build Coastguard Worker foreach_block (block, &ir->block_list) {
603*61046927SAndroid Build Coastguard Worker struct ir3_instruction *terminator = ir3_block_get_terminator(block);
604*61046927SAndroid Build Coastguard Worker if (terminator)
605*61046927SAndroid Build Coastguard Worker instr_cp(&ctx, terminator);
606*61046927SAndroid Build Coastguard Worker
607*61046927SAndroid Build Coastguard Worker for (unsigned i = 0; i < block->keeps_count; i++) {
608*61046927SAndroid Build Coastguard Worker instr_cp(&ctx, block->keeps[i]);
609*61046927SAndroid Build Coastguard Worker block->keeps[i] = eliminate_output_mov(&ctx, block->keeps[i]);
610*61046927SAndroid Build Coastguard Worker }
611*61046927SAndroid Build Coastguard Worker }
612*61046927SAndroid Build Coastguard Worker
613*61046927SAndroid Build Coastguard Worker return ctx.progress;
614*61046927SAndroid Build Coastguard Worker }
615