1 /*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 /**
25 * \file rogue_validate.c
26 *
27 * \brief Contains rules and functions for validating Rogue data structures.
28 */
29
30 #include <stdbool.h>
31
32 #include "rogue_operand.h"
33 #include "rogue_shader.h"
34 #include "rogue_util.h"
35 #include "rogue_validate.h"
36 #include "util/list.h"
37 #include "util/macros.h"
38
39 /**
40 * \brief Register operand rules.
41 */
42 #define REG_RULE(OPERAND, ACCESS, MAX, MODIFIERS) \
43 [ROGUE_OPERAND_TYPE_REG_##OPERAND] = { \
44 .access = ROGUE_REG_ACCESS_##ACCESS, \
45 .max = MAX, \
46 .modifiers = ROGUE_REG_MOD_##MODIFIERS, \
47 }
48
49 /* TODO: Support register indexing > ROGUE_MAX_REG_TEMP. */
50 static const struct rogue_register_rule reg_rules[ROGUE_NUM_REG_TYPES] = {
51 REG_RULE(TEMP, RW, MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_TEMP), ALL),
52 REG_RULE(COEFF, RW, MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_COEFF), ALL),
53 REG_RULE(CONST, RW, MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_CONST), NONE),
54 REG_RULE(SHARED, RW, MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_SHARED), ALL),
55 REG_RULE(PIXEL_OUT,
56 RW,
57 MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_PIXEL_OUT),
58 NONE),
59 REG_RULE(VERTEX_IN,
60 RW,
61 MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_VERTEX_IN),
62 ALL),
63 REG_RULE(INTERNAL,
64 RW,
65 MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_INTERNAL),
66 NONE),
67 };
68 #undef REG_RULE
69
70 /**
71 * \brief Instruction rules.
72 */
73 /* TODO: Common up register classes to prevent long lines. */
74 static const struct rogue_instr_rule instr_rules[ROGUE_OP_COUNT] = {
75 [ROGUE_OP_NOP] = { .flags = 0, .num_operands = 0, .operand_rules = NULL, },
76 [ROGUE_OP_END_FRAG] = { .flags = 0, .num_operands = 0, .operand_rules = NULL, },
77 [ROGUE_OP_END_VERT] = { .flags = 0, .num_operands = 0, .operand_rules = NULL, },
78 [ROGUE_OP_WDF] = { .flags = 0,
79 .num_operands = 1, .operand_rules = (struct rogue_instr_operand_rule[]){
80 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_DRC), .min = -1, .max = -1, .align = -1, },
81 },
82 },
83 [ROGUE_OP_PIX_ITER_W] = { .flags = ROH(ROGUE_INSTR_FLAG_SAT),
84 .num_operands = 5, .operand_rules = (struct rogue_instr_operand_rule[]){
85 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
86 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_DRC), .min = -1, .max = -1, .align = -1, },
87 [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_COEFF), .min = -1, .max = -1, .align = ROGUE_COEFF_ALIGN, },
88 [3] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_COEFF), .min = -1, .max = -1, .align = ROGUE_COEFF_ALIGN, },
89 [4] = { .mask = ROH(ROGUE_OPERAND_TYPE_IMMEDIATE), .min = 1, .max = 16, .align = -1, },
90 },
91 },
92 [ROGUE_OP_MAX] = { .flags = 0,
93 .num_operands = 3, .operand_rules = (struct rogue_instr_operand_rule[]){
94 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
95 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
96 [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_CONST) | ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
97 },
98 },
99 [ROGUE_OP_MIN] = { .flags = 0,
100 .num_operands = 3, .operand_rules = (struct rogue_instr_operand_rule[]){
101 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP) | ROH(ROGUE_OPERAND_TYPE_REG_INTERNAL), .min = -1, .max = -1, .align = -1, },
102 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
103 [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_CONST) | ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
104 },
105 },
106 /* TODO: Add representation for 4 sequential registers. */
107 [ROGUE_OP_PACK_U8888] = { .flags = 0,
108 .num_operands = 2, .operand_rules = (struct rogue_instr_operand_rule[]){
109 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
110 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_INTERNAL), .min = -1, .max = -1, .align = -1, },
111 },
112 },
113 [ROGUE_OP_MOV] = { .flags = ROH(ROGUE_INSTR_FLAG_OLCHK),
114 .num_operands = 2, .operand_rules = (struct rogue_instr_operand_rule[]){
115 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP) | ROH(ROGUE_OPERAND_TYPE_REG_INTERNAL) | ROH(ROGUE_OPERAND_TYPE_REG_PIXEL_OUT), .min = -1, .max = -1, .align = -1, },
116 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_CONST) | ROH(ROGUE_OPERAND_TYPE_REG_TEMP) | ROH(ROGUE_OPERAND_TYPE_REG_SHARED) | ROH(ROGUE_OPERAND_TYPE_REG_VERTEX_IN), .min = -1, .max = -1, .align = -1, },
117 },
118 },
119 [ROGUE_OP_MOV_IMM] = { .flags = 0,
120 .num_operands = 2, .operand_rules = (struct rogue_instr_operand_rule[]){
121 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
122 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_IMMEDIATE), .min = 0, .max = UINT32_MAX, .align = -1, },
123 },
124 },
125 [ROGUE_OP_FMA] = { .flags = ROH(ROGUE_INSTR_FLAG_SAT) | ROH(ROGUE_INSTR_FLAG_LP),
126 .num_operands = 4, .operand_rules = (struct rogue_instr_operand_rule[]){
127 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
128 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
129 [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
130 [3] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
131 },
132 },
133 [ROGUE_OP_MUL] = { .flags = ROH(ROGUE_INSTR_FLAG_SAT) | ROH(ROGUE_INSTR_FLAG_LP),
134 .num_operands = 3, .operand_rules = (struct rogue_instr_operand_rule[]){
135 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
136 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
137 [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
138 },
139 },
140 [ROGUE_OP_VTXOUT] = { .flags = 0,
141 .num_operands = 2, .operand_rules = (struct rogue_instr_operand_rule[]){
142 [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_IMMEDIATE), .min = 0, .max = ROGUE_MAX_VERTEX_OUTPUTS, .align = -1, },
143 [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, },
144 },
145 },
146 };
147
148 /**
149 * \brief Validates an operand.
150 *
151 * \param[in] operand The operand.
152 * \return true if valid, otherwise false.
153 */
rogue_validate_operand(const struct rogue_operand * operand)154 bool rogue_validate_operand(const struct rogue_operand *operand)
155 {
156 ASSERT_OPERAND_RANGE(operand->type);
157
158 switch (operand->type) {
159 case ROGUE_OPERAND_TYPE_IMMEDIATE:
160 return true;
161
162 case ROGUE_OPERAND_TYPE_DRC:
163 CHECKF(operand->drc.number < ROGUE_NUM_DRCS,
164 "Invalid DRC number '%zu'.",
165 operand->drc.number);
166 return true;
167
168 case ROGUE_OPERAND_TYPE_REG_TEMP:
169 case ROGUE_OPERAND_TYPE_REG_COEFF:
170 case ROGUE_OPERAND_TYPE_REG_CONST:
171 case ROGUE_OPERAND_TYPE_REG_SHARED:
172 case ROGUE_OPERAND_TYPE_REG_PIXEL_OUT:
173 case ROGUE_OPERAND_TYPE_REG_VERTEX_IN:
174 case ROGUE_OPERAND_TYPE_REG_INTERNAL:
175 CHECKF(operand->reg.number < reg_rules[operand->type].max,
176 "Register number '%zu' out of range.",
177 operand->reg.number);
178 return true;
179
180 default:
181 break;
182 }
183
184 return false;
185 }
186
187 /**
188 * \brief Validates an instruction.
189 *
190 * \param[in] instr The instruction.
191 * \return true if valid, otherwise false.
192 */
rogue_validate_instr(const struct rogue_instr * instr)193 bool rogue_validate_instr(const struct rogue_instr *instr)
194 {
195 const struct rogue_instr_rule *rule;
196
197 ASSERT_OPCODE_RANGE(instr->opcode);
198
199 rule = &instr_rules[instr->opcode];
200
201 /* Validate flags. */
202 CHECKF(rogue_check_bitset(instr->flags, rule->flags),
203 "Invalid instruction flags specified.");
204
205 /* Validate number of operands. */
206 CHECKF(instr->num_operands == rule->num_operands,
207 "Invalid number of operands specified.");
208
209 CHECK(!rule->num_operands || instr->operands);
210 for (size_t u = 0U; u < instr->num_operands; ++u) {
211 /* Validate operand types. */
212 CHECKF(rogue_check_bitset(rogue_onehot(instr->operands[u].type),
213 rule->operand_rules[u].mask),
214 "Invalid type for operand %zu.",
215 u);
216
217 /* Validate immediate ranges. */
218 if (rogue_check_bitset(rogue_onehot(instr->operands[u].type),
219 ROH(ROGUE_OPERAND_TYPE_IMMEDIATE)) &&
220 rule->operand_rules[u].min != -1 &&
221 rule->operand_rules[u].max != -1) {
222 CHECKF(
223 instr->operands[u].immediate.value >= rule->operand_rules[u].min &&
224 instr->operands[u].immediate.value <= rule->operand_rules[u].max,
225 "Immediate value out of range for operand %zu.",
226 u);
227 }
228
229 /* Validate register alignment. */
230 if (rogue_check_bitset(rogue_onehot(instr->operands[u].type),
231 ROGUE_MASK_ANY_REG) &&
232 rule->operand_rules[u].align != -1) {
233 CHECKF(!(instr->operands[u].reg.number % rule->operand_rules[u].align),
234 "Invalid register alignment in operand %zu.",
235 u);
236 }
237
238 /* Validate each operand. */
239 CHECKF(rogue_validate_operand(&instr->operands[u]),
240 "Failed to validate operand.");
241 }
242
243 return true;
244 }
245
246 /**
247 * \brief Validates a shader.
248 *
249 * \param[in] shader The shader.
250 * \return true if valid, otherwise false.
251 */
rogue_validate_shader(const struct rogue_shader * shader)252 bool rogue_validate_shader(const struct rogue_shader *shader)
253 {
254 CHECK(!list_is_empty(&shader->instr_list));
255 ASSERT_SHADER_STAGE_RANGE(shader->stage);
256
257 /* Shader stage-specific validation. */
258 switch (shader->stage) {
259 case MESA_SHADER_VERTEX:
260 /* Make sure there is (only) one end vertex shader instruction. */
261 CHECKF(rogue_shader_instr_count_type(shader, ROGUE_OP_END_VERT) == 1,
262 "Shader must contain a single end.vert instruction.");
263
264 /* Make sure the end vertex shader instruction is the last one. */
265 CHECKF(instr_last_entry(&shader->instr_list)->opcode == ROGUE_OP_END_VERT,
266 "end.vert not last instruction.");
267 break;
268
269 case MESA_SHADER_FRAGMENT:
270 /* Make sure there is (only) one end fragment shader instruction. */
271 CHECKF(rogue_shader_instr_count_type(shader, ROGUE_OP_END_FRAG) == 1,
272 "Shader must contain a single end.frag instruction.");
273
274 /* Make sure the end fragment shader instruction is the last one. */
275 CHECKF(instr_last_entry(&shader->instr_list)->opcode == ROGUE_OP_END_FRAG,
276 "end.frag not last instruction.");
277 break;
278
279 default:
280 return false;
281 }
282
283 /* Validate each instruction. */
284 foreach_instr (instr, &shader->instr_list)
285 CHECKF(rogue_validate_instr(instr), "Failed to validate instruction.");
286
287 return true;
288 }
289