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