• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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