1 /* 2 * Copyright 2011 Tom Stellard <tstellar (at) 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 (at) 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 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 76 static int match_length(regmatch_t * matches, int index) 77 { 78 return matches[index].rm_eo - matches[index].rm_so; 79 } 80 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 = %d end = %d\n", i, 103 matches[i].rm_so, matches[i].rm_eo); 104 } 105 if (err_code) { 106 regerror(err_code, ®ex, 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 */ 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 */ 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 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 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 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 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 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 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 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