Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v2: Avoid another instance of YYEOF, which is only available since Bison 3.6.
include/private/vkd3d_memory.h | 11 +++ libs/vkd3d-shader/preproc.h | 17 ++++ libs/vkd3d-shader/preproc.l | 108 +++++++++++++++++++++-- libs/vkd3d-shader/preproc.y | 102 ++++++++++++++++++++- libs/vkd3d-shader/vkd3d_shader_main.c | 15 ++++ libs/vkd3d-shader/vkd3d_shader_private.h | 6 ++ tests/hlsl_d3d12.c | 2 +- 7 files changed, 251 insertions(+), 10 deletions(-)
diff --git a/include/private/vkd3d_memory.h b/include/private/vkd3d_memory.h index df93abf5..bd56d30a 100644 --- a/include/private/vkd3d_memory.h +++ b/include/private/vkd3d_memory.h @@ -22,6 +22,7 @@ #include <assert.h> #include <stdbool.h> #include <stdlib.h> +#include <string.h>
#include "vkd3d_debug.h"
@@ -54,6 +55,16 @@ static inline void vkd3d_free(void *ptr) free(ptr); }
+static inline char *vkd3d_strdup(const char *string) +{ + size_t len = strlen(string) + 1; + char *ptr; + + if ((ptr = vkd3d_malloc(len))) + memcpy(ptr, string, len); + return ptr; +} + bool vkd3d_array_reserve(void **elements, size_t *capacity, size_t element_count, size_t element_size) DECLSPEC_HIDDEN;
diff --git a/libs/vkd3d-shader/preproc.h b/libs/vkd3d-shader/preproc.h index 769b8c23..224ae5b2 100644 --- a/libs/vkd3d-shader/preproc.h +++ b/libs/vkd3d-shader/preproc.h @@ -29,6 +29,12 @@ struct preproc_location unsigned int first_line, first_column; };
+struct preproc_if_state +{ + /* Are we currently in a "true" block? */ + bool current_true; +}; + struct preproc_ctx { void *scanner; @@ -38,7 +44,18 @@ struct preproc_ctx unsigned int line, column; const char *source_name;
+ struct preproc_if_state *if_stack; + size_t if_count, if_stack_size; + + int current_directive; + + bool last_was_newline; + bool last_was_eof; + bool error; };
+void preproc_warning(struct preproc_ctx *ctx, const struct preproc_location *loc, + enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(4, 5) DECLSPEC_HIDDEN; + #endif diff --git a/libs/vkd3d-shader/preproc.l b/libs/vkd3d-shader/preproc.l index 116a7ed5..dd2b9c0a 100644 --- a/libs/vkd3d-shader/preproc.l +++ b/libs/vkd3d-shader/preproc.l @@ -50,6 +50,7 @@ static void update_location(struct preproc_ctx *ctx); %s C_COMMENT %s CXX_COMMENT
+NEWLINE \r?\n WS [ \t] IDENTIFIER [A-Za-z_][A-Za-z0-9_]*
@@ -57,10 +58,10 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]*
<INITIAL>"//" {yy_push_state(CXX_COMMENT, yyscanner);} <INITIAL>"/*" {yy_push_state(C_COMMENT, yyscanner);} -<CXX_COMMENT>\\r?\n {} +<CXX_COMMENT>\{NEWLINE} {} <CXX_COMMENT>\n { yy_pop_state(yyscanner); - return T_TEXT; + return T_NEWLINE; } <C_COMMENT>"*/" {yy_pop_state(yyscanner);} <C_COMMENT,CXX_COMMENT><<EOF>> {yy_pop_state(yyscanner);} @@ -68,13 +69,15 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]*
<INITIAL>{IDENTIFIER} {return T_TEXT;}
+ /* We have no use for floats, but shouldn't parse them as integers. */ + <INITIAL>[0-9]*.[0-9]+([eE][+-]?[0-9]+)?[hHfF]? {return T_TEXT;} <INITIAL>[0-9]+.([eE][+-]?[0-9]+)?[hHfF]? {return T_TEXT;} <INITIAL>[0-9]+([eE][+-]?[0-9]+)?[hHfF] {return T_TEXT;} <INITIAL>[0-9]+[eE][+-]?[0-9]+ {return T_TEXT;} -<INITIAL>0[xX][0-9a-fA-f]+[ul]{0,2} {return T_TEXT;} -<INITIAL>0[0-7]*[ul]{0,2} {return T_TEXT;} -<INITIAL>[1-9][0-9]*[ul]{0,2} {return T_TEXT;} +<INITIAL>0[xX][0-9a-fA-f]+[ul]{0,2} {return T_INTEGER;} +<INITIAL>0[0-7]*[ul]{0,2} {return T_INTEGER;} +<INITIAL>[1-9][0-9]*[ul]{0,2} {return T_INTEGER;}
<INITIAL>"&&" {return T_TEXT;} <INITIAL>"||" {return T_TEXT;} @@ -87,6 +90,29 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]* /* C strings (including escaped quotes). */ <INITIAL>"([^"\]|\.)*" {return T_TEXT;}
+<INITIAL>#{WS}*{IDENTIFIER} { + struct preproc_ctx *ctx = yyget_extra(yyscanner); + const char *p; + + if (!ctx->last_was_newline) + return T_TEXT; + + for (p = yytext + 1; strchr(" \t", *p); ++p) + ; + + if (!strcmp(p, "endif")) + return T_ENDIF; + if (!strcmp(p, "if")) + return T_IF; + + preproc_warning(ctx, yyget_lloc(yyscanner), VKD3D_SHADER_WARNING_PP_UNKNOWN_DIRECTIVE, + "Ignoring unknown directive "%s".", yytext); + return T_TEXT; + } + +<INITIAL>\{NEWLINE} {} +<INITIAL>{NEWLINE} {return T_NEWLINE;} + <INITIAL>{WS}+ {} <INITIAL>. {return T_TEXT;}
@@ -114,6 +140,27 @@ static void update_location(struct preproc_ctx *ctx) } }
+static bool preproc_is_writing(struct preproc_ctx *ctx) +{ + if (!ctx->if_count) + return true; + return ctx->if_stack[ctx->if_count - 1].current_true; +} + +static int return_token(int token, YYSTYPE *lval, const char *text) +{ + switch (token) + { + case T_INTEGER: + case T_TEXT: + if (!(lval->string = vkd3d_strdup(text))) + return 0; + break; + } + + return token; +} + int yylex(YYSTYPE *lval, YYLTYPE *lloc, yyscan_t scanner) { struct preproc_ctx *ctx = yyget_extra(scanner); @@ -123,11 +170,46 @@ int yylex(YYSTYPE *lval, YYLTYPE *lloc, yyscan_t scanner) const char *text; int token;
- if (!(token = preproc_lexer_lex(lval, lloc, scanner))) + if (ctx->last_was_eof) return 0; - text = yyget_text(scanner);
- TRACE("Parsing token %d, line %d, string %s.\n", token, lloc->first_line, debugstr_a(text)); + if (!(token = preproc_lexer_lex(lval, lloc, scanner))) + { + ctx->last_was_eof = true; + token = T_NEWLINE; + text = "\n"; + } + else + { + text = yyget_text(scanner); + } + + lloc->filename = ctx->source_name; + + if (ctx->last_was_newline) + { + switch (token) + { + case T_ENDIF: + case T_IF: + ctx->current_directive = token; + break; + + default: + ctx->current_directive = 0; + } + } + + ctx->last_was_newline = (token == T_NEWLINE); + + TRACE("Parsing token %d, line %d, in directive %d, string %s.\n", token, + lloc->first_line, ctx->current_directive, debugstr_a(text)); + + if (!ctx->current_directive && !preproc_is_writing(ctx)) + continue; + + if (ctx->current_directive) + return return_token(token, lval, text);
vkd3d_string_buffer_printf(&ctx->buffer, "%s ", text); } @@ -148,12 +230,22 @@ int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info,
yylex_init_extra(&ctx, &ctx.scanner); top_buffer = yy_scan_bytes(compile_info->source.code, compile_info->source.size, ctx.scanner); + ctx.last_was_newline = true;
preproc_yyparse(ctx.scanner, &ctx);
yy_delete_buffer(top_buffer, ctx.scanner); yylex_destroy(ctx.scanner);
+ if (ctx.if_count) + { + const struct preproc_location loc = {.filename = ctx.source_name}; + + preproc_warning(&ctx, &loc, VKD3D_SHADER_WARNING_PP_UNTERMINATED_IF, "Unterminated #if block."); + } + + vkd3d_free(ctx.if_stack); + if (ctx.error) { WARN("Failed to preprocess.\n"); diff --git a/libs/vkd3d-shader/preproc.y b/libs/vkd3d-shader/preproc.y index 186a0e1d..fda04a07 100644 --- a/libs/vkd3d-shader/preproc.y +++ b/libs/vkd3d-shader/preproc.y @@ -74,11 +74,71 @@ static void preproc_error(struct preproc_ctx *ctx, const struct preproc_location ctx->error = true; }
+void preproc_warning(struct preproc_ctx *ctx, const struct preproc_location *loc, + enum vkd3d_shader_error error, const char *format, ...) +{ + va_list args; + + set_location(ctx, loc); + va_start(args, format); + vkd3d_shader_vwarning(ctx->message_context, error, format, args); + va_end(args); +} + static void yyerror(const YYLTYPE *loc, void *scanner, struct preproc_ctx *ctx, const char *string) { preproc_error(ctx, loc, VKD3D_SHADER_ERROR_PP_INVALID_SYNTAX, "%s", string); }
+static bool preproc_was_writing(struct preproc_ctx *ctx) +{ + if (ctx->if_count < 2) + return true; + return ctx->if_stack[ctx->if_count - 2].current_true; +} + +static bool preproc_push_if(struct preproc_ctx *ctx, bool condition) +{ + struct preproc_if_state *state; + + if (!vkd3d_array_reserve((void **)&ctx->if_stack, &ctx->if_stack_size, ctx->if_count + 1, sizeof(*ctx->if_stack))) + return false; + state = &ctx->if_stack[ctx->if_count++]; + state->current_true = condition && preproc_was_writing(ctx); + return true; +} + +static int char_to_int(char c) +{ + if ('0' <= c && c <= '9') + return c - '0'; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + return -1; +} + +static uint32_t preproc_parse_integer(const char *s) +{ + uint32_t base = 10, ret = 0; + int digit; + + if (s[0] == '0') + { + base = 8; + if (s[1] == 'x' || s[1] == 'X') + { + base = 16; + s += 2; + } + } + + while ((digit = char_to_int(*s++)) >= 0) + ret = ret * base + (uint32_t)digit; + return ret; +} + }
%define api.prefix {preproc_yy} @@ -90,9 +150,49 @@ static void yyerror(const YYLTYPE *loc, void *scanner, struct preproc_ctx *ctx, %parse-param {void *scanner} %parse-param {struct preproc_ctx *ctx}
-%token T_TEXT +%union +{ + char *string; + uint32_t integer; +} + +%token <string> T_INTEGER +%token <string> T_TEXT + +%token T_NEWLINE + +%token T_ENDIF "#endif" +%token T_IF "#if" + +%type <integer> expr
%%
shader_text : %empty + | shader_text directive + { + vkd3d_string_buffer_printf(&ctx->buffer, "\n"); + } + +directive + : T_IF expr T_NEWLINE + { + if (!preproc_push_if(ctx, !!$2)) + YYABORT; + } + | T_ENDIF T_NEWLINE + { + if (ctx->if_count) + --ctx->if_count; + else + preproc_warning(ctx, &@$, VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE, + "Ignoring #endif without prior #if."); + } + +expr + : T_INTEGER + { + $$ = preproc_parse_integer($1); + vkd3d_free($1); + } diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index eed0316c..ad456133 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -148,6 +148,21 @@ bool vkd3d_shader_message_context_copy_messages(struct vkd3d_shader_message_cont return true; }
+void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, + enum vkd3d_shader_error error, const char *format, va_list args) +{ + if (context->log_level < VKD3D_SHADER_LOG_WARNING) + return; + + if (context->line) + vkd3d_string_buffer_printf(&context->messages, "%s:%u:%u: W%04u: ", + context->source_name, context->line, context->column, error); + else + vkd3d_string_buffer_printf(&context->messages, "%s: W%04u: ", context->source_name, error); + vkd3d_string_buffer_vprintf(&context->messages, format, args); + vkd3d_string_buffer_printf(&context->messages, "\n"); +} + void vkd3d_shader_verror(struct vkd3d_shader_message_context *context, enum vkd3d_shader_error error, const char *format, va_list args) { diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 5a022708..01a74ede 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -81,6 +81,10 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_RS_MIXED_DESCRIPTOR_RANGE_TYPES = 3004,
VKD3D_SHADER_ERROR_PP_INVALID_SYNTAX = 4000, + + VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE = 5001, + VKD3D_SHADER_WARNING_PP_UNKNOWN_DIRECTIVE = 5003, + VKD3D_SHADER_WARNING_PP_UNTERMINATED_IF = 5005, };
enum VKD3D_SHADER_INSTRUCTION_HANDLER @@ -867,6 +871,8 @@ void vkd3d_shader_error(struct vkd3d_shader_message_context *context, enum vkd3d const char *format, ...) VKD3D_PRINTF_FUNC(3, 4) DECLSPEC_HIDDEN; void vkd3d_shader_verror(struct vkd3d_shader_message_context *context, enum vkd3d_shader_error error, const char *format, va_list args) DECLSPEC_HIDDEN; +void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, + enum vkd3d_shader_error error, const char *format, va_list args) DECLSPEC_HIDDEN;
int shader_extract_from_dxbc(const void *dxbc, size_t dxbc_length, struct vkd3d_shader_message_context *message_context, struct vkd3d_shader_desc *desc) DECLSPEC_HIDDEN; diff --git a/tests/hlsl_d3d12.c b/tests/hlsl_d3d12.c index 61324fa9..77a7ea1a 100644 --- a/tests/hlsl_d3d12.c +++ b/tests/hlsl_d3d12.c @@ -414,7 +414,7 @@ static void test_preprocess(void) hr = D3DPreprocess(test_include_top, strlen(test_include_top), NULL, NULL, &test_include_fail, &blob, &errors); todo ok(hr == E_FAIL, "Got hr %#x.\n", hr); todo ok(blob == (ID3D10Blob *)0xdeadbeef, "Expected no compiled shader blob.\n"); - todo ok(!!errors, "Expected non-NULL error blob.\n"); + ok(!!errors, "Expected non-NULL error blob.\n"); if (errors) { if (vkd3d_test_state.debug_level)