• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = %zu end = %zu\n", i,
103 					(size_t)matches[i].rm_so,
104 					(size_t)matches[i].rm_eo);
105 	}
106 	if (err_code) {
107 		regerror(err_code, &regex, err_buf, REGEX_ERR_BUF_SIZE);
108 		fprintf(stderr, "Failed to match regex: %s\n", err_buf);
109 		return 0;
110 	}
111 	return 1;
112 }
113 
114 #define REGEX_SRC_MATCHES 6
115 
116 struct src_tokens {
117 	struct match_info Negate;
118 	struct match_info Abs;
119 	struct match_info File;
120 	struct match_info Index;
121 	struct match_info Swizzle;
122 };
123 
124 /**
125  * Initialize the source register at index src_index for the instruction based
126  * on src_str.
127  *
128  * NOTE: Warning in init_rc_normal_instruction() applies to this function as
129  * well.
130  *
131  * @param src_str A string that represents the source register.  The format for
132  * this string is the same that is output by rc_program_print.
133  * @return 1 On success, 0 on failure
134  */
init_rc_normal_src(struct rc_instruction * inst,unsigned int src_index,const char * src_str)135 int init_rc_normal_src(
136 	struct rc_instruction * inst,
137 	unsigned int src_index,
138 	const char * src_str)
139 {
140 	const char * regex_str = "(-*)(\\|*)([[:lower:]]*)\\[*([[:digit:]]*)\\]*(\\.*[[:lower:]_]*)";
141 	regmatch_t matches[REGEX_SRC_MATCHES];
142 	struct src_tokens tokens;
143 	struct rc_src_register * src_reg = &inst->U.I.SrcReg[src_index];
144 	unsigned int i;
145 
146 	/* Execute the regex */
147 	if (!regex_helper(regex_str, src_str, matches, REGEX_SRC_MATCHES)) {
148 		fprintf(stderr, "Failed to execute regex for src register.\n");
149 		return 0;
150 	}
151 
152 	/* Create Tokens */
153 	tokens.Negate.String = src_str + matches[1].rm_so;
154 	tokens.Negate.Length = match_length(matches, 1);
155 	tokens.Abs.String = src_str + matches[2].rm_so;
156 	tokens.Abs.Length = match_length(matches, 2);
157 	tokens.File.String = src_str + matches[3].rm_so;
158 	tokens.File.Length = match_length(matches, 3);
159 	tokens.Index.String = src_str + matches[4].rm_so;
160 	tokens.Index.Length = match_length(matches, 4);
161 	tokens.Swizzle.String = src_str + matches[5].rm_so;
162 	tokens.Swizzle.Length = match_length(matches, 5);
163 
164 	/* Negate */
165 	if (tokens.Negate.Length  > 0) {
166 		src_reg->Negate = RC_MASK_XYZW;
167 	}
168 
169 	/* Abs */
170 	if (tokens.Abs.Length > 0) {
171 		src_reg->Abs = 1;
172 	}
173 
174 	/* File */
175 	if (!strncmp(tokens.File.String, "temp", tokens.File.Length)) {
176 		src_reg->File = RC_FILE_TEMPORARY;
177 	} else if (!strncmp(tokens.File.String, "input", tokens.File.Length)) {
178 		src_reg->File = RC_FILE_INPUT;
179 	} else if (!strncmp(tokens.File.String, "const", tokens.File.Length)) {
180 		src_reg->File = RC_FILE_CONSTANT;
181 	} else if (!strncmp(tokens.File.String, "none", tokens.File.Length)) {
182 		src_reg->File = RC_FILE_NONE;
183 	}
184 
185 	/* Index */
186 	errno = 0;
187 	src_reg->Index = strtol(tokens.Index.String, NULL, 10);
188 	if (errno > 0) {
189 		fprintf(stderr, "Could not convert src register index.\n");
190 		return 0;
191 	}
192 
193 	/* Swizzle */
194 	if (tokens.Swizzle.Length == 0) {
195 		src_reg->Swizzle = RC_SWIZZLE_XYZW;
196 	} else {
197 		int str_index = 1;
198 		src_reg->Swizzle = RC_MAKE_SWIZZLE_SMEAR(RC_SWIZZLE_UNUSED);
199 		if (tokens.Swizzle.String[0] != '.') {
200 			fprintf(stderr, "First char of swizzle is not valid.\n");
201 			return 0;
202 		}
203 		for (i = 0; i < 4 && str_index < tokens.Swizzle.Length;
204 							i++, str_index++) {
205 			if (tokens.Swizzle.String[str_index] == '-') {
206 				src_reg->Negate |= (1 << i);
207 				str_index++;
208 			}
209 			switch(tokens.Swizzle.String[str_index]) {
210 			case 'x':
211 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_X);
212 				break;
213 			case 'y':
214 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Y);
215 				break;
216 			case 'z':
217 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Z);
218 				break;
219 			case 'w':
220 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_W);
221 				break;
222 			case '1':
223 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ONE);
224 				break;
225 			case '0':
226 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ZERO);
227 				break;
228 			case 'H':
229 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_HALF);
230 				break;
231 			case '_':
232 				SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_UNUSED);
233 				break;
234 			default:
235 				fprintf(stderr, "Unknown src register swizzle: %c\n",
236 						tokens.Swizzle.String[str_index]);
237 				return 0;
238 			}
239 		}
240 	}
241 	DBG("File=%u index=%u swizzle=%x negate=%u abs=%u\n",
242 			src_reg->File, src_reg->Index, src_reg->Swizzle,
243 			src_reg->Negate, src_reg->Abs);
244 	return 1;
245 }
246 
247 #define REGEX_DST_MATCHES 4
248 
249 struct dst_tokens {
250 	struct match_info File;
251 	struct match_info Index;
252 	struct match_info WriteMask;
253 };
254 
255 /**
256  * Initialize the destination for the instruction based on dst_str.
257  *
258  * NOTE: Warning in init_rc_normal_instruction() applies to this function as
259  * well.
260  *
261  * @param dst_str A string that represents the destination register.  The format
262  * for this string is the same that is output by rc_program_print.
263  * @return 1 On success, 0 on failure
264  */
init_rc_normal_dst(struct rc_instruction * inst,const char * dst_str)265 int init_rc_normal_dst(
266 	struct rc_instruction * inst,
267 	const char * dst_str)
268 {
269 	const char * regex_str = "([[:lower:]]*)\\[*([[:digit:]]*)\\]*(\\.*[[:lower:]]*)";
270 	regmatch_t matches[REGEX_DST_MATCHES];
271 	struct dst_tokens tokens;
272 	unsigned int i;
273 
274 	/* Execute the regex */
275 	if (!regex_helper(regex_str, dst_str, matches, REGEX_DST_MATCHES)) {
276 		fprintf(stderr, "Failed to execute regex for dst register.\n");
277 		return 0;
278 	}
279 
280 	/* Create Tokens */
281 	tokens.File.String = dst_str + matches[1].rm_so;
282 	tokens.File.Length = match_length(matches, 1);
283 	tokens.Index.String = dst_str + matches[2].rm_so;
284 	tokens.Index.Length = match_length(matches, 2);
285 	tokens.WriteMask.String = dst_str + matches[3].rm_so;
286 	tokens.WriteMask.Length = match_length(matches, 3);
287 
288 	/* File Type */
289 	if (!strncmp(tokens.File.String, "temp", tokens.File.Length)) {
290 		inst->U.I.DstReg.File = RC_FILE_TEMPORARY;
291 	} else if (!strncmp(tokens.File.String, "output", tokens.File.Length)) {
292 		inst->U.I.DstReg.File = RC_FILE_OUTPUT;
293 	} else if (!strncmp(tokens.File.String, "none", tokens.File.Length)) {
294 		inst->U.I.DstReg.File = RC_FILE_NONE;
295 		return 1;
296 	} else {
297 		fprintf(stderr, "Unknown dst register file type.\n");
298 		return 0;
299 	}
300 
301 	/* File Index */
302 	errno = 0;
303 	inst->U.I.DstReg.Index = strtol(tokens.Index.String, NULL, 10);
304 
305 	if (errno > 0) {
306 		fprintf(stderr, "Could not convert dst register index\n");
307 		return 0;
308 	}
309 
310 	/* WriteMask */
311 	if (tokens.WriteMask.Length == 0) {
312 		inst->U.I.DstReg.WriteMask = RC_MASK_XYZW;
313 	} else {
314 		inst->U.I.DstReg.WriteMask = 0;
315 		/* The first character should be '.' */
316 		if (tokens.WriteMask.String[0] != '.') {
317 			fprintf(stderr, "1st char of writemask is not valid.\n");
318 			return 0;
319 		}
320 		for (i = 1; i < tokens.WriteMask.Length; i++) {
321 			switch(tokens.WriteMask.String[i]) {
322 			case 'x':
323 				inst->U.I.DstReg.WriteMask |= RC_MASK_X;
324 				break;
325 			case 'y':
326 				inst->U.I.DstReg.WriteMask |= RC_MASK_Y;
327 				break;
328 			case 'z':
329 				inst->U.I.DstReg.WriteMask |= RC_MASK_Z;
330 				break;
331 			case 'w':
332 				inst->U.I.DstReg.WriteMask |= RC_MASK_W;
333 				break;
334 			default:
335 				fprintf(stderr, "Unknown swizzle in writemask: %c\n",
336 							tokens.WriteMask.String[i]);
337 				return 0;
338 			}
339 		}
340 	}
341 	DBG("Dst Reg File=%u Index=%d Writemask=%d\n",
342 			inst->U.I.DstReg.File,
343 			inst->U.I.DstReg.Index,
344 			inst->U.I.DstReg.WriteMask);
345 	return 1;
346 }
347 
348 #define REGEX_INST_MATCHES 7
349 #define REGEX_CONST_MATCHES 5
350 
351 struct inst_tokens {
352 	struct match_info Opcode;
353 	struct match_info Sat;
354 	struct match_info Dst;
355 	struct match_info Srcs[3];
356 };
357 
358 /**
359  * Initialize a normal instruction based on inst_str.
360  *
361  * WARNING: This function might not be able to handle every kind of format that
362  * rc_program_print() can output.  If you are having problems with a
363  * particular string, you may need to add support for it to this functions.
364  *
365  * @param inst_str A string that represents the source register.  The format for
366  * this string is the same that is output by rc_program_print.
367  * @return 1 On success, 0 on failure
368  */
369 
parse_rc_normal_instruction(struct rc_instruction * inst,const char * inst_str)370 int parse_rc_normal_instruction(
371 	struct rc_instruction * inst,
372 	const char * inst_str)
373 {
374 	const char * regex_str = "[[:digit:]: ]*([[:upper:][:digit:]]+)(_SAT)*[ ]*([^,;]*)[, ]*([^,;]*)[, ]*([^,;]*)[, ]*([^;]*)";
375 	int i;
376 	regmatch_t matches[REGEX_INST_MATCHES];
377 	struct inst_tokens tokens;
378 
379 	/* Execute the regex */
380 	if (!regex_helper(regex_str, inst_str, matches, REGEX_INST_MATCHES)) {
381 		return 0;
382 	}
383 	memset(&tokens, 0, sizeof(tokens));
384 
385 	/* Create Tokens */
386 	tokens.Opcode.String = inst_str + matches[1].rm_so;
387 	tokens.Opcode.Length = match_length(matches, 1);
388 	if (matches[2].rm_so > -1) {
389 		tokens.Sat.String = inst_str + matches[2].rm_so;
390 		tokens.Sat.Length = match_length(matches, 2);
391 	}
392 
393 
394 	/* Fill out the rest of the instruction. */
395 	inst->Type = RC_INSTRUCTION_NORMAL;
396 
397 	for (i = 0; i < MAX_RC_OPCODE; i++) {
398 		const struct rc_opcode_info * info = rc_get_opcode_info(i);
399 		unsigned int first_src = 3;
400 		unsigned int j;
401 		if (strncmp(tokens.Opcode.String, info->Name, tokens.Opcode.Length)) {
402 			continue;
403 		}
404 		inst->U.I.Opcode = info->Opcode;
405 		if (info->HasDstReg) {
406 			char * dst_str;
407 			tokens.Dst.String = inst_str + matches[3].rm_so;
408 			tokens.Dst.Length = match_length(matches, 3);
409 			first_src++;
410 
411 			dst_str = malloc(sizeof(char) * (tokens.Dst.Length + 1));
412 			strncpy(dst_str, tokens.Dst.String, tokens.Dst.Length);
413 			dst_str[tokens.Dst.Length] = '\0';
414 			init_rc_normal_dst(inst, dst_str);
415 			free(dst_str);
416 		}
417 		for (j = 0; j < info->NumSrcRegs; j++) {
418 			char * src_str;
419 			tokens.Srcs[j].String =
420 				inst_str + matches[first_src + j].rm_so;
421 			tokens.Srcs[j].Length =
422 				match_length(matches, first_src + j);
423 
424 			src_str = malloc(sizeof(char) *
425 						(tokens.Srcs[j].Length + 1));
426 			strncpy(src_str, tokens.Srcs[j].String,
427 						tokens.Srcs[j].Length);
428 			src_str[tokens.Srcs[j].Length] = '\0';
429 			init_rc_normal_src(inst, j, src_str);
430 		}
431 		if (info->HasTexture) {
432 			/* XXX: Will this always be XYZW ? */
433 			inst->U.I.TexSwizzle = RC_SWIZZLE_XYZW;
434 		}
435 		break;
436 	}
437 	return 1;
438 }
439 
440 #define INDEX_TOKEN_LEN 4
441 #define FLOAT_TOKEN_LEN 50
parse_constant(unsigned * index,float * data,const char * const_str)442 int parse_constant(unsigned *index, float *data, const char *const_str)
443 {
444 	int matched = sscanf(const_str, "const[%d] {%f, %f, %f, %f}", index,
445 				&data[0], &data[1], &data[2], &data[3]);
446 	return matched == 5;
447 }
448 
init_rc_normal_instruction(struct rc_instruction * inst,const char * inst_str)449 int init_rc_normal_instruction(
450 	struct rc_instruction * inst,
451 	const char * inst_str)
452 {
453 	/* Initialize inst */
454 	memset(inst, 0, sizeof(struct rc_instruction));
455 
456 	return parse_rc_normal_instruction(inst, inst_str);
457 }
458 
add_instruction(struct radeon_compiler * c,const char * inst_string)459 void add_instruction(struct radeon_compiler *c, const char * inst_string)
460 {
461 	struct rc_instruction * new_inst =
462 		rc_insert_new_instruction(c, c->Program.Instructions.Prev);
463 
464 	parse_rc_normal_instruction(new_inst, inst_string);
465 
466 }
467 
add_constant(struct radeon_compiler * c,const char * const_str)468 int add_constant(struct radeon_compiler *c, const char *const_str)
469 {
470 	float data[4];
471 	unsigned index;
472 	struct rc_constant_list *constants;
473 	struct rc_constant constant;
474 
475 	if (!parse_constant(&index, data, const_str)) {
476 		return 0;
477 	}
478 
479 	constants = &c->Program.Constants;
480 	if (constants->_Reserved < index) {
481 		struct rc_constant * newlist;
482 
483 		constants->_Reserved = index + 100;
484 
485 		newlist = malloc(sizeof(struct rc_constant) * constants->_Reserved);
486 		if (constants->Constants) {
487 			memcpy(newlist, constants->Constants,
488 				sizeof(struct rc_constant) *
489 					constants->_Reserved);
490 			free(constants->Constants);
491 		}
492 
493 		constants->Constants = newlist;
494 	}
495 
496 	memset(&constant, 0, sizeof(constant));
497 	constant.Type = RC_CONSTANT_IMMEDIATE;
498 	constant.Size = 4;
499 	memcpy(constant.u.Immediate, data, sizeof(float) * 4);
500 	constants->Constants[index] = constant;
501 	constants->Count = MAX2(constants->Count, index + 1);
502 
503 	return 1;
504 }
505 
init_compiler(struct radeon_compiler * c,enum rc_program_type program_type,unsigned is_r500,unsigned is_r400)506 void init_compiler(
507 	struct radeon_compiler *c,
508 	enum rc_program_type program_type,
509 	unsigned is_r500,
510 	unsigned is_r400)
511 {
512 	struct rc_regalloc_state *rs = malloc(sizeof(struct rc_regalloc_state));
513 	rc_init_regalloc_state(rs);
514 	rc_init(c, rs);
515 
516 	c->is_r500 = is_r500;
517 	c->max_temp_regs = is_r500 ? 128 : (is_r400 ? 64 : 32);
518 	c->max_constants = is_r500 ? 256 : 32;
519 	c->max_alu_insts = (is_r500 || is_r400) ? 512 : 64;
520 	c->max_tex_insts = (is_r500 || is_r400) ? 512 : 32;
521 	if (program_type == RC_FRAGMENT_PROGRAM) {
522 		c->has_half_swizzles = 1;
523 		c->has_presub = 1;
524 		c->has_omod = 1;
525 		c->SwizzleCaps =
526 			is_r500 ? &r500_swizzle_caps : &r300_swizzle_caps;
527 	} else {
528 		c->SwizzleCaps = &r300_vertprog_swizzle_caps;
529 	}
530 }
531 
532 #define MAX_LINE_LENGTH 100
533 
load_program(struct radeon_compiler * c,struct rc_test_file * test,const char * filename)534 unsigned load_program(
535 	struct radeon_compiler *c,
536 	struct rc_test_file *test,
537 	const char *filename)
538 {
539 	char line[MAX_LINE_LENGTH];
540 	char path[PATH_MAX];
541 	FILE *file;
542 	unsigned *count;
543 	char **string_store;
544 	unsigned i = 0;
545 	int n;
546 
547 	memset(line, 0, sizeof(line));
548 	n = snprintf(path, PATH_MAX, TEST_PATH "/%s", filename);
549 	if (n < 0 || n >= PATH_MAX) {
550 		return 0;
551 	}
552 
553 	file = fopen(path, "r");
554 	if (!file) {
555 		return 0;
556 	}
557 	memset(test, 0, sizeof(struct rc_test_file));
558 
559 	count = &test->num_input_lines;
560 
561 	while (fgets(line, MAX_LINE_LENGTH, file)){
562 		char last_char = line[MAX_LINE_LENGTH - 1];
563 		if (last_char && last_char != '\n') {
564 			fprintf(stderr, "Error line cannot be longer than 100 "
565 				"characters:\n%s\n", line);
566 			fclose(file);
567 			return 0;
568 		}
569 
570 		// Comment
571 		if (line[0] == '#' || is_whitespace(line)) {
572 			continue;
573 		}
574 
575 		if (line[0] == '=') {
576 			count = &test->num_expected_lines;
577 			continue;
578 		}
579 
580 		(*count)++;
581 	}
582 
583 	test->input = malloc(sizeof(char *) * test->num_input_lines);
584 	test->expected = malloc(sizeof(char *) * test->num_expected_lines);
585 
586 	rewind(file);
587 	string_store = test->input;
588 
589 	while(fgets(line, MAX_LINE_LENGTH, file)) {
590 		// Comment
591 		char * dst;
592 		if (line[0] == '#' || is_whitespace(line)) {
593 			continue;
594 		}
595 
596 		if (line[0] == '=') {
597 			i = 0;
598 			string_store = test->expected;
599 			continue;
600 		}
601 
602 		dst = string_store[i++] = malloc((strlen(line) + 1) *
603 							sizeof (char));
604 		strcpy(dst, line);
605 	}
606 
607 	for (i = 0; i < test->num_input_lines; i++) {
608 		if (test->input[i][0] == 'c') {
609 			add_constant(c, test->input[i]);
610 			continue;
611 		}
612 		// XXX: Parse immediates from the file.
613 		add_instruction(c, test->input[i]);
614 	}
615 
616 	fclose(file);
617 	return 1;
618 }
619