Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: ea9b06d4fc
Fetching contributors…

Cannot retrieve contributors at this time

2059 lines (1702 sloc) 44.023 kb
// This file is distributed under a BSD license. See LICENSE.txt for details.
#include "shadercompile.hpp"
#include "_startdx.hpp"
/****************************************************************************/
enum ShaderToken
{
TOK_EOW = 256, // end of word
TOK_WORD,
TOK_DOTDOT, // ".."
// predefined word tokens from here on
TOK_FIRSTPREDEF = 512,
TOK_DCL = TOK_FIRSTPREDEF,
TOK_DEF,
TOK_NOP,
TOK_TEXKILL,
TOK_TEMP,
TOK_ALIAS,
TOK_VMOV,
TOK_IF,
TOK_ELSE,
TOK_ELIF,
TOK_ENDIF,
TOK_FOR,
TOK_ENDFOR,
TOK_WHILE,
TOK_ENDWHILE,
TOK_FLAGS,
TOK_ENDFLAGS,
TOK_ERROR,
};
static const sChar *Tokens[] =
{
"dcl",
"def",
"nop",
"texkill",
"temp",
"alias",
"vmov",
"if",
"else",
"elif",
"endif",
"for",
"endfor",
"while",
"endwhile",
"flags",
"endflags",
"error",
0,
};
struct ShaderInstr
{
sU32 opcode;
const sChar *name;
sInt params;
sU8 vsver;
sU8 psver;
sU16 flags;
};
enum ControlStructure
{
CS_IF,
CS_WHILE,
CS_FOR,
};
static const sChar *controlNames[] =
{
"if",
"while",
"for",
};
enum ShaderInstrFlags
{
SIF_NONE = 0x0000, // no flags
SIF_NOCOISSUE = 0x0001, // cannot be coissued
SIF_VECTORONLY = 0x0002, // must be issued in vector pipe
SIF_REPLICATELAST = 0x0004, // last parameter must have replicate swizzle
SIF_NOSWIZZLE = 0x0008, // source parameters must have default swizzle
SIF_NOSRCDESTEQUAL = 0x0010, // dest != all sources
SIF_DESTMUSTTEMP = 0x0020, // dest register must be temporary
SIF_DESTNOW = 0x0040, // destination write mask may not include w
SIF_VS1xWRITEYORXY = 0x0080, // for vs1.1, may only write .y or .xy
SIF_LASTNOSWIZZLE = 0x0100, // last parameter may not have swizzle
SIF_REPLICATE = 0x0200, // all sources must have replicate swizzle
SIF_NOWRITEMASK = 0x0400, // write mask must be .xyzw (default)
SIF_TEXLD20 = 0x0800, // second parameter must be a sampler
SIF_WRITEXY = 0x1000, // write mask must be .xy
SIF_WRITEXYZ = 0x2000, // write mask must be .xyz
SIF_DESTMUSTA0 = 0x4000, // destination register must be a0
SIF_SRC23DIFFER = 0x8000, // all source register need to be unique
};
static const ShaderInstr ShaderInstrs[] =
{
{ XO_ABS, "abs", 1, 0x20,0x20, SIF_NONE },
{ XO_ADD, "add", 2, 0x10,0x10, SIF_NONE },
{ XO_CMP, "cmp", 3, 0x00,0x10, SIF_NOSRCDESTEQUAL },
{ XO_CND, "cnd", 3, 0x00,0x10, SIF_NONE },
{ XO_CRS, "crs", 2, 0x20,0x20, SIF_NOSWIZZLE|SIF_NOSRCDESTEQUAL|SIF_DESTMUSTTEMP|SIF_DESTNOW },
{ XO_DP2ADD, "dp2add", 3, 0x00,0x20, SIF_REPLICATELAST },
{ XO_DP3, "dp3", 2, 0x10,0x10, SIF_VECTORONLY },
{ XO_DP4, "dp4", 2, 0x10,0x12, SIF_NOCOISSUE },
{ XO_DST, "dst", 2, 0x10,0x00, SIF_NONE },
{ XO_EXP, "exp", 1, 0x10,0x20, SIF_REPLICATELAST },
{ XO_EXPP, "expp", 1, 0x10,0x00, SIF_REPLICATELAST },
{ XO_FRC, "frc", 1, 0x10,0x20, SIF_VS1xWRITEYORXY },
{ XO_LOG, "log", 1, 0x10,0x20, SIF_REPLICATELAST },
{ XO_LOGP, "logp", 1, 0x10,0x00, SIF_REPLICATELAST },
{ XO_LRP, "lrp", 3, 0x20,0x10, SIF_NONE },
{ XO_M3x2, "m3x2", 2, 0x10,0x20, SIF_LASTNOSWIZZLE|SIF_WRITEXY },
{ XO_M3x3, "m3x3", 2, 0x10,0x20, SIF_LASTNOSWIZZLE|SIF_WRITEXYZ },
{ XO_M3x4, "m3x4", 2, 0x10,0x20, SIF_LASTNOSWIZZLE|SIF_NOWRITEMASK },
{ XO_M4x3, "m4x3", 2, 0x10,0x20, SIF_LASTNOSWIZZLE|SIF_WRITEXYZ },
{ XO_M4x4, "m4x4", 2, 0x10,0x20, SIF_LASTNOSWIZZLE|SIF_NOWRITEMASK },
{ XO_MAD, "mad", 3, 0x10,0x10, SIF_NONE },
{ XO_MAX, "max", 2, 0x10,0x20, SIF_NONE },
{ XO_MIN, "min", 2, 0x10,0x20, SIF_NONE },
{ XO_MOV, "mov", 1, 0x10,0x10, SIF_NONE },
{ XO_MOVA, "mova", 1, 0x10,0x00, SIF_DESTMUSTA0 },
{ XO_MUL, "mul", 2, 0x10,0x10, SIF_NONE },
{ XO_NRM, "nrm", 1, 0x20,0x20, SIF_LASTNOSWIZZLE },
{ XO_POW, "pow", 2, 0x20,0x20, SIF_REPLICATE },
{ XO_RCP, "rcp", 1, 0x10,0x20, SIF_REPLICATELAST },
{ XO_RSQ, "rsq", 1, 0x10,0x20, SIF_REPLICATELAST },
{ XO_SGE, "sge", 2, 0x10,0x00, SIF_NONE },
{ XO_SGN, "sgn", 3, 0x20,0x00, SIF_SRC23DIFFER },
{ XO_SLT, "slt", 2, 0x10,0x00, SIF_NONE },
{ XO_SUB, "sub", 2, 0x10,0x10, SIF_NONE },
{ XO_TEX13, "tex", 0, 0x00,0x10, SIF_NOWRITEMASK },
{ XO_TEXLD, "texld", 2, 0x00,0x20, SIF_NOWRITEMASK|SIF_NOSWIZZLE|SIF_TEXLD20|SIF_DESTMUSTTEMP },
{ XO_TEXLDB, "texldb", 2, 0x00,0x20, SIF_NOWRITEMASK|SIF_NOSWIZZLE|SIF_TEXLD20|SIF_DESTMUSTTEMP },
{ XO_TEXLDP, "texldp", 2, 0x00,0x20, SIF_NOWRITEMASK|SIF_NOSWIZZLE|SIF_TEXLD20|SIF_DESTMUSTTEMP },
{ XO_EXT_FREE,"free", 0, 0x10,0x10, SIF_NOWRITEMASK|SIF_NOSWIZZLE|SIF_DESTMUSTTEMP },
{ 0 },
};
static const sChar *emptylist[] =
{
0
};
static const sChar *vsusagesuffix[] =
{
"_position",
"_blendweight",
"_blendindices",
"_normal",
"_psize",
"_texcoord",
"_tangent",
"_binormal",
"_tessfactor",
"_positiont",
"_color",
"_fog",
"_depth",
0
};
static const sChar *pssamplersuffix[] =
{
" ",
" ",
"_2d",
"_cube",
"_volume",
0
};
static const sChar *vs2instmodifier[] =
{
0,
};
static const sChar *ps1instmodifier[] =
{
"_sat",
0,
};
static const sChar *ps1instshift[] =
{
" ",
"_x2",
"_x4",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
"_d2",
0,
};
static const sChar *ps1srcmodifier[] =
{
" ",
"_bias",
"_bx2",
0,
};
static const sChar *ps2instmodifier[] =
{
"_sat",
"_pp",
"_centroid",
0,
};
static const sChar *compareops[] =
{
"==",
"<",
">",
" ",
"!=",
">=",
"<=",
0
};
/****************************************************************************/
static sBool IsSeperator(const sChar *scan)
{
return (*scan == 0 ||
*scan == ' ' || *scan == '\n' || *scan == '\t' || *scan == '\r' ||
scan[0] == '/' && (scan[1] == '/' || scan[1] == '*') ||
*scan == '(' || *scan == ')' || *scan == '[' || *scan == ']' ||
*scan == ',' ||
*scan == '=' || *scan == '!' || *scan == '<' || *scan == '>');
}
void sShaderCompiler::WordScan()
{
sChar *s = Word;
*s = 0;
SubScanPtr = Word;
IsNewLine = sFALSE;
for(;;)
{
// skip whitespace
while(*ScanPtr==' ' || *ScanPtr=='\n' || *ScanPtr=='\t' || *ScanPtr=='\r')
{
if(*ScanPtr=='\n')
{
if(ErrorLine)
return;
Line++;
IsNewLine = sTRUE;
}
ScanPtr++;
}
// handle comments
if(ScanPtr[0]=='/' && ScanPtr[1]=='/') // eol comment
{
ScanPtr+=2;
while(*ScanPtr!='\n' && *ScanPtr!=0)
ScanPtr++;
}
else if(ScanPtr[0]=='/' && ScanPtr[1]=='*') // block comment
{
ScanPtr+=2;
while(*ScanPtr!=0)
{
if(ScanPtr[0]=='*' && ScanPtr[1]=='/')
{
ScanPtr+=2;
break;
}
if(*ScanPtr=='\n')
Line++;
ScanPtr++;
}
if(*ScanPtr==0)
{
ErrorLineNum = Line;
Error("Block comment not closed");
return;
}
}
else // no comment
break;
}
switch(*ScanPtr)
{
case 0:
break;
case '(': case ')':
case '[': case ']':
case ',':
*s++ = *ScanPtr++;
*s++ = 0;
break;
case '!': case '=':
case '<': case '>':
*s++ = *ScanPtr++;
if(*ScanPtr == '=')
*s++ = *ScanPtr++;
*s++ = 0;
break;
default:
while(!IsSeperator(ScanPtr))
{
*s++ = *ScanPtr++;
if(s-Word >= MAXTOKEN)
{
ErrorLineNum = Line;
Error("Token too long");
break;
}
}
*s++ = 0;
break;
}
Scan();
}
sInt sShaderCompiler::Scan()
{
sChar *s = TokValue;
Token = TOK_EOW;
// copy first char, advance if it's not 0
*s++ = *SubScanPtr;
if(!*SubScanPtr)
return Token;
else // recognize one-character tokens
{
sChar ch = *SubScanPtr++;
*s = 0;
if(ch == '-' || ch == '+')
{
Token = ch;
return Token;
}
else if(!*SubScanPtr && ch == '!')
{
Token = ch;
return Token;
}
else if(ch == '.' && *SubScanPtr == '.')
{
SubScanPtr++;
Token = TOK_DOTDOT;
return Token;
}
}
// copy other characters up to next . or _
while(*SubScanPtr && *SubScanPtr != '.' && *SubScanPtr != '_'
&& *SubScanPtr != '-' && *SubScanPtr != '+')
*s++ = *SubScanPtr++;
*s++ = 0;
Token = TOK_WORD;
// try and recognize known word
for(sInt i=0;Tokens[i];i++)
{
if(sCmpString(TokValue,Tokens[i]) == 0)
{
Token = TOK_FIRSTPREDEF + i;
break;
}
}
return Token;
}
void sShaderCompiler::SkipRestOfLine()
{
do
{
WordScan();
}
while(!IsNewLine && *ScanPtr);
}
void sShaderCompiler::ExpectEndOfLine()
{
if(!IsNewLine && *ScanPtr)
{
Error("Extra characters on line!");
ErrorLine = 0;
SkipRestOfLine();
}
}
void sShaderCompiler::ExpectEndOfWord()
{
if(Token != TOK_EOW)
Error("Extra characters in word!");
WordScan();
}
void sShaderCompiler::ExpectEndOfWordAndLine()
{
if(Token != TOK_EOW)
Error("Extra characters in word!");
ErrorLine = 0;
WordScan();
if(!IsNewLine && *ScanPtr)
{
Error("Extra characters on line!");
SkipRestOfLine();
}
}
void sShaderCompiler::Error(const sChar *name,...)
{
if(ErrorLine==0)
{
ErrorCount++;
Errors->PrintF("%s(%d): ",FileName,ErrorLineNum);
Errors->PrintArg(name,&name);
Errors->Print("\n");
}
ErrorLine++;
}
sBool sShaderCompiler::ExpectWord(const sChar *word)
{
if(sCmpString(Word,word))
{
Error("Syntax error: '%s' expected",word);
WordScan();
return sFALSE;
}
WordScan();
return sTRUE;
}
sBool sShaderCompiler::ExpectSubWord(const sChar *subWord)
{
if(sCmpString(TokValue,subWord))
{
Error("Syntax error: '%s' expected",subWord);
Scan();
return sFALSE;
}
Scan();
return sTRUE;
}
void sShaderCompiler::VerifyToken(sInt token)
{
if(Token != token)
Error("Internal parse error");
Scan();
}
/****************************************************************************/
struct sShaderCompiler::Symbol
{
sChar Name[MAXTOKEN+1];
sU32 Register;
sInt FieldOffset;
sInt FieldDimension; // 0 = single value
};
class sShaderCompiler::SymbolTable
{
sArray<Symbol> Table;
sShaderCompiler *Compiler;
Symbol *LookupInternal(const sChar *name);
void AddInternal(const sChar *name,sU32 reg,sInt offs,sInt dim);
public:
SymbolTable *ParentScope;
SymbolTable(sShaderCompiler *compiler,SymbolTable *parent = 0);
~SymbolTable();
void AddReg(const sChar *name,sU32 reg);
void AddField(const sChar *name,sInt offs,sInt dim);
Symbol *Lookup(const sChar *name);
};
sShaderCompiler::Symbol *sShaderCompiler::SymbolTable::LookupInternal(const sChar *name)
{
for(sInt i=0;i<Table.Count;i++)
if(!sCmpString(Table[i].Name,name))
return &Table[i];
return 0;
}
void sShaderCompiler::SymbolTable::AddInternal(const sChar *name,sU32 reg,sInt offs,sInt dim)
{
Symbol *sym = LookupInternal(name);
if(sym)
Compiler->Error("Symbol '%s' already defined in this scope",name);
else
{
sym = Table.Add();
sCopyString(sym->Name,name,MAXTOKEN+1);
sym->Register = reg;
sym->FieldOffset = offs;
sym->FieldDimension = dim;
}
}
sShaderCompiler::SymbolTable::SymbolTable(sShaderCompiler *compiler,sShaderCompiler::SymbolTable *parent)
{
Table.Init();
Compiler = compiler;
ParentScope = parent;
}
sShaderCompiler::SymbolTable::~SymbolTable()
{
Table.Exit();
}
void sShaderCompiler::SymbolTable::AddReg(const sChar *name,sU32 reg)
{
AddInternal(name,reg,0,-1);
}
void sShaderCompiler::SymbolTable::AddField(const sChar *name,sInt offs,sInt dim)
{
AddInternal(name,~0U,offs,dim);
}
sShaderCompiler::Symbol *sShaderCompiler::SymbolTable::Lookup(const sChar *name)
{
for(SymbolTable *table=this;table;table=table->ParentScope)
{
Symbol *sym = table->LookupInternal(name);
if(sym)
return sym;
}
return 0;
}
/****************************************************************************/
void sShaderCompiler::OpenScope()
{
Identifiers = new SymbolTable(this,Identifiers);
}
void sShaderCompiler::EndScope()
{
if(Identifiers->ParentScope)
{
SymbolTable *old = Identifiers;
Identifiers = Identifiers->ParentScope;
delete old;
}
}
sU32 sShaderCompiler::FindSymRegister(const sChar *name)
{
Symbol *sym = Identifiers->Lookup(name);
if(!sym)
{
Error("'%s': Identifier must be declared before use",name);
return ~0;
}
else
return sym->Register;
}
void sShaderCompiler::AddTempRegister(const sChar *name)
{
if(NumTempRegs >= 256)
Error("Only 256 temporaries supported at the moment");
else
Identifiers->AddReg(name,NumTempRegs++);
}
void sShaderCompiler::AddPredefinedSymbols()
{
if(ShaderType == 0) // vertex shader
{
Identifiers->AddReg("oPos",X_OPOS);
Identifiers->AddReg("oFog",X_OFOG);
Identifiers->AddReg("oPts",X_PSIZE);
Identifiers->AddReg("oD0",X_OCOLOR|0);
Identifiers->AddReg("oD1",X_OCOLOR|1);
Identifiers->AddReg("oT0",X_OUV|0);
Identifiers->AddReg("oT1",X_OUV|1);
Identifiers->AddReg("oT2",X_OUV|2);
Identifiers->AddReg("oT3",X_OUV|3);
Identifiers->AddReg("oT4",X_OUV|4);
Identifiers->AddReg("oT5",X_OUV|5);
Identifiers->AddReg("oT6",X_OUV|6);
Identifiers->AddReg("oT7",X_OUV|7);
}
else // pixel shader
{
Identifiers->AddReg("oC0",X_COLOR|0);
Identifiers->AddReg("oC1",X_COLOR|1);
Identifiers->AddReg("oC2",X_COLOR|2);
Identifiers->AddReg("oC3",X_COLOR|3);
Identifiers->AddReg("oDepth",X_DEPTH);
}
}
/****************************************************************************/
void sShaderCompiler::AddFlag(const sChar *name,sInt dim)
{
Flags->AddField(name,NumFlags,dim);
NumFlags += dim ? dim : 1;
if(NumFlags >= 496)
Error("You may not use more than 496 words of flags");
}
/****************************************************************************/
struct sShaderCompiler::Control
{
sInt Type;
sInt Param;
};
void sShaderCompiler::PushControl(sInt type,sInt param)
{
if(ControlStack.Count >= 32)
Error("Control structures nested too deeply! (Max. 32 levels)");
Control *ctrl = ControlStack.Add();
ctrl->Type = type;
ctrl->Param = param;
OpenScope();
}
void sShaderCompiler::PopControl(sInt type)
{
if(!ControlStack.Count)
Error("'end%s' without %s",controlNames[type],controlNames[type]);
else
{
sInt refType = TopControl()->Type;
if(type != refType)
Error("'end%s' read, 'end%s' expected",controlNames[type],controlNames[refType]);
EndScope();
ControlStack.Count--;
}
}
sShaderCompiler::Control *sShaderCompiler::TopControl()
{
static Control defaultControl = { 0 };
if(!ControlStack.Count)
return &defaultControl;
else
return &ControlStack[ControlStack.Count-1];
}
void sShaderCompiler::ElseControl(sBool set)
{
if(!ControlStack.Count)
Error("else without if");
else
{
Control *top = TopControl();
if(top->Type != CS_IF)
Error("'%s' has no 'else'",controlNames[top->Type]);
else if(top->Param)
Error("More than one else per if isn't allowed");
else
{
EndScope();
OpenScope();
if(set)
top->Param = 1;
}
}
}
/****************************************************************************/
struct sShaderCompiler::IndexedPatch
{
sInt Location;
sInt Reg;
};
void sShaderCompiler::Emit(sU32 token)
{
*Output.Add() = token;
}
void sShaderCompiler::EmitIndexBackpatches()
{
sInt baseOffs = Output.Count;
for(sInt i=0;i<IndexedPatches.Count;i++)
{
IndexedPatch *patch = &IndexedPatches[i];
if(baseOffs - patch->Location >= 256)
Error("Code generation error: Indexed backpatch offset exceeds 255");
Emit(XO_EXT_INDEXED);
Emit((baseOffs - patch->Location) | (patch->Reg << 8));
}
IndexedPatches.Count = 0;
}
/****************************************************************************/
sInt sShaderCompiler::CharInList(sChar ch,const sChar *list)
{
sInt i;
for(i=0;list[i] && list[i] != ch;i++);
return list[i] ? i : -1;
}
sInt sShaderCompiler::StringInPrefixList(const sChar *str,const sChar **prefixList,const sChar *&rest)
{
for(sInt i=0;prefixList[i];i++)
{
const sChar *prefix = prefixList[i];
sInt j;
for(j=0;prefix[j] && prefix[j] == str[j];j++);
if(!prefix[j])
{
rest = str + j;
return i;
}
}
return -1;
}
sInt sShaderCompiler::MatchStringList(const sChar **stringList)
{
for(sInt i=0;stringList[i];i++)
{
if(!sCmpString(stringList[i],TokValue))
return i;
}
return -1;
}
sInt sShaderCompiler::DecodeInt(const sChar *what)
{
sInt number = 0;
while(*what)
{
if(*what >= '0' && *what <= '9')
number = (number * 10) + (*what++ - '0');
else
return -1;
}
return number;
}
/****************************************************************************/
sF32 sShaderCompiler::ParseFloat()
{
const sChar *scan = Word;
sF64 val,dec,frac,neg;
val = 0;
dec = 1;
frac = 0;
neg = 1;
if((*scan >= '0' && *scan <= '9') || *scan == '.' || *scan == '-')
{
if(*scan == '-')
{
neg = -1;
scan++;
}
while(*scan >= '0' && *scan <= '9')
val = val * 10 + (*scan++ - '0');
if(*scan == '.')
{
scan++;
while(*scan >= '0' && *scan <= '9')
{
frac = frac * 10 + (*scan++ - '0');
dec *= 10;
}
val += frac / dec;
}
if(*scan != 0)
{
val = 0.0f;
Error("Not a valid float value");
}
}
else
Error("Not a valid float value");
WordScan();
return val * neg;
}
sInt sShaderCompiler::ParseWriteMask()
{
sInt mask = 0;
if(Token == TOK_WORD && TokValue[0] == '.')
{
static const sChar *maskChars[] = { "xyzw", "rgba" };
sInt maskType = -1;
const sChar *namePtr = TokValue + 1;
while(*namePtr)
{
if(maskType == -1)
{
if(CharInList(*namePtr,maskChars[0]) != -1)
maskType = 0;
else if(CharInList(*namePtr,maskChars[1]) != -1)
maskType = 1;
else
Error("Invalid character in write mask");
}
sInt ind = CharInList(*namePtr,maskChars[maskType]);
if(ind == -1)
Error("Invalid character in write mask");
else if(mask & (1 << ind))
Error("Same channel specified twice in write mask (%c)",*namePtr);
mask |= 1 << ind;
namePtr++;
}
}
Scan();
if(mask == 0)
Error("Invalid write mask");
return mask;
}
sU32 sShaderCompiler::ParseSwizzle()
{
if(Token != TOK_WORD || TokValue[0] != '.') // no swizzle
return XS_XYZW;
else
{
const sChar *str = TokValue + 1; // skip '.'
static const sChar *maskChars[] = { "xyzw", "rgba" };
sU32 mask = 0;
sInt lastOne=0,numComponents=0;
sInt maskType = -1;
// swizzle (hopefully) follows
while(*str)
{
if(maskType == -1)
{
if(CharInList(*str,maskChars[0]) != -1)
maskType = 0;
else if(CharInList(*str,maskChars[1]) != -1)
maskType = 1;
else
Error("Invalid character in swizzle mask");
}
lastOne = CharInList(*str,maskChars[maskType]);
if(lastOne == -1)
Error("Invalid character in swizzle mask");
else
mask |= lastOne << (16 + 2*numComponents);
str++;
if(++numComponents > 4)
Error("Swizzle mask too long (4 components only)");
}
while(numComponents < 4)
{
mask |= lastOne << (16 + 2*numComponents);
numComponents++;
}
Scan();
return mask;
}
}
sU32 sShaderCompiler::ParseRegNum()
{
sU32 regNum = 0;
if(Token != TOK_WORD)
Error("'%s': Register name expected",TokValue);
else
{
// check whether register is directly addressed
const sChar *regs[] = { " vca", " vct s" };
sInt regType = CharInList(TokValue[0],regs[ShaderType]);
sInt index = DecodeInt(TokValue+1);
if(regType != -1 && index != -1) // yes, directly addressed
regNum = ((regType & 7) << 28) | ((regType & 24) << 8) | index;
else // no, look up name in symbol table
regNum = FindSymRegister(TokValue);
}
Scan();
if(Token == '+') // indexed addressing
{
Scan();
// actual address reg or virtual index reg?
if(ShaderType == 0 && TokValue[0] == 'a') // assume address reg
{
// strip last char
sInt len=0;
while(TokValue[len])
len++;
sInt lastChar = TokValue[--len];
TokValue[len] = 0;
// decode reg num
sInt num = DecodeInt(TokValue+1);
if(num != 0)
Error("Invalid address register!");
Scan();
sInt regPart = CharInList(lastChar,"xyzw");
if(regPart == -1)
Error("Need to specify address register component, without a dot!");
if(ShaderVer < 0x20 && regPart)
Error("Can only use a0.x in vs.1.1!");
sU8 masks[4] = { 0x00,0x55,0xaa,0xff };
regNum |= 0x2000; // relative addressing
AddrReg = (3 << 28) | masks[regPart] | num;
}
else
{
sInt index = ParseIndexReg();
if(index < 0)
Error("Index register expected");
else if(index >= 15)
Error("Only 15 index registers supported");
else
{
IndexedPatch *patch = IndexedPatches.Add();
patch->Location = Output.Count;
patch->Reg = index;
}
}
}
return regNum;
}
sU32 sShaderCompiler::ParseFlagIdentifier(sBool expectIt)
{
sU32 code;
Symbol *sym = Flags->Lookup(TokValue);
if(sym)
{
WordScan();
code = 0x1e000000 | (sym->FieldOffset << 16);
if(sym->FieldDimension) // for arrays, we now need an index
{
if(!ExpectWord("["))
return ~0U;
sInt indexNum = ParseIndexReg();
if(indexNum >= 0 && Token == TOK_EOW) // index register
{
if(indexNum >= 15)
Error("Only 15 index registers supported");
code = (code & ~0x1e000000) | (indexNum << 25);
}
else
{
sInt index = DecodeInt(Word);
if(index < 0)
Error("Array index expected");
else if(index >= sym->FieldDimension)
Error("Array index out of range");
code += index << 16;
}
WordScan();
if(!ExpectWord("]"))
return ~0U;
}
}
else
{
if(expectIt)
{
WordScan();
Error("Unknown flag identifier '%s'",Word);
}
return ~0U;
}
return code;
}
/****************************************************************************/
sU32 sShaderCompiler::ParseDestReg(sBool writeMask)
{
sU32 out = 0;
sInt mask = 0xf;
sU32 regNum = ParseRegNum();
// Parse write mask if desired
if(Token == TOK_WORD && TokValue[0] == '.')
{
if(writeMask)
mask = ParseWriteMask();
else
Error("No write mask allowed");
}
// Emit register specifier
out = 0x80000000 | (mask << 16) | regNum | DestModifier;
DestModifier = 0;
Emit(out);
ExpectEndOfWord();
return out;
}
sU32 sShaderCompiler::ParseSourceReg()
{
sU32 out = 0;
// complement modifier?
if(ShaderType && ShaderVer < 0x20 && Token == TOK_WORD && TokValue[0] == '1' && !TokValue[1])
{
Scan();
ExpectSubWord("-");
out |= XS_COMP;
}
// negate modifier?
if(Token == '-')
{
if(out & XS_COMP)
Error("Negate and complement modifiers cannot be combined");
out |= XS_NEG;
Scan();
}
out |= ParseRegNum(); // register number
out |= ParseSwizzle(); // swizzle
out |= 0x80000000; // tag bit
// source modifiers
while(Token != TOK_EOW)
{
const sChar **list;
if(ShaderType)
list = (ShaderVer >= 0x20) ? emptylist : ps1srcmodifier;
else
list = emptylist;
sInt mod = MatchStringList(list);
if(mod != -1)
{
if(out & XS_COMP)
Error("Complement modifier cannot be combined with other modifiers");
out |= mod << 25;
}
else
Error("Unknown source modifier '%s'",TokValue);
Scan();
}
Emit(out);
ExpectEndOfWord();
if((out & 0x2000) && ShaderVer >= 0x20) // for relative addressing, need to emit addr. reg now
{
Emit(AddrReg | 0x80000000);
AddrCount++;
}
return out;
}
sInt sShaderCompiler::ParseIndexReg()
{
sInt num = DecodeInt(TokValue+1);
if(TokValue[0] == 'i' && num >= 0)
{
Scan();
return num;
}
else
return -1;
}
void sShaderCompiler::ParseRange(sInt &start,sInt &end)
{
if(Token == TOK_WORD)
{
start = DecodeInt(TokValue);
Scan();
if(start >= 0 && (Token == TOK_EOW || Token == TOK_DOTDOT))
{
end = start;
if(Token == TOK_DOTDOT)
{
Scan();
end = DecodeInt(TokValue);
Scan();
if(end < start || Token != TOK_EOW)
Error("Illegal range");
}
}
else
Error("Single number or range expected");
}
else
Error("Single number or range expected");
WordScan();
}
sU32 sShaderCompiler::ParseIfCondition()
{
sBool negate = sFALSE;
sU32 code;
// remove all negate prefixes
while(Token == '!')
{
negate = !negate;
WordScan();
}
// now, there should either be a flag identifier or an index register specification
if(Token == TOK_WORD)
{
sInt indexNum = ParseIndexReg();
if(indexNum >= 0) // index register
{
if(indexNum >= 15)
Error("Only 15 index registers supported");
code = 0x1e0000e0 | (((indexNum >> 2) + 496) << 16) | ((indexNum & 3) << 3);
WordScan();
}
else
{
code = ParseFlagIdentifier(sTRUE);
if(code != ~0U)
{
// now, the bitfield specifier
if(!ExpectWord("["))
return 0;
sInt bitStart,bitEnd;
ParseRange(bitStart,bitEnd);
if(bitEnd > 31)
Error("Invalid bitfield end point! (Max 31)");
else if(bitEnd - bitStart >= 8)
Error("Invalid bitfield width! (Max 8 bit)");
code |= bitStart | ((bitEnd - bitStart) << 5);
if(!ExpectWord("]"))
return 0;
}
else
return 0;
}
}
else
Error("Identifier expected");
// Next, expect either a comparision operator or end of line
if(!*Word || IsNewLine) // end of line
{
negate = !negate; // we get != 0 as test
}
else
{
sInt cond = MatchStringList(compareops);
Scan();
if(cond == -1 || Token != TOK_EOW)
Error("Invalid comparision operator!");
code |= (cond & 3) << 29;
if(cond & 4)
negate = !negate;
WordScan();
sInt value = DecodeInt(Word);
if(value < 0 || value > 255)
Error("'%s': Integer in the range 0-255 expected",Word);
code |= value << 8;
WordScan();
}
if(negate)
code ^= 0x80000000;
Emit(code);
return code;
}
/****************************************************************************/
void sShaderCompiler::ParseVersion()
{
ErrorLineNum = Line;
if(sCmpString(TokValue,"vs") && sCmpString(TokValue,"ps"))
Error("Shader must start with version directive");
else
{
ShaderType = TokValue[0] == 'p';
ShaderVer = 0;
Scan();
sInt MajorVer = TokValue[0] == '.' ? DecodeInt(TokValue+1) : -1;
Scan();
sInt MinorVer = TokValue[0] == '.' ? DecodeInt(TokValue+1) : -1;
Scan();
if(MajorVer == -1 || MinorVer == -1)
Error("Invalid syntax for version directive");
else
ShaderVer = (MajorVer << 4) | MinorVer;
if(ShaderVer != 0x20 && ShaderVer != 0x11)
Error("Sorry, only 2.0 shaders and 1.1 vertex shaders supported at the moment");
Emit(((0xfffe + ShaderType) << 16) | (MajorVer << 8) | MinorVer);
ExpectEndOfWordAndLine();
}
}
void sShaderCompiler::ParseDcl()
{
VerifyToken(TOK_DCL);
Emit(XO_DCL);
if(ShaderType == 0) // vertex
{
const sChar *rest;
sInt usage = StringInPrefixList(TokValue,vsusagesuffix,rest);
if(usage == -1)
{
Error("Unknown usage type");
return;
}
// determine usage index
sInt index = DecodeInt(rest);
if(index < 0 || index > 15)
Error("Invalid usage index");
Scan();
Emit(0x80000000 | usage | (index << 16));
ExpectEndOfWord();
// determine dest register
sU32 regType = ParseDestReg(sFALSE) & 0xf0001800;
if(regType != X_V)
Error("Destination register needs to be an input register");
}
else if(ShaderType == 1) // pixel
{
if(Token != TOK_EOW) // sampler declaration
{
sInt samplerId = MatchStringList(pssamplersuffix);
Scan();
if(samplerId == -1)
Error("Invalid sampler specification");
else
{
Emit(0x80000000 | (samplerId << 27));
ExpectEndOfWord();
// determine destination sampler register
sU32 regType = ParseDestReg(sFALSE) & 0xf0001800;
if(regType != X_S)
Error("Destination register needs to be a sampler register");
}
}
else // register
{
Emit(XD_REG);
WordScan();
sU32 regType = ParseDestReg(sTRUE) & 0xf0001800;
if(regType != X_V && regType != X_T)
Error("Destination register needs to be a color or texture register");
}
}
}
void sShaderCompiler::ParseDef()
{
VerifyToken(TOK_DEF);
Emit(XO_DEF);
ExpectEndOfWord();
// destination register
sU32 dstreg = ParseDestReg(sFALSE);
if((dstreg & 0xf0000000) != X_C)
Error("Destination register needs to be a constant register for def");
// parameters
for(sInt i=0;i<4;i++)
{
ExpectWord(",");
sF32 value = ParseFloat();
Emit(*(sU32 *) &value);
}
}
void sShaderCompiler::ParseTemp()
{
VerifyToken(TOK_TEMP);
ExpectEndOfWord();
sBool first = sTRUE;
// read variable definitions till end of line
while(!IsNewLine)
{
if(ErrorLine)
return;
if(!first)
ExpectWord(",");
if(Token == TOK_WORD)
AddTempRegister(TokValue);
else
Error("Invalid name for temporary: '%s'",Word);
Scan();
if(Token != TOK_EOW)
Error("Invalid name for temporary: '%s'",Word);
WordScan();
first = sFALSE;
}
}
void sShaderCompiler::ParseAlias()
{
VerifyToken(TOK_ALIAS);
ExpectEndOfWord();
sBool first = sTRUE;
// read alias definitions till end of line
while(!IsNewLine)
{
if(ErrorLine)
return;
if(!first)
ExpectWord(",");
if(Token == TOK_WORD)
{
sChar aliasName[MAXTOKEN+1];
sCopyString(aliasName,TokValue,sizeof(aliasName));
Scan();
if(Token != TOK_EOW)
Error("Invalid name for alias: '%s'",Word);
WordScan();
ExpectWord("=");
if(Token == TOK_WORD)
{
sU32 srcReg = ParseRegNum();
if(srcReg != ~0U)
Identifiers->AddReg(aliasName,srcReg);
Scan();
if(Token != TOK_EOW)
Error("Invalid name for alias target. '%s'",Word);
}
else
Error("Aliases must be of the form newreg = oldreg");
}
else
Error("Aliases must be of the form newreg = oldreg");
WordScan();
first = sFALSE;
}
}
void sShaderCompiler::ParseFor()
{
VerifyToken(TOK_FOR);
ExpectEndOfWord();
sInt indexNum = DecodeInt(Word+1);
if(Word[0] == 'i' && indexNum >= 0)
{
if(indexNum >= 15)
Error("Only 15 index registers supported");
WordScan();
ExpectWord("=");
sInt start,end;
ParseRange(start,end);
if(end > 254)
Error("For end point must be <=254.");
Emit(XO_EXT_IADD);
Emit(0xf000 | (indexNum << 8) | start);
Emit(XO_EXT_WHILE);
Emit(0xde0000e0 | (((indexNum >> 2) + 496) << 16) | (end << 8)
| ((indexNum & 3) << 3));
PushControl(CS_FOR,indexNum);
}
else
Error("Index register expected");
}
static sBool HasReplicateSwizzle(sU32 reg)
{
reg &= 0x00ff0000; // use swizzle only
return reg == XS_X || reg == XS_Y || reg == XS_Z || reg == XS_W;
}
void sShaderCompiler::ParseArith()
{
const ShaderInstr *instr;
sU32 opModifier = 0;
AddrCount = 0; // need to reset this
if(Token == '+' && ShaderType && ShaderVer < 0x20) // coissue
{
opModifier |= XO_CO;
Scan();
}
for(instr=ShaderInstrs;instr->name;instr++)
{
if(sCmpString(TokValue,instr->name) == 0)
break;
}
if(!instr->name) // instruction not recognized
{
Error("Instruction expected");
return;
}
// version check, emit instr
if((ShaderType == 0 && instr->vsver > ShaderVer)
|| (ShaderType == 1 && instr->psver > ShaderVer))
Error("Instruction '%s' not supported in this shader version!",instr->name);
sInt instrOffs = Output.Count;
Emit(instr->opcode | opModifier);
Scan();
// parse (optional) instruction modifiers
while(Token != TOK_EOW)
{
const sChar **list;
if(ShaderType)
list = (ShaderVer >= 0x20) ? ps2instmodifier : ps1instmodifier;
else
list = vs2instmodifier;
sInt mod = MatchStringList(list);
if(mod != -1)
{
sU32 modmask = 1 << (mod + 20);
if(DestModifier & modmask)
Error("Instruction modifier '%s' already specified",TokValue);
else
DestModifier |= modmask;
}
else if(ShaderType && ShaderVer < 0x20)
{
sInt shift = MatchStringList(ps1instshift);
if(shift != -1)
{
if(DestModifier & 0x0f000000)
Error("A shift modifier has already been specified");
else
DestModifier |= shift << 24;
}
else
Error("Unknown instruction modifier '%s'",TokValue);
}
else
Error("Unknown instruction modifier '%s'",TokValue);
Scan();
}
WordScan();
// destination register
sU32 dstreg = ParseDestReg(sTRUE);
if((instr->flags & SIF_DESTMUSTTEMP) && (dstreg & 0x70000000) != 0)
Error("Destination register must be a temporary register for '%s'",instr->name);
if((instr->flags & SIF_DESTNOW) && (dstreg & 0x80000))
Error("Destination register may not include .w in write mask for '%s'",instr->name);
if((instr->flags & SIF_NOWRITEMASK) && (dstreg & 0xf0000) != 0xf0000)
Error("Destination register needs to have .xyzw write mask for '%s'",instr->name);
if((instr->flags & SIF_WRITEXY) && (dstreg & 0xf0000) != 0x30000)
Error("Destination register needs to have .xy write mask for '%s'",instr->name);
if((instr->flags & SIF_WRITEXYZ) && (dstreg & 0xf0000) != 0x70000)
Error("Destination register needs to have .xy write mask for '%s'",instr->name);
if((instr->flags & SIF_DESTMUSTA0) && (dstreg & 0xf0001fff) != X_T)
Error("Destination register needs to be a0 for '%s'",instr->name);
sU32 srcreg = 0;
// source registers
for(sInt i=0;i<instr->params;i++)
{
ExpectWord(",");
sU32 oldsrcreg = srcreg;
srcreg = ParseSourceReg();
if((instr->flags & SIF_NOSWIZZLE) && (srcreg & 0x00ff0000) != XS_XYZW)
Error("'%s' doesn't allow source swizzle",instr->name);
if((instr->flags & SIF_NOSRCDESTEQUAL) && (srcreg & 0x70001fff) == (dstreg & 0x70001fff) && ShaderVer < 0x20)
Error("'%s' requires source registers to be different from dest registers",instr->name);
if((instr->flags & SIF_REPLICATE) && !HasReplicateSwizzle(srcreg))
Error("'%s' requires source registers to have replicate swizzle",instr->name);
if(instr->flags & SIF_TEXLD20)
{
sU32 regType = srcreg & 0xf0001800;
if(i == 0 && regType != X_T && regType != X_R)
Error("'%s' requires first source register to be either a temporary or texture register",instr->name);
else if(i == 1 && regType != X_S)
Error("'%s' requires second source register to be a sampler register",instr->name);
}
if(i == 0 && (instr->flags & SIF_SRC23DIFFER) && (oldsrcreg & 0x70001fff) == (srcreg & 0x70001fff))
Error("'%s' requires second and third source register to be different",instr->name);
}
if(instr->flags & SIF_REPLICATELAST && !HasReplicateSwizzle(srcreg))
Error("'%s' requires last parameter to have replicate swizzle",instr->name);
if ((instr->flags & SIF_LASTNOSWIZZLE) && (srcreg & 0x00ff0000) != XS_XYZW)
Error("'%s' doesn't allow last parameter to have swizzle",instr->name);
// If there were address registers used somewhere, need to increment
// opcode size.
if(AddrCount)
Output[instrOffs] += AddrCount<<24;
}
sBool sShaderCompiler::ParseIndexInstr()
{
if(!sCmpString(TokValue,"imov"))
{
Emit(XO_EXT_IADD);
Scan();
ExpectEndOfWord();
sInt dest = ParseIndexReg();
if(dest < 0)
Error("Index register expected");
if(dest >= 15)
Error("Only 15 index registers supported");
WordScan();
ExpectWord(",");
sInt src = ParseIndexReg();
if(src >= 0) // register source?
{
ExpectEndOfWord();
if(src >= 15)
Error("Only 15 index registers supported");
Emit((dest << 8) | (src << 12));
}
else // assume constant
{
sInt value = DecodeInt(Word);
WordScan();
if(value < 0)
Error("Invalid integer value");
else if(value > 255)
Error("Integer value out of range");
Emit(0xf000 | (dest << 8) | value);
}
}
else if(!sCmpString(TokValue,"iadd"))
{
Emit(XO_EXT_IADD);
Scan();
ExpectEndOfWord();
sInt dest = ParseIndexReg();
if(dest < 0)
Error("Index register expected");
if(dest >= 15)
Error("Only 15 index registers supported");
WordScan();
ExpectWord(",");
sInt src = ParseIndexReg();
if(src >= 0) // register source
{
if(src >= 15)
Error("Only 15 index registers supported");
WordScan();
ExpectWord(",");
}
else
src = dest;
sInt value = DecodeInt(Word);
WordScan();
if(value < 0)
Error("Invalid integer value");
else if(value > 255)
Error("Integer value out of range");
Emit((src << 12) | (dest << 8) | value);
}
else
return sFALSE;
return sTRUE;
}
void sShaderCompiler::ParseVMov()
{
VerifyToken(TOK_VMOV);
ExpectEndOfWord();
Emit(XO_EXT_VMOV);
// destination register
sU32 dstreg = ParseDestReg(sTRUE);
if(dstreg & 0x70000000)
Error("Destination register must be a temporary register for 'vmov'");
else if((dstreg & 0xf0000) != 0xf0000)
Error("Destination register needs to have .xyzw write mask for 'vmov'");
// source operand
ExpectWord(",");
sU32 flag = ParseFlagIdentifier(sFALSE);
if(flag != ~0U) // flag? emit it
Emit(flag);
else // assume it's a register
ParseSourceReg();
}
/****************************************************************************/
void sShaderCompiler::ParseFlags()
{
while(*ScanPtr && Token != TOK_ENDFLAGS)
{
ErrorLine = 0;
ErrorLineNum = Line;
if(Token != TOK_WORD)
Error("Identifier expected!");
else
{
sChar name[MAXTOKEN+1];
sCopyString(name,TokValue,sizeof(name));
Scan();
ExpectEndOfWord();
if(!sCmpString(Word,"[")) // array declaration?
{
ExpectWord("[");
sInt dim = DecodeInt(Word);
WordScan();
if(dim == -1)
Error("Array dimension expected!");
else if(dim == 0)
Error("Invalid array dimension!");
AddFlag(name,dim);
ExpectWord("]");
}
else
AddFlag(name,0);
}
if(!ErrorLine)
ExpectEndOfLine();
else
{
ErrorLine = 0;
SkipRestOfLine();
}
}
if(!*ScanPtr)
Error("'flags' without terminating 'endflags'");
else
{
WordScan();
ExpectEndOfLine();
}
}
void sShaderCompiler::ParseCode()
{
while(*ScanPtr)
{
ErrorLine = 0;
ErrorLineNum = Line;
switch(Token)
{
case TOK_DCL:
ParseDcl();
break;
case TOK_DEF:
ParseDef();
break;
case TOK_NOP:
VerifyToken(TOK_NOP);
Emit(XO_NOP);
ExpectEndOfWordAndLine();
break;
case TOK_TEXKILL:
{
VerifyToken(TOK_TEXKILL);
if(ShaderType != 1)
Error("Instruction 'texkill' not supported in this shader version!");
Emit(XO_TEXKILL);
ExpectEndOfWord();
sU32 regType = ParseSourceReg() & 0xf0001800;
if(regType != X_R && regType != X_T)
Error("'texkill' requires source register to be either a temporary or texture register");
}
break;
case TOK_TEMP:
ParseTemp();
break;
case TOK_ALIAS:
ParseAlias();
break;
case TOK_IF:
VerifyToken(TOK_IF);
ExpectEndOfWord();
Emit(XO_EXT_IF);
ParseIfCondition();
PushControl(CS_IF,0);
break;
case TOK_ELSE:
VerifyToken(TOK_ELSE);
ExpectEndOfWord();
Emit(XO_EXT_ELSE);
ElseControl(sTRUE);
break;
case TOK_ELIF:
VerifyToken(TOK_ELIF);
ExpectEndOfWord();
Emit(XO_EXT_ELIF);
ParseIfCondition();
ElseControl(sFALSE);
break;
case TOK_ENDIF:
VerifyToken(TOK_ENDIF);
ExpectEndOfWord();
Emit(XO_EXT_END);
PopControl(CS_IF);
break;
case TOK_FOR:
ParseFor();
break;
case TOK_ENDFOR:
VerifyToken(TOK_ENDFOR);
ExpectEndOfWord();
Emit(XO_EXT_IADD);
Emit((TopControl()->Param << 8) | 0x01);
Emit(XO_EXT_END);
PopControl(CS_FOR);
break;
case TOK_WHILE:
VerifyToken(TOK_WHILE);
ExpectEndOfWord();
Emit(XO_EXT_WHILE);
ParseIfCondition();
PushControl(CS_WHILE,0);
break;
case TOK_ENDWHILE:
VerifyToken(TOK_ENDWHILE);
ExpectEndOfWord();
Emit(XO_EXT_END);
PopControl(CS_WHILE);
break;
case TOK_FLAGS:
VerifyToken(TOK_FLAGS);
ExpectEndOfWord();
ExpectEndOfLine();
ParseFlags();
break;
case TOK_ERROR:
VerifyToken(TOK_ERROR);
ExpectEndOfWord();
Emit(XO_EXT_ERROR);
break;
case TOK_VMOV:
ParseVMov();
break;
default:
if(!ParseIndexInstr())
ParseArith();
break;
}
if(!ErrorLine)
{
EmitIndexBackpatches();
ExpectEndOfLine();
}
else
{
ErrorLine = 0;
SkipRestOfLine();
}
}
}
/****************************************************************************/
sShaderCompiler::sShaderCompiler()
{
Identifiers = 0;
Flags = 0;
ControlStack.Init();
IndexedPatches.Init();
Output.Init();
}
sShaderCompiler::~sShaderCompiler()
{
ControlStack.Exit();
IndexedPatches.Exit();
Output.Exit();
}
sBool sShaderCompiler::Compile(const sChar *source,const sChar *fileName,sText *errors)
{
if(!source)
source = "";
Line = 1;
ErrorCount = 0;
ErrorLine = 0;
ScanPtr = source;
Identifiers = new SymbolTable(this);
Flags = new SymbolTable(this);
ControlStack.Count = 0;
Output.Count = 0;
IndexedPatches.Count = 0;
FileName = fileName;
Errors = errors;
Errors->Clear();
DestModifier = 0;
NumTempRegs = 0;
NumFlags = 0;
WordScan();
ParseVersion();
AddPredefinedSymbols();
ParseCode();
if(ControlStack.Count)
Error("Not all control structures closed!");
Emit(XO_END);
if(ErrorCount != 0)
{
Errors->PrintF("\n%d error(s)\n",ErrorCount);
Output[0] = XO_END;
Output.Count = 1;
}
delete Identifiers;
delete Flags;
FileName = 0;
Errors = 0;
return ErrorCount == 0;
}
const sU32 *sShaderCompiler::GetShader() const
{
return &Output[0];
}
sU32 *sShaderCompiler::GetShaderCopy() const
{
sU32 *shaderCopy = new sU32[Output.Count];
sCopyMem4(shaderCopy,&Output[0],Output.Count);
return shaderCopy;
}
sU32 sShaderCompiler::GetShaderSize() const
{
return Output.Count;
}
/****************************************************************************/
Jump to Line
Something went wrong with that request. Please try again.