2 * Copyright 2011 Tom Stellard <tstellar@gmail.com>
3 * Copyright 2013 Advanced Micro Devices, Inc.
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:
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.
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.
27 * Author: Tom Stellard <thomas.stellard@amd.com>
36 #include <sys/types.h>
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"
48 #include "rc_test_helpers.h"
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().
57 #define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0)
59 #define REGEX_ERR_BUF_SIZE 50
66 static int is_whitespace(const char *str)
69 if (regcomp(®ex, "^[ \n]+$", REG_EXTENDED)) {
70 fprintf(stderr, "Failed to compile whitespace regex\n");
73 return regexec(®ex, str, 0, NULL, 0) != REG_NOMATCH;
76 static int match_length(regmatch_t * matches, int index)
78 return matches[index].rm_eo - matches[index].rm_so;
81 static int regex_helper(
82 const char * regex_str,
83 const char * search_str,
87 char err_buf[REGEX_ERR_BUF_SIZE];
92 err_code = regcomp(®ex, regex_str, REG_EXTENDED);
94 regerror(err_code, ®ex, err_buf, REGEX_ERR_BUF_SIZE);
95 fprintf(stderr, "Failed to compile regex: %s\n", err_buf);
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 = %d end = %d\n", i,
103 matches[i].rm_so, matches[i].rm_eo);
106 regerror(err_code, ®ex, err_buf, REGEX_ERR_BUF_SIZE);
107 fprintf(stderr, "Failed to match regex: %s\n", err_buf);
113 #define REGEX_SRC_MATCHES 6
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;
124 * Initialize the source register at index src_index for the instruction based
127 * NOTE: Warning in init_rc_normal_instruction() applies to this function as
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
134 int init_rc_normal_src(
135 struct rc_instruction * inst,
136 unsigned int src_index,
137 const char * src_str)
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];
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");
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);
164 if (tokens.Negate.Length > 0) {
165 src_reg->Negate = RC_MASK_XYZW;
169 if (tokens.Abs.Length > 0) {
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;
186 src_reg->Index = strtol(tokens.Index.String, NULL, 10);
188 fprintf(stderr, "Could not convert src register index.\n");
193 if (tokens.Swizzle.Length == 0) {
194 src_reg->Swizzle = RC_SWIZZLE_XYZW;
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");
202 for (i = 0; i < 4 && str_index < tokens.Swizzle.Length;
204 if (tokens.Swizzle.String[str_index] == '-') {
205 src_reg->Negate |= (1 << i);
208 switch(tokens.Swizzle.String[str_index]) {
210 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_X);
213 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Y);
216 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Z);
219 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_W);
222 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ONE);
225 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ZERO);
228 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_HALF);
231 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_UNUSED);
234 fprintf(stderr, "Unknown src register swizzle: %c\n",
235 tokens.Swizzle.String[str_index]);
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);
246 #define REGEX_DST_MATCHES 4
249 struct match_info File;
250 struct match_info Index;
251 struct match_info WriteMask;
255 * Initialize the destination for the instruction based on dst_str.
257 * NOTE: Warning in init_rc_normal_instruction() applies to this function as
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
264 int init_rc_normal_dst(
265 struct rc_instruction * inst,
266 const char * dst_str)
268 const char * regex_str = "([[:lower:]]*)\\[*([[:digit:]]*)\\]*(\\.*[[:lower:]]*)";
269 regmatch_t matches[REGEX_DST_MATCHES];
270 struct dst_tokens tokens;
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");
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);
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;
296 fprintf(stderr, "Unknown dst register file type.\n");
302 inst->U.I.DstReg.Index = strtol(tokens.Index.String, NULL, 10);
305 fprintf(stderr, "Could not convert dst register index\n");
310 if (tokens.WriteMask.Length == 0) {
311 inst->U.I.DstReg.WriteMask = RC_MASK_XYZW;
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");
319 for (i = 1; i < tokens.WriteMask.Length; i++) {
320 switch(tokens.WriteMask.String[i]) {
322 inst->U.I.DstReg.WriteMask |= RC_MASK_X;
325 inst->U.I.DstReg.WriteMask |= RC_MASK_Y;
328 inst->U.I.DstReg.WriteMask |= RC_MASK_Z;
331 inst->U.I.DstReg.WriteMask |= RC_MASK_W;
334 fprintf(stderr, "Unknown swizzle in writemask: %c\n",
335 tokens.WriteMask.String[i]);
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);
347 #define REGEX_INST_MATCHES 7
348 #define REGEX_CONST_MATCHES 5
351 struct match_info Opcode;
352 struct match_info Sat;
353 struct match_info Dst;
354 struct match_info Srcs[3];
358 * Initialize a normal instruction based on inst_str.
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.
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
369 int parse_rc_normal_instruction(
370 struct rc_instruction * inst,
371 const char * inst_str)
373 const char * regex_str = "[[:digit:]: ]*([[:upper:][:digit:]]+)(_SAT)*[ ]*([^,;]*)[, ]*([^,;]*)[, ]*([^,;]*)[, ]*([^;]*)";
375 regmatch_t matches[REGEX_INST_MATCHES];
376 struct inst_tokens tokens;
378 /* Execute the regex */
379 if (!regex_helper(regex_str, inst_str, matches, REGEX_INST_MATCHES)) {
382 memset(&tokens, 0, sizeof(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);
393 /* Fill out the rest of the instruction. */
394 inst->Type = RC_INSTRUCTION_NORMAL;
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;
400 if (strncmp(tokens.Opcode.String, info->Name, tokens.Opcode.Length)) {
403 inst->U.I.Opcode = info->Opcode;
404 if (info->HasDstReg) {
406 tokens.Dst.String = inst_str + matches[3].rm_so;
407 tokens.Dst.Length = match_length(matches, 3);
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);
416 for (j = 0; j < info->NumSrcRegs; j++) {
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);
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);
430 if (info->HasTexture) {
431 /* XXX: Will this always be XYZW ? */
432 inst->U.I.TexSwizzle = RC_SWIZZLE_XYZW;
439 #define INDEX_TOKEN_LEN 4
440 #define FLOAT_TOKEN_LEN 50
441 int parse_constant(unsigned *index, float *data, const char *const_str)
443 int matched = sscanf(const_str, "const[%d] {%f, %f, %f, %f}", index,
444 &data[0], &data[1], &data[2], &data[3]);
448 int init_rc_normal_instruction(
449 struct rc_instruction * inst,
450 const char * inst_str)
452 /* Initialize inst */
453 memset(inst, 0, sizeof(struct rc_instruction));
455 return parse_rc_normal_instruction(inst, inst_str);
458 void add_instruction(struct radeon_compiler *c, const char * inst_string)
460 struct rc_instruction * new_inst =
461 rc_insert_new_instruction(c, c->Program.Instructions.Prev);
463 parse_rc_normal_instruction(new_inst, inst_string);
467 int add_constant(struct radeon_compiler *c, const char *const_str)
471 struct rc_constant_list *constants;
472 struct rc_constant constant;
474 if (!parse_constant(&index, data, const_str)) {
478 constants = &c->Program.Constants;
479 if (constants->_Reserved < index) {
480 struct rc_constant * newlist;
482 constants->_Reserved = index + 100;
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);
492 constants->Constants = newlist;
495 memset(&constant, 0, sizeof(constant));
496 constant.Type = RC_CONSTANT_IMMEDIATE;
498 memcpy(constant.u.Immediate, data, sizeof(float) * 4);
499 constants->Constants[index] = constant;
500 constants->Count = MAX2(constants->Count, index + 1);
506 struct radeon_compiler *c,
507 enum rc_program_type program_type,
511 struct rc_regalloc_state *rs = malloc(sizeof(struct rc_regalloc_state));
512 rc_init_regalloc_state(rs);
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;
525 is_r500 ? &r500_swizzle_caps : &r300_swizzle_caps;
527 c->SwizzleCaps = &r300_vertprog_swizzle_caps;
531 #define MAX_LINE_LENGTH 100
533 unsigned load_program(
534 struct radeon_compiler *c,
535 struct rc_test_file *test,
536 const char *filename)
538 char line[MAX_LINE_LENGTH];
546 memset(line, 0, sizeof(line));
547 n = snprintf(path, PATH_MAX, TEST_PATH "/%s", filename);
548 if (n < 0 || n >= PATH_MAX) {
552 file = fopen(path, "r");
556 memset(test, 0, sizeof(struct rc_test_file));
558 count = &test->num_input_lines;
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);
570 if (line[0] == '#' || is_whitespace(line)) {
574 if (line[0] == '=') {
575 count = &test->num_expected_lines;
582 test->input = malloc(sizeof(char *) * test->num_input_lines);
583 test->expected = malloc(sizeof(char *) * test->num_expected_lines);
586 string_store = test->input;
588 while(fgets(line, MAX_LINE_LENGTH, file)) {
591 if (line[0] == '#' || is_whitespace(line)) {
595 if (line[0] == '=') {
597 string_store = test->expected;
601 dst = string_store[i++] = malloc((strlen(line) + 1) *
606 for (i = 0; i < test->num_input_lines; i++) {
607 if (test->input[i][0] == 'c') {
608 add_constant(c, test->input[i]);
611 // XXX: Parse immediates from the file.
612 add_instruction(c, test->input[i]);