/
mojoshader_assembler.c
2084 lines (1766 loc) · 58.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 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.
*/
#define __MOJOSHADER_INTERNAL__ 1
#include "mojoshader_internal.h"
13
#define DEBUG_TOKENIZER 0
14
15
// !!! FIXME: no #define support yet.
16
17
typedef struct TokenizerContext
18
19
20
21
22
23
24
{
const char *source;
int on_endline;
unsigned int linenum;
char prevchar;
char token[64];
char pushedback;
25
} TokenizerContext;
26
27
28
29
30
31
32
33
typedef struct SourcePos
{
const char *filename;
uint32 line;
} SourcePos;
34
35
// Context...this is state that changes as we assemble a shader...
36
typedef struct Context
37
{
38
39
40
int isfail;
int out_of_memory;
int eof;
41
42
43
MOJOSHADER_malloc malloc;
MOJOSHADER_free free;
void *malloc_data;
44
45
int error_count;
ErrorList *errors;
46
TokenizerContext tctx;
47
MOJOSHADER_parsePhase parse_phase;
48
49
50
MOJOSHADER_shaderType shader_type;
uint8 major_ver;
uint8 minor_ver;
51
uint32 version_token;
52
53
54
uint32 tokenbuf[16];
int tokenbufpos;
DestArgInfo dest_arg;
55
uint32 *output;
56
SourcePos *token_to_source;
57
58
59
uint8 *ctab;
uint32 ctab_len;
uint32 ctab_allocation;
60
61
size_t output_len;
size_t output_allocation;
62
} Context;
63
64
65
66
// Convenience functions for allocators...
67
static inline void out_of_memory(Context *ctx)
68
{
69
ctx->isfail = ctx->out_of_memory = 1;
70
71
72
73
74
75
76
77
78
79
} // 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
80
81
82
83
84
85
86
87
88
89
static inline char *StrDup(Context *ctx, const char *str)
{
char *retval = (char *) Malloc(ctx, strlen(str) + 1);
if (retval == NULL)
out_of_memory(ctx);
else
strcpy(retval, str);
return retval;
} // StrDup
90
91
92
93
94
95
static inline void Free(Context *ctx, void *ptr)
{
if (ptr != NULL) // check for NULL in case of dumb free() impl.
ctx->free(ptr, ctx->malloc_data);
} // Free
96
97
static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
static void failf(Context *ctx, const char *fmt, ...)
98
{
99
100
101
102
const char *fname = NULL;
unsigned int linenum = 0;
int error_position = 0;
103
104
ctx->isfail = 1;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
switch (ctx->parse_phase)
{
case MOJOSHADER_PARSEPHASE_NOTSTARTED:
error_position = -2;
break;
case MOJOSHADER_PARSEPHASE_WORKING:
// !!! FIXME: fname == base source file if output_pos == 0.
if (ctx->output_len > 0)
{
const size_t idx = ctx->output_len - 1;
linenum = ctx->token_to_source[idx].line;
fname = ctx->token_to_source[idx].filename;
} // if
error_position = linenum;
break;
case MOJOSHADER_PARSEPHASE_DONE:
error_position = -1;
break;
default:
assert(0 && "Unexpected value");
return;
} // switch
ErrorList *error = (ErrorList *) Malloc(ctx, sizeof (ErrorList));
if (error == NULL)
return;
char scratch = 0;
va_list ap;
va_start(ap, fmt);
const int len = vsnprintf(&scratch, sizeof (scratch), fmt, ap);
va_end(ap);
char *failstr = (char *) Malloc(ctx, len + 1);
if (failstr == NULL)
Free(ctx, error);
else
142
143
{
va_start(ap, fmt);
144
vsnprintf(failstr, len + 1, fmt, ap); // rebuild it.
145
146
va_end(ap);
147
148
149
error->error.error = failstr;
error->error.filename = fname ? StrDup(ctx, fname) : NULL;
error->error.error_position = error_position;
150
error->next = NULL;
151
152
ErrorList *prev = NULL;
153
154
ErrorList *item = ctx->errors;
while (item != NULL)
155
{
156
157
prev = item;
item = error->next;
158
159
} // while
160
if (prev == NULL)
161
ctx->errors = error;
162
163
else
prev->next = error;
164
165
166
ctx->error_count++;
} // else
167
168
} // failf
169
static inline void fail(Context *ctx, const char *reason)
170
{
171
failf(ctx, "%s", reason);
172
173
174
175
} // fail
static inline int isfail(const Context *ctx)
{
176
return ctx->isfail;
177
178
179
} // isfail
180
181
182
183
184
185
static inline int tokeq(const TokenizerContext *tctx, const char *token)
{
return (strcasecmp(tctx->token, token) == 0);
} // tokeq
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// 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
210
211
212
213
214
215
216
217
218
static int ui32fromstr(const char *str, uint32 *ui32)
{
//*ui32 = (uint32) atoi(minstr);
char *endptr = NULL;
const long val = strtol(str, &endptr, 10);
*ui32 = (uint32) val;
return ((val >= 0) && (*str != '\0') && (*endptr == '\0'));
} // ui32fromstr
219
220
221
222
223
224
225
226
static inline void add_token_sourcepos(Context *ctx, const size_t idx)
{
ctx->token_to_source[idx].line = ctx->tctx.linenum;
ctx->token_to_source[idx].filename = NULL;
} // add_token_sourcepos
227
228
229
230
231
static void output_token_noswap(Context *ctx, const uint32 token)
{
if (isfail(ctx))
return;
232
233
234
235
if (ctx->output_len >= ctx->output_allocation)
{
const size_t output_alloc_bump = 1024; // that's tokens, not bytes.
const size_t newsize = ctx->output_allocation + output_alloc_bump;
236
237
238
void *ptr;
ptr = Malloc(ctx, newsize * sizeof (uint32));
239
240
241
242
243
244
if (ptr == NULL)
return;
if (ctx->output_len > 0)
memcpy(ptr, ctx->output, ctx->output_len * sizeof (uint32));
Free(ctx, ctx->output);
ctx->output = (uint32 *) ptr;
245
246
ptr = Malloc(ctx, newsize * sizeof (SourcePos));
247
248
249
if (ptr == NULL)
return;
if (ctx->output_len > 0)
250
251
252
memcpy(ptr, ctx->token_to_source, ctx->output_len * sizeof (SourcePos));
Free(ctx, ctx->token_to_source);
ctx->token_to_source = (SourcePos *) ptr;
253
254
ctx->output_allocation = newsize;
255
256
} // if
257
ctx->output[ctx->output_len] = token;
258
add_token_sourcepos(ctx, ctx->output_len);
259
ctx->output_len++;
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
} // 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
301
static int tokenize_ctx(Context *ctx, TokenizerContext *tctx)
302
303
304
{
int idx = 0;
305
if (tctx->pushedback)
306
{
307
tctx->pushedback = 0;
308
return 1;
309
310
} // if
311
if (tctx->on_endline)
312
{
313
314
tctx->on_endline = 0;
tctx->linenum++; // passed a newline, update.
315
316
} // if
317
318
while (1)
{
319
// !!! FIXME: carefully crafted (but legal) comments can trigger this.
320
if (idx >= sizeof (tctx->token))
321
322
323
324
{
fail(ctx, "buffer overflow");
return 0;
} // if
325
326
char ch = *tctx->source;
327
328
329
330
if (ch == '\t')
ch = ' '; // collapse tabs into single spaces.
else if (ch == '\r')
{
331
if (tctx->source[1] == '\n')
332
333
334
335
continue; // ignore '\r' if this is "\r\n" ...
ch = '\n';
} // else if
336
if ((ch >= '0') && (ch <= '9'))
337
338
{
// starting a number, but rest of current token was not number.
339
if ((idx > 0) && ((tctx->prevchar < '0') || (tctx->prevchar > '9')))
340
{
341
tctx->token[idx++] = '\0';
342
return 1;
343
344
345
346
347
} // if
} // if
else
{
// starting a non-number, but rest of current token was numbers.
348
if ((idx > 0) && ((tctx->prevchar >= '0') && (tctx->prevchar <= '9')))
349
{
350
tctx->token[idx++] = '\0';
351
return 1;
352
353
354
355
356
357
358
359
} // if
} // else
switch (ch)
{
case '/':
case ';': // !!! FIXME: comment, right?
if (idx != 0) // finish off existing token.
360
tctx->token[idx] = '\0';
361
362
else
{
363
364
365
tctx->token[idx++] = ch;
tctx->source++;
if ((ch == '/') && (*tctx->source == '/'))
366
{
367
368
tctx->token[idx++] = '/';
tctx->source++;
369
} // if
370
tctx->token[idx++] = '\0';
371
} // else
372
return 1;
373
374
case ' ':
375
if (tctx->prevchar == ' ')
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
break; // multiple whitespace collapses into one.
// intentional fall-through...
case '_':
case '[':
case ']':
case '(':
case ')':
case '!':
case '+':
case '-':
case ',':
case '.':
case '\n':
if (idx != 0) // finish off existing token.
391
tctx->token[idx] = '\0';
392
393
394
else // this is a token in itself.
{
if (ch == '\n')
395
396
397
398
tctx->on_endline = 1;
tctx->source++;
tctx->token[idx++] = ch;
tctx->token[idx++] = '\0';
399
} // else
400
return 1;
401
402
case '\0':
403
tctx->token[idx] = '\0';
404
if (idx != 0) // had any chars? It's a token.
405
406
407
return 1;
ctx->eof = 1;
return 0;
408
409
default:
410
411
tctx->source++;
tctx->token[idx++] = ch;
412
413
414
break;
} // switch
415
tctx->prevchar = ch;
416
417
} // while
418
419
assert(0 && "Shouldn't hit this code");
return 0;
420
} // tokenize_ctx
421
422
423
424
static inline int tokenize(Context *ctx)
{
425
426
const int rc = tokenize_ctx(ctx, &ctx->tctx);
427
#if DEBUG_TOKENIZER
428
printf("TOKENIZE: %d '%s'\n", rc,
429
(ctx->tctx.token[0] == '\n') ? "\\n" : ctx->tctx.token);
430
#endif
431
432
433
434
435
return rc;
} // tokenize
436
static void pushback_ctx(Context *ctx, TokenizerContext *tctx)
437
{
438
439
440
assert(!tctx->pushedback);
tctx->pushedback = 1;
} // pushback_ctx
441
442
443
static inline void pushback(Context *ctx)
444
{
445
pushback_ctx(ctx, &ctx->tctx);
446
#if DEBUG_TOKENIZER
447
printf("PUSHBACK\n");
448
#endif
449
450
451
} // pushback
452
453
454
static int nexttoken_ctx(Context *ctx, TokenizerContext *tctx,
const int ignoreeol, const int ignorewhitespace,
const int eolok, const int eosok)
455
{
456
while (tokenize_ctx(ctx, tctx))
457
{
458
if (tokeq(tctx, "\n"))
459
460
461
462
{
if (ignoreeol)
continue;
else if (!eolok)
463
464
465
466
{
fail(ctx, "Unexpected EOL");
return 0;
} // else if
467
468
} // if
469
else if (tokeq(tctx, " "))
470
471
472
473
474
475
{
if (ignorewhitespace)
continue;
} // else if
// skip comments...
476
else if (tokeq(tctx, "//") || tokeq(tctx, ";"))
477
{
478
while (tokenize_ctx(ctx, tctx))
479
{
480
if (tokeq(tctx, "\n"))
481
{
482
pushback_ctx(ctx, tctx);
483
break;
484
} // if
485
} // while
486
continue; // pick up from newline, go again.
487
488
489
490
491
} // if
break;
} // while
492
493
494
495
496
if ((ctx->eof) && (!eosok))
{
fail(ctx, "Unexpected EOF");
return 0;
} // if
497
498
return 1;
499
500
501
502
503
504
505
506
507
508
} // nexttoken_ctx
static inline int nexttoken(Context *ctx, const int ignoreeol,
const int ignorewhitespace, const int eolok,
const int eosok)
{
const int rc = nexttoken_ctx(ctx, &ctx->tctx, ignoreeol,
ignorewhitespace, eolok, eosok);
509
#if DEBUG_TOKENIZER
510
printf("NEXTTOKEN: %d '%s'\n", rc,
511
(ctx->tctx.token[0] == '\n') ? "\\n" : ctx->tctx.token);
512
513
514
515
516
517
#endif
return rc;
} // nexttoken
518
519
520
521
522
523
524
525
526
527
528
529
530
531
static void skip_line(Context *ctx)
{
if (!tokeq(&ctx->tctx, "\n"))
{
while (nexttoken(ctx, 0, 1, 1, 1))
{
if (tokeq(&ctx->tctx, "\n"))
break;
} // while
} // if
} // skip_line
static void require_endline(Context *ctx)
532
{
533
TokenizerContext *tctx = &ctx->tctx;
534
const int rc = nexttoken(ctx, 0, 1, 1, 1);
535
536
537
538
539
540
541
if (ctx->eof)
return; // we'll call this an EOL.
else if ((rc == 0) || (!tokeq(tctx, "\n")))
{
fail(ctx, "Endline expected");
skip_line(ctx);
} // else if
542
543
544
} // require_endline
545
static int require_comma(Context *ctx)
546
{
547
TokenizerContext *tctx = &ctx->tctx;
548
const int rc = nexttoken(ctx, 0, 1, 0, 0);
549
550
551
552
553
554
if ((rc == 0) || (!tokeq(tctx, ",")))
{
fail(ctx, "Comma expected");
return 0;
} // if
return 1;
555
} // require_comma
556
557
558
559
static int parse_register_name(Context *ctx, RegisterType *rtype, int *rnum)
{
560
TokenizerContext *tctx = &ctx->tctx;
561
562
if (!nexttoken(ctx, 0, 1, 0, 0))
return 0;
563
564
565
566
int neednum = 1;
int regnum = 0;
RegisterType regtype = REG_TYPE_TEMP;
567
if (tokeq(tctx, "r"))
568
regtype = REG_TYPE_TEMP;
569
else if (tokeq(tctx, "v"))
570
regtype = REG_TYPE_INPUT;
571
else if (tokeq(tctx, "c"))
572
regtype = REG_TYPE_CONST;
573
else if (tokeq(tctx, "i"))
574
regtype = REG_TYPE_CONSTINT;
575
else if (tokeq(tctx, "b"))
576
regtype = REG_TYPE_CONSTBOOL;
577
else if (tokeq(tctx, "oC"))
578
regtype = REG_TYPE_COLOROUT;
579
else if (tokeq(tctx, "s"))
580
regtype = REG_TYPE_SAMPLER;
581
else if (tokeq(tctx, "oD"))
582
regtype = REG_TYPE_ATTROUT;
583
else if (tokeq(tctx, "l"))
584
regtype = REG_TYPE_LABEL;
585
else if (tokeq(tctx, "p"))
586
regtype = REG_TYPE_PREDICATE;
587
else if (tokeq(tctx, "oDepth"))
588
589
590
591
{
regtype = REG_TYPE_DEPTHOUT;
neednum = 0;
} // else if
592
else if (tokeq(tctx, "aL"))
593
594
595
596
{
regtype = REG_TYPE_LOOP;
neednum = 0;
} // else if
597
else if (tokeq(tctx, "o"))
598
599
{
if (!shader_is_vertex(ctx) || !shader_version_atleast(ctx, 3, 0))
600
fail(ctx, "Output register not valid in this shader type");
601
602
regtype = REG_TYPE_OUTPUT;
} // else if
603
else if (tokeq(tctx, "oT"))
604
{
605
if (shader_is_vertex(ctx) && shader_version_atleast(ctx, 3, 0))
606
fail(ctx, "Output register not valid in this shader type");
607
608
regtype = REG_TYPE_OUTPUT;
} // else if
609
else if (tokeq(tctx, "a"))
610
611
{
if (!shader_is_vertex(ctx))
612
fail(ctx, "Address register only valid in vertex shaders.");
613
614
regtype = REG_TYPE_ADDRESS;
} // else if
615
else if (tokeq(tctx, "t"))
616
617
{
if (!shader_is_pixel(ctx))
618
fail(ctx, "Address register only valid in pixel shaders.");
619
620
regtype = REG_TYPE_ADDRESS;
} // else if
621
else if (tokeq(tctx, "vPos"))
622
623
624
625
626
{
regtype = REG_TYPE_MISCTYPE;
regnum = (int) MISCTYPE_TYPE_POSITION;
neednum = 0;
} // else if
627
else if (tokeq(tctx, "vFace"))
628
629
630
631
632
{
regtype = REG_TYPE_MISCTYPE;
regnum = (int) MISCTYPE_TYPE_FACE;
neednum = 0;
} // else if
633
else if (tokeq(tctx, "oPos"))
634
635
636
637
638
{
regtype = REG_TYPE_RASTOUT;
regnum = (int) RASTOUT_TYPE_POSITION;
neednum = 0;
} // else if
639
else if (tokeq(tctx, "oFog"))
640
641
642
643
644
{
regtype = REG_TYPE_RASTOUT;
regnum = (int) RASTOUT_TYPE_FOG;
neednum = 0;
} // else if
645
else if (tokeq(tctx, "oPts"))
646
647
648
649
650
651
652
653
654
655
{
regtype = REG_TYPE_RASTOUT;
regnum = (int) RASTOUT_TYPE_POINT_SIZE;
neednum = 0;
} // else if
//case REG_TYPE_TEMPFLOAT16: // !!! FIXME: don't know this asm string
else
{
656
657
658
659
fail(ctx, "expected register type");
regtype = REG_TYPE_CONST;
regnum = 0;
neednum = 0;
660
661
} // else
662
663
if (neednum)
{
664
665
666
667
668
// Make a temp TokenizerContext, since we need to skip whitespace here,
// but if the next non-whitespace token isn't '[', we'll want to get
// that whitespace back.
TokenizerContext tmptctx;
memcpy(&tmptctx, tctx, sizeof (TokenizerContext));
669
670
if (!nexttoken_ctx(ctx, &tmptctx, 0, 1, 1, 1))
return 0;
671
else if (tokeq(&tmptctx, "["))
672
673
674
neednum = 0;
} // if
675
676
if (neednum)
{
677
678
if (!nexttoken(ctx, 0, 0, 0, 0))
return 0;
679
680
uint32 ui32 = 0;
681
if (!ui32fromstr(tctx->token, &ui32))
682
fail(ctx, "Invalid register index");
683
regnum = (int) ui32;
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
} // 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
{
711
fail(ctx, "Invalid const register index");
712
713
714
715
716
717
} // else
} // if
*rtype = regtype;
*rnum = regnum;
718
return 1;
719
720
721
} // parse_register_name
722
static void set_result_shift(Context *ctx, DestArgInfo *info, const int val)
723
{
724
if (info->result_shift != 0)
725
fail(ctx, "Multiple result shift modifiers");
726
727
info->result_shift = val;
} // set_result_shift
728
729
730
static int parse_destination_token(Context *ctx)
731
{
732
733
734
TokenizerContext *tctx = &ctx->tctx;
DestArgInfo *info = &ctx->dest_arg;
735
memset(info, '\0', sizeof (DestArgInfo));
736
737
738
739
// See if there are destination modifiers on the instruction itself...
while (1)
{
740
741
if (!nexttoken(ctx, 0, 0, 0, 0))
return 1;
742
else if (tokeq(tctx, " "))
743
break; // done with modifiers.
744
else if (!tokeq(tctx, "_"))
745
746
747
fail(ctx, "Expected modifier or whitespace");
else if (!nexttoken(ctx, 0, 0, 0, 0))
return 1;
748
// !!! FIXME: this can be cleaned up when tokenizer is fixed.
749
else if (tokeq(tctx, "x"))
750
{
751
752
if (!nexttoken(ctx, 0, 0, 0, 0))
return 1;
753
else if (tokeq(tctx, "2"))
754
set_result_shift(ctx, info, 0x1);
755
else if (tokeq(tctx, "4"))
756
set_result_shift(ctx, info, 0x2);
757
else if (tokeq(tctx, "8"))
758
759
set_result_shift(ctx, info, 0x3);
else
760
fail(ctx, "Expected modifier");
761
762
} // else if
// !!! FIXME: this can be cleaned up when tokenizer is fixed.
763
else if (tokeq(tctx, "d"))
764
{
765
766
if (!nexttoken(ctx, 0, 0, 0, 0))
return 1;
767
else if (tokeq(tctx, "8"))
768
set_result_shift(ctx, info, 0xD);
769
else if (tokeq(tctx, "4"))
770
set_result_shift(ctx, info, 0xE);
771
else if (tokeq(tctx, "2"))
772
773
set_result_shift(ctx, info, 0xF);
else
774
fail(ctx, "Expected modifier");
775
} // else if
776
else if (tokeq(tctx, "sat"))
777
info->result_mod |= MOD_SATURATE;
778
else if (tokeq(tctx, "pp"))
779
info->result_mod |= MOD_PP;
780
else if (tokeq(tctx, "centroid"))
781
782
info->result_mod |= MOD_CENTROID;
else
783
fail(ctx, "Expected modifier");
784
785
} // while
786
787
if (!nexttoken(ctx, 0, 1, 0, 0))
return 1;
788
789
// !!! FIXME: predicates.
790
if (tokeq(tctx, "("))
791
792
fail(ctx, "Predicates unsupported at this time"); // !!! FIXME: ...
793
794
pushback(ctx); // parse_register_name calls nexttoken().
795
parse_register_name(ctx, &info->regtype, &info->regnum);
796
797
798
if (!nexttoken(ctx, 0, 1, 1, 1))
return 1;
799
800
801
// !!! FIXME: can dest registers do relative addressing?
802
int invalid_writemask = 0;
803
int implicit_writemask = 0;
804
if (!tokeq(tctx, "."))
805
{
806
implicit_writemask = 1;
807
808
809
810
info->writemask = 0xF;
info->writemask0 = info->writemask1 = info->writemask2 = info->writemask3 = 1;
pushback(ctx); // no explicit writemask; do full mask.
} // if
811
812
813
// !!! 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) )
814
815
816
fail(ctx, "Writemask specified for scalar register");
else if (!nexttoken(ctx, 0, 1, 0, 0))
return 1;
817
else if (tokeq(tctx, ""))
818
invalid_writemask = 1;
819
820
else
{
821
char *ptr = tctx->token;
822
info->writemask0 = info->writemask1 = info->writemask2 = info->writemask3 = 0;
823
824
825
826
if (*ptr == 'x') { info->writemask0 = 1; ptr++; }
if (*ptr == 'y') { info->writemask1 = 1; ptr++; }
if (*ptr == 'z') { info->writemask2 = 1; ptr++; }
if (*ptr == 'w') { info->writemask3 = 1; ptr++; }
827
if ((ptr == tctx->token) && (shader_is_pixel(ctx)))
828
{
829
830
831
832
if (*ptr == 'r') { info->writemask0 = 1; ptr++; }
if (*ptr == 'g') { info->writemask1 = 1; ptr++; }
if (*ptr == 'b') { info->writemask2 = 1; ptr++; }
if (*ptr == 'a') { info->writemask3 = 1; ptr++; }
833
834
835
} // if
if (*ptr != '\0')
836
invalid_writemask = 1;
837
838
839
840
841
842
843
info->writemask = ( ((info->writemask0 & 0x1) << 0) |
((info->writemask1 & 0x1) << 1) |
((info->writemask2 & 0x1) << 2) |
((info->writemask3 & 0x1) << 3) );
} // else
844
845
846
if (invalid_writemask)
fail(ctx, "Invalid writemask");
847
848
849
850
851
// !!! 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) )
852
fail(ctx, "Writemask specified for scalar register");
853
854
} // if
855
856
857
info->orig_writemask = info->writemask;
if (ctx->tokenbufpos >= STATICARRAYLEN(ctx->tokenbuf))
858
859
860
861
{
fail(ctx, "Too many tokens");
return 1;
} // if
862
863
ctx->tokenbuf[ctx->tokenbufpos++] =
864
( ((((uint32) 1)) << 31) |
865
866
867
868
((((uint32) info->regnum) & 0x7ff) << 0) |
((((uint32) info->relative) & 0x1) << 13) |
((((uint32) info->result_mod) & 0xF) << 20) |
((((uint32) info->result_shift) & 0xF) << 24) |
869
((((uint32) info->writemask) & 0xF) << 16) |
870
871
872
873
874
875
876
((((uint32) info->regtype) & 0x7) << 28) |
((((uint32) info->regtype) & 0x18) << 8) );
return 1;
} // parse_destination_token
877
878
879
static void set_source_mod(Context *ctx, const int negate,
const SourceMod norm, const SourceMod negated,
SourceMod *srcmod)
880
{
881
882
883
884
885
if ( (*srcmod != SRCMOD_NONE) || (negate && (negated == SRCMOD_NONE)) )
fail(ctx, "Incompatible source modifiers");
else
*srcmod = ((negate) ? negated : norm);
} // set_source_mod
886
887
888
static int parse_source_token_maybe_relative(Context *ctx, const int relok)
889
{
890
TokenizerContext *tctx = &ctx->tctx;
891
892
893
int retval = 1;
if (ctx->tokenbufpos >= STATICARRAYLEN(ctx->tokenbuf))
894
895
896
897
{
fail(ctx, "Too many tokens");
return 0;
} // if
898
899
900
// mark this now, so optional relative addressing token is placed second.
uint32 *token = &ctx->tokenbuf[ctx->tokenbufpos++];
901
*token = 0;
902
903
904
SourceMod srcmod = SRCMOD_NONE;
int negate = 0;
905
906
if (!nexttoken(ctx, 0, 1, 0, 0))
return 1;
907
else if (tokeq(tctx, "1"))
908
{
909
910
if (!nexttoken(ctx, 0, 1, 0, 0))
return 1;
911
else if (!tokeq(tctx, "-"))
912
fail(ctx, "Unexpected value");
913
914
915
else
srcmod = SRCMOD_COMPLEMENT;
} // else
916
else if (tokeq(tctx, "!"))
917
srcmod = SRCMOD_NOT;
918
else if (tokeq(tctx, "-"))
919
920
921
negate = 1;
else
pushback(ctx);
922
923
924
RegisterType regtype;
int regnum;
925
926
927
parse_register_name(ctx, ®type, ®num);
if (!nexttoken(ctx, 0, 1, 1, 1))
return 1;
928
else if (!tokeq(tctx, "_"))
929
pushback(ctx);
930
931
else if (!nexttoken(ctx, 0, 0, 0, 0))
return 1;
932
else if (tokeq(tctx, "bias"))
933
set_source_mod(ctx, negate, SRCMOD_BIAS, SRCMOD_BIASNEGATE, &srcmod);
934
else if (tokeq(tctx, "bx2"))
935
set_source_mod(ctx, negate, SRCMOD_SIGN, SRCMOD_SIGNNEGATE, &srcmod);
936
else if (tokeq(tctx, "x2"))
937
set_source_mod(ctx, negate, SRCMOD_X2, SRCMOD_X2NEGATE, &srcmod);
938
else if (tokeq(tctx, "dz"))
939
set_source_mod(ctx, negate, SRCMOD_DZ, SRCMOD_NONE, &srcmod);
940
else if (tokeq(tctx, "dw"))
941
set_source_mod(ctx, negate, SRCMOD_DW, SRCMOD_NONE, &srcmod);
942
else if (tokeq(tctx, "abs"))
943
set_source_mod(ctx, negate, SRCMOD_ABS, SRCMOD_ABSNEGATE, &srcmod);
944
else
945
fail(ctx, "Invalid source modifier");
946
947
948
if (!nexttoken(ctx, 0, 1, 1, 1))
return 1;
949
950
uint32 relative = 0;
951
if (!tokeq(tctx, "["))
952
953
954
pushback(ctx); // not relative addressing?
else
{
955
956
957
958
959
960
if (!relok)
fail(ctx, "Relative addressing not permitted here.");
else
retval++;
parse_source_token_maybe_relative(ctx, 0);
961
relative = 1;
962
963
if (!nexttoken(ctx, 0, 1, 0, 0))
return retval;
964
else if (!tokeq(tctx, "+"))
965
pushback(ctx);
966
967
else if (!nexttoken(ctx, 0, 1, 0, 0))
return retval;
968
969
970
971
972
else
{
if (regnum != 0) // !!! FIXME: maybe c3[a0.x + 5] is legal and becomes c[a0.x + 8] ?
fail(ctx, "Relative addressing with explicit register number.");
uint32 ui32 = 0;
973
if (!ui32fromstr(tctx->token, &ui32))
974
fail(ctx, "Invalid relative addressing offset");
975
976
977
regnum += (int) ui32;
} // else
978
979
if (!nexttoken(ctx, 0, 1, 0, 0))
return retval;
980
else if (!tokeq(tctx, "]"))
981
fail(ctx, "Expected ']'");
982
} // else
983
984
985
if (!nexttoken(ctx, 0, 1, 1, 1))
return retval;
986
987
int invalid_swizzle = 0;
988
uint32 swizzle = 0;
989
if (!tokeq(tctx, "."))
990
991
992
993
{
swizzle = 0xE4; // 0xE4 == 11100100 ... 0 1 2 3. No swizzle.
pushback(ctx); // no explicit writemask; do full mask.
} // if
994
else if (scalar_register(ctx->shader_type, regtype, regnum))
995
996
997
fail(ctx, "Swizzle specified for scalar register");
else if (!nexttoken(ctx, 0, 1, 0, 0))
return retval;
998
else if (tokeq(tctx, ""))
999
invalid_swizzle = 1;
1000
else