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