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
25struct match_info {
26	const char * String;
27	int Length;
28};
29
30static int match_length(regmatch_t * matches, int index)
31{
32	return matches[index].rm_eo - matches[index].rm_so;
33}
34
35static 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
69struct 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 */
88int 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
200struct 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 */
216int 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
296struct 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 */
314int 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