// This file is distributed under a BSD license. See LICENSE.txt for details. #include "shadercodegen.hpp" #include "_startdx.hpp" /****************************************************************************/ // Control structure state struct sShaderCodeGen::Control { sBool Exec; // execute instrs yes/no sBool Free; // no if in a chain yet taken? const sU32 *Loop; // loop point sU32 Condition; // while condition }; // Evaluates a condition // Encoding: // Bit 0-4: Bitfield start // Bit 5-7: Bitfield len (minus 1) // Bit 8-15: Compare ref value // Bit 16-24: Word offset (>=496 reserved) // Bit 25-28: Index register specifier // Bit 29-30: Comparision op (0 = "==", 1 = "<", 2 = ">") // Bit 31: Comparision negate sU32 sShaderCodeGen::GetFlagWord(sU32 addr) { sU32 wordOffs = ((addr >> 16) + Index[(addr >> 25) & 0xf]) & 0x1ff; if(wordOffs >= 0x1f0) // special return ((sU32 *) Index)[wordOffs - 0x1f0]; else return DataArea[wordOffs]; } sBool sShaderCodeGen::EvalCond(sU32 condition) { // get the input value sU32 fieldStart = condition & 0x1f; sU32 fieldLen = (condition >> 5) & 0x7; sU32 value = (GetFlagWord(condition) >> fieldStart) & ((2 << fieldLen) - 1); // perform the comparision sU32 ref = (condition >> 8) & 0xff; sBool result; switch((condition >> 29) & 3) { case 0: result = value == ref; break; case 1: result = value < ref; break; case 2: result = value > ref; break; default: result = sFALSE; break; } // negate if required if(condition & 0x80000000) result = !result; return result; } // Phase 1 reads in the shader, evaluates if statements, and resolves aliases sBool sShaderCodeGen::Phase1(const sU32 *input) { sU32 aliasTable[MAXTEMP]; sU32 remapUp[MAXTEMP]; Control control[MAXNEST+1],*ctrl=control; sU32 *codePtr = Code; sInt tempReg = MAXTEMP; // Check whether input actually *is* a shader if((*input & 0xfffe0000) != 0xfffe0000) return sFALSE; IsPS11 = *input == 0xffff0101; IsV1Shader = (*input & 0xff00) == 0x100; // copy first (version) word, initialize tables and global control scope *codePtr++ = *input++; sSetMem(aliasTable,0,sizeof(aliasTable)); sSetMem(remapUp,0,sizeof(remapUp)); sSetMem(Index,0,sizeof(Index)); FirstInstr = 0; ctrl->Exec = sTRUE; ctrl->Loop = input; ctrl->Condition = 0; // Loop through the shader instruction by instruction, throwing every- // thing in non-taken ifs away and processing virtual moves sU32 opcode; do { sU32 *outOpStart = codePtr; if(codePtr + LONGESTINSTR > &Code[MAXCODEWORDS]) return sFALSE; opcode = *input++; sInt nOperands = (opcode >> 24) & 0xf; const sU32 *opNext = input + nOperands; sBool exec = ctrl->Exec, taken; switch(opcode) { // pseudoinstructions case XO_EXT_IF: taken = EvalCond(*input++); (++ctrl)->Exec = taken & exec; ctrl->Free = !taken; ctrl->Loop = 0; break; case XO_EXT_ELSE: ctrl->Exec = ctrl->Free & ctrl[-1].Exec; break; case XO_EXT_ELIF: taken = EvalCond(*input++); ctrl->Exec = ctrl->Free & taken & ctrl[-1].Exec; if(taken) ctrl->Free = sFALSE; break; case XO_EXT_WHILE: (++ctrl)->Condition = *input++; ctrl->Exec = EvalCond(ctrl->Condition) & exec; ctrl->Loop = input; break; case XO_EXT_END: if(exec && ctrl->Loop && EvalCond(ctrl->Condition)) opNext = ctrl->Loop; else ctrl--; break; case XO_EXT_VMOV: if(exec) { sU32 destReg = *input++ & 0x70001fff; sU32 src = *input; sVERIFY(destReg < MAXTEMP); // resolve aliasing here, the mapping may change later if(src & 0x80000000) // register source { sU32 srcn = src & 0x70001fff; if(srcn < MAXTEMP && aliasTable[srcn]) srcn = aliasTable[srcn]; aliasTable[destReg] = (src & 0x8fffe000) | srcn; } else aliasTable[destReg] = GetFlagWord(src); } break; case XO_EXT_INDEXED: if(exec) { sU32 code = *input; codePtr[-sInt(code & 0xff)] += Index[(code >> 8) & 0xf]; } break; case XO_EXT_IADD: if(exec) { sU32 code = *input; Index[(code >> 8) & 0xf] = Index[(code >> 12) & 0xf] + (code & 0xff); } break; case XO_EXT_ERROR: if(exec) return sFALSE; break; case XO_EXT_FREE: if(exec) { sU32 reg = *input & 0x70001fff; if(reg < MAXTEMP) { remapUp[reg] = ++tempReg; aliasTable[reg] = tempReg | XSALL; if(tempReg >= MAXTEMP2) return sFALSE; } } break; // instructions case XO_DEF: // needs special handling because the operands are floats if(exec) { // just copy 6 words straight, starting from the opcode sCopyMem(codePtr,input-1,6*sizeof(sU32)); codePtr += 6; } break; default: if(exec) { if(opcode != XO_DCL && !FirstInstr) FirstInstr = codePtr; *codePtr++ = opcode; sU32 destReg = MAXTEMP; // go through operands, processing aliasing for(sInt i=0;i> 24) & 0xf; // remap registers, alloc registers whose lifetime starts this instr sInt physRegCtr = 0; for(sInt i=0;i> 24) & 0xf; *current &= 0xf0ffffff; current += nOperands + 1; } } /****************************************************************************/ sU32 *sShaderCodeGen::GenCode(const sU32 *input,const sU32 *data) { sU32 *result = 0; DataArea = data; if(Phase1(input) && Phase2()) { if(IsV1Shader) RemoveOpcodeLengths(); result = new sU32[CodeLen]; sCopyMem4(result,Code,CodeLen); } return result; } /****************************************************************************/