[PATCH vkd3d v3 2/4] vkd3d-shader: Implement basic support for #if and #endif.

Zebediah Figura zfigura at codeweavers.com
Tue Dec 15 17:13:21 CST 2020


Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
 include/private/vkd3d_memory.h           |  11 +++
 libs/vkd3d-shader/preproc.h              |  17 ++++
 libs/vkd3d-shader/preproc.l              | 106 +++++++++++++++++++++--
 libs/vkd3d-shader/preproc.y              | 101 ++++++++++++++++++++-
 libs/vkd3d-shader/vkd3d_shader_main.c    |  24 +++++
 libs/vkd3d-shader/vkd3d_shader_private.h |   6 ++
 tests/hlsl_d3d12.c                       |   2 +-
 7 files changed, 257 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 bd9dfdd3..0f494fcd 100644
--- a/libs/vkd3d-shader/preproc.h
+++ b/libs/vkd3d-shader/preproc.h
@@ -23,6 +23,12 @@
 
 #include "vkd3d_shader_private.h"
 
+struct preproc_if_state
+{
+    /* Are we currently in a "true" block? */
+    bool current_true;
+};
+
 struct preproc_ctx
 {
     void *scanner;
@@ -31,7 +37,18 @@ struct preproc_ctx
     struct vkd3d_string_buffer buffer;
     struct vkd3d_shader_location location;
 
+    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 vkd3d_shader_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 b406af09..c60970cd 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;}
 
@@ -113,6 +139,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);
@@ -122,11 +169,44 @@ 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->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);
+        }
+
+        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->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);
     }
@@ -147,12 +227,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 vkd3d_shader_location loc = {.source_name = ctx.location.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 16b84ea9..024ff4c2 100644
--- a/libs/vkd3d-shader/preproc.y
+++ b/libs/vkd3d-shader/preproc.y
@@ -51,11 +51,70 @@ static void preproc_error(struct preproc_ctx *ctx, const struct vkd3d_shader_loc
     ctx->error = true;
 }
 
+void preproc_warning(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
+        enum vkd3d_shader_error error, const char *format, ...)
+{
+    va_list args;
+
+    va_start(args, format);
+    vkd3d_shader_vwarning(ctx->message_context, loc, 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}
@@ -67,9 +126,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 173ff170..df3b1a57 100644
--- a/libs/vkd3d-shader/vkd3d_shader_main.c
+++ b/libs/vkd3d-shader/vkd3d_shader_main.c
@@ -144,6 +144,30 @@ bool vkd3d_shader_message_context_copy_messages(struct vkd3d_shader_message_cont
     return true;
 }
 
+void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
+        enum vkd3d_shader_error error, const char *format, va_list args)
+{
+    if (context->log_level < VKD3D_SHADER_LOG_WARNING)
+        return;
+
+    if (location)
+    {
+        const char *source_name = location->source_name ? location->source_name : "<anonymous>";
+
+        if (location->line)
+            vkd3d_string_buffer_printf(&context->messages, "%s:%u:%u: W%04u: ",
+                    source_name, location->line, location->column, error);
+        else
+            vkd3d_string_buffer_printf(&context->messages, "%s: W%04u: ", source_name, error);
+    }
+    else
+    {
+        vkd3d_string_buffer_printf(&context->messages, "W%04u: ", 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, const struct vkd3d_shader_location *location,
         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 882a8060..b89d20e2 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           = 4301,
+    VKD3D_SHADER_WARNING_PP_UNKNOWN_DIRECTIVE           = 4303,
+    VKD3D_SHADER_WARNING_PP_UNTERMINATED_IF             = 4305,
 };
 
 enum VKD3D_SHADER_INSTRUCTION_HANDLER
@@ -871,6 +875,8 @@ void vkd3d_shader_error(struct vkd3d_shader_message_context *context, const stru
         enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(4, 5) DECLSPEC_HIDDEN;
 void vkd3d_shader_verror(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
         enum vkd3d_shader_error error, const char *format, va_list args) DECLSPEC_HIDDEN;
+void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location,
+        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, const char *source_name,
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)
-- 
2.29.2




More information about the wine-devel mailing list