1 /*
2  * Copyright 2011 Tom Stellard <tstellar@gmail.com>
3  * Copyright 2013 Advanced Micro Devices, Inc.
4  *
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial
17  * portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
23  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Author: Tom Stellard <thomas.stellard@amd.com>
28  */
29 
30 #include <errno.h>
31 #include <limits.h>
32 #include <regex.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/types.h>
37 
38 #include "r500_fragprog.h"
39 #include "r300_fragprog_swizzle.h"
40 #include "radeon_compiler.h"
41 #include "radeon_compiler_util.h"
42 #include "radeon_opcodes.h"
43 #include "radeon_program.h"
44 #include "radeon_regalloc.h"
45 #include "radeon_swizzle.h"
46 #include "util/u_math.h"
47 
48 #include "rc_test_helpers.h"
49 
50 /* This file contains some helper functions for filling out the rc_instruction
51  * data structures.  These functions take a string as input based on the format
52  * output by rc_program_print().
53  */
54 
55 #define VERBOSE 0
56 
57 #define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0)
58 
59 #define REGEX_ERR_BUF_SIZE 50
60 
61 struct match_info {
62 	const char * String;
63 	int Length;
64 };
65 
is_whitespace(const char * str)66 static int is_whitespace(const char *str)
67 {
68 	regex_t regex;
69 	if (regcomp(&regex, "^[ \n]+$", REG_EXTENDED)) {
70 		fprintf(stderr, "Failed to compile whitespace regex\n");
71 		return 0;
72 	}
73 	return regexec(&regex, str, 0, NULL, 0) != REG_NOMATCH;
74 }
75 
match_length(regmatch_t * matches,int index)76 static int match_length(regmatch_t * matches, int index)
77 {
78 	return matches[index].rm_eo - matches[index].rm_so;
79 }
80 
regex_helper(const char * regex_str,const char * search_str,regmatch_t * matches,int num_matches)81 static int regex_helper(
82 	const char * regex_str,
83 	const char * search_str,
84 	regmatch_t * matches,
85 	int num_matches)
86 {
87 	char err_buf[REGEX_ERR_BUF_SIZE];
88 	regex_t regex;
89 	int err_code;
90 	unsigned int i;
91 
92 	err_code = regcomp(&regex, regex_str, REG_EXTENDED);
93 	if (err_code) {
94 		regerror(err_code, &regex, err_buf, REGEX_ERR_BUF_SIZE);
95 		fprintf(stderr, "Failed to compile regex: %s\n", err_buf);
96 		return 0;
97 	}
98 
99 	err_code = regexec(&regex, search_str, num_matches, matches, 0);
100 	DBG("Search string: '%s'\n", search_str);
101 	for (i = 0; i < num_matches; i++) {
102 		DBG("Match %u start = %d end = %d\n", i,
103 					matches[i].rm_so, matches[i].rm_eo);
104 	}
105 	if (err_code) {
106 		regerror(err_code, &regex, err_buf, REGEX_ERR_BUF_SIZE);
107 		fprintf(stderr, "Failed to match regex: %s\n", err_buf);
108 		return 0;
109 	}
110 	return 1;
111 }
112 
113 #define REGEX_SRC_MATCHES 6
114 
115 struct src_tokens {
116 	struct match_info Negate;
117 	struct match_info Abs;
118 	struct match_info File;
119 	struct match_info Index;
120 	struct match_info Swizzle;
121 };
122 
123 /**
124  * Initialize the source register at index src_index for the instruction based
125  * on src_str.
126  *
127  * NOTE: Warning in init_rc_normal_instruction() applies to this function as
128  * well.
129  *
130  * @param src_str A string that represents the source register.  The format for
131  * this string is the same that is output by rc_program_print.
132  * @return 1 On success, 0 on failure
133  */
init_rc_normal_src(struct rc_instruction * inst,unsigned int src_index,const char * src_str)134 int init_rc_normal_src(
135 	struct rc_instruction * inst,
136 	unsigned int src_index,
137 	const char * src_str)
138 {
139 	const char * regex_str = "(-*)(\\|*)([[:lower:]]*)\\[*([[:digit:]]*)\\]*(\\.*[[:lower:]_]*)";
140 	regmatch_t matches[REGEX_SRC_MATCHES];
141 	struct src_tokens tokens;
142 	struct rc_src_register * src_reg = &inst->U.I.SrcReg[src_index];
143 	unsigned int i;
144 
145 	/* Execute the regex */
146 	if (!regex_helper(regex_str, src_str, matches, REGEX_SRC_MATCHES)) {
147 		fprintf(stderr, "Failed to execute regex for src register.\n");
148 		return 0;
149 	}
150 
151 	/* Create Tokens */
152 	tokens.Negate.String = src_str + matches[1].rm_so;
153 	tokens.Negate.Length = match_length(matches, 1);
154 	tokens.Abs.String = src_str + matches[2].rm_so;
155 	tokens.Abs.Length = match_length(matches, 2);
156 	tokens.File.String = src_str + matches[3].rm_so;
157 	tokens.File.Length = match_length(matches, 3);
158 	tokens.Index.String = src_str + matches[4].rm_so;
159 	tokens.Index.Length = match_length(matches, 4);
160 	tokens.Swizzle.String = src_str + matches[5].rm_so;
161 	tokens.Swizzle.Length = match_length(matches, 5);
162 
163 	/* Negate */
164 	if (tokens.Negate.Length  > 0) {
165 		src_reg->Negate = RC_MASK_XYZW;
166 	}
167 
168 	/* Abs */
169 	if (tokens.Abs.Length > 0) {
170 		src_reg->Abs = 1;
171 	}
172 
173 	/* File */
174 	if (!strncmp(tokens.File.String, "temp", tokens.File.Length)) {
175 		src_reg->File = RC_FILE_TEMPORARY;
176 	} else if (!strncmp(tokens.File.String, "input", tokens.File.Length)) {
177 		src_reg->File = RC_FILE_INPUT;
178 	} else if (!strncmp(tokens.File.String, "const", tokens.File.Length)) {
179 		src_reg->File = RC_FILE_CONSTANT;
180 	} else if (!strncmp(tokens.File.String, "none", tokens.File.Length)) {
181 		src_reg->File = RC_FILE_NONE;
182 	}
183 
184 	/* Index */
185 	errno = 0;
186 	src_reg->Index = strtol(tokens.Index.String, NULL, 10);
187 	if (errno > 0) {
188 		fprintf(stderr, "Could not convert src register index.\n");
189 		return 0;
190 	}
191 
192 	/* Swizzle */
193 	if (tokens.Swizzle.Length == 0) {
194 		src_reg->Swizzle = RC_SWIZZLE_XYZW;
195 	} else {
196 		int str_index = 1;
197 		src_reg->Swizzle = RC_MAKE_SWIZZLE_SMEAR(RC_SWIZZLE_UNUSED);
198 		if (tokens.Swizzle.String[0] != '.') {
199 			fprintf(stderr, "First char of swizzle is not valid.\n");
200 			return 0;
201 		}
202 		for (i = 0; i < 4 && str_index < tokens.Swizzle.Length;
203 							i++, str_index++) {
204 			if (tokens.Swizzle.String[str_index] == '-') {
205 				src_reg->Negate |= (1 << i);
206 				str_index++;
207 			}
208 			switch(tokens.Swizzle.String[str_index]) {
209 			case 'x':
210 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_X);
211 				break;
212 			case 'y':
213 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Y);
214 				break;
215 			case 'z':
216 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Z);
217 				break;
218 			case 'w':
219 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_W);
220 				break;
221 			case '1':
222 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ONE);
223 				break;
224 			case '0':
225 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ZERO);
226 				break;
227 			case 'H':
228 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_HALF);
229 				break;
230 			case '_':
231 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_UNUSED);
232 				break;
233 			default:
234 				fprintf(stderr, "Unknown src register swizzle: %c\n",
235 						tokens.Swizzle.String[str_index]);
236 				return 0;
237 			}
238 		}
239 	}
240 	DBG("File=%u index=%u swizzle=%x negate=%u abs=%u\n",
241 			src_reg->File, src_reg->Index, src_reg->Swizzle,
242 			src_reg->Negate, src_reg->Abs);
243 	return 1;
244 }
245 
246 #define REGEX_DST_MATCHES 4
247 
248 struct dst_tokens {
249 	struct match_info File;
250 	struct match_info Index;
251 	struct match_info WriteMask;
252 };
253 
254 /**
255  * Initialize the destination for the instruction based on dst_str.
256  *
257  * NOTE: Warning in init_rc_normal_instruction() applies to this function as
258  * well.
259  *
260  * @param dst_str A string that represents the destination register.  The format
261  * for this string is the same that is output by rc_program_print.
262  * @return 1 On success, 0 on failure
263  */
init_rc_normal_dst(struct rc_instruction * inst,const char * dst_str)264 int init_rc_normal_dst(
265 	struct rc_instruction * inst,
266 	const char * dst_str)
267 {
268 	const char * regex_str = "([[:lower:]]*)\\[*([[:digit:]]*)\\]*(\\.*[[:lower:]]*)";
269 	regmatch_t matches[REGEX_DST_MATCHES];
270 	struct dst_tokens tokens;
271 	unsigned int i;
272 
273 	/* Execute the regex */
274 	if (!regex_helper(regex_str, dst_str, matches, REGEX_DST_MATCHES)) {
275 		fprintf(stderr, "Failed to execute regex for dst register.\n");
276 		return 0;
277 	}
278 
279 	/* Create Tokens */
280 	tokens.File.String = dst_str + matches[1].rm_so;
281 	tokens.File.Length = match_length(matches, 1);
282 	tokens.Index.String = dst_str + matches[2].rm_so;
283 	tokens.Index.Length = match_length(matches, 2);
284 	tokens.WriteMask.String = dst_str + matches[3].rm_so;
285 	tokens.WriteMask.Length = match_length(matches, 3);
286 
287 	/* File Type */
288 	if (!strncmp(tokens.File.String, "temp", tokens.File.Length)) {
289 		inst->U.I.DstReg.File = RC_FILE_TEMPORARY;
290 	} else if (!strncmp(tokens.File.String, "output", tokens.File.Length)) {
291 		inst->U.I.DstReg.File = RC_FILE_OUTPUT;
292 	} else if (!strncmp(tokens.File.String, "none", tokens.File.Length)) {
293 		inst->U.I.DstReg.File = RC_FILE_NONE;
294 		return 1;
295 	} else {
296 		fprintf(stderr, "Unknown dst register file type.\n");
297 		return 0;
298 	}
299 
300 	/* File Index */
301 	errno = 0;
302 	inst->U.I.DstReg.Index = strtol(tokens.Index.String, NULL, 10);
303 
304 	if (errno > 0) {
305 		fprintf(stderr, "Could not convert dst register index\n");
306 		return 0;
307 	}
308 
309 	/* WriteMask */
310 	if (tokens.WriteMask.Length == 0) {
311 		inst->U.I.DstReg.WriteMask = RC_MASK_XYZW;
312 	} else {
313 		inst->U.I.DstReg.WriteMask = 0;
314 		/* The first character should be '.' */
315 		if (tokens.WriteMask.String[0] != '.') {
316 			fprintf(stderr, "1st char of writemask is not valid.\n");
317 			return 0;
318 		}
319 		for (i = 1; i < tokens.WriteMask.Length; i++) {
320 			switch(tokens.WriteMask.String[i]) {
321 			case 'x':
322 				inst->U.I.DstReg.WriteMask |= RC_MASK_X;
323 				break;
324 			case 'y':
325 				inst->U.I.DstReg.WriteMask |= RC_MASK_Y;
326 				break;
327 			case 'z':
328 				inst->U.I.DstReg.WriteMask |= RC_MASK_Z;
329 				break;
330 			case 'w':
331 				inst->U.I.DstReg.WriteMask |= RC_MASK_W;
332 				break;
333 			default:
334 				fprintf(stderr, "Unknown swizzle in writemask: %c\n",
335 							tokens.WriteMask.String[i]);
336 				return 0;
337 			}
338 		}
339 	}
340 	DBG("Dst Reg File=%u Index=%d Writemask=%d\n",
341 			inst->U.I.DstReg.File,
342 			inst->U.I.DstReg.Index,
343 			inst->U.I.DstReg.WriteMask);
344 	return 1;
345 }
346 
347 #define REGEX_INST_MATCHES 7
348 #define REGEX_CONST_MATCHES 5
349 
350 struct inst_tokens {
351 	struct match_info Opcode;
352 	struct match_info Sat;
353 	struct match_info Dst;
354 	struct match_info Srcs[3];
355 };
356 
357 /**
358  * Initialize a normal instruction based on inst_str.
359  *
360  * WARNING: This function might not be able to handle every kind of format that
361  * rc_program_print() can output.  If you are having problems with a
362  * particular string, you may need to add support for it to this functions.
363  *
364  * @param inst_str A string that represents the source register.  The format for
365  * this string is the same that is output by rc_program_print.
366  * @return 1 On success, 0 on failure
367  */
368 
parse_rc_normal_instruction(struct rc_instruction * inst,const char * inst_str)369 int parse_rc_normal_instruction(
370 	struct rc_instruction * inst,
371 	const char * inst_str)
372 {
373 	const char * regex_str = "[[:digit:]: ]*([[:upper:][:digit:]]+)(_SAT)*[ ]*([^,;]*)[, ]*([^,;]*)[, ]*([^,;]*)[, ]*([^;]*)";
374 	int i;
375 	regmatch_t matches[REGEX_INST_MATCHES];
376 	struct inst_tokens tokens;
377 
378 	/* Execute the regex */
379 	if (!regex_helper(regex_str, inst_str, matches, REGEX_INST_MATCHES)) {
380 		return 0;
381 	}
382 	memset(&tokens, 0, sizeof(tokens));
383 
384 	/* Create Tokens */
385 	tokens.Opcode.String = inst_str + matches[1].rm_so;
386 	tokens.Opcode.Length = match_length(matches, 1);
387 	if (matches[2].rm_so > -1) {
388 		tokens.Sat.String = inst_str + matches[2].rm_so;
389 		tokens.Sat.Length = match_length(matches, 2);
390 	}
391 
392 
393 	/* Fill out the rest of the instruction. */
394 	inst->Type = RC_INSTRUCTION_NORMAL;
395 
396 	for (i = 0; i < MAX_RC_OPCODE; i++) {
397 		const struct rc_opcode_info * info = rc_get_opcode_info(i);
398 		unsigned int first_src = 3;
399 		unsigned int j;
400 		if (strncmp(tokens.Opcode.String, info->Name, tokens.Opcode.Length)) {
401 			continue;
402 		}
403 		inst->U.I.Opcode = info->Opcode;
404 		if (info->HasDstReg) {
405 			char * dst_str;
406 			tokens.Dst.String = inst_str + matches[3].rm_so;
407 			tokens.Dst.Length = match_length(matches, 3);
408 			first_src++;
409 
410 			dst_str = malloc(sizeof(char) * (tokens.Dst.Length + 1));
411 			strncpy(dst_str, tokens.Dst.String, tokens.Dst.Length);
412 			dst_str[tokens.Dst.Length] = '\0';
413 			init_rc_normal_dst(inst, dst_str);
414 			free(dst_str);
415 		}
416 		for (j = 0; j < info->NumSrcRegs; j++) {
417 			char * src_str;
418 			tokens.Srcs[j].String =
419 				inst_str + matches[first_src + j].rm_so;
420 			tokens.Srcs[j].Length =
421 				match_length(matches, first_src + j);
422 
423 			src_str = malloc(sizeof(char) *
424 						(tokens.Srcs[j].Length + 1));
425 			strncpy(src_str, tokens.Srcs[j].String,
426 						tokens.Srcs[j].Length);
427 			src_str[tokens.Srcs[j].Length] = '\0';
428 			init_rc_normal_src(inst, j, src_str);
429 		}
430 		if (info->HasTexture) {
431 			/* XXX: Will this always be XYZW ? */
432 			inst->U.I.TexSwizzle = RC_SWIZZLE_XYZW;
433 		}
434 		break;
435 	}
436 	return 1;
437 }
438 
439 #define INDEX_TOKEN_LEN 4
440 #define FLOAT_TOKEN_LEN 50
parse_constant(unsigned * index,float * data,const char * const_str)441 int parse_constant(unsigned *index, float *data, const char *const_str)
442 {
443 	int matched = sscanf(const_str, "const[%d] {%f, %f, %f, %f}", index,
444 				&data[0], &data[1], &data[2], &data[3]);
445 	return matched == 5;
446 }
447 
init_rc_normal_instruction(struct rc_instruction * inst,const char * inst_str)448 int init_rc_normal_instruction(
449 	struct rc_instruction * inst,
450 	const char * inst_str)
451 {
452 	/* Initialize inst */
453 	memset(inst, 0, sizeof(struct rc_instruction));
454 
455 	return parse_rc_normal_instruction(inst, inst_str);
456 }
457 
add_instruction(struct radeon_compiler * c,const char * inst_string)458 void add_instruction(struct radeon_compiler *c, const char * inst_string)
459 {
460 	struct rc_instruction * new_inst =
461 		rc_insert_new_instruction(c, c->Program.Instructions.Prev);
462 
463 	parse_rc_normal_instruction(new_inst, inst_string);
464 
465 }
466 
add_constant(struct radeon_compiler * c,const char * const_str)467 int add_constant(struct radeon_compiler *c, const char *const_str)
468 {
469 	float data[4];
470 	unsigned index;
471 	struct rc_constant_list *constants;
472 	struct rc_constant constant;
473 
474 	if (!parse_constant(&index, data, const_str)) {
475 		return 0;
476 	}
477 
478 	constants = &c->Program.Constants;
479 	if (constants->_Reserved < index) {
480 		struct rc_constant * newlist;
481 
482 		constants->_Reserved = index + 100;
483 
484 		newlist = malloc(sizeof(struct rc_constant) * constants->_Reserved);
485 		if (constants->Constants) {
486 			memcpy(newlist, constants->Constants,
487 				sizeof(struct rc_constant) *
488 					constants->_Reserved);
489 			free(constants->Constants);
490 		}
491 
492 		constants->Constants = newlist;
493 	}
494 
495 	memset(&constant, 0, sizeof(constant));
496 	constant.Type = RC_CONSTANT_IMMEDIATE;
497 	constant.Size = 4;
498 	memcpy(constant.u.Immediate, data, sizeof(float) * 4);
499 	constants->Constants[index] = constant;
500 	constants->Count = MAX2(constants->Count, index + 1);
501 
502 	return 1;
503 }
504 
init_compiler(struct radeon_compiler * c,enum rc_program_type program_type,unsigned is_r500,unsigned is_r400)505 void init_compiler(
506 	struct radeon_compiler *c,
507 	enum rc_program_type program_type,
508 	unsigned is_r500,
509 	unsigned is_r400)
510 {
511 	struct rc_regalloc_state *rs = malloc(sizeof(struct rc_regalloc_state));
512 	rc_init_regalloc_state(rs);
513 	rc_init(c, rs);
514 
515 	c->is_r500 = is_r500;
516 	c->max_temp_regs = is_r500 ? 128 : (is_r400 ? 64 : 32);
517 	c->max_constants = is_r500 ? 256 : 32;
518 	c->max_alu_insts = (is_r500 || is_r400) ? 512 : 64;
519 	c->max_tex_insts = (is_r500 || is_r400) ? 512 : 32;
520 	if (program_type == RC_FRAGMENT_PROGRAM) {
521 		c->has_half_swizzles = 1;
522 		c->has_presub = 1;
523 		c->has_omod = 1;
524 		c->SwizzleCaps =
525 			is_r500 ? &r500_swizzle_caps : &r300_swizzle_caps;
526 	} else {
527 		c->SwizzleCaps = &r300_vertprog_swizzle_caps;
528 	}
529 }
530 
531 #define MAX_LINE_LENGTH 100
532 
load_program(struct radeon_compiler * c,struct rc_test_file * test,const char * filename)533 unsigned load_program(
534 	struct radeon_compiler *c,
535 	struct rc_test_file *test,
536 	const char *filename)
537 {
538 	char line[MAX_LINE_LENGTH];
539 	char path[PATH_MAX];
540 	FILE *file;
541 	unsigned *count;
542 	char **string_store;
543 	unsigned i = 0;
544 	int n;
545 
546 	memset(line, 0, sizeof(line));
547 	n = snprintf(path, PATH_MAX, TEST_PATH "/%s", filename);
548 	if (n < 0 || n >= PATH_MAX) {
549 		return 0;
550 	}
551 
552 	file = fopen(path, "r");
553 	if (!file) {
554 		return 0;
555 	}
556 	memset(test, 0, sizeof(struct rc_test_file));
557 
558 	count = &test->num_input_lines;
559 
560 	while (fgets(line, MAX_LINE_LENGTH, file)){
561 		char last_char = line[MAX_LINE_LENGTH - 1];
562 		if (last_char && last_char != '\n') {
563 			fprintf(stderr, "Error line cannot be longer than 100 "
564 				"characters:\n%s\n", line);
565 			fclose(file);
566 			return 0;
567 		}
568 
569 		// Comment
570 		if (line[0] == '#' || is_whitespace(line)) {
571 			continue;
572 		}
573 
574 		if (line[0] == '=') {
575 			count = &test->num_expected_lines;
576 			continue;
577 		}
578 
579 		(*count)++;
580 	}
581 
582 	test->input = malloc(sizeof(char *) * test->num_input_lines);
583 	test->expected = malloc(sizeof(char *) * test->num_expected_lines);
584 
585 	rewind(file);
586 	string_store = test->input;
587 
588 	while(fgets(line, MAX_LINE_LENGTH, file)) {
589 		// Comment
590 		char * dst;
591 		if (line[0] == '#' || is_whitespace(line)) {
592 			continue;
593 		}
594 
595 		if (line[0] == '=') {
596 			i = 0;
597 			string_store = test->expected;
598 			continue;
599 		}
600 
601 		dst = string_store[i++] = malloc((strlen(line) + 1) *
602 							sizeof (char));
603 		strcpy(dst, line);
604 	}
605 
606 	for (i = 0; i < test->num_input_lines; i++) {
607 		if (test->input[i][0] == 'c') {
608 			add_constant(c, test->input[i]);
609 			continue;
610 		}
611 		// XXX: Parse immediates from the file.
612 		add_instruction(c, test->input[i]);
613 	}
614 
615 	fclose(file);
616 	return 1;
617 }
618