1 #include <errno.h>
2 #include <regex.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <sys/types.h>
7 
8 #include "../radeon_compiler_util.h"
9 #include "../radeon_opcodes.h"
10 #include "../radeon_program.h"
11 
12 #include "rc_test_helpers.h"
13 
14 /* This file contains some helper functions for filling out the rc_instruction
15  * data structures.  These functions take a string as input based on the format
16  * output by rc_program_print().
17  */
18 
19 #define VERBOSE 0
20 
21 #define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0)
22 
23 #define REGEX_ERR_BUF_SIZE 50
24 
25 struct match_info {
26 	const char * String;
27 	int Length;
28 };
29 
match_length(regmatch_t * matches,int index)30 static int match_length(regmatch_t * matches, int index)
31 {
32 	return matches[index].rm_eo - matches[index].rm_so;
33 }
34 
regex_helper(const char * regex_str,const char * search_str,regmatch_t * matches,int num_matches)35 static int regex_helper(
36 	const char * regex_str,
37 	const char * search_str,
38 	regmatch_t * matches,
39 	int num_matches)
40 {
41 	char err_buf[REGEX_ERR_BUF_SIZE];
42 	regex_t regex;
43 	int err_code;
44 	unsigned int i;
45 
46 	err_code = regcomp(&regex, regex_str, REG_EXTENDED);
47 	if (err_code) {
48 		regerror(err_code, &regex, err_buf, REGEX_ERR_BUF_SIZE);
49 		fprintf(stderr, "Failed to compile regex: %s\n", err_buf);
50 		return 0;
51 	}
52 
53 	err_code = regexec(&regex, search_str, num_matches, matches, 0);
54 	DBG("Search string: '%s'\n", search_str);
55 	for (i = 0; i < num_matches; i++) {
56 		DBG("Match %u start = %d end = %d\n", i,
57 					matches[i].rm_so, matches[i].rm_eo);
58 	}
59 	if (err_code) {
60 		regerror(err_code, &regex, err_buf, REGEX_ERR_BUF_SIZE);
61 		fprintf(stderr, "Failed to match regex: %s\n", err_buf);
62 		return 0;
63 	}
64 	return 1;
65 }
66 
67 #define REGEX_SRC_MATCHES 6
68 
69 struct src_tokens {
70 	struct match_info Negate;
71 	struct match_info Abs;
72 	struct match_info File;
73 	struct match_info Index;
74 	struct match_info Swizzle;
75 };
76 
77 /**
78  * Initialize the source register at index src_index for the instruction based
79  * on src_str.
80  *
81  * NOTE: Warning in init_rc_normal_instruction() applies to this function as
82  * well.
83  *
84  * @param src_str A string that represents the source register.  The format for
85  * this string is the same that is output by rc_program_print.
86  * @return 1 On success, 0 on failure
87  */
init_rc_normal_src(struct rc_instruction * inst,unsigned int src_index,const char * src_str)88 int init_rc_normal_src(
89 	struct rc_instruction * inst,
90 	unsigned int src_index,
91 	const char * src_str)
92 {
93 	const char * regex_str = "(-*)(\\|*)([[:lower:]]*)\\[([[:digit:]])\\](\\.*[[:lower:]-]*)";
94 	regmatch_t matches[REGEX_SRC_MATCHES];
95 	struct src_tokens tokens;
96 	struct rc_src_register * src_reg = &inst->U.I.SrcReg[src_index];
97 	unsigned int i;
98 
99 	/* Execute the regex */
100 	if (!regex_helper(regex_str, src_str, matches, REGEX_SRC_MATCHES)) {
101 		fprintf(stderr, "Failed to execute regex for src register.\n");
102 		return 0;
103 	}
104 
105 	/* Create Tokens */
106 	tokens.Negate.String = src_str + matches[1].rm_so;
107 	tokens.Negate.Length = match_length(matches, 1);
108 	tokens.Abs.String = src_str + matches[2].rm_so;
109 	tokens.Abs.Length = match_length(matches, 2);
110 	tokens.File.String = src_str + matches[3].rm_so;
111 	tokens.File.Length = match_length(matches, 3);
112 	tokens.Index.String = src_str + matches[4].rm_so;
113 	tokens.Index.Length = match_length(matches, 4);
114 	tokens.Swizzle.String = src_str + matches[5].rm_so;
115 	tokens.Swizzle.Length = match_length(matches, 5);
116 
117 	/* Negate */
118 	if (tokens.Negate.Length  > 0) {
119 		src_reg->Negate = RC_MASK_XYZW;
120 	}
121 
122 	/* Abs */
123 	if (tokens.Abs.Length > 0) {
124 		src_reg->Abs = 1;
125 	}
126 
127 	/* File */
128 	if (!strncmp(tokens.File.String, "temp", tokens.File.Length)) {
129 		src_reg->File = RC_FILE_TEMPORARY;
130 	} else if (!strncmp(tokens.File.String, "input", tokens.File.Length)) {
131 		src_reg->File = RC_FILE_INPUT;
132 	} else if (!strncmp(tokens.File.String, "const", tokens.File.Length)) {
133 		src_reg->File = RC_FILE_CONSTANT;
134 	} else if (!strncmp(tokens.File.String, "none", tokens.File.Length)) {
135 		src_reg->File = RC_FILE_NONE;
136 	}
137 
138 	/* Index */
139 	errno = 0;
140 	src_reg->Index = strtol(tokens.Index.String, NULL, 10);
141 	if (errno > 0) {
142 		fprintf(stderr, "Could not convert src register index.\n");
143 		return 0;
144 	}
145 
146 	/* Swizzle */
147 	if (tokens.Swizzle.Length == 0) {
148 		src_reg->Swizzle = RC_SWIZZLE_XYZW;
149 	} else {
150 		int str_index = 1;
151 		src_reg->Swizzle = RC_MAKE_SWIZZLE_SMEAR(RC_SWIZZLE_UNUSED);
152 		if (tokens.Swizzle.String[0] != '.') {
153 			fprintf(stderr, "First char of swizzle is not valid.\n");
154 			return 0;
155 		}
156 		for (i = 0; i < 4; i++, str_index++) {
157 			if (tokens.Swizzle.String[str_index] == '-') {
158 				src_reg->Negate |= (1 << i);
159 				str_index++;
160 			}
161 			switch(tokens.Swizzle.String[str_index]) {
162 			case 'x':
163 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_X);
164 				break;
165 			case 'y':
166 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Y);
167 				break;
168 			case 'z':
169 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Z);
170 				break;
171 			case 'w':
172 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_W);
173 				break;
174 			case '1':
175 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ONE);
176 				break;
177 			case '0':
178 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ZERO);
179 				break;
180 			case 'H':
181 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_HALF);
182 				break;
183 			case '_':
184 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_UNUSED);
185 				break;
186 			default:
187 				fprintf(stderr, "Unknown src register swizzle.\n");
188 				return 0;
189 			}
190 		}
191 	}
192 	DBG("File=%u index=%u swizzle=%x negate=%u abs=%u\n",
193 			src_reg->File, src_reg->Index, src_reg->Swizzle,
194 			src_reg->Negate, src_reg->Abs);
195 	return 1;
196 }
197 
198 #define REGEX_DST_MATCHES 4
199 
200 struct dst_tokens {
201 	struct match_info File;
202 	struct match_info Index;
203 	struct match_info WriteMask;
204 };
205 
206 /**
207  * Initialize the destination for the instruction based on dst_str.
208  *
209  * NOTE: Warning in init_rc_normal_instruction() applies to this function as
210  * well.
211  *
212  * @param dst_str A string that represents the destination register.  The format
213  * for this string is the same that is output by rc_program_print.
214  * @return 1 On success, 0 on failure
215  */
init_rc_normal_dst(struct rc_instruction * inst,const char * dst_str)216 int init_rc_normal_dst(
217 	struct rc_instruction * inst,
218 	const char * dst_str)
219 {
220 	const char * regex_str = "([[:lower:]]*)\\[([[:digit:]]*)\\](\\.*[[:lower:]]*)";
221 	regmatch_t matches[REGEX_DST_MATCHES];
222 	struct dst_tokens tokens;
223 	unsigned int i;
224 
225 	/* Execute the regex */
226 	if (!regex_helper(regex_str, dst_str, matches, REGEX_DST_MATCHES)) {
227 		fprintf(stderr, "Failed to execute regex for dst register.\n");
228 		return 0;
229 	}
230 
231 	/* Create Tokens */
232 	tokens.File.String = dst_str + matches[1].rm_so;
233 	tokens.File.Length = match_length(matches, 1);
234 	tokens.Index.String = dst_str + matches[2].rm_so;
235 	tokens.Index.Length = match_length(matches, 2);
236 	tokens.WriteMask.String = dst_str + matches[3].rm_so;
237 	tokens.WriteMask.Length = match_length(matches, 3);
238 
239 	/* File Type */
240 	if (!strncmp(tokens.File.String, "temp", tokens.File.Length)) {
241 		inst->U.I.DstReg.File = RC_FILE_TEMPORARY;
242 	} else if (!strncmp(tokens.File.String, "output", tokens.File.Length)) {
243 		inst->U.I.DstReg.File = RC_FILE_OUTPUT;
244 	} else {
245 		fprintf(stderr, "Unknown dst register file type.\n");
246 		return 0;
247 	}
248 
249 	/* File Index */
250 	errno = 0;
251 	inst->U.I.DstReg.Index = strtol(tokens.Index.String, NULL, 10);
252 
253 	if (errno > 0) {
254 		fprintf(stderr, "Could not convert dst register index\n");
255 		return 0;
256 	}
257 
258 	/* WriteMask */
259 	if (tokens.WriteMask.Length == 0) {
260 		inst->U.I.DstReg.WriteMask = RC_MASK_XYZW;
261 	} else {
262 		/* The first character should be '.' */
263 		if (tokens.WriteMask.String[0] != '.') {
264 			fprintf(stderr, "1st char of writemask is not valid.\n");
265 			return 0;
266 		}
267 		for (i = 1; i < tokens.WriteMask.Length; i++) {
268 			switch(tokens.WriteMask.String[i]) {
269 			case 'x':
270 				inst->U.I.DstReg.WriteMask |= RC_MASK_X;
271 				break;
272 			case 'y':
273 				inst->U.I.DstReg.WriteMask |= RC_MASK_Y;
274 				break;
275 			case 'z':
276 				inst->U.I.DstReg.WriteMask |= RC_MASK_Z;
277 				break;
278 			case 'w':
279 				inst->U.I.DstReg.WriteMask |= RC_MASK_W;
280 				break;
281 			default:
282 				fprintf(stderr, "Unknown swizzle in writemask.\n");
283 				return 0;
284 			}
285 		}
286 	}
287 	DBG("Dst Reg File=%u Index=%d Writemask=%d\n",
288 			inst->U.I.DstReg.File,
289 			inst->U.I.DstReg.Index,
290 			inst->U.I.DstReg.WriteMask);
291 	return 1;
292 }
293 
294 #define REGEX_INST_MATCHES 7
295 
296 struct inst_tokens {
297 	struct match_info Opcode;
298 	struct match_info Sat;
299 	struct match_info Dst;
300 	struct match_info Srcs[3];
301 };
302 
303 /**
304  * Initialize a normal instruction based on inst_str.
305  *
306  * WARNING: This function might not be able to handle every kind of format that
307  * rc_program_print() can output.  If you are having problems with a
308  * particular string, you may need to add support for it to this functions.
309  *
310  * @param inst_str A string that represents the source register.  The format for
311  * this string is the same that is output by rc_program_print.
312  * @return 1 On success, 0 on failure
313  */
init_rc_normal_instruction(struct rc_instruction * inst,const char * inst_str)314 int init_rc_normal_instruction(
315 	struct rc_instruction * inst,
316 	const char * inst_str)
317 {
318 	const char * regex_str = "([[:upper:]]+)(_SAT)* ([^,]*)[, ]*([^,]*)[, ]*([^,]*)[, ]*([^;]*)";
319 	int i;
320 	regmatch_t matches[REGEX_INST_MATCHES];
321 	struct inst_tokens tokens;
322 
323 	/* Initialize inst */
324 	memset(inst, 0, sizeof(struct rc_instruction));
325 	inst->Type = RC_INSTRUCTION_NORMAL;
326 
327 	/* Execute the regex */
328 	if (!regex_helper(regex_str, inst_str, matches, REGEX_INST_MATCHES)) {
329 		return 0;
330 	}
331 	memset(&tokens, 0, sizeof(tokens));
332 
333 	/* Create Tokens */
334 	tokens.Opcode.String = inst_str + matches[1].rm_so;
335 	tokens.Opcode.Length = match_length(matches, 1);
336 	if (matches[2].rm_so > -1) {
337 		tokens.Sat.String = inst_str + matches[2].rm_so;
338 		tokens.Sat.Length = match_length(matches, 2);
339 	}
340 
341 
342 	/* Fill out the rest of the instruction. */
343 	for (i = 0; i < MAX_RC_OPCODE; i++) {
344 		const struct rc_opcode_info * info = rc_get_opcode_info(i);
345 		unsigned int first_src = 3;
346 		unsigned int j;
347 		if (strncmp(tokens.Opcode.String, info->Name, tokens.Opcode.Length)) {
348 			continue;
349 		}
350 		inst->U.I.Opcode = info->Opcode;
351 		if (info->HasDstReg) {
352 			char * dst_str;
353 			tokens.Dst.String = inst_str + matches[3].rm_so;
354 			tokens.Dst.Length = match_length(matches, 3);
355 			first_src++;
356 
357 			dst_str = malloc(sizeof(char) * (tokens.Dst.Length + 1));
358 			strncpy(dst_str, tokens.Dst.String, tokens.Dst.Length);
359 			dst_str[tokens.Dst.Length] = '\0';
360 			init_rc_normal_dst(inst, dst_str);
361 			free(dst_str);
362 		}
363 		for (j = 0; j < info->NumSrcRegs; j++) {
364 			char * src_str;
365 			tokens.Srcs[j].String =
366 				inst_str + matches[first_src + j].rm_so;
367 			tokens.Srcs[j].Length =
368 				match_length(matches, first_src + j);
369 
370 			src_str = malloc(sizeof(char) *
371 						(tokens.Srcs[j].Length + 1));
372 			strncpy(src_str, tokens.Srcs[j].String,
373 						tokens.Srcs[j].Length);
374 			src_str[tokens.Srcs[j].Length] = '\0';
375 			init_rc_normal_src(inst, j, src_str);
376 		}
377 		break;
378 	}
379 	return 1;
380 }
381