/** * Find a good ALU instruction or pair of ALU instruction and emit it. * * Prefer emitting full ALU instructions, so that when we reach a point * where no full ALU instruction can be emitted, we have more candidates * for RGB/Alpha pairing. */ static void emit_one_alu(struct schedule_state *s, struct rc_instruction * before) { struct schedule_instruction * sinst; if (s->ReadyFullALU || !(s->ReadyRGB && s->ReadyAlpha)) { if (s->ReadyFullALU) { sinst = s->ReadyFullALU; s->ReadyFullALU = s->ReadyFullALU->NextReady; } else if (s->ReadyRGB) { sinst = s->ReadyRGB; s->ReadyRGB = s->ReadyRGB->NextReady; } else { sinst = s->ReadyAlpha; s->ReadyAlpha = s->ReadyAlpha->NextReady; } rc_insert_instruction(before->Prev, sinst->Instruction); commit_alu_instruction(s, sinst); } else { struct schedule_instruction **prgb; struct schedule_instruction **palpha; /* Some pairings might fail because they require too * many source slots; try all possible pairings if necessary */ for(prgb = &s->ReadyRGB; *prgb; prgb = &(*prgb)->NextReady) { for(palpha = &s->ReadyAlpha; *palpha; palpha = &(*palpha)->NextReady) { struct schedule_instruction * psirgb = *prgb; struct schedule_instruction * psialpha = *palpha; if (!merge_instructions(&psirgb->Instruction->U.P, &psialpha->Instruction->U.P)) continue; *prgb = (*prgb)->NextReady; *palpha = (*palpha)->NextReady; rc_insert_instruction(before->Prev, psirgb->Instruction); commit_alu_instruction(s, psirgb); commit_alu_instruction(s, psialpha); goto success; } } /* No success in pairing; just take the first RGB instruction */ sinst = s->ReadyRGB; s->ReadyRGB = s->ReadyRGB->NextReady; rc_insert_instruction(before->Prev, sinst->Instruction); commit_alu_instruction(s, sinst); success: ; } /* If the instruction we just emitted uses a presubtract value, and * the presubtract sources were written by the previous intstruction, * the previous instruction needs a nop. */ presub_nop(before->Prev); }
struct rc_instruction *rc_insert_new_instruction(struct radeon_compiler * c, struct rc_instruction * after) { struct rc_instruction * inst = rc_alloc_instruction(c); rc_insert_instruction(after, inst); return inst; }
/** * This function prepares a loop to be unrolled by converting it into an if * statement. Here is an outline of the conversion process: * BGNLOOP; -> BGNLOOP; * <Additional conditional code> -> <Additional conditional code> * SGE/SLT temp[0], temp[1], temp[2]; -> SLT/SGE temp[0], temp[1], temp[2]; * IF temp[0]; -> IF temp[0]; * BRK; -> * ENDIF; -> <Loop Body> * <Loop Body> -> ENDIF; * ENDLOOP; -> ENDLOOP * * @param inst A pointer to a BGNLOOP instruction. * @return 1 for success, 0 for failure */ static int transform_loop(struct emulate_loop_state * s, struct rc_instruction * inst) { struct loop_info * loop; memory_pool_array_reserve(&s->C->Pool, struct loop_info, s->Loops, s->LoopCount, s->LoopReserved, 1); loop = &s->Loops[s->LoopCount++]; if (!build_loop_info(s->C, loop, inst)) { rc_error(s->C, "Failed to build loop info\n"); return 0; } if(try_unroll_loop(s->C, loop)){ return 1; } /* Reverse the conditional instruction */ switch(loop->Cond->U.I.Opcode){ case RC_OPCODE_SGE: loop->Cond->U.I.Opcode = RC_OPCODE_SLT; break; case RC_OPCODE_SLT: loop->Cond->U.I.Opcode = RC_OPCODE_SGE; break; case RC_OPCODE_SLE: loop->Cond->U.I.Opcode = RC_OPCODE_SGT; break; case RC_OPCODE_SGT: loop->Cond->U.I.Opcode = RC_OPCODE_SLE; break; case RC_OPCODE_SEQ: loop->Cond->U.I.Opcode = RC_OPCODE_SNE; break; case RC_OPCODE_SNE: loop->Cond->U.I.Opcode = RC_OPCODE_SEQ; break; default: rc_error(s->C, "loop->Cond is not a conditional.\n"); return 0; } /* Prepare the loop to be emulated */ rc_remove_instruction(loop->Brk); rc_remove_instruction(loop->EndIf); rc_insert_instruction(loop->EndLoop->Prev, loop->EndIf); return 1; }
/** * Emit all ready texture instructions in a single block. * * Emit as a single block to (hopefully) sample many textures in parallel, * and to avoid hardware indirections on R300. */ static void emit_all_tex(struct schedule_state * s, struct rc_instruction * before) { struct schedule_instruction *readytex; struct rc_instruction * inst_begin; assert(s->ReadyTEX); notify_sem_wait(s); /* Node marker for R300 */ inst_begin = rc_insert_new_instruction(s->C, before->Prev); inst_begin->U.I.Opcode = RC_OPCODE_BEGIN_TEX; /* Link texture instructions back in */ readytex = s->ReadyTEX; while(readytex) { rc_insert_instruction(before->Prev, readytex->Instruction); DBG("%i: commit TEX reads\n", readytex->Instruction->IP); /* All of the TEX instructions in the same TEX block have * their source registers read from before any of the * instructions in that block write to their destination * registers. This means that when we commit a TEX * instruction, any other TEX instruction that wants to write * to one of the committed instruction's source register can be * marked as ready and should be emitted in the same TEX * block. This prevents the following sequence from being * emitted in two different TEX blocks: * 0: TEX temp[0].xyz, temp[1].xy__, 2D[0]; * 1: TEX temp[1].xyz, temp[2].xy__, 2D[0]; */ commit_update_reads(s, readytex); readytex = readytex->NextReady; } readytex = s->ReadyTEX; s->ReadyTEX = 0; while(readytex){ DBG("%i: commit TEX writes\n", readytex->Instruction->IP); commit_update_writes(s, readytex); /* Set semaphore bits for last TEX instruction in the block */ if (!readytex->NextReady) { readytex->Instruction->U.I.TexSemAcquire = 1; readytex->Instruction->U.I.TexSemWait = 1; } rc_list_add(&s->PendingTEX, rc_list(&s->C->Pool, readytex)); readytex = readytex->NextReady; } }
static void emit_instruction( struct schedule_state * s, struct rc_instruction * before) { int max_score = -1; struct schedule_instruction * max_inst = NULL; struct schedule_instruction ** max_list = NULL; unsigned tex_count = 0; struct schedule_instruction * tex_ptr; pair_instructions(s); #if VERBOSE fprintf(stderr, "Full:\n"); print_list(s->ReadyFullALU); fprintf(stderr, "RGB:\n"); print_list(s->ReadyRGB); fprintf(stderr, "Alpha:\n"); print_list(s->ReadyAlpha); fprintf(stderr, "TEX:\n"); print_list(s->ReadyTEX); #endif for (tex_ptr = s->ReadyTEX; tex_ptr; tex_ptr = tex_ptr->NextReady) { if (tex_ptr->Instruction->U.I.Opcode == RC_OPCODE_KIL) { emit_all_tex(s, before); return; } tex_count++; } update_max_score(s, &s->ReadyFullALU, &max_score, &max_inst, &max_list); update_max_score(s, &s->ReadyRGB, &max_score, &max_inst, &max_list); update_max_score(s, &s->ReadyAlpha, &max_score, &max_inst, &max_list); if (tex_count >= s->max_tex_group || max_score == -1 || (s->TEXCount > 0 && tex_count == s->TEXCount) || (!s->C->is_r500 && tex_count > 0 && max_score == -1)) { emit_all_tex(s, before); } else { remove_inst_from_list(max_list, max_inst); rc_insert_instruction(before->Prev, max_inst->Instruction); commit_alu_instruction(s, max_inst); presub_nop(before->Prev); } }
/** * Find a good ALU instruction or pair of ALU instruction and emit it. * * Prefer emitting full ALU instructions, so that when we reach a point * where no full ALU instruction can be emitted, we have more candidates * for RGB/Alpha pairing. */ static void emit_one_alu(struct schedule_state *s, struct rc_instruction * before) { struct schedule_instruction * sinst; if (s->ReadyFullALU) { sinst = s->ReadyFullALU; s->ReadyFullALU = s->ReadyFullALU->NextReady; rc_insert_instruction(before->Prev, sinst->Instruction); commit_alu_instruction(s, sinst); } else { struct schedule_instruction **prgb; struct schedule_instruction **palpha; struct schedule_instruction *prev; pair: /* Some pairings might fail because they require too * many source slots; try all possible pairings if necessary */ for(prgb = &s->ReadyRGB; *prgb; prgb = &(*prgb)->NextReady) { for(palpha = &s->ReadyAlpha; *palpha; palpha = &(*palpha)->NextReady) { struct schedule_instruction * psirgb = *prgb; struct schedule_instruction * psialpha = *palpha; if (!merge_instructions(&psirgb->Instruction->U.P, &psialpha->Instruction->U.P)) continue; *prgb = (*prgb)->NextReady; *palpha = (*palpha)->NextReady; rc_insert_instruction(before->Prev, psirgb->Instruction); commit_alu_instruction(s, psirgb); commit_alu_instruction(s, psialpha); goto success; } } prev = NULL; /* No success in pairing, now try to convert one of the RGB * instructions to an Alpha so we can pair it with another RGB. */ if (s->ReadyRGB && s->ReadyRGB->NextReady) { for(prgb = &s->ReadyRGB; *prgb; prgb = &(*prgb)->NextReady) { if ((*prgb)->NumWriteValues == 1) { struct schedule_instruction * prgb_next; if (!convert_rgb_to_alpha(s, *prgb)) goto cont_loop; prgb_next = (*prgb)->NextReady; /* Add instruction to the Alpha ready list. */ (*prgb)->NextReady = s->ReadyAlpha; s->ReadyAlpha = *prgb; /* Remove instruction from the RGB ready list.*/ if (prev) prev->NextReady = prgb_next; else s->ReadyRGB = prgb_next; goto pair; } cont_loop: prev = *prgb; } } /* Still no success in pairing, just take the first RGB * or alpha instruction. */ if (s->ReadyRGB) { sinst = s->ReadyRGB; s->ReadyRGB = s->ReadyRGB->NextReady; } else if (s->ReadyAlpha) { sinst = s->ReadyAlpha; s->ReadyAlpha = s->ReadyAlpha->NextReady; } else { /*XXX Something real bad has happened. */ assert(0); } rc_insert_instruction(before->Prev, sinst->Instruction); commit_alu_instruction(s, sinst); success: ; } /* If the instruction we just emitted uses a presubtract value, and * the presubtract sources were written by the previous intstruction, * the previous instruction needs a nop. */ presub_nop(before->Prev); }