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
]);