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(®ex, "^[ \n]+$", REG_EXTENDED)) {
70 fprintf(stderr, "Failed to compile whitespace regex\n");
71 return 0;
72 }
73 return regexec(®ex, 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(®ex, regex_str, REG_EXTENDED);
93 if (err_code) {
94 regerror(err_code, ®ex, 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(®ex, 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, ®ex, 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