1*61046927SAndroid Build Coastguard Worker /*
2*61046927SAndroid Build Coastguard Worker * Copyright © 2019 Google.
3*61046927SAndroid Build Coastguard Worker * SPDX-License-Identifier: MIT
4*61046927SAndroid Build Coastguard Worker */
5*61046927SAndroid Build Coastguard Worker
6*61046927SAndroid Build Coastguard Worker #include "util/ralloc.h"
7*61046927SAndroid Build Coastguard Worker
8*61046927SAndroid Build Coastguard Worker #include "ir3.h"
9*61046927SAndroid Build Coastguard Worker
10*61046927SAndroid Build Coastguard Worker static bool
is_safe_conv(struct ir3_instruction * instr,type_t src_type,opc_t * src_opc)11*61046927SAndroid Build Coastguard Worker is_safe_conv(struct ir3_instruction *instr, type_t src_type, opc_t *src_opc)
12*61046927SAndroid Build Coastguard Worker {
13*61046927SAndroid Build Coastguard Worker if (instr->opc != OPC_MOV)
14*61046927SAndroid Build Coastguard Worker return false;
15*61046927SAndroid Build Coastguard Worker
16*61046927SAndroid Build Coastguard Worker /* Only allow half->full or full->half without any type conversion (like
17*61046927SAndroid Build Coastguard Worker * int to float).
18*61046927SAndroid Build Coastguard Worker */
19*61046927SAndroid Build Coastguard Worker if (type_size(instr->cat1.src_type) == type_size(instr->cat1.dst_type) ||
20*61046927SAndroid Build Coastguard Worker full_type(instr->cat1.src_type) != full_type(instr->cat1.dst_type))
21*61046927SAndroid Build Coastguard Worker return false;
22*61046927SAndroid Build Coastguard Worker
23*61046927SAndroid Build Coastguard Worker /* mul.s24/u24 always return 32b result regardless of its sources size,
24*61046927SAndroid Build Coastguard Worker * hence we cannot guarantee the high 16b of dst being zero or sign extended.
25*61046927SAndroid Build Coastguard Worker */
26*61046927SAndroid Build Coastguard Worker if ((*src_opc == OPC_MUL_S24 || *src_opc == OPC_MUL_U24) &&
27*61046927SAndroid Build Coastguard Worker type_size(instr->cat1.src_type) == 16)
28*61046927SAndroid Build Coastguard Worker return false;
29*61046927SAndroid Build Coastguard Worker
30*61046927SAndroid Build Coastguard Worker struct ir3_register *dst = instr->dsts[0];
31*61046927SAndroid Build Coastguard Worker struct ir3_register *src = instr->srcs[0];
32*61046927SAndroid Build Coastguard Worker
33*61046927SAndroid Build Coastguard Worker /* disallow conversions that cannot be folded into
34*61046927SAndroid Build Coastguard Worker * alu instructions:
35*61046927SAndroid Build Coastguard Worker */
36*61046927SAndroid Build Coastguard Worker if (instr->cat1.round != ROUND_ZERO)
37*61046927SAndroid Build Coastguard Worker return false;
38*61046927SAndroid Build Coastguard Worker
39*61046927SAndroid Build Coastguard Worker if (dst->flags & (IR3_REG_RELATIV | IR3_REG_ARRAY))
40*61046927SAndroid Build Coastguard Worker return false;
41*61046927SAndroid Build Coastguard Worker if (src->flags & (IR3_REG_RELATIV | IR3_REG_ARRAY))
42*61046927SAndroid Build Coastguard Worker return false;
43*61046927SAndroid Build Coastguard Worker
44*61046927SAndroid Build Coastguard Worker /* Check that the source of the conv matches the type of the src
45*61046927SAndroid Build Coastguard Worker * instruction.
46*61046927SAndroid Build Coastguard Worker */
47*61046927SAndroid Build Coastguard Worker if (src_type == instr->cat1.src_type)
48*61046927SAndroid Build Coastguard Worker return true;
49*61046927SAndroid Build Coastguard Worker
50*61046927SAndroid Build Coastguard Worker /* We can handle mismatches with integer types by converting the opcode
51*61046927SAndroid Build Coastguard Worker * but not when an integer is reinterpreted as a float or vice-versa.
52*61046927SAndroid Build Coastguard Worker */
53*61046927SAndroid Build Coastguard Worker if (type_float(src_type) != type_float(instr->cat1.src_type))
54*61046927SAndroid Build Coastguard Worker return false;
55*61046927SAndroid Build Coastguard Worker
56*61046927SAndroid Build Coastguard Worker /* We have types with mismatched signedness. Mismatches on the signedness
57*61046927SAndroid Build Coastguard Worker * don't matter when narrowing:
58*61046927SAndroid Build Coastguard Worker */
59*61046927SAndroid Build Coastguard Worker if (type_size(instr->cat1.dst_type) < type_size(instr->cat1.src_type))
60*61046927SAndroid Build Coastguard Worker return true;
61*61046927SAndroid Build Coastguard Worker
62*61046927SAndroid Build Coastguard Worker /* Try swapping the opcode: */
63*61046927SAndroid Build Coastguard Worker bool can_swap = true;
64*61046927SAndroid Build Coastguard Worker *src_opc = ir3_try_swap_signedness(*src_opc, &can_swap);
65*61046927SAndroid Build Coastguard Worker return can_swap;
66*61046927SAndroid Build Coastguard Worker }
67*61046927SAndroid Build Coastguard Worker
68*61046927SAndroid Build Coastguard Worker static bool
all_uses_safe_conv(struct ir3_instruction * conv_src,type_t src_type)69*61046927SAndroid Build Coastguard Worker all_uses_safe_conv(struct ir3_instruction *conv_src, type_t src_type)
70*61046927SAndroid Build Coastguard Worker {
71*61046927SAndroid Build Coastguard Worker opc_t opc = conv_src->opc;
72*61046927SAndroid Build Coastguard Worker bool first = true;
73*61046927SAndroid Build Coastguard Worker foreach_ssa_use (use, conv_src) {
74*61046927SAndroid Build Coastguard Worker opc_t new_opc = opc;
75*61046927SAndroid Build Coastguard Worker if (!is_safe_conv(use, src_type, &new_opc))
76*61046927SAndroid Build Coastguard Worker return false;
77*61046927SAndroid Build Coastguard Worker /* Check if multiple uses have conflicting requirements on the opcode.
78*61046927SAndroid Build Coastguard Worker */
79*61046927SAndroid Build Coastguard Worker if (!first && opc != new_opc)
80*61046927SAndroid Build Coastguard Worker return false;
81*61046927SAndroid Build Coastguard Worker first = false;
82*61046927SAndroid Build Coastguard Worker opc = new_opc;
83*61046927SAndroid Build Coastguard Worker }
84*61046927SAndroid Build Coastguard Worker conv_src->opc = opc;
85*61046927SAndroid Build Coastguard Worker return true;
86*61046927SAndroid Build Coastguard Worker }
87*61046927SAndroid Build Coastguard Worker
88*61046927SAndroid Build Coastguard Worker /* For an instruction which has a conversion folded in, re-write the
89*61046927SAndroid Build Coastguard Worker * uses of *all* conv's that used that src to be a simple mov that
90*61046927SAndroid Build Coastguard Worker * cp can eliminate. This avoids invalidating the SSA uses, it just
91*61046927SAndroid Build Coastguard Worker * shifts the use to a simple mov.
92*61046927SAndroid Build Coastguard Worker */
93*61046927SAndroid Build Coastguard Worker static void
rewrite_src_uses(struct ir3_instruction * src)94*61046927SAndroid Build Coastguard Worker rewrite_src_uses(struct ir3_instruction *src)
95*61046927SAndroid Build Coastguard Worker {
96*61046927SAndroid Build Coastguard Worker foreach_ssa_use (use, src) {
97*61046927SAndroid Build Coastguard Worker assert(use->opc == OPC_MOV);
98*61046927SAndroid Build Coastguard Worker
99*61046927SAndroid Build Coastguard Worker if (is_half(src)) {
100*61046927SAndroid Build Coastguard Worker use->srcs[0]->flags |= IR3_REG_HALF;
101*61046927SAndroid Build Coastguard Worker } else {
102*61046927SAndroid Build Coastguard Worker use->srcs[0]->flags &= ~IR3_REG_HALF;
103*61046927SAndroid Build Coastguard Worker }
104*61046927SAndroid Build Coastguard Worker
105*61046927SAndroid Build Coastguard Worker use->cat1.src_type = use->cat1.dst_type;
106*61046927SAndroid Build Coastguard Worker }
107*61046927SAndroid Build Coastguard Worker }
108*61046927SAndroid Build Coastguard Worker
109*61046927SAndroid Build Coastguard Worker static bool
try_conversion_folding(struct ir3_instruction * conv)110*61046927SAndroid Build Coastguard Worker try_conversion_folding(struct ir3_instruction *conv)
111*61046927SAndroid Build Coastguard Worker {
112*61046927SAndroid Build Coastguard Worker struct ir3_instruction *src;
113*61046927SAndroid Build Coastguard Worker
114*61046927SAndroid Build Coastguard Worker if (conv->opc != OPC_MOV)
115*61046927SAndroid Build Coastguard Worker return false;
116*61046927SAndroid Build Coastguard Worker
117*61046927SAndroid Build Coastguard Worker /* Don't fold in conversions to/from shared */
118*61046927SAndroid Build Coastguard Worker if ((conv->srcs[0]->flags & IR3_REG_SHARED) !=
119*61046927SAndroid Build Coastguard Worker (conv->dsts[0]->flags & IR3_REG_SHARED))
120*61046927SAndroid Build Coastguard Worker return false;
121*61046927SAndroid Build Coastguard Worker
122*61046927SAndroid Build Coastguard Worker /* NOTE: we can have non-ssa srcs after copy propagation: */
123*61046927SAndroid Build Coastguard Worker src = ssa(conv->srcs[0]);
124*61046927SAndroid Build Coastguard Worker if (!src)
125*61046927SAndroid Build Coastguard Worker return false;
126*61046927SAndroid Build Coastguard Worker
127*61046927SAndroid Build Coastguard Worker if (!is_alu(src))
128*61046927SAndroid Build Coastguard Worker return false;
129*61046927SAndroid Build Coastguard Worker
130*61046927SAndroid Build Coastguard Worker bool can_fold;
131*61046927SAndroid Build Coastguard Worker type_t base_type = ir3_output_conv_type(src, &can_fold);
132*61046927SAndroid Build Coastguard Worker if (!can_fold)
133*61046927SAndroid Build Coastguard Worker return false;
134*61046927SAndroid Build Coastguard Worker
135*61046927SAndroid Build Coastguard Worker type_t src_type = ir3_output_conv_src_type(src, base_type);
136*61046927SAndroid Build Coastguard Worker type_t dst_type = ir3_output_conv_dst_type(src, base_type);
137*61046927SAndroid Build Coastguard Worker
138*61046927SAndroid Build Coastguard Worker /* Avoid cases where we've already folded in a conversion. We assume that
139*61046927SAndroid Build Coastguard Worker * if there is a chain of conversions that's foldable then it's been
140*61046927SAndroid Build Coastguard Worker * folded in NIR already.
141*61046927SAndroid Build Coastguard Worker */
142*61046927SAndroid Build Coastguard Worker if (src_type != dst_type)
143*61046927SAndroid Build Coastguard Worker return false;
144*61046927SAndroid Build Coastguard Worker
145*61046927SAndroid Build Coastguard Worker if (!all_uses_safe_conv(src, src_type))
146*61046927SAndroid Build Coastguard Worker return false;
147*61046927SAndroid Build Coastguard Worker
148*61046927SAndroid Build Coastguard Worker ir3_set_dst_type(src, is_half(conv));
149*61046927SAndroid Build Coastguard Worker rewrite_src_uses(src);
150*61046927SAndroid Build Coastguard Worker
151*61046927SAndroid Build Coastguard Worker return true;
152*61046927SAndroid Build Coastguard Worker }
153*61046927SAndroid Build Coastguard Worker
154*61046927SAndroid Build Coastguard Worker bool
ir3_cf(struct ir3 * ir)155*61046927SAndroid Build Coastguard Worker ir3_cf(struct ir3 *ir)
156*61046927SAndroid Build Coastguard Worker {
157*61046927SAndroid Build Coastguard Worker void *mem_ctx = ralloc_context(NULL);
158*61046927SAndroid Build Coastguard Worker bool progress = false;
159*61046927SAndroid Build Coastguard Worker
160*61046927SAndroid Build Coastguard Worker ir3_find_ssa_uses(ir, mem_ctx, false);
161*61046927SAndroid Build Coastguard Worker
162*61046927SAndroid Build Coastguard Worker foreach_block (block, &ir->block_list) {
163*61046927SAndroid Build Coastguard Worker foreach_instr (instr, &block->instr_list) {
164*61046927SAndroid Build Coastguard Worker progress |= try_conversion_folding(instr);
165*61046927SAndroid Build Coastguard Worker }
166*61046927SAndroid Build Coastguard Worker }
167*61046927SAndroid Build Coastguard Worker
168*61046927SAndroid Build Coastguard Worker ralloc_free(mem_ctx);
169*61046927SAndroid Build Coastguard Worker
170*61046927SAndroid Build Coastguard Worker return progress;
171*61046927SAndroid Build Coastguard Worker }
172