1 /* -*- mesa-c++ -*-
2 *
3 * Copyright (c) 2022 Collabora LTD
4 *
5 * Author: Gert Wollny <gert.wollny@collabora.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27 #include "sfn_nir_lower_alu.h"
28
29 #include "sfn_nir.h"
30
31 namespace r600 {
32
33 class Lower2x16 : public NirLowerInstruction {
34 private:
35 bool filter(const nir_instr *instr) const override;
36 nir_def *lower(nir_instr *instr) override;
37 };
38
39 bool
filter(const nir_instr * instr) const40 Lower2x16::filter(const nir_instr *instr) const
41 {
42 if (instr->type != nir_instr_type_alu)
43 return false;
44 auto alu = nir_instr_as_alu(instr);
45 switch (alu->op) {
46 case nir_op_unpack_half_2x16:
47 case nir_op_pack_half_2x16:
48 return true;
49 default:
50 return false;
51 }
52 }
53
54 nir_def *
lower(nir_instr * instr)55 Lower2x16::lower(nir_instr *instr)
56 {
57 nir_alu_instr *alu = nir_instr_as_alu(instr);
58
59 switch (alu->op) {
60 case nir_op_unpack_half_2x16: {
61 nir_def *packed = nir_ssa_for_alu_src(b, alu, 0);
62 return nir_vec2(b,
63 nir_unpack_half_2x16_split_x(b, packed),
64 nir_unpack_half_2x16_split_y(b, packed));
65 }
66 case nir_op_pack_half_2x16: {
67 nir_def *src_vec2 = nir_ssa_for_alu_src(b, alu, 0);
68 return nir_pack_half_2x16_split(b,
69 nir_channel(b, src_vec2, 0),
70 nir_channel(b, src_vec2, 1));
71 }
72 default:
73 unreachable("Lower2x16 filter doesn't filter correctly");
74 }
75 }
76
77 class LowerSinCos : public NirLowerInstruction {
78 public:
LowerSinCos(amd_gfx_level gxf_level)79 LowerSinCos(amd_gfx_level gxf_level):
80 m_gxf_level(gxf_level)
81 {
82 }
83
84 private:
85 bool filter(const nir_instr *instr) const override;
86 nir_def *lower(nir_instr *instr) override;
87 amd_gfx_level m_gxf_level;
88 };
89
90 bool
filter(const nir_instr * instr) const91 LowerSinCos::filter(const nir_instr *instr) const
92 {
93 if (instr->type != nir_instr_type_alu)
94 return false;
95
96 auto alu = nir_instr_as_alu(instr);
97 switch (alu->op) {
98 case nir_op_fsin:
99 case nir_op_fcos:
100 return true;
101 default:
102 return false;
103 }
104 }
105
106 nir_def *
lower(nir_instr * instr)107 LowerSinCos::lower(nir_instr *instr)
108 {
109 auto alu = nir_instr_as_alu(instr);
110
111 assert(alu->op == nir_op_fsin || alu->op == nir_op_fcos);
112
113 auto fract = nir_ffract(b,
114 nir_ffma_imm12(b,
115 nir_ssa_for_alu_src(b, alu, 0),
116 0.15915494,
117 0.5));
118
119 auto normalized =
120 m_gxf_level != R600
121 ? nir_fadd_imm(b, fract, -0.5)
122 : nir_ffma_imm12(b, fract, 2.0f * M_PI, -M_PI);
123
124 if (alu->op == nir_op_fsin)
125 return nir_fsin_amd(b, normalized);
126 else
127 return nir_fcos_amd(b, normalized);
128 }
129
130 class FixKcacheIndirectRead : public NirLowerInstruction {
131 private:
132 bool filter(const nir_instr *instr) const override;
133 nir_def *lower(nir_instr *instr) override;
134 };
135
filter(const nir_instr * instr) const136 bool FixKcacheIndirectRead::filter(const nir_instr *instr) const
137 {
138 if (instr->type != nir_instr_type_intrinsic)
139 return false;
140
141 auto intr = nir_instr_as_intrinsic(instr);
142 if (intr->intrinsic != nir_intrinsic_load_ubo)
143 return false;
144
145 return nir_src_as_const_value(intr->src[0]) == nullptr;
146 }
147
lower(nir_instr * instr)148 nir_def *FixKcacheIndirectRead::lower(nir_instr *instr)
149 {
150 auto intr = nir_instr_as_intrinsic(instr);
151 assert(nir_src_as_const_value(intr->src[0]) == nullptr);
152
153 nir_def *result = &intr->def;
154 for (unsigned i = 14; i < b->shader->info.num_ubos; ++i) {
155 auto test_bufid = nir_imm_int(b, i);
156 auto direct_value =
157 nir_load_ubo(b, intr->num_components,
158 intr->def.bit_size,
159 test_bufid,
160 intr->src[1].ssa);
161 auto direct_load = nir_instr_as_intrinsic(direct_value->parent_instr);
162 nir_intrinsic_copy_const_indices(direct_load, intr);
163 result = nir_bcsel(b,
164 nir_ieq(b, test_bufid, intr->src[0].ssa),
165 direct_value,
166 result);
167 }
168 return result;
169 }
170
171 } // namespace r600
172
173 bool
r600_nir_lower_pack_unpack_2x16(nir_shader * shader)174 r600_nir_lower_pack_unpack_2x16(nir_shader *shader)
175 {
176 return r600::Lower2x16().run(shader);
177 }
178
179 bool
r600_nir_lower_trigen(nir_shader * shader,amd_gfx_level gfx_level)180 r600_nir_lower_trigen(nir_shader *shader, amd_gfx_level gfx_level)
181 {
182 return r600::LowerSinCos(gfx_level).run(shader);
183 }
184
185 bool
r600_nir_fix_kcache_indirect_access(nir_shader * shader)186 r600_nir_fix_kcache_indirect_access(nir_shader *shader)
187 {
188 return shader->info.num_ubos > 14 ?
189 r600::FixKcacheIndirectRead().run(shader) : false;
190 }
191