| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770 |
- /**
- * MojoShader; generate shader programs from bytecode of compiled
- * Direct3D shaders.
- *
- * Please see the file LICENSE.txt in the source's root directory.
- *
- * This file written by Ryan C. Gordon.
- */
- // !!! FIXME: this should probably use a formal grammar and not a hand-written
- // !!! FIXME: pile of C code.
- #define __MOJOSHADER_INTERNAL__ 1
- #include "mojoshader_internal.h"
- #if !SUPPORT_PROFILE_BYTECODE
- #error Shader assembler needs bytecode profile. Fix your build.
- #endif
- #if DEBUG_ASSEMBLER_PARSER
- #define print_debug_token(token, len, val) \
- MOJOSHADER_print_debug_token("ASSEMBLER", token, len, val)
- #else
- #define print_debug_token(token, len, val)
- #endif
- typedef struct SourcePos
- {
- const char *filename;
- uint32 line;
- } SourcePos;
- // Context...this is state that changes as we assemble a shader...
- typedef struct Context
- {
- int isfail;
- int out_of_memory;
- MOJOSHADER_malloc malloc;
- MOJOSHADER_free free;
- void *malloc_data;
- const char *current_file;
- int current_position;
- ErrorList *errors;
- Preprocessor *preprocessor;
- MOJOSHADER_shaderType shader_type;
- uint8 major_ver;
- uint8 minor_ver;
- int pushedback;
- const char *token; // assembler token!
- unsigned int tokenlen; // assembler token!
- Token tokenval; // assembler token!
- uint32 version_token; // bytecode token!
- uint32 tokenbuf[16]; // bytecode tokens!
- int tokenbufpos; // bytecode tokens!
- DestArgInfo dest_arg;
- Buffer *output;
- Buffer *token_to_source;
- Buffer *ctab;
- } Context;
- // !!! FIXME: cut and paste between every damned source file follows...
- // !!! FIXME: We need to make some sort of ContextBase that applies to all
- // !!! FIXME: files and move this stuff to mojoshader_common.c ...
- // Convenience functions for allocators...
- static inline void out_of_memory(Context *ctx)
- {
- ctx->isfail = ctx->out_of_memory = 1;
- } // out_of_memory
- static inline void *Malloc(Context *ctx, const size_t len)
- {
- void *retval = ctx->malloc((int) len, ctx->malloc_data);
- if (retval == NULL)
- out_of_memory(ctx);
- return retval;
- } // Malloc
- static inline char *StrDup(Context *ctx, const char *str)
- {
- char *retval = (char *) Malloc(ctx, strlen(str) + 1);
- if (retval != NULL)
- strcpy(retval, str);
- return retval;
- } // StrDup
- static inline void Free(Context *ctx, void *ptr)
- {
- ctx->free(ptr, ctx->malloc_data);
- } // Free
- static void *MallocBridge(int bytes, void *data)
- {
- return Malloc((Context *) data, (size_t) bytes);
- } // MallocBridge
- static void FreeBridge(void *ptr, void *data)
- {
- Free((Context *) data, ptr);
- } // FreeBridge
- static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
- static void failf(Context *ctx, const char *fmt, ...)
- {
- ctx->isfail = 1;
- if (ctx->out_of_memory)
- return;
- va_list ap;
- va_start(ap, fmt);
- errorlist_add_va(ctx->errors, ctx->current_file, ctx->current_position, fmt, ap);
- va_end(ap);
- } // failf
- static inline void fail(Context *ctx, const char *reason)
- {
- failf(ctx, "%s", reason);
- } // fail
- static inline int isfail(const Context *ctx)
- {
- return ctx->isfail;
- } // isfail
- // Shader model version magic...
- static inline uint32 ver_ui32(const uint8 major, const uint8 minor)
- {
- return ( (((uint32) major) << 16) | (((minor) == 0xFF) ? 0 : (minor)) );
- } // version_ui32
- static inline int shader_version_atleast(const Context *ctx, const uint8 maj,
- const uint8 min)
- {
- return (ver_ui32(ctx->major_ver, ctx->minor_ver) >= ver_ui32(maj, min));
- } // shader_version_atleast
- static inline int shader_is_pixel(const Context *ctx)
- {
- return (ctx->shader_type == MOJOSHADER_TYPE_PIXEL);
- } // shader_is_pixel
- static inline int shader_is_vertex(const Context *ctx)
- {
- return (ctx->shader_type == MOJOSHADER_TYPE_VERTEX);
- } // shader_is_vertex
- static inline void pushback(Context *ctx)
- {
- #if DEBUG_ASSEMBLER_PARSER
- printf("ASSEMBLER PUSHBACK\n");
- #endif
- assert(!ctx->pushedback);
- ctx->pushedback = 1;
- } // pushback
- static Token nexttoken(Context *ctx)
- {
- if (ctx->pushedback)
- ctx->pushedback = 0;
- else
- {
- while (1)
- {
- ctx->token = preprocessor_nexttoken(ctx->preprocessor,
- &ctx->tokenlen,
- &ctx->tokenval);
- if (preprocessor_outofmemory(ctx->preprocessor))
- {
- ctx->tokenval = TOKEN_EOI;
- ctx->token = NULL;
- ctx->tokenlen = 0;
- break;
- } // if
- unsigned int line;
- ctx->current_file = preprocessor_sourcepos(ctx->preprocessor,&line);
- ctx->current_position = (int) line;
- if (ctx->tokenval == TOKEN_BAD_CHARS)
- {
- fail(ctx, "Bad characters in source file");
- continue;
- } // else if
- else if (ctx->tokenval == TOKEN_PREPROCESSING_ERROR)
- {
- fail(ctx, ctx->token);
- continue;
- } // else if
- break;
- } // while
- } // else
- print_debug_token(ctx->token, ctx->tokenlen, ctx->tokenval);
- return ctx->tokenval;
- } // nexttoken
- static void output_token_noswap(Context *ctx, const uint32 token)
- {
- if (!isfail(ctx))
- {
- buffer_append(ctx->output, &token, sizeof (token));
- // We only need a list of these that grows throughout processing, and
- // is flattened for reference at the end of the run, so we use a
- // Buffer. It's sneaky!
- unsigned int pos = 0;
- const char *fname = preprocessor_sourcepos(ctx->preprocessor, &pos);
- SourcePos srcpos;
- memset(&srcpos, '\0', sizeof (SourcePos));
- srcpos.line = pos;
- srcpos.filename = fname; // cached in preprocessor!
- buffer_append(ctx->token_to_source, &srcpos, sizeof (SourcePos));
- } // if
- } // output_token_noswap
- static inline void output_token(Context *ctx, const uint32 token)
- {
- output_token_noswap(ctx, SWAP32(token));
- } // output_token
- static void output_comment_bytes(Context *ctx, const uint8 *buf, size_t len)
- {
- if (len > (0xFFFF * 4)) // length is stored as token count, in 16 bits.
- fail(ctx, "Comment field is too big");
- else if (!isfail(ctx))
- {
- const uint32 tokencount = (len / 4) + ((len % 4) ? 1 : 0);
- output_token(ctx, 0xFFFE | (tokencount << 16));
- while (len >= 4)
- {
- output_token_noswap(ctx, *((const uint32 *) buf));
- len -= 4;
- buf += 4;
- } // while
- if (len > 0) // handle spillover...
- {
- union { uint8 ui8[4]; uint32 ui32; } overflow;
- overflow.ui32 = 0;
- memcpy(overflow.ui8, buf, len);
- output_token_noswap(ctx, overflow.ui32);
- } // if
- } // else if
- } // output_comment_bytes
- static inline void output_comment_string(Context *ctx, const char *str)
- {
- output_comment_bytes(ctx, (const uint8 *) str, strlen(str));
- } // output_comment_string
- static int require_comma(Context *ctx)
- {
- const Token token = nexttoken(ctx);
- if (token != ((Token) ','))
- {
- fail(ctx, "Comma expected");
- return 0;
- } // if
- return 1;
- } // require_comma
- static int check_token_segment(Context *ctx, const char *str)
- {
- // !!! FIXME: these are case-insensitive, right?
- const size_t len = strlen(str);
- if ( (ctx->tokenlen < len) || (strncasecmp(ctx->token, str, len) != 0) )
- return 0;
- ctx->token += len;
- ctx->tokenlen -= len;
- return 1;
- } // check_token_segment
- static int check_token(Context *ctx, const char *str)
- {
- const size_t len = strlen(str);
- if ( (ctx->tokenlen != len) || (strncasecmp(ctx->token, str, len) != 0) )
- return 0;
- ctx->token += len;
- ctx->tokenlen = 0;
- return 1;
- } // check_token
- static int ui32fromtoken(Context *ctx, uint32 *_val)
- {
- unsigned int i;
- for (i = 0; i < ctx->tokenlen; i++)
- {
- if ((ctx->token[i] < '0') || (ctx->token[i] > '9'))
- break;
- } // for
- if (i == 0)
- {
- *_val = 0;
- return 0;
- } // if
- const unsigned int len = i;
- uint32 val = 0;
- uint32 mult = 1;
- while (i--)
- {
- val += ((uint32) (ctx->token[i] - '0')) * mult;
- mult *= 10;
- } // while
- ctx->token += len;
- ctx->tokenlen -= len;
- *_val = val;
- return 1;
- } // ui32fromtoken
- static int parse_register_name(Context *ctx, RegisterType *rtype, int *rnum)
- {
- if (nexttoken(ctx) != TOKEN_IDENTIFIER)
- {
- fail(ctx, "Expected register");
- return 0;
- } // if
- int neednum = 1;
- int regnum = 0;
- RegisterType regtype = REG_TYPE_TEMP;
- // Watch out for substrings! oDepth must be checked before oD, since
- // the latter will match either case.
- if (check_token_segment(ctx, "oDepth"))
- {
- regtype = REG_TYPE_DEPTHOUT;
- neednum = 0;
- } // else if
- else if (check_token_segment(ctx, "vFace"))
- {
- regtype = REG_TYPE_MISCTYPE;
- regnum = (int) MISCTYPE_TYPE_FACE;
- neednum = 0;
- } // else if
- else if (check_token_segment(ctx, "vPos"))
- {
- regtype = REG_TYPE_MISCTYPE;
- regnum = (int) MISCTYPE_TYPE_POSITION;
- neednum = 0;
- } // else if
- else if (check_token_segment(ctx, "oPos"))
- {
- regtype = REG_TYPE_RASTOUT;
- regnum = (int) RASTOUT_TYPE_POSITION;
- neednum = 0;
- } // else if
- else if (check_token_segment(ctx, "oFog"))
- {
- regtype = REG_TYPE_RASTOUT;
- regnum = (int) RASTOUT_TYPE_FOG;
- neednum = 0;
- } // else if
- else if (check_token_segment(ctx, "oPts"))
- {
- regtype = REG_TYPE_RASTOUT;
- regnum = (int) RASTOUT_TYPE_POINT_SIZE;
- neednum = 0;
- } // else if
- else if (check_token_segment(ctx, "aL"))
- {
- regtype = REG_TYPE_LOOP;
- neednum = 0;
- } // else if
- else if (check_token_segment(ctx, "oC"))
- regtype = REG_TYPE_COLOROUT;
- else if (check_token_segment(ctx, "oT"))
- regtype = REG_TYPE_OUTPUT;
- else if (check_token_segment(ctx, "oD"))
- regtype = REG_TYPE_ATTROUT;
- else if (check_token_segment(ctx, "r"))
- regtype = REG_TYPE_TEMP;
- else if (check_token_segment(ctx, "v"))
- regtype = REG_TYPE_INPUT;
- else if (check_token_segment(ctx, "c"))
- regtype = REG_TYPE_CONST;
- else if (check_token_segment(ctx, "i"))
- regtype = REG_TYPE_CONSTINT;
- else if (check_token_segment(ctx, "b"))
- regtype = REG_TYPE_CONSTBOOL;
- else if (check_token_segment(ctx, "s"))
- regtype = REG_TYPE_SAMPLER;
- else if (check_token_segment(ctx, "l"))
- regtype = REG_TYPE_LABEL;
- else if (check_token_segment(ctx, "p"))
- regtype = REG_TYPE_PREDICATE;
- else if (check_token_segment(ctx, "o"))
- regtype = REG_TYPE_OUTPUT;
- else if (check_token_segment(ctx, "a"))
- regtype = REG_TYPE_ADDRESS;
- else if (check_token_segment(ctx, "t"))
- regtype = REG_TYPE_ADDRESS;
-
- //case REG_TYPE_TEMPFLOAT16: // !!! FIXME: don't know this asm string
- else
- {
- fail(ctx, "expected register type");
- regtype = REG_TYPE_CONST;
- regnum = 0;
- neednum = 0;
- } // else
- // "c[5]" is the same as "c5", so if the token is done, see if next is '['.
- if ((neednum) && (ctx->tokenlen == 0))
- {
- const int tlen = ctx->tokenlen; // we need to protect this for later.
- if (nexttoken(ctx) == ((Token) '['))
- neednum = 0; // don't need a number on register name itself.
- pushback(ctx);
- ctx->tokenlen = tlen;
- } // if
- if (neednum)
- {
- uint32 ui32 = 0;
- if (!ui32fromtoken(ctx, &ui32))
- fail(ctx, "Invalid register index");
- regnum = (int) ui32;
- } // if
- // split up REG_TYPE_CONST
- if (regtype == REG_TYPE_CONST)
- {
- if (regnum < 2048)
- {
- regtype = REG_TYPE_CONST;
- regnum -= 0;
- } // if
- else if (regnum < 4096)
- {
- regtype = REG_TYPE_CONST2;
- regnum -= 2048;
- } // if
- else if (regnum < 6144)
- {
- regtype = REG_TYPE_CONST3;
- regnum -= 4096;
- } // if
- else if (regnum < 8192)
- {
- regtype = REG_TYPE_CONST4;
- regnum -= 6144;
- } // if
- else
- {
- fail(ctx, "Invalid const register index");
- } // else
- } // if
- *rtype = regtype;
- *rnum = regnum;
- return 1;
- } // parse_register_name
- static void set_result_shift(Context *ctx, DestArgInfo *info, const int val)
- {
- if (info->result_shift != 0)
- fail(ctx, "Multiple result shift modifiers");
- info->result_shift = val;
- } // set_result_shift
- static inline int tokenbuf_overflow(Context *ctx)
- {
- if ( ctx->tokenbufpos >= ((int) (STATICARRAYLEN(ctx->tokenbuf))) )
- {
- fail(ctx, "Too many tokens");
- return 1;
- } // if
- return 0;
- } // tokenbuf_overflow
- static int parse_destination_token(Context *ctx)
- {
- DestArgInfo *info = &ctx->dest_arg;
- memset(info, '\0', sizeof (DestArgInfo));
- // parse_instruction_token() sets ctx->token to the end of the instruction
- // so we can see if there are destination modifiers on the instruction
- // itself...
- int invalid_modifier = 0;
- while ((ctx->tokenlen > 0) && (!invalid_modifier))
- {
- if (check_token_segment(ctx, "_x2"))
- set_result_shift(ctx, info, 0x1);
- else if (check_token_segment(ctx, "_x4"))
- set_result_shift(ctx, info, 0x2);
- else if (check_token_segment(ctx, "_x8"))
- set_result_shift(ctx, info, 0x3);
- else if (check_token_segment(ctx, "_d8"))
- set_result_shift(ctx, info, 0xD);
- else if (check_token_segment(ctx, "_d4"))
- set_result_shift(ctx, info, 0xE);
- else if (check_token_segment(ctx, "_d2"))
- set_result_shift(ctx, info, 0xF);
- else if (check_token_segment(ctx, "_sat"))
- info->result_mod |= MOD_SATURATE;
- else if (check_token_segment(ctx, "_pp"))
- info->result_mod |= MOD_PP;
- else if (check_token_segment(ctx, "_centroid"))
- info->result_mod |= MOD_CENTROID;
- else
- invalid_modifier = 1;
- } // while
- if (invalid_modifier)
- fail(ctx, "Invalid destination modifier");
- // !!! FIXME: predicates.
- if (nexttoken(ctx) == ((Token) '('))
- fail(ctx, "Predicates unsupported at this time"); // !!! FIXME: ...
- pushback(ctx); // parse_register_name calls nexttoken().
- parse_register_name(ctx, &info->regtype, &info->regnum);
- // parse_register_name() can't check this: dest regs might have modifiers.
- if (ctx->tokenlen > 0)
- fail(ctx, "invalid register name");
- // !!! FIXME: can dest registers do relative addressing?
- int invalid_writemask = 0;
- int implicit_writemask = 0;
- if (nexttoken(ctx) != ((Token) '.'))
- {
- implicit_writemask = 1;
- info->writemask = 0xF;
- info->writemask0 = info->writemask1 = info->writemask2 = info->writemask3 = 1;
- pushback(ctx); // no explicit writemask; do full mask.
- } // if
- // !!! FIXME: Cg generates code with oDepth.z ... this is a bug, I think.
- //else if (scalar_register(ctx->shader_type, info->regtype, info->regnum))
- else if ( (scalar_register(ctx->shader_type, info->regtype, info->regnum)) && (info->regtype != REG_TYPE_DEPTHOUT) )
- fail(ctx, "Writemask specified for scalar register");
- else if (nexttoken(ctx) != TOKEN_IDENTIFIER)
- invalid_writemask = 1;
- else
- {
- char tokenbytes[5] = { '\0', '\0', '\0', '\0', '\0' };
- const unsigned int tokenlen = ctx->tokenlen;
- memcpy(tokenbytes, ctx->token, ((tokenlen < 4) ? tokenlen : 4));
- char *ptr = tokenbytes;
- if ((*ptr == 'r') || (*ptr == 'x')) { info->writemask0 = 1; ptr++; }
- if ((*ptr == 'g') || (*ptr == 'y')) { info->writemask1 = 1; ptr++; }
- if ((*ptr == 'b') || (*ptr == 'z')) { info->writemask2 = 1; ptr++; }
- if ((*ptr == 'a') || (*ptr == 'w')) { info->writemask3 = 1; ptr++; }
- if (*ptr != '\0')
- invalid_writemask = 1;
- info->writemask = ( ((info->writemask0 & 0x1) << 0) |
- ((info->writemask1 & 0x1) << 1) |
- ((info->writemask2 & 0x1) << 2) |
- ((info->writemask3 & 0x1) << 3) );
- } // else
- if (invalid_writemask)
- fail(ctx, "Invalid writemask");
- // !!! FIXME: Cg generates code with oDepth.z ... this is a bug, I think.
- if (info->regtype == REG_TYPE_DEPTHOUT)
- {
- if ( (!implicit_writemask) && ((info->writemask0 + info->writemask1 +
- info->writemask2 + info->writemask3) > 1) )
- fail(ctx, "Writemask specified for scalar register");
- } // if
- info->orig_writemask = info->writemask;
- if (tokenbuf_overflow(ctx))
- return 1;
- ctx->tokenbuf[ctx->tokenbufpos++] =
- ( ((((uint32) 1)) << 31) |
- ((((uint32) info->regnum) & 0x7ff) << 0) |
- ((((uint32) info->relative) & 0x1) << 13) |
- ((((uint32) info->result_mod) & 0xF) << 20) |
- ((((uint32) info->result_shift) & 0xF) << 24) |
- ((((uint32) info->writemask) & 0xF) << 16) |
- ((((uint32) info->regtype) & 0x7) << 28) |
- ((((uint32) info->regtype) & 0x18) << 8) );
- return 1;
- } // parse_destination_token
- static void set_source_mod(Context *ctx, const int negate,
- const SourceMod norm, const SourceMod negated,
- SourceMod *srcmod)
- {
- if ( (*srcmod != SRCMOD_NONE) || (negate && (negated == SRCMOD_NONE)) )
- fail(ctx, "Incompatible source modifiers");
- else
- *srcmod = ((negate) ? negated : norm);
- } // set_source_mod
- static int parse_source_token_maybe_relative(Context *ctx, const int relok)
- {
- int retval = 1;
- if (tokenbuf_overflow(ctx))
- return 0;
- // mark this now, so optional relative addressing token is placed second.
- uint32 *outtoken = &ctx->tokenbuf[ctx->tokenbufpos++];
- *outtoken = 0;
- SourceMod srcmod = SRCMOD_NONE;
- int negate = 0;
- Token token = nexttoken(ctx);
- if (token == ((Token) '!'))
- srcmod = SRCMOD_NOT;
- else if (token == ((Token) '-'))
- negate = 1;
- else if ( (token == TOKEN_INT_LITERAL) && (check_token(ctx, "1")) )
- {
- if (nexttoken(ctx) != ((Token) '-'))
- fail(ctx, "Unexpected token");
- else
- srcmod = SRCMOD_COMPLEMENT;
- } // else
- else
- {
- pushback(ctx);
- } // else
- RegisterType regtype;
- int regnum;
- parse_register_name(ctx, ®type, ®num);
- if (ctx->tokenlen == 0)
- {
- if (negate)
- set_source_mod(ctx, negate, SRCMOD_NONE, SRCMOD_NEGATE, &srcmod);
- } // if
- else
- {
- assert(ctx->tokenlen > 0);
- if (check_token_segment(ctx, "_bias"))
- set_source_mod(ctx, negate, SRCMOD_BIAS, SRCMOD_BIASNEGATE, &srcmod);
- else if (check_token_segment(ctx, "_bx2"))
- set_source_mod(ctx, negate, SRCMOD_SIGN, SRCMOD_SIGNNEGATE, &srcmod);
- else if (check_token_segment(ctx, "_x2"))
- set_source_mod(ctx, negate, SRCMOD_X2, SRCMOD_X2NEGATE, &srcmod);
- else if (check_token_segment(ctx, "_dz"))
- set_source_mod(ctx, negate, SRCMOD_DZ, SRCMOD_NONE, &srcmod);
- else if (check_token_segment(ctx, "_db"))
- set_source_mod(ctx, negate, SRCMOD_DZ, SRCMOD_NONE, &srcmod);
- else if (check_token_segment(ctx, "_dw"))
- set_source_mod(ctx, negate, SRCMOD_DW, SRCMOD_NONE, &srcmod);
- else if (check_token_segment(ctx, "_da"))
- set_source_mod(ctx, negate, SRCMOD_DW, SRCMOD_NONE, &srcmod);
- else if (check_token_segment(ctx, "_abs"))
- set_source_mod(ctx, negate, SRCMOD_ABS, SRCMOD_ABSNEGATE, &srcmod);
- else
- fail(ctx, "Invalid source modifier");
- } // else
- uint32 relative = 0;
- if (nexttoken(ctx) != ((Token) '['))
- pushback(ctx); // not relative addressing?
- else
- {
- if (!relok)
- fail(ctx, "Relative addressing not permitted here.");
- else
- retval++;
- parse_source_token_maybe_relative(ctx, 0);
- relative = 1;
- if (nexttoken(ctx) != ((Token) '+'))
- pushback(ctx);
- else
- {
- // !!! FIXME: maybe c3[a0.x + 5] is legal and becomes c[a0.x + 8] ?
- if (regnum != 0)
- fail(ctx, "Relative addressing with explicit register number.");
- uint32 ui32 = 0;
- if ( (nexttoken(ctx) != TOKEN_INT_LITERAL) ||
- (!ui32fromtoken(ctx, &ui32)) ||
- (ctx->tokenlen != 0) )
- {
- fail(ctx, "Invalid relative addressing offset");
- } // if
- regnum += (int) ui32;
- } // else
- if (nexttoken(ctx) != ((Token) ']'))
- fail(ctx, "Expected ']'");
- } // else
- int invalid_swizzle = 0;
- uint32 swizzle = 0;
- if (nexttoken(ctx) != ((Token) '.'))
- {
- swizzle = 0xE4; // 0xE4 == 11100100 ... 0 1 2 3. No swizzle.
- pushback(ctx); // no explicit writemask; do full mask.
- } // if
- else if (scalar_register(ctx->shader_type, regtype, regnum))
- fail(ctx, "Swizzle specified for scalar register");
- else if (nexttoken(ctx) != TOKEN_IDENTIFIER)
- invalid_swizzle = 1;
- else
- {
- char tokenbytes[5] = { '\0', '\0', '\0', '\0', '\0' };
- const unsigned int tokenlen = ctx->tokenlen;
- memcpy(tokenbytes, ctx->token, ((tokenlen < 4) ? tokenlen : 4));
- // deal with shortened form (.x = .xxxx, etc).
- if (tokenlen == 1)
- tokenbytes[1] = tokenbytes[2] = tokenbytes[3] = tokenbytes[0];
- else if (tokenlen == 2)
- tokenbytes[2] = tokenbytes[3] = tokenbytes[1];
- else if (tokenlen == 3)
- tokenbytes[3] = tokenbytes[2];
- else if (tokenlen != 4)
- invalid_swizzle = 1;
- tokenbytes[4] = '\0';
- uint32 val = 0;
- int i;
- for (i = 0; i < 4; i++)
- {
- const int component = (int) tokenbytes[i];
- switch (component)
- {
- case 'r': case 'x': val = 0; break;
- case 'g': case 'y': val = 1; break;
- case 'b': case 'z': val = 2; break;
- case 'a': case 'w': val = 3; break;
- default: invalid_swizzle = 1; break;
- } // switch
- swizzle |= (val << (i * 2));
- } // for
- } // else
- if (invalid_swizzle)
- fail(ctx, "Invalid swizzle");
- *outtoken = ( ((((uint32) 1)) << 31) |
- ((((uint32) regnum) & 0x7ff) << 0) |
- ((((uint32) relative) & 0x1) << 13) |
- ((((uint32) swizzle) & 0xFF) << 16) |
- ((((uint32) srcmod) & 0xF) << 24) |
- ((((uint32) regtype) & 0x7) << 28) |
- ((((uint32) regtype) & 0x18) << 8) );
- return retval;
- } // parse_source_token_maybe_relative
- static inline int parse_source_token(Context *ctx)
- {
- return parse_source_token_maybe_relative(ctx, 1);
- } // parse_source_token
- static int parse_args_NULL(Context *ctx)
- {
- return 1;
- } // parse_args_NULL
- static int parse_num(Context *ctx, const int floatok, uint32 *value)
- {
- union { float f; int32 si32; uint32 ui32; } cvt;
- int negative = 0;
- Token token = nexttoken(ctx);
- if (token == ((Token) '-'))
- {
- negative = 1;
- token = nexttoken(ctx);
- } // if
- if (token == TOKEN_INT_LITERAL)
- {
- int d = 0;
- sscanf(ctx->token, "%d", &d);
- if (floatok)
- cvt.f = (float) ((negative) ? -d : d);
- else
- cvt.si32 = (int32) ((negative) ? -d : d);
- } // if
- else if (token == TOKEN_FLOAT_LITERAL)
- {
- if (!floatok)
- {
- fail(ctx, "Expected whole number");
- *value = 0;
- return 0;
- } // if
- sscanf(ctx->token, "%f", &cvt.f);
- if (negative)
- cvt.f = -cvt.f;
- } // if
- else
- {
- fail(ctx, "Expected number");
- *value = 0;
- return 0;
- } // else
- *value = cvt.ui32;
- return 1;
- } // parse_num
- static int parse_args_DEFx(Context *ctx, const int isflt)
- {
- parse_destination_token(ctx);
- require_comma(ctx);
- parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
- require_comma(ctx);
- parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
- require_comma(ctx);
- parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
- require_comma(ctx);
- parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
- return 6;
- } // parse_args_DEFx
- static int parse_args_DEF(Context *ctx)
- {
- return parse_args_DEFx(ctx, 1);
- } // parse_args_DEF
- static int parse_args_DEFI(Context *ctx)
- {
- return parse_args_DEFx(ctx, 0);
- } // parse_args_DEFI
- static int parse_args_DEFB(Context *ctx)
- {
- parse_destination_token(ctx);
- require_comma(ctx);
- // !!! FIXME: do a TOKEN_TRUE and TOKEN_FALSE? Is this case-sensitive?
- const Token token = nexttoken(ctx);
- int bad = 0;
- if (token != TOKEN_IDENTIFIER)
- bad = 1;
- else if (check_token_segment(ctx, "true"))
- ctx->tokenbuf[ctx->tokenbufpos++] = 1;
- else if (check_token_segment(ctx, "false"))
- ctx->tokenbuf[ctx->tokenbufpos++] = 0;
- else
- bad = 1;
- if (ctx->tokenlen != 0)
- bad = 1;
- if (bad)
- fail(ctx, "Expected 'true' or 'false'");
- return 3;
- } // parse_args_DEFB
- static int parse_dcl_usage(Context *ctx, uint32 *val, int *issampler)
- {
- size_t i;
- static const char *samplerusagestrs[] = { "_2d", "_cube", "_volume" };
- static const char *usagestrs[] = {
- "_position", "_blendweight", "_blendindices", "_normal", "_psize",
- "_texcoord", "_tangent", "_binormal", "_tessfactor", "_positiont",
- "_color", "_fog", "_depth", "_sample"
- };
- for (i = 0; i < STATICARRAYLEN(usagestrs); i++)
- {
- if (check_token_segment(ctx, usagestrs[i]))
- {
- *issampler = 0;
- *val = i;
- return 1;
- } // if
- } // for
- for (i = 0; i < STATICARRAYLEN(samplerusagestrs); i++)
- {
- if (check_token_segment(ctx, samplerusagestrs[i]))
- {
- *issampler = 1;
- *val = i + 2;
- return 1;
- } // if
- } // for
- *issampler = 0;
- *val = 0;
- return 0;
- } // parse_dcl_usage
- static int parse_args_DCL(Context *ctx)
- {
- int issampler = 0;
- uint32 usage = 0;
- uint32 index = 0;
- ctx->tokenbufpos++; // save a spot for the usage/index token.
- ctx->tokenbuf[0] = 0;
- // parse_instruction_token() sets ctx->token to the end of the instruction
- // so we can see if there are destination modifiers on the instruction
- // itself...
- if (parse_dcl_usage(ctx, &usage, &issampler))
- {
- if ((ctx->tokenlen > 0) && (*ctx->token != '_'))
- {
- if (!ui32fromtoken(ctx, &index))
- fail(ctx, "Expected usage index");
- } // if
- } // if
- parse_destination_token(ctx);
- const int samplerreg = (ctx->dest_arg.regtype == REG_TYPE_SAMPLER);
- if (issampler != samplerreg)
- fail(ctx, "Invalid usage");
- else if (samplerreg)
- ctx->tokenbuf[0] = (usage << 27) | 0x80000000;
- else
- ctx->tokenbuf[0] = usage | (index << 16) | 0x80000000;
- return 3;
- } // parse_args_DCL
- static int parse_args_D(Context *ctx)
- {
- int retval = 1;
- retval += parse_destination_token(ctx);
- return retval;
- } // parse_args_D
- static int parse_args_S(Context *ctx)
- {
- int retval = 1;
- retval += parse_source_token(ctx);
- return retval;
- } // parse_args_S
- static int parse_args_SS(Context *ctx)
- {
- int retval = 1;
- retval += parse_source_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- return retval;
- } // parse_args_SS
- static int parse_args_DS(Context *ctx)
- {
- int retval = 1;
- retval += parse_destination_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- return retval;
- } // parse_args_DS
- static int parse_args_DSS(Context *ctx)
- {
- int retval = 1;
- retval += parse_destination_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- return retval;
- } // parse_args_DSS
- static int parse_args_DSSS(Context *ctx)
- {
- int retval = 1;
- retval += parse_destination_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- return retval;
- } // parse_args_DSSS
- static int parse_args_DSSSS(Context *ctx)
- {
- int retval = 1;
- retval += parse_destination_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- require_comma(ctx);
- retval += parse_source_token(ctx);
- return retval;
- } // parse_args_DSSSS
- static int parse_args_SINCOS(Context *ctx)
- {
- // this opcode needs extra registers for sm2 and lower.
- if (!shader_version_atleast(ctx, 3, 0))
- return parse_args_DSSS(ctx);
- return parse_args_DS(ctx);
- } // parse_args_SINCOS
- static int parse_args_TEXCRD(Context *ctx)
- {
- // added extra register in ps_1_4.
- if (shader_version_atleast(ctx, 1, 4))
- return parse_args_DS(ctx);
- return parse_args_D(ctx);
- } // parse_args_TEXCRD
- static int parse_args_TEXLD(Context *ctx)
- {
- // different registers in px_1_3, ps_1_4, and ps_2_0!
- if (shader_version_atleast(ctx, 2, 0))
- return parse_args_DSS(ctx);
- else if (shader_version_atleast(ctx, 1, 4))
- return parse_args_DS(ctx);
- return parse_args_D(ctx);
- } // parse_args_TEXLD
- // one args function for each possible sequence of opcode arguments.
- typedef int (*args_function)(Context *ctx);
- // Lookup table for instruction opcodes...
- typedef struct
- {
- const char *opcode_string;
- args_function parse_args;
- } Instruction;
- static const Instruction instructions[] =
- {
- #define INSTRUCTION_STATE(op, opstr, s, a, t) { opstr, parse_args_##a },
- #define INSTRUCTION(op, opstr, slots, a, t) { opstr, parse_args_##a },
- #define MOJOSHADER_DO_INSTRUCTION_TABLE 1
- #include "mojoshader_internal.h"
- #undef MOJOSHADER_DO_INSTRUCTION_TABLE
- #undef INSTRUCTION
- #undef INSTRUCTION_STATE
- };
- static int parse_condition(Context *ctx, uint32 *controls)
- {
- static const char *comps[] = { "_gt", "_eq", "_ge", "_lt", "_ne", "_le" };
- size_t i;
- if (ctx->tokenlen >= 3)
- {
- for (i = 0; i < STATICARRAYLEN(comps); i++)
- {
- if (check_token_segment(ctx, comps[i]))
- {
- *controls = (uint32) (i + 1);
- return 1;
- } // if
- } // for
- } // if
- return 0;
- } // parse_condition
- static int parse_instruction_token(Context *ctx, Token token)
- {
- int coissue = 0;
- int predicated = 0;
- if (token == ((Token) '+'))
- {
- coissue = 1;
- token = nexttoken(ctx);
- } // if
- if (token != TOKEN_IDENTIFIER)
- {
- fail(ctx, "Expected instruction");
- return 0;
- } // if
- uint32 controls = 0;
- uint32 opcode = OPCODE_TEXLD;
- const char *origtoken = ctx->token;
- const unsigned int origtokenlen = ctx->tokenlen;
- // This might need to be TEXLD instead of TEXLDP.
- if (check_token_segment(ctx, "TEXLDP"))
- controls = CONTROL_TEXLDP;
- // This might need to be TEXLD instead of TEXLDB.
- else if (check_token_segment(ctx, "TEXLDB"))
- controls = CONTROL_TEXLDB;
- else // find the instruction.
- {
- size_t i;
- for (i = 0; i < STATICARRAYLEN(instructions); i++)
- {
- const char *opcode_string = instructions[i].opcode_string;
- if (opcode_string == NULL)
- continue; // skip this.
- else if (!check_token_segment(ctx, opcode_string))
- continue; // not us.
- else if ((ctx->tokenlen > 0) && (*ctx->token != '_'))
- {
- ctx->token = origtoken;
- ctx->tokenlen = origtokenlen;
- continue; // not the match: TEXLD when we wanted TEXLDL, etc.
- } // if
- break; // found it!
- } // for
- opcode = (uint32) i;
- // This might need to be IFC instead of IF.
- if (opcode == OPCODE_IF)
- {
- if (parse_condition(ctx, &controls))
- opcode = OPCODE_IFC;
- } // if
- // This might need to be BREAKC instead of BREAK.
- else if (opcode == OPCODE_BREAK)
- {
- if (parse_condition(ctx, &controls))
- opcode = OPCODE_BREAKC;
- } // else if
- // SETP has a conditional code, always.
- else if (opcode == OPCODE_SETP)
- {
- if (!parse_condition(ctx, &controls))
- fail(ctx, "SETP requires a condition");
- } // else if
- } // else
- if ( (opcode == STATICARRAYLEN(instructions)) ||
- ((ctx->tokenlen > 0) && (ctx->token[0] != '_')) )
- {
- char opstr[32];
- const int len = Min(sizeof (opstr) - 1, origtokenlen);
- memcpy(opstr, origtoken, len);
- opstr[len] = '\0';
- failf(ctx, "Unknown instruction '%s'", opstr);
- return 0;
- } // if
- const Instruction *instruction = &instructions[opcode];
- // !!! FIXME: predicated instructions
- ctx->tokenbufpos = 0;
- const int tokcount = instruction->parse_args(ctx);
- // insttoks bits are reserved and should be zero if < SM2.
- const uint32 insttoks = shader_version_atleast(ctx, 2, 0) ? tokcount-1 : 0;
- // write out the instruction token.
- output_token(ctx, ((opcode & 0xFFFF) << 0) |
- ((controls & 0xFF) << 16) |
- ((insttoks & 0xF) << 24) |
- ((coissue) ? 0x40000000 : 0x00000000) |
- ((predicated) ? 0x10000000 : 0x00000000) );
- // write out the argument tokens.
- int i;
- for (i = 0; i < (tokcount-1); i++)
- output_token(ctx, ctx->tokenbuf[i]);
- return 1;
- } // parse_instruction_token
- static void parse_version_token(Context *ctx)
- {
- int bad = 0;
- int dot_form = 0;
- uint32 shader_type = 0;
- if (nexttoken(ctx) != TOKEN_IDENTIFIER)
- bad = 1;
- else if (check_token_segment(ctx, "vs"))
- {
- ctx->shader_type = MOJOSHADER_TYPE_VERTEX;
- shader_type = 0xFFFE;
- } // if
- else if (check_token_segment(ctx, "ps"))
- {
- ctx->shader_type = MOJOSHADER_TYPE_PIXEL;
- shader_type = 0xFFFF;
- } // if
- else
- {
- // !!! FIXME: geometry shaders?
- bad = 1;
- } // else
- dot_form = ((!bad) && (ctx->tokenlen == 0)); // it's in xs.x.x form?
- uint32 major = 0;
- uint32 minor = 0;
- if (dot_form)
- {
- Token t = TOKEN_UNKNOWN;
- if (!bad)
- {
- t = nexttoken(ctx);
- // stupid lexer sees "vs.2.0" and makes the ".2" into a float.
- if (t == ((Token) '.'))
- t = nexttoken(ctx);
- else
- {
- if ((t != TOKEN_FLOAT_LITERAL) || (ctx->token[0] != '.'))
- bad = 1;
- else
- {
- ctx->tokenval = t = TOKEN_INT_LITERAL;
- ctx->token++;
- ctx->tokenlen--;
- } // else
- } // else
- } // if
- if (!bad)
- {
- if (t != TOKEN_INT_LITERAL)
- bad = 1;
- else if (!ui32fromtoken(ctx, &major))
- bad = 1;
- } // if
- if (!bad)
- {
- t = nexttoken(ctx);
- // stupid lexer sees "vs.2.0" and makes the ".2" into a float.
- if (t == ((Token) '.'))
- t = nexttoken(ctx);
- else
- {
- if ((t != TOKEN_FLOAT_LITERAL) || (ctx->token[0] != '.'))
- bad = 1;
- else
- {
- ctx->tokenval = t = TOKEN_INT_LITERAL;
- ctx->token++;
- ctx->tokenlen--;
- } // else
- } // else
- } // if
- if (!bad)
- {
- if ((t == TOKEN_INT_LITERAL) && (ui32fromtoken(ctx, &minor)))
- ; // good to go.
- else if ((t == TOKEN_IDENTIFIER) && (check_token_segment(ctx, "x")))
- minor = 1;
- else if ((t == TOKEN_IDENTIFIER) && (check_token_segment(ctx, "sw")))
- minor = 255;
- else
- bad = 1;
- } // if
- } // if
- else
- {
- if (!check_token_segment(ctx, "_"))
- bad = 1;
- else if (!ui32fromtoken(ctx, &major))
- bad = 1;
- else if (!check_token_segment(ctx, "_"))
- bad = 1;
- else if (check_token_segment(ctx, "x"))
- minor = 1;
- else if (check_token_segment(ctx, "sw"))
- minor = 255;
- else if (!ui32fromtoken(ctx, &minor))
- bad = 1;
- } // else
- if ((!bad) && (ctx->tokenlen != 0))
- bad = 1;
- if (bad)
- fail(ctx, "Expected valid version string");
- ctx->major_ver = major;
- ctx->minor_ver = minor;
- ctx->version_token = (shader_type << 16) | (major << 8) | (minor << 0);
- output_token(ctx, ctx->version_token);
- } // parse_version_token
- static void parse_phase_token(Context *ctx)
- {
- output_token(ctx, 0x0000FFFD); // phase token always 0x0000FFFD.
- } // parse_phase_token
- static void parse_end_token(Context *ctx)
- {
- // We don't emit the end token bits here, since it's valid for a shader
- // to not specify an "end" string at all; it's implicit, in that case.
- // Instead, we make sure if we see "end" that it's the last thing we see.
- if (nexttoken(ctx) != TOKEN_EOI)
- fail(ctx, "Content after END");
- } // parse_end_token
- static void parse_token(Context *ctx, const Token token)
- {
- if (token != TOKEN_IDENTIFIER)
- parse_instruction_token(ctx, token); // might be a coissue '+', etc.
- else
- {
- if (check_token(ctx, "end"))
- parse_end_token(ctx);
- else if (check_token(ctx, "phase"))
- parse_phase_token(ctx);
- else
- parse_instruction_token(ctx, token);
- } // if
- } // parse_token
- static void destroy_context(Context *ctx)
- {
- if (ctx != NULL)
- {
- MOJOSHADER_free f = ((ctx->free != NULL) ? ctx->free : MOJOSHADER_internal_free);
- void *d = ctx->malloc_data;
- preprocessor_end(ctx->preprocessor);
- errorlist_destroy(ctx->errors);
- buffer_destroy(ctx->ctab);
- buffer_destroy(ctx->token_to_source);
- buffer_destroy(ctx->output);
- f(ctx, d);
- } // if
- } // destroy_context
- static Context *build_context(const char *filename,
- const char *source, unsigned int sourcelen,
- const MOJOSHADER_preprocessorDefine *defines,
- unsigned int define_count,
- MOJOSHADER_includeOpen include_open,
- MOJOSHADER_includeClose include_close,
- MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
- {
- if (!m) m = MOJOSHADER_internal_malloc;
- if (!f) f = MOJOSHADER_internal_free;
- if (!include_open) include_open = MOJOSHADER_internal_include_open;
- if (!include_close) include_close = MOJOSHADER_internal_include_close;
- Context *ctx = (Context *) m(sizeof (Context), d);
- if (ctx == NULL)
- return NULL;
- memset(ctx, '\0', sizeof (Context));
- ctx->malloc = m;
- ctx->free = f;
- ctx->malloc_data = d;
- ctx->current_position = MOJOSHADER_POSITION_BEFORE;
- const size_t outblk = sizeof (uint32) * 4 * 64; // 64 4-token instrs.
- ctx->output = buffer_create(outblk, MallocBridge, FreeBridge, ctx);
- if (ctx->output == NULL)
- goto build_context_failed;
- const size_t mapblk = sizeof (SourcePos) * 4 * 64; // 64 * 4-tokens.
- ctx->token_to_source = buffer_create(mapblk, MallocBridge, FreeBridge, ctx);
- if (ctx->token_to_source == NULL)
- goto build_context_failed;
- ctx->errors = errorlist_create(MallocBridge, FreeBridge, ctx);
- if (ctx->errors == NULL)
- goto build_context_failed;
- ctx->preprocessor = preprocessor_start(filename, source, sourcelen,
- include_open, include_close,
- defines, define_count, 1,
- MallocBridge, FreeBridge, ctx);
- if (ctx->preprocessor == NULL)
- goto build_context_failed;
- return ctx;
- build_context_failed: // ctx is allocated and zeroed before this is called.
- destroy_context(ctx);
- return NULL;
- } // build_context
- static const MOJOSHADER_parseData *build_failed_assembly(Context *ctx)
- {
- assert(isfail(ctx));
- if (ctx->out_of_memory)
- return &MOJOSHADER_out_of_mem_data;
-
- MOJOSHADER_parseData *retval = NULL;
- retval = (MOJOSHADER_parseData*) Malloc(ctx, sizeof(MOJOSHADER_parseData));
- if (retval == NULL)
- return &MOJOSHADER_out_of_mem_data;
- memset(retval, '\0', sizeof (MOJOSHADER_parseData));
- retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
- retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
- retval->malloc_data = ctx->malloc_data;
- retval->error_count = errorlist_count(ctx->errors);
- retval->errors = errorlist_flatten(ctx->errors);
- if (ctx->out_of_memory)
- {
- Free(ctx, retval->errors);
- Free(ctx, retval);
- return &MOJOSHADER_out_of_mem_data;
- } // if
- return retval;
- } // build_failed_assembly
- static uint32 add_ctab_bytes(Context *ctx, const uint8 *bytes, const size_t len)
- {
- if (isfail(ctx))
- return 0;
- const size_t extra = CTAB_SIZE + sizeof (uint32);
- const ssize_t pos = buffer_find(ctx->ctab, extra, bytes, len);
- if (pos >= 0) // blob is already in here.
- return ((uint32) pos) - sizeof (uint32);
- // add it to the byte pile...
- const uint32 retval = ((uint32) buffer_size(ctx->ctab)) - sizeof (uint32);
- buffer_append(ctx->ctab, bytes, len);
- return retval;
- } // add_ctab_bytes
- static inline uint32 add_ctab_string(Context *ctx, const char *str)
- {
- return add_ctab_bytes(ctx, (const uint8 *) str, strlen(str) + 1);
- } // add_ctab_string
- static uint32 add_ctab_typeinfo(Context *ctx, const MOJOSHADER_symbolTypeInfo *info);
- static uint32 add_ctab_members(Context *ctx, const MOJOSHADER_symbolTypeInfo *info)
- {
- unsigned int i;
- const size_t len = info->member_count * CMEMBERINFO_SIZE;
- uint8 *bytes = (uint8 *) Malloc(ctx, len);
- if (bytes == NULL)
- return 0;
- union { uint8 *ui8; uint16 *ui16; uint32 *ui32; } ptr;
- ptr.ui8 = bytes;
- for (i = 0; i < info->member_count; i++)
- {
- const MOJOSHADER_symbolStructMember *member = &info->members[i];
- *(ptr.ui32++) = SWAP32(add_ctab_string(ctx, member->name));
- *(ptr.ui32++) = SWAP32(add_ctab_typeinfo(ctx, &member->info));
- } // for
- const uint32 retval = add_ctab_bytes(ctx, bytes, len);
- Free(ctx, bytes);
- return retval;
- } // add_ctab_members
- static uint32 add_ctab_typeinfo(Context *ctx, const MOJOSHADER_symbolTypeInfo *info)
- {
- uint8 bytes[CTYPEINFO_SIZE];
- union { uint8 *ui8; uint16 *ui16; uint32 *ui32; } ptr;
- ptr.ui8 = bytes;
- *(ptr.ui16++) = SWAP16((uint16) info->parameter_class);
- *(ptr.ui16++) = SWAP16((uint16) info->parameter_type);
- *(ptr.ui16++) = SWAP16((uint16) info->rows);
- *(ptr.ui16++) = SWAP16((uint16) info->columns);
- *(ptr.ui16++) = SWAP16((uint16) info->elements);
- *(ptr.ui16++) = SWAP16((uint16) info->member_count);
- *(ptr.ui32++) = SWAP32(add_ctab_members(ctx, info));
- return add_ctab_bytes(ctx, bytes, sizeof (bytes));
- } // add_ctab_typeinfo
- static uint32 add_ctab_info(Context *ctx, const MOJOSHADER_symbol *symbols,
- const unsigned int symbol_count)
- {
- unsigned int i;
- const size_t len = symbol_count * CINFO_SIZE;
- uint8 *bytes = (uint8 *) Malloc(ctx, len);
- if (bytes == NULL)
- return 0;
- union { uint8 *ui8; uint16 *ui16; uint32 *ui32; } ptr;
- ptr.ui8 = bytes;
- for (i = 0; i < symbol_count; i++)
- {
- const MOJOSHADER_symbol *sym = &symbols[i];
- *(ptr.ui32++) = SWAP32(add_ctab_string(ctx, sym->name));
- *(ptr.ui16++) = SWAP16((uint16) sym->register_set);
- *(ptr.ui16++) = SWAP16((uint16) sym->register_index);
- *(ptr.ui16++) = SWAP16((uint16) sym->register_count);
- *(ptr.ui16++) = SWAP16(0); // reserved
- *(ptr.ui32++) = SWAP32(add_ctab_typeinfo(ctx, &sym->info));
- *(ptr.ui32++) = SWAP32(0); // !!! FIXME: default value.
- } // for
- const uint32 retval = add_ctab_bytes(ctx, bytes, len);
- Free(ctx, bytes);
- return retval;
- } // add_ctab_info
- static void output_ctab(Context *ctx, const MOJOSHADER_symbol *symbols,
- unsigned int symbol_count, const char *creator)
- {
- const size_t tablelen = CTAB_SIZE + sizeof (uint32);
- ctx->ctab = buffer_create(256, MallocBridge, FreeBridge, ctx);
- if (ctx->ctab == NULL)
- return; // out of memory.
- uint32 *table = (uint32 *) buffer_reserve(ctx->ctab, tablelen);
- if (table == NULL)
- {
- buffer_destroy(ctx->ctab);
- ctx->ctab = NULL;
- return; // out of memory.
- } // if
- *(table++) = SWAP32(CTAB_ID);
- *(table++) = SWAP32(CTAB_SIZE);
- *(table++) = SWAP32(add_ctab_string(ctx, creator));
- *(table++) = SWAP32(ctx->version_token);
- *(table++) = SWAP32(((uint32) symbol_count));
- *(table++) = SWAP32(add_ctab_info(ctx, symbols, symbol_count));
- *(table++) = SWAP32(0); // build flags.
- *(table++) = SWAP32(add_ctab_string(ctx, "")); // !!! FIXME: target?
- const size_t ctablen = buffer_size(ctx->ctab);
- uint8 *buf = (uint8 *) buffer_flatten(ctx->ctab);
- if (buf != NULL)
- {
- output_comment_bytes(ctx, buf, ctablen);
- Free(ctx, buf);
- } // if
- buffer_destroy(ctx->ctab);
- ctx->ctab = NULL;
- } // output_ctab
- static void output_comments(Context *ctx, const char **comments,
- unsigned int comment_count,
- const MOJOSHADER_symbol *symbols,
- unsigned int symbol_count)
- {
- if (isfail(ctx))
- return;
- // make error messages sane if CTAB fails, etc.
- const char *prev_fname = ctx->current_file;
- const int prev_position = ctx->current_position;
- ctx->current_file = NULL;
- ctx->current_position = MOJOSHADER_POSITION_BEFORE;
- const char *creator = "MojoShader revision " MOJOSHADER_CHANGESET;
- if (symbol_count > 0)
- output_ctab(ctx, symbols, symbol_count, creator);
- else
- output_comment_string(ctx, creator);
- unsigned int i;
- for (i = 0; i < comment_count; i++)
- output_comment_string(ctx, comments[i]);
- ctx->current_file = prev_fname;
- ctx->current_position = prev_position;
- } // output_comments
- static const MOJOSHADER_parseData *build_final_assembly(Context *ctx)
- {
- if (isfail(ctx))
- return build_failed_assembly(ctx);
- // get the final bytecode!
- const unsigned int output_len = (unsigned int) buffer_size(ctx->output);
- unsigned char *bytecode = (unsigned char *) buffer_flatten(ctx->output);
- buffer_destroy(ctx->output);
- ctx->output = NULL;
- if (bytecode == NULL)
- return build_failed_assembly(ctx);
- // This validates the shader; there are lots of things that are
- // invalid, but will successfully parse in the assembler,
- // generating bad bytecode; this will catch them without us
- // having to duplicate most of the validation here.
- // It also saves us the trouble of duplicating all the other work,
- // like setting up the uniforms list, etc.
- MOJOSHADER_parseData *retval = (MOJOSHADER_parseData *)
- MOJOSHADER_parse(MOJOSHADER_PROFILE_BYTECODE,
- bytecode, output_len, NULL, 0, NULL, 0,
- ctx->malloc, ctx->free, ctx->malloc_data);
- Free(ctx, bytecode);
- SourcePos *token_to_src = NULL;
- if (retval->error_count > 0)
- token_to_src = (SourcePos *) buffer_flatten(ctx->token_to_source);
- buffer_destroy(ctx->token_to_source);
- ctx->token_to_source = NULL;
- if (retval->error_count > 0)
- {
- if (token_to_src == NULL)
- {
- assert(ctx->out_of_memory);
- MOJOSHADER_freeParseData(retval);
- return build_failed_assembly(ctx);
- } // if
- // on error, map the bytecode back to a line number.
- int i;
- for (i = 0; i < retval->error_count; i++)
- {
- MOJOSHADER_error *error = &retval->errors[i];
- if (error->error_position >= 0)
- {
- assert(retval != &MOJOSHADER_out_of_mem_data);
- assert((error->error_position % sizeof (uint32)) == 0);
- const size_t pos = error->error_position / sizeof(uint32);
- if (pos >= output_len)
- error->error_position = -1; // oh well.
- else
- {
- const SourcePos *srcpos = &token_to_src[pos];
- Free(ctx, (void *) error->filename);
- char *fname = NULL;
- if (srcpos->filename != NULL)
- fname = StrDup(ctx, srcpos->filename);
- error->error_position = srcpos->line;
- error->filename = fname; // may be NULL, that's okay.
- } // else
- } // if
- } // for
- Free(ctx, token_to_src);
- } // if
- return retval;
- } // build_final_assembly
- // API entry point...
- const MOJOSHADER_parseData *MOJOSHADER_assemble(const char *filename,
- const char *source, unsigned int sourcelen,
- const char **comments, unsigned int comment_count,
- const MOJOSHADER_symbol *symbols,
- unsigned int symbol_count,
- const MOJOSHADER_preprocessorDefine *defines,
- unsigned int define_count,
- MOJOSHADER_includeOpen include_open,
- MOJOSHADER_includeClose include_close,
- MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
- {
- const MOJOSHADER_parseData *retval = NULL;
- Context *ctx = NULL;
- if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) )
- return &MOJOSHADER_out_of_mem_data; // supply both or neither.
- ctx = build_context(filename, source, sourcelen, defines, define_count,
- include_open, include_close, m, f, d);
- if (ctx == NULL)
- return &MOJOSHADER_out_of_mem_data;
- // Version token always comes first.
- parse_version_token(ctx);
- output_comments(ctx, comments, comment_count, symbols, symbol_count);
- // parse out the rest of the tokens after the version token...
- Token token;
- while ((token = nexttoken(ctx)) != TOKEN_EOI)
- parse_token(ctx, token);
- ctx->current_file = NULL;
- ctx->current_position = MOJOSHADER_POSITION_AFTER;
- output_token(ctx, 0x0000FFFF); // end token always 0x0000FFFF.
- retval = build_final_assembly(ctx);
- destroy_context(ctx);
- return retval;
- } // MOJOSHADER_assemble
- // end of mojoshader_assembler.c ...
|