1/* 2 * Mesa 3-D graphics library 3 * Version: 7.5 4 * 5 * Copyright (C) 2009 VMware, Inc. All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * VMWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 26 27#include "main/glheader.h" 28#include "main/context.h" 29#include "main/macros.h" 30#include "program.h" 31#include "prog_instruction.h" 32#include "prog_optimize.h" 33#include "prog_print.h" 34 35 36#define MAX_LOOP_NESTING 50 37/* MAX_PROGRAM_TEMPS is a low number (256), and we want to be able to 38 * register allocate many temporary values into that small number of 39 * temps. So allow large temporary indices coming into the register 40 * allocator. 41 */ 42#define REG_ALLOCATE_MAX_PROGRAM_TEMPS ((1 << INST_INDEX_BITS) - 1) 43 44static GLboolean dbg = GL_FALSE; 45 46#define NO_MASK 0xf 47 48/** 49 * Returns the mask of channels (bitmask of WRITEMASK_X,Y,Z,W) which 50 * are read from the given src in this instruction, We also provide 51 * one optional masks which may mask other components in the dst 52 * register 53 */ 54static GLuint 55get_src_arg_mask(const struct prog_instruction *inst, 56 GLuint arg, GLuint dst_mask) 57{ 58 GLuint read_mask, channel_mask; 59 GLuint comp; 60 61 ASSERT(arg < _mesa_num_inst_src_regs(inst->Opcode)); 62 63 /* Form the dst register, find the written channels */ 64 if (inst->CondUpdate) { 65 channel_mask = WRITEMASK_XYZW; 66 } 67 else { 68 switch (inst->Opcode) { 69 case OPCODE_MOV: 70 case OPCODE_MIN: 71 case OPCODE_MAX: 72 case OPCODE_ABS: 73 case OPCODE_ADD: 74 case OPCODE_MAD: 75 case OPCODE_MUL: 76 case OPCODE_SUB: 77 case OPCODE_CMP: 78 case OPCODE_FLR: 79 case OPCODE_FRC: 80 case OPCODE_LRP: 81 case OPCODE_SEQ: 82 case OPCODE_SGE: 83 case OPCODE_SGT: 84 case OPCODE_SLE: 85 case OPCODE_SLT: 86 case OPCODE_SNE: 87 case OPCODE_SSG: 88 channel_mask = inst->DstReg.WriteMask & dst_mask; 89 break; 90 case OPCODE_RCP: 91 case OPCODE_SIN: 92 case OPCODE_COS: 93 case OPCODE_RSQ: 94 case OPCODE_POW: 95 case OPCODE_EX2: 96 case OPCODE_LOG: 97 channel_mask = WRITEMASK_X; 98 break; 99 case OPCODE_DP2: 100 channel_mask = WRITEMASK_XY; 101 break; 102 case OPCODE_DP3: 103 case OPCODE_XPD: 104 channel_mask = WRITEMASK_XYZ; 105 break; 106 default: 107 channel_mask = WRITEMASK_XYZW; 108 break; 109 } 110 } 111 112 /* Now, given the src swizzle and the written channels, find which 113 * components are actually read 114 */ 115 read_mask = 0x0; 116 for (comp = 0; comp < 4; ++comp) { 117 const GLuint coord = GET_SWZ(inst->SrcReg[arg].Swizzle, comp); 118 ASSERT(coord < 4); 119 if (channel_mask & (1 << comp) && coord <= SWIZZLE_W) 120 read_mask |= 1 << coord; 121 } 122 123 return read_mask; 124} 125 126 127/** 128 * For a MOV instruction, compute a write mask when src register also has 129 * a mask 130 */ 131static GLuint 132get_dst_mask_for_mov(const struct prog_instruction *mov, GLuint src_mask) 133{ 134 const GLuint mask = mov->DstReg.WriteMask; 135 GLuint comp; 136 GLuint updated_mask = 0x0; 137 138 ASSERT(mov->Opcode == OPCODE_MOV); 139 140 for (comp = 0; comp < 4; ++comp) { 141 GLuint src_comp; 142 if ((mask & (1 << comp)) == 0) 143 continue; 144 src_comp = GET_SWZ(mov->SrcReg[0].Swizzle, comp); 145 if ((src_mask & (1 << src_comp)) == 0) 146 continue; 147 updated_mask |= 1 << comp; 148 } 149 150 return updated_mask; 151} 152 153 154/** 155 * Ensure that the swizzle is regular. That is, all of the swizzle 156 * terms are SWIZZLE_X,Y,Z,W and not SWIZZLE_ZERO or SWIZZLE_ONE. 157 */ 158static GLboolean 159is_swizzle_regular(GLuint swz) 160{ 161 return GET_SWZ(swz,0) <= SWIZZLE_W && 162 GET_SWZ(swz,1) <= SWIZZLE_W && 163 GET_SWZ(swz,2) <= SWIZZLE_W && 164 GET_SWZ(swz,3) <= SWIZZLE_W; 165} 166 167 168/** 169 * In 'prog' remove instruction[i] if removeFlags[i] == TRUE. 170 * \return number of instructions removed 171 */ 172static GLuint 173remove_instructions(struct gl_program *prog, const GLboolean *removeFlags) 174{ 175 GLint i, removeEnd = 0, removeCount = 0; 176 GLuint totalRemoved = 0; 177 178 /* go backward */ 179 for (i = prog->NumInstructions - 1; i >= 0; i--) { 180 if (removeFlags[i]) { 181 totalRemoved++; 182 if (removeCount == 0) { 183 /* begin a run of instructions to remove */ 184 removeEnd = i; 185 removeCount = 1; 186 } 187 else { 188 /* extend the run of instructions to remove */ 189 removeCount++; 190 } 191 } 192 else { 193 /* don't remove this instruction, but check if the preceeding 194 * instructions are to be removed. 195 */ 196 if (removeCount > 0) { 197 GLint removeStart = removeEnd - removeCount + 1; 198 _mesa_delete_instructions(prog, removeStart, removeCount); 199 removeStart = removeCount = 0; /* reset removal info */ 200 } 201 } 202 } 203 /* Finish removing if the first instruction was to be removed. */ 204 if (removeCount > 0) { 205 GLint removeStart = removeEnd - removeCount + 1; 206 _mesa_delete_instructions(prog, removeStart, removeCount); 207 } 208 return totalRemoved; 209} 210 211 212/** 213 * Remap register indexes according to map. 214 * \param prog the program to search/replace 215 * \param file the type of register file to search/replace 216 * \param map maps old register indexes to new indexes 217 */ 218static void 219replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[]) 220{ 221 GLuint i; 222 223 for (i = 0; i < prog->NumInstructions; i++) { 224 struct prog_instruction *inst = prog->Instructions + i; 225 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); 226 GLuint j; 227 for (j = 0; j < numSrc; j++) { 228 if (inst->SrcReg[j].File == file) { 229 GLuint index = inst->SrcReg[j].Index; 230 ASSERT(map[index] >= 0); 231 inst->SrcReg[j].Index = map[index]; 232 } 233 } 234 if (inst->DstReg.File == file) { 235 const GLuint index = inst->DstReg.Index; 236 ASSERT(map[index] >= 0); 237 inst->DstReg.Index = map[index]; 238 } 239 } 240} 241 242 243/** 244 * Remove dead instructions from the given program. 245 * This is very primitive for now. Basically look for temp registers 246 * that are written to but never read. Remove any instructions that 247 * write to such registers. Be careful with condition code setters. 248 */ 249static GLboolean 250_mesa_remove_dead_code_global(struct gl_program *prog) 251{ 252 GLboolean tempRead[REG_ALLOCATE_MAX_PROGRAM_TEMPS][4]; 253 GLboolean *removeInst; /* per-instruction removal flag */ 254 GLuint i, rem = 0, comp; 255 256 memset(tempRead, 0, sizeof(tempRead)); 257 258 if (dbg) { 259 printf("Optimize: Begin dead code removal\n"); 260 /*_mesa_print_program(prog);*/ 261 } 262 263 removeInst = (GLboolean *) 264 calloc(1, prog->NumInstructions * sizeof(GLboolean)); 265 266 /* Determine which temps are read and written */ 267 for (i = 0; i < prog->NumInstructions; i++) { 268 const struct prog_instruction *inst = prog->Instructions + i; 269 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); 270 GLuint j; 271 272 /* check src regs */ 273 for (j = 0; j < numSrc; j++) { 274 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) { 275 const GLuint index = inst->SrcReg[j].Index; 276 GLuint read_mask; 277 ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); 278 read_mask = get_src_arg_mask(inst, j, NO_MASK); 279 280 if (inst->SrcReg[j].RelAddr) { 281 if (dbg) 282 printf("abort remove dead code (indirect temp)\n"); 283 goto done; 284 } 285 286 for (comp = 0; comp < 4; comp++) { 287 const GLuint swz = GET_SWZ(inst->SrcReg[j].Swizzle, comp); 288 ASSERT(swz < 4); 289 if ((read_mask & (1 << swz)) == 0) 290 continue; 291 if (swz <= SWIZZLE_W) 292 tempRead[index][swz] = GL_TRUE; 293 } 294 } 295 } 296 297 /* check dst reg */ 298 if (inst->DstReg.File == PROGRAM_TEMPORARY) { 299 const GLuint index = inst->DstReg.Index; 300 ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); 301 302 if (inst->DstReg.RelAddr) { 303 if (dbg) 304 printf("abort remove dead code (indirect temp)\n"); 305 goto done; 306 } 307 308 if (inst->CondUpdate) { 309 /* If we're writing to this register and setting condition 310 * codes we cannot remove the instruction. Prevent removal 311 * by setting the 'read' flag. 312 */ 313 tempRead[index][0] = GL_TRUE; 314 tempRead[index][1] = GL_TRUE; 315 tempRead[index][2] = GL_TRUE; 316 tempRead[index][3] = GL_TRUE; 317 } 318 } 319 } 320 321 /* find instructions that write to dead registers, flag for removal */ 322 for (i = 0; i < prog->NumInstructions; i++) { 323 struct prog_instruction *inst = prog->Instructions + i; 324 const GLuint numDst = _mesa_num_inst_dst_regs(inst->Opcode); 325 326 if (numDst != 0 && inst->DstReg.File == PROGRAM_TEMPORARY) { 327 GLint chan, index = inst->DstReg.Index; 328 329 for (chan = 0; chan < 4; chan++) { 330 if (!tempRead[index][chan] && 331 inst->DstReg.WriteMask & (1 << chan)) { 332 if (dbg) { 333 printf("Remove writemask on %u.%c\n", i, 334 chan == 3 ? 'w' : 'x' + chan); 335 } 336 inst->DstReg.WriteMask &= ~(1 << chan); 337 rem++; 338 } 339 } 340 341 if (inst->DstReg.WriteMask == 0) { 342 /* If we cleared all writes, the instruction can be removed. */ 343 if (dbg) 344 printf("Remove instruction %u: \n", i); 345 removeInst[i] = GL_TRUE; 346 } 347 } 348 } 349 350 /* now remove the instructions which aren't needed */ 351 rem = remove_instructions(prog, removeInst); 352 353 if (dbg) { 354 printf("Optimize: End dead code removal.\n"); 355 printf(" %u channel writes removed\n", rem); 356 printf(" %u instructions removed\n", rem); 357 /*_mesa_print_program(prog);*/ 358 } 359 360done: 361 free(removeInst); 362 return rem != 0; 363} 364 365 366enum inst_use 367{ 368 READ, 369 WRITE, 370 FLOW, 371 END 372}; 373 374 375/** 376 * Scan forward in program from 'start' for the next occurances of TEMP[index]. 377 * We look if an instruction reads the component given by the masks and if they 378 * are overwritten. 379 * Return READ, WRITE, FLOW or END to indicate the next usage or an indicator 380 * that we can't look further. 381 */ 382static enum inst_use 383find_next_use(const struct gl_program *prog, 384 GLuint start, 385 GLuint index, 386 GLuint mask) 387{ 388 GLuint i; 389 390 for (i = start; i < prog->NumInstructions; i++) { 391 const struct prog_instruction *inst = prog->Instructions + i; 392 switch (inst->Opcode) { 393 case OPCODE_BGNLOOP: 394 case OPCODE_BGNSUB: 395 case OPCODE_BRA: 396 case OPCODE_CAL: 397 case OPCODE_CONT: 398 case OPCODE_IF: 399 case OPCODE_ELSE: 400 case OPCODE_ENDIF: 401 case OPCODE_ENDLOOP: 402 case OPCODE_ENDSUB: 403 case OPCODE_RET: 404 return FLOW; 405 case OPCODE_END: 406 return END; 407 default: 408 { 409 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); 410 GLuint j; 411 for (j = 0; j < numSrc; j++) { 412 if (inst->SrcReg[j].RelAddr || 413 (inst->SrcReg[j].File == PROGRAM_TEMPORARY && 414 inst->SrcReg[j].Index == index && 415 (get_src_arg_mask(inst,j,NO_MASK) & mask))) 416 return READ; 417 } 418 if (_mesa_num_inst_dst_regs(inst->Opcode) == 1 && 419 inst->DstReg.File == PROGRAM_TEMPORARY && 420 inst->DstReg.Index == index) { 421 mask &= ~inst->DstReg.WriteMask; 422 if (mask == 0) 423 return WRITE; 424 } 425 } 426 } 427 } 428 return END; 429} 430 431 432/** 433 * Is the given instruction opcode a flow-control opcode? 434 * XXX maybe move this into prog_instruction.[ch] 435 */ 436static GLboolean 437_mesa_is_flow_control_opcode(enum prog_opcode opcode) 438{ 439 switch (opcode) { 440 case OPCODE_BGNLOOP: 441 case OPCODE_BGNSUB: 442 case OPCODE_BRA: 443 case OPCODE_CAL: 444 case OPCODE_CONT: 445 case OPCODE_IF: 446 case OPCODE_ELSE: 447 case OPCODE_END: 448 case OPCODE_ENDIF: 449 case OPCODE_ENDLOOP: 450 case OPCODE_ENDSUB: 451 case OPCODE_RET: 452 return GL_TRUE; 453 default: 454 return GL_FALSE; 455 } 456} 457 458 459/** 460 * Test if the given instruction is a simple MOV (no conditional updating, 461 * not relative addressing, no negation/abs, etc). 462 */ 463static GLboolean 464can_downward_mov_be_modifed(const struct prog_instruction *mov) 465{ 466 return 467 mov->Opcode == OPCODE_MOV && 468 mov->CondUpdate == GL_FALSE && 469 mov->SrcReg[0].RelAddr == 0 && 470 mov->SrcReg[0].Negate == 0 && 471 mov->SrcReg[0].Abs == 0 && 472 mov->SrcReg[0].HasIndex2 == 0 && 473 mov->SrcReg[0].RelAddr2 == 0 && 474 mov->DstReg.RelAddr == 0 && 475 mov->DstReg.CondMask == COND_TR; 476} 477 478 479static GLboolean 480can_upward_mov_be_modifed(const struct prog_instruction *mov) 481{ 482 return 483 can_downward_mov_be_modifed(mov) && 484 mov->DstReg.File == PROGRAM_TEMPORARY && 485 mov->SaturateMode == SATURATE_OFF; 486} 487 488 489/** 490 * Try to remove use of extraneous MOV instructions, to free them up for dead 491 * code removal. 492 */ 493static void 494_mesa_remove_extra_move_use(struct gl_program *prog) 495{ 496 GLuint i, j; 497 498 if (dbg) { 499 printf("Optimize: Begin remove extra move use\n"); 500 _mesa_print_program(prog); 501 } 502 503 /* 504 * Look for sequences such as this: 505 * MOV tmpX, arg0; 506 * ... 507 * FOO tmpY, tmpX, arg1; 508 * and convert into: 509 * MOV tmpX, arg0; 510 * ... 511 * FOO tmpY, arg0, arg1; 512 */ 513 514 for (i = 0; i + 1 < prog->NumInstructions; i++) { 515 const struct prog_instruction *mov = prog->Instructions + i; 516 GLuint dst_mask, src_mask; 517 if (can_upward_mov_be_modifed(mov) == GL_FALSE) 518 continue; 519 520 /* Scanning the code, we maintain the components which are still active in 521 * these two masks 522 */ 523 dst_mask = mov->DstReg.WriteMask; 524 src_mask = get_src_arg_mask(mov, 0, NO_MASK); 525 526 /* Walk through remaining instructions until the or src reg gets 527 * rewritten or we get into some flow-control, eliminating the use of 528 * this MOV. 529 */ 530 for (j = i + 1; j < prog->NumInstructions; j++) { 531 struct prog_instruction *inst2 = prog->Instructions + j; 532 GLuint arg; 533 534 if (_mesa_is_flow_control_opcode(inst2->Opcode)) 535 break; 536 537 /* First rewrite this instruction's args if appropriate. */ 538 for (arg = 0; arg < _mesa_num_inst_src_regs(inst2->Opcode); arg++) { 539 GLuint comp, read_mask; 540 541 if (inst2->SrcReg[arg].File != mov->DstReg.File || 542 inst2->SrcReg[arg].Index != mov->DstReg.Index || 543 inst2->SrcReg[arg].RelAddr || 544 inst2->SrcReg[arg].Abs) 545 continue; 546 read_mask = get_src_arg_mask(inst2, arg, NO_MASK); 547 548 /* Adjust the swizzles of inst2 to point at MOV's source if ALL the 549 * components read still come from the mov instructions 550 */ 551 if (is_swizzle_regular(inst2->SrcReg[arg].Swizzle) && 552 (read_mask & dst_mask) == read_mask) { 553 for (comp = 0; comp < 4; comp++) { 554 const GLuint inst2_swz = 555 GET_SWZ(inst2->SrcReg[arg].Swizzle, comp); 556 const GLuint s = GET_SWZ(mov->SrcReg[0].Swizzle, inst2_swz); 557 inst2->SrcReg[arg].Swizzle &= ~(7 << (3 * comp)); 558 inst2->SrcReg[arg].Swizzle |= s << (3 * comp); 559 inst2->SrcReg[arg].Negate ^= (((mov->SrcReg[0].Negate >> 560 inst2_swz) & 0x1) << comp); 561 } 562 inst2->SrcReg[arg].File = mov->SrcReg[0].File; 563 inst2->SrcReg[arg].Index = mov->SrcReg[0].Index; 564 } 565 } 566 567 /* The source of MOV is written. This potentially deactivates some 568 * components from the src and dst of the MOV instruction 569 */ 570 if (inst2->DstReg.File == mov->DstReg.File && 571 (inst2->DstReg.RelAddr || 572 inst2->DstReg.Index == mov->DstReg.Index)) { 573 dst_mask &= ~inst2->DstReg.WriteMask; 574 src_mask = get_src_arg_mask(mov, 0, dst_mask); 575 } 576 577 /* Idem when the destination of mov is written */ 578 if (inst2->DstReg.File == mov->SrcReg[0].File && 579 (inst2->DstReg.RelAddr || 580 inst2->DstReg.Index == mov->SrcReg[0].Index)) { 581 src_mask &= ~inst2->DstReg.WriteMask; 582 dst_mask &= get_dst_mask_for_mov(mov, src_mask); 583 } 584 if (dst_mask == 0) 585 break; 586 } 587 } 588 589 if (dbg) { 590 printf("Optimize: End remove extra move use.\n"); 591 /*_mesa_print_program(prog);*/ 592 } 593} 594 595 596/** 597 * Complements dead_code_global. Try to remove code in block of code by 598 * carefully monitoring the swizzles. Both functions should be merged into one 599 * with a proper control flow graph 600 */ 601static GLboolean 602_mesa_remove_dead_code_local(struct gl_program *prog) 603{ 604 GLboolean *removeInst; 605 GLuint i, arg, rem = 0; 606 607 removeInst = (GLboolean *) 608 calloc(1, prog->NumInstructions * sizeof(GLboolean)); 609 610 for (i = 0; i < prog->NumInstructions; i++) { 611 const struct prog_instruction *inst = prog->Instructions + i; 612 const GLuint index = inst->DstReg.Index; 613 const GLuint mask = inst->DstReg.WriteMask; 614 enum inst_use use; 615 616 /* We must deactivate the pass as soon as some indirection is used */ 617 if (inst->DstReg.RelAddr) 618 goto done; 619 for (arg = 0; arg < _mesa_num_inst_src_regs(inst->Opcode); arg++) 620 if (inst->SrcReg[arg].RelAddr) 621 goto done; 622 623 if (_mesa_is_flow_control_opcode(inst->Opcode) || 624 _mesa_num_inst_dst_regs(inst->Opcode) == 0 || 625 inst->DstReg.File != PROGRAM_TEMPORARY || 626 inst->DstReg.RelAddr) 627 continue; 628 629 use = find_next_use(prog, i+1, index, mask); 630 if (use == WRITE || use == END) 631 removeInst[i] = GL_TRUE; 632 } 633 634 rem = remove_instructions(prog, removeInst); 635 636done: 637 free(removeInst); 638 return rem != 0; 639} 640 641 642/** 643 * Try to inject the destination of mov as the destination of inst and recompute 644 * the swizzles operators for the sources of inst if required. Return GL_TRUE 645 * of the substitution was possible, GL_FALSE otherwise 646 */ 647static GLboolean 648_mesa_merge_mov_into_inst(struct prog_instruction *inst, 649 const struct prog_instruction *mov) 650{ 651 /* Indirection table which associates destination and source components for 652 * the mov instruction 653 */ 654 const GLuint mask = get_src_arg_mask(mov, 0, NO_MASK); 655 656 /* Some components are not written by inst. We cannot remove the mov */ 657 if (mask != (inst->DstReg.WriteMask & mask)) 658 return GL_FALSE; 659 660 inst->SaturateMode |= mov->SaturateMode; 661 662 /* Depending on the instruction, we may need to recompute the swizzles. 663 * Also, some other instructions (like TEX) are not linear. We will only 664 * consider completely active sources and destinations 665 */ 666 switch (inst->Opcode) { 667 668 /* Carstesian instructions: we compute the swizzle */ 669 case OPCODE_MOV: 670 case OPCODE_MIN: 671 case OPCODE_MAX: 672 case OPCODE_ABS: 673 case OPCODE_ADD: 674 case OPCODE_MAD: 675 case OPCODE_MUL: 676 case OPCODE_SUB: 677 { 678 GLuint dst_to_src_comp[4] = {0,0,0,0}; 679 GLuint dst_comp, arg; 680 for (dst_comp = 0; dst_comp < 4; ++dst_comp) { 681 if (mov->DstReg.WriteMask & (1 << dst_comp)) { 682 const GLuint src_comp = GET_SWZ(mov->SrcReg[0].Swizzle, dst_comp); 683 ASSERT(src_comp < 4); 684 dst_to_src_comp[dst_comp] = src_comp; 685 } 686 } 687 688 /* Patch each source of the instruction */ 689 for (arg = 0; arg < _mesa_num_inst_src_regs(inst->Opcode); arg++) { 690 const GLuint arg_swz = inst->SrcReg[arg].Swizzle; 691 inst->SrcReg[arg].Swizzle = 0; 692 693 /* Reset each active component of the swizzle */ 694 for (dst_comp = 0; dst_comp < 4; ++dst_comp) { 695 GLuint src_comp, arg_comp; 696 if ((mov->DstReg.WriteMask & (1 << dst_comp)) == 0) 697 continue; 698 src_comp = dst_to_src_comp[dst_comp]; 699 ASSERT(src_comp < 4); 700 arg_comp = GET_SWZ(arg_swz, src_comp); 701 ASSERT(arg_comp < 4); 702 inst->SrcReg[arg].Swizzle |= arg_comp << (3*dst_comp); 703 } 704 } 705 inst->DstReg = mov->DstReg; 706 return GL_TRUE; 707 } 708 709 /* Dot products and scalar instructions: we only change the destination */ 710 case OPCODE_RCP: 711 case OPCODE_SIN: 712 case OPCODE_COS: 713 case OPCODE_RSQ: 714 case OPCODE_POW: 715 case OPCODE_EX2: 716 case OPCODE_LOG: 717 case OPCODE_DP2: 718 case OPCODE_DP3: 719 case OPCODE_DP4: 720 inst->DstReg = mov->DstReg; 721 return GL_TRUE; 722 723 /* All other instructions require fully active components with no swizzle */ 724 default: 725 if (mov->SrcReg[0].Swizzle != SWIZZLE_XYZW || 726 inst->DstReg.WriteMask != WRITEMASK_XYZW) 727 return GL_FALSE; 728 inst->DstReg = mov->DstReg; 729 return GL_TRUE; 730 } 731} 732 733 734/** 735 * Try to remove extraneous MOV instructions from the given program. 736 */ 737static GLboolean 738_mesa_remove_extra_moves(struct gl_program *prog) 739{ 740 GLboolean *removeInst; /* per-instruction removal flag */ 741 GLuint i, rem = 0, nesting = 0; 742 743 if (dbg) { 744 printf("Optimize: Begin remove extra moves\n"); 745 _mesa_print_program(prog); 746 } 747 748 removeInst = (GLboolean *) 749 calloc(1, prog->NumInstructions * sizeof(GLboolean)); 750 751 /* 752 * Look for sequences such as this: 753 * FOO tmpX, arg0, arg1; 754 * MOV tmpY, tmpX; 755 * and convert into: 756 * FOO tmpY, arg0, arg1; 757 */ 758 759 for (i = 0; i < prog->NumInstructions; i++) { 760 const struct prog_instruction *mov = prog->Instructions + i; 761 762 switch (mov->Opcode) { 763 case OPCODE_BGNLOOP: 764 case OPCODE_BGNSUB: 765 case OPCODE_IF: 766 nesting++; 767 break; 768 case OPCODE_ENDLOOP: 769 case OPCODE_ENDSUB: 770 case OPCODE_ENDIF: 771 nesting--; 772 break; 773 case OPCODE_MOV: 774 if (i > 0 && 775 can_downward_mov_be_modifed(mov) && 776 mov->SrcReg[0].File == PROGRAM_TEMPORARY && 777 nesting == 0) 778 { 779 780 /* see if this MOV can be removed */ 781 const GLuint id = mov->SrcReg[0].Index; 782 struct prog_instruction *prevInst; 783 GLuint prevI; 784 785 /* get pointer to previous instruction */ 786 prevI = i - 1; 787 while (prevI > 0 && removeInst[prevI]) 788 prevI--; 789 prevInst = prog->Instructions + prevI; 790 791 if (prevInst->DstReg.File == PROGRAM_TEMPORARY && 792 prevInst->DstReg.Index == id && 793 prevInst->DstReg.RelAddr == 0 && 794 prevInst->DstReg.CondSrc == 0 && 795 prevInst->DstReg.CondMask == COND_TR) { 796 797 const GLuint dst_mask = prevInst->DstReg.WriteMask; 798 enum inst_use next_use = find_next_use(prog, i+1, id, dst_mask); 799 800 if (next_use == WRITE || next_use == END) { 801 /* OK, we can safely remove this MOV instruction. 802 * Transform: 803 * prevI: FOO tempIndex, x, y; 804 * i: MOV z, tempIndex; 805 * Into: 806 * prevI: FOO z, x, y; 807 */ 808 if (_mesa_merge_mov_into_inst(prevInst, mov)) { 809 removeInst[i] = GL_TRUE; 810 if (dbg) { 811 printf("Remove MOV at %u\n", i); 812 printf("new prev inst %u: ", prevI); 813 _mesa_print_instruction(prevInst); 814 } 815 } 816 } 817 } 818 } 819 break; 820 default: 821 ; /* nothing */ 822 } 823 } 824 825 /* now remove the instructions which aren't needed */ 826 rem = remove_instructions(prog, removeInst); 827 828 free(removeInst); 829 830 if (dbg) { 831 printf("Optimize: End remove extra moves. %u instructions removed\n", rem); 832 /*_mesa_print_program(prog);*/ 833 } 834 835 return rem != 0; 836} 837 838 839/** A live register interval */ 840struct interval 841{ 842 GLuint Reg; /** The temporary register index */ 843 GLuint Start, End; /** Start/end instruction numbers */ 844}; 845 846 847/** A list of register intervals */ 848struct interval_list 849{ 850 GLuint Num; 851 struct interval Intervals[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; 852}; 853 854 855static void 856append_interval(struct interval_list *list, const struct interval *inv) 857{ 858 list->Intervals[list->Num++] = *inv; 859} 860 861 862/** Insert interval inv into list, sorted by interval end */ 863static void 864insert_interval_by_end(struct interval_list *list, const struct interval *inv) 865{ 866 /* XXX we could do a binary search insertion here since list is sorted */ 867 GLint i = list->Num - 1; 868 while (i >= 0 && list->Intervals[i].End > inv->End) { 869 list->Intervals[i + 1] = list->Intervals[i]; 870 i--; 871 } 872 list->Intervals[i + 1] = *inv; 873 list->Num++; 874 875#ifdef DEBUG 876 { 877 GLuint i; 878 for (i = 0; i + 1 < list->Num; i++) { 879 ASSERT(list->Intervals[i].End <= list->Intervals[i + 1].End); 880 } 881 } 882#endif 883} 884 885 886/** Remove the given interval from the interval list */ 887static void 888remove_interval(struct interval_list *list, const struct interval *inv) 889{ 890 /* XXX we could binary search since list is sorted */ 891 GLuint k; 892 for (k = 0; k < list->Num; k++) { 893 if (list->Intervals[k].Reg == inv->Reg) { 894 /* found, remove it */ 895 ASSERT(list->Intervals[k].Start == inv->Start); 896 ASSERT(list->Intervals[k].End == inv->End); 897 while (k < list->Num - 1) { 898 list->Intervals[k] = list->Intervals[k + 1]; 899 k++; 900 } 901 list->Num--; 902 return; 903 } 904 } 905} 906 907 908/** called by qsort() */ 909static int 910compare_start(const void *a, const void *b) 911{ 912 const struct interval *ia = (const struct interval *) a; 913 const struct interval *ib = (const struct interval *) b; 914 if (ia->Start < ib->Start) 915 return -1; 916 else if (ia->Start > ib->Start) 917 return +1; 918 else 919 return 0; 920} 921 922 923/** sort the interval list according to interval starts */ 924static void 925sort_interval_list_by_start(struct interval_list *list) 926{ 927 qsort(list->Intervals, list->Num, sizeof(struct interval), compare_start); 928#ifdef DEBUG 929 { 930 GLuint i; 931 for (i = 0; i + 1 < list->Num; i++) { 932 ASSERT(list->Intervals[i].Start <= list->Intervals[i + 1].Start); 933 } 934 } 935#endif 936} 937 938struct loop_info 939{ 940 GLuint Start, End; /**< Start, end instructions of loop */ 941}; 942 943/** 944 * Update the intermediate interval info for register 'index' and 945 * instruction 'ic'. 946 */ 947static void 948update_interval(GLint intBegin[], GLint intEnd[], 949 struct loop_info *loopStack, GLuint loopStackDepth, 950 GLuint index, GLuint ic) 951{ 952 int i; 953 GLuint begin = ic; 954 GLuint end = ic; 955 956 /* If the register is used in a loop, extend its lifetime through the end 957 * of the outermost loop that doesn't contain its definition. 958 */ 959 for (i = 0; i < loopStackDepth; i++) { 960 if (intBegin[index] < loopStack[i].Start) { 961 end = loopStack[i].End; 962 break; 963 } 964 } 965 966 /* Variables that are live at the end of a loop will also be live at the 967 * beginning, so an instruction inside of a loop should have its live 968 * interval begin at the start of the outermost loop. 969 */ 970 if (loopStackDepth > 0 && ic > loopStack[0].Start && ic < loopStack[0].End) { 971 begin = loopStack[0].Start; 972 } 973 974 ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); 975 if (intBegin[index] == -1) { 976 ASSERT(intEnd[index] == -1); 977 intBegin[index] = begin; 978 intEnd[index] = end; 979 } 980 else { 981 intEnd[index] = end; 982 } 983} 984 985 986/** 987 * Find first/last instruction that references each temporary register. 988 */ 989GLboolean 990_mesa_find_temp_intervals(const struct prog_instruction *instructions, 991 GLuint numInstructions, 992 GLint intBegin[REG_ALLOCATE_MAX_PROGRAM_TEMPS], 993 GLint intEnd[REG_ALLOCATE_MAX_PROGRAM_TEMPS]) 994{ 995 struct loop_info loopStack[MAX_LOOP_NESTING]; 996 GLuint loopStackDepth = 0; 997 GLuint i; 998 999 for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++){ 1000 intBegin[i] = intEnd[i] = -1; 1001 } 1002 1003 /* Scan instructions looking for temporary registers */ 1004 for (i = 0; i < numInstructions; i++) { 1005 const struct prog_instruction *inst = instructions + i; 1006 if (inst->Opcode == OPCODE_BGNLOOP) { 1007 loopStack[loopStackDepth].Start = i; 1008 loopStack[loopStackDepth].End = inst->BranchTarget; 1009 loopStackDepth++; 1010 } 1011 else if (inst->Opcode == OPCODE_ENDLOOP) { 1012 loopStackDepth--; 1013 } 1014 else if (inst->Opcode == OPCODE_CAL) { 1015 return GL_FALSE; 1016 } 1017 else { 1018 const GLuint numSrc = 3;/*_mesa_num_inst_src_regs(inst->Opcode);*/ 1019 GLuint j; 1020 for (j = 0; j < numSrc; j++) { 1021 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) { 1022 const GLuint index = inst->SrcReg[j].Index; 1023 if (inst->SrcReg[j].RelAddr) 1024 return GL_FALSE; 1025 update_interval(intBegin, intEnd, loopStack, loopStackDepth, 1026 index, i); 1027 } 1028 } 1029 if (inst->DstReg.File == PROGRAM_TEMPORARY) { 1030 const GLuint index = inst->DstReg.Index; 1031 if (inst->DstReg.RelAddr) 1032 return GL_FALSE; 1033 update_interval(intBegin, intEnd, loopStack, loopStackDepth, 1034 index, i); 1035 } 1036 } 1037 } 1038 1039 return GL_TRUE; 1040} 1041 1042 1043/** 1044 * Find the live intervals for each temporary register in the program. 1045 * For register R, the interval [A,B] indicates that R is referenced 1046 * from instruction A through instruction B. 1047 * Special consideration is needed for loops and subroutines. 1048 * \return GL_TRUE if success, GL_FALSE if we cannot proceed for some reason 1049 */ 1050static GLboolean 1051find_live_intervals(struct gl_program *prog, 1052 struct interval_list *liveIntervals) 1053{ 1054 GLint intBegin[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; 1055 GLint intEnd[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; 1056 GLuint i; 1057 1058 /* 1059 * Note: we'll return GL_FALSE below if we find relative indexing 1060 * into the TEMP register file. We can't handle that yet. 1061 * We also give up on subroutines for now. 1062 */ 1063 1064 if (dbg) { 1065 printf("Optimize: Begin find intervals\n"); 1066 } 1067 1068 /* build intermediate arrays */ 1069 if (!_mesa_find_temp_intervals(prog->Instructions, prog->NumInstructions, 1070 intBegin, intEnd)) 1071 return GL_FALSE; 1072 1073 /* Build live intervals list from intermediate arrays */ 1074 liveIntervals->Num = 0; 1075 for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++) { 1076 if (intBegin[i] >= 0) { 1077 struct interval inv; 1078 inv.Reg = i; 1079 inv.Start = intBegin[i]; 1080 inv.End = intEnd[i]; 1081 append_interval(liveIntervals, &inv); 1082 } 1083 } 1084 1085 /* Sort the list according to interval starts */ 1086 sort_interval_list_by_start(liveIntervals); 1087 1088 if (dbg) { 1089 /* print interval info */ 1090 for (i = 0; i < liveIntervals->Num; i++) { 1091 const struct interval *inv = liveIntervals->Intervals + i; 1092 printf("Reg[%d] live [%d, %d]:", 1093 inv->Reg, inv->Start, inv->End); 1094 if (1) { 1095 GLuint j; 1096 for (j = 0; j < inv->Start; j++) 1097 printf(" "); 1098 for (j = inv->Start; j <= inv->End; j++) 1099 printf("x"); 1100 } 1101 printf("\n"); 1102 } 1103 } 1104 1105 return GL_TRUE; 1106} 1107 1108 1109/** Scan the array of used register flags to find free entry */ 1110static GLint 1111alloc_register(GLboolean usedRegs[REG_ALLOCATE_MAX_PROGRAM_TEMPS]) 1112{ 1113 GLuint k; 1114 for (k = 0; k < REG_ALLOCATE_MAX_PROGRAM_TEMPS; k++) { 1115 if (!usedRegs[k]) { 1116 usedRegs[k] = GL_TRUE; 1117 return k; 1118 } 1119 } 1120 return -1; 1121} 1122 1123 1124/** 1125 * This function implements "Linear Scan Register Allocation" to reduce 1126 * the number of temporary registers used by the program. 1127 * 1128 * We compute the "live interval" for all temporary registers then 1129 * examine the overlap of the intervals to allocate new registers. 1130 * Basically, if two intervals do not overlap, they can use the same register. 1131 */ 1132static void 1133_mesa_reallocate_registers(struct gl_program *prog) 1134{ 1135 struct interval_list liveIntervals; 1136 GLint registerMap[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; 1137 GLboolean usedRegs[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; 1138 GLuint i; 1139 GLint maxTemp = -1; 1140 1141 if (dbg) { 1142 printf("Optimize: Begin live-interval register reallocation\n"); 1143 _mesa_print_program(prog); 1144 } 1145 1146 for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++){ 1147 registerMap[i] = -1; 1148 usedRegs[i] = GL_FALSE; 1149 } 1150 1151 if (!find_live_intervals(prog, &liveIntervals)) { 1152 if (dbg) 1153 printf("Aborting register reallocation\n"); 1154 return; 1155 } 1156 1157 { 1158 struct interval_list activeIntervals; 1159 activeIntervals.Num = 0; 1160 1161 /* loop over live intervals, allocating a new register for each */ 1162 for (i = 0; i < liveIntervals.Num; i++) { 1163 const struct interval *live = liveIntervals.Intervals + i; 1164 1165 if (dbg) 1166 printf("Consider register %u\n", live->Reg); 1167 1168 /* Expire old intervals. Intervals which have ended with respect 1169 * to the live interval can have their remapped registers freed. 1170 */ 1171 { 1172 GLint j; 1173 for (j = 0; j < (GLint) activeIntervals.Num; j++) { 1174 const struct interval *inv = activeIntervals.Intervals + j; 1175 if (inv->End >= live->Start) { 1176 /* Stop now. Since the activeInterval list is sorted 1177 * we know we don't have to go further. 1178 */ 1179 break; 1180 } 1181 else { 1182 /* Interval 'inv' has expired */ 1183 const GLint regNew = registerMap[inv->Reg]; 1184 ASSERT(regNew >= 0); 1185 1186 if (dbg) 1187 printf(" expire interval for reg %u\n", inv->Reg); 1188 1189 /* remove interval j from active list */ 1190 remove_interval(&activeIntervals, inv); 1191 j--; /* counter-act j++ in for-loop above */ 1192 1193 /* return register regNew to the free pool */ 1194 if (dbg) 1195 printf(" free reg %d\n", regNew); 1196 ASSERT(usedRegs[regNew] == GL_TRUE); 1197 usedRegs[regNew] = GL_FALSE; 1198 } 1199 } 1200 } 1201 1202 /* find a free register for this live interval */ 1203 { 1204 const GLint k = alloc_register(usedRegs); 1205 if (k < 0) { 1206 /* out of registers, give up */ 1207 return; 1208 } 1209 registerMap[live->Reg] = k; 1210 maxTemp = MAX2(maxTemp, k); 1211 if (dbg) 1212 printf(" remap register %u -> %d\n", live->Reg, k); 1213 } 1214 1215 /* Insert this live interval into the active list which is sorted 1216 * by increasing end points. 1217 */ 1218 insert_interval_by_end(&activeIntervals, live); 1219 } 1220 } 1221 1222 if (maxTemp + 1 < (GLint) liveIntervals.Num) { 1223 /* OK, we've reduced the number of registers needed. 1224 * Scan the program and replace all the old temporary register 1225 * indexes with the new indexes. 1226 */ 1227 replace_regs(prog, PROGRAM_TEMPORARY, registerMap); 1228 1229 prog->NumTemporaries = maxTemp + 1; 1230 } 1231 1232 if (dbg) { 1233 printf("Optimize: End live-interval register reallocation\n"); 1234 printf("Num temp regs before: %u after: %u\n", 1235 liveIntervals.Num, maxTemp + 1); 1236 _mesa_print_program(prog); 1237 } 1238} 1239 1240 1241#if 0 1242static void 1243print_it(struct gl_context *ctx, struct gl_program *program, const char *txt) { 1244 fprintf(stderr, "%s (%u inst):\n", txt, program->NumInstructions); 1245 _mesa_print_program(program); 1246 _mesa_print_program_parameters(ctx, program); 1247 fprintf(stderr, "\n\n"); 1248} 1249#endif 1250 1251/** 1252 * This pass replaces CMP T0, T1 T2 T0 with MOV T0, T2 when the CMP 1253 * instruction is the first instruction to write to register T0. The are 1254 * several lowering passes done in GLSL IR (e.g. branches and 1255 * relative addressing) that create a large number of conditional assignments 1256 * that ir_to_mesa converts to CMP instructions like the one mentioned above. 1257 * 1258 * Here is why this conversion is safe: 1259 * CMP T0, T1 T2 T0 can be expanded to: 1260 * if (T1 < 0.0) 1261 * MOV T0, T2; 1262 * else 1263 * MOV T0, T0; 1264 * 1265 * If (T1 < 0.0) evaluates to true then our replacement MOV T0, T2 is the same 1266 * as the original program. If (T1 < 0.0) evaluates to false, executing 1267 * MOV T0, T0 will store a garbage value in T0 since T0 is uninitialized. 1268 * Therefore, it doesn't matter that we are replacing MOV T0, T0 with MOV T0, T2 1269 * because any instruction that was going to read from T0 after this was going 1270 * to read a garbage value anyway. 1271 */ 1272static void 1273_mesa_simplify_cmp(struct gl_program * program) 1274{ 1275 GLuint tempWrites[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; 1276 GLuint outputWrites[MAX_PROGRAM_OUTPUTS]; 1277 GLuint i; 1278 1279 if (dbg) { 1280 printf("Optimize: Begin reads without writes\n"); 1281 _mesa_print_program(program); 1282 } 1283 1284 for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++) { 1285 tempWrites[i] = 0; 1286 } 1287 1288 for (i = 0; i < MAX_PROGRAM_OUTPUTS; i++) { 1289 outputWrites[i] = 0; 1290 } 1291 1292 for (i = 0; i < program->NumInstructions; i++) { 1293 struct prog_instruction *inst = program->Instructions + i; 1294 GLuint prevWriteMask; 1295 1296 /* Give up if we encounter relative addressing or flow control. */ 1297 if (_mesa_is_flow_control_opcode(inst->Opcode) || inst->DstReg.RelAddr) { 1298 return; 1299 } 1300 1301 if (inst->DstReg.File == PROGRAM_OUTPUT) { 1302 assert(inst->DstReg.Index < MAX_PROGRAM_OUTPUTS); 1303 prevWriteMask = outputWrites[inst->DstReg.Index]; 1304 outputWrites[inst->DstReg.Index] |= inst->DstReg.WriteMask; 1305 } else if (inst->DstReg.File == PROGRAM_TEMPORARY) { 1306 assert(inst->DstReg.Index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); 1307 prevWriteMask = tempWrites[inst->DstReg.Index]; 1308 tempWrites[inst->DstReg.Index] |= inst->DstReg.WriteMask; 1309 } else { 1310 /* No other register type can be a destination register. */ 1311 continue; 1312 } 1313 1314 /* For a CMP to be considered a conditional write, the destination 1315 * register and source register two must be the same. */ 1316 if (inst->Opcode == OPCODE_CMP 1317 && !(inst->DstReg.WriteMask & prevWriteMask) 1318 && inst->SrcReg[2].File == inst->DstReg.File 1319 && inst->SrcReg[2].Index == inst->DstReg.Index 1320 && inst->DstReg.WriteMask == get_src_arg_mask(inst, 2, NO_MASK)) { 1321 1322 inst->Opcode = OPCODE_MOV; 1323 inst->SrcReg[0] = inst->SrcReg[1]; 1324 1325 /* Unused operands are expected to have the file set to 1326 * PROGRAM_UNDEFINED. This is how _mesa_init_instructions initializes 1327 * all of the sources. 1328 */ 1329 inst->SrcReg[1].File = PROGRAM_UNDEFINED; 1330 inst->SrcReg[1].Swizzle = SWIZZLE_NOOP; 1331 inst->SrcReg[2].File = PROGRAM_UNDEFINED; 1332 inst->SrcReg[2].Swizzle = SWIZZLE_NOOP; 1333 } 1334 } 1335 if (dbg) { 1336 printf("Optimize: End reads without writes\n"); 1337 _mesa_print_program(program); 1338 } 1339} 1340 1341/** 1342 * Apply optimizations to the given program to eliminate unnecessary 1343 * instructions, temp regs, etc. 1344 */ 1345void 1346_mesa_optimize_program(struct gl_context *ctx, struct gl_program *program) 1347{ 1348 GLboolean any_change; 1349 1350 _mesa_simplify_cmp(program); 1351 /* Stop when no modifications were output */ 1352 do { 1353 any_change = GL_FALSE; 1354 _mesa_remove_extra_move_use(program); 1355 if (_mesa_remove_dead_code_global(program)) 1356 any_change = GL_TRUE; 1357 if (_mesa_remove_extra_moves(program)) 1358 any_change = GL_TRUE; 1359 if (_mesa_remove_dead_code_local(program)) 1360 any_change = GL_TRUE; 1361 1362 any_change = _mesa_constant_fold(program) || any_change; 1363 _mesa_reallocate_registers(program); 1364 } while (any_change); 1365} 1366 1367