]> git.tdb.fi Git - libs/gl.git/commitdiff
Add basic validation to the GLSL compiler
authorMikko Rasa <tdb@tdb.fi>
Thu, 4 Mar 2021 10:50:52 +0000 (12:50 +0200)
committerMikko Rasa <tdb@tdb.fi>
Thu, 4 Mar 2021 15:32:04 +0000 (17:32 +0200)
Currently the only thing validated is multiple definitions.

source/glsl/compiler.cpp
source/glsl/compiler.h
source/glsl/glsl_error.h
source/glsl/syntax.h
source/glsl/validate.cpp [new file with mode: 0644]
source/glsl/validate.h [new file with mode: 0644]

index 7fe608bf6b0c80cce51a987f79ea4be225632494..803862cb45787974a21a50628d9495009eb2fbca 100644 (file)
@@ -6,9 +6,11 @@
 #include "debug.h"
 #include "error.h"
 #include "generate.h"
+#include "glsl_error.h"
 #include "optimize.h"
 #include "output.h"
 #include "resources.h"
+#include "validate.h"
 
 #undef interface
 
@@ -74,6 +76,14 @@ void Compiler::compile(Mode mode)
 {
        for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
                generate(*i, mode);
+
+       bool valid = true;
+       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
+               if(!validate(*i))
+                       valid = false;
+       if(!valid)
+               throw invalid_shader_source(get_diagnostics());
+
        unsigned n = 0;
        for(list<Stage>::iterator i=module->stages.begin(); (i!=module->stages.end() && n<10000); ++n)
        {
@@ -150,6 +160,15 @@ string Compiler::get_stage_debug(Stage::Type stage_type) const
        throw key_error(Stage::get_stage_name(stage_type));
 }
 
+string Compiler::get_diagnostics() const
+{
+       string combined;
+       for(list<Stage>::const_iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
+               for(vector<Diagnostic>::const_iterator j=i->diagnostics.begin(); j!=i->diagnostics.end(); ++j)
+                       combined += format("%s:%d: %s\n", module->source_map.get_name(j->source), j->line, j->message);
+       return combined;
+}
+
 void Compiler::append_module(Module &mod, DataFile::Collection *res)
 {
        module->source_map.merge_from(mod.source_map);
@@ -237,6 +256,17 @@ void Compiler::generate(Stage &stage, Mode mode)
                LegacyConverter().apply(stage, features);
 }
 
+bool Compiler::validate(Stage &stage)
+{
+       DeclarationValidator().apply(stage);
+
+       for(vector<Diagnostic>::const_iterator i=stage.diagnostics.begin(); i!=stage.diagnostics.end(); ++i)
+               if(i->severity==Diagnostic::ERR)
+                       return false;
+
+       return true;
+}
+
 Compiler::OptimizeResult Compiler::optimize(Stage &stage)
 {
        ConstantConditionEliminator().apply(stage);
index ec410c03ce45419c9a32de5cc6c0d5e27da5bd2a..29561407f9fa80282fbbd5296fe7736cca0aac43 100644 (file)
@@ -95,6 +95,10 @@ public:
        Intended for debugging purposes. */
        std::string get_stage_debug(Stage::Type) const;
 
+       /** Returns diagnostics from compilation.  The output is intended to be
+       viewed by humans. */
+       std::string get_diagnostics() const;
+
 private:
        /** Appends a module to the target, processing any imports found in it. */
        void append_module(Module &, DataFile::Collection *);
@@ -109,6 +113,8 @@ private:
        variables. */
        void generate(Stage &, Mode);
 
+       bool validate(Stage &);
+
        /** Applies optimizations to a stage.  The return value indicates which
        stage should be optimized next. */
        OptimizeResult optimize(Stage &);
index 1b005e0fc9db96c4230a9268fc8cc54e7eedf1f5..1fd7169c1b3affad1e6d390b04ef6d8213b6f934 100644 (file)
@@ -45,6 +45,23 @@ public:
        virtual ~unsupported_shader() throw() { }
 };
 
+struct Diagnostic
+{
+       enum Severity
+       {
+               INFO,
+               WARN,
+               ERR
+       };
+
+       Severity severity;
+       int source;
+       unsigned line;
+       std::string message;
+
+       Diagnostic(): severity(INFO), source(-2), line(0) { }
+};
+
 } // namespace SL
 } // namespace GL
 } // namespace Msp
index 384bd60f603dc04cef77d41a67f5f190d5414851..da7b0907e37460d5b7041d32e2c921dee9473563 100644 (file)
@@ -8,6 +8,7 @@
 #include <vector>
 #include <msp/core/refptr.h>
 #include "features.h"
+#include "glsl_error.h"
 #include "sourcemap.h"
 
 #pragma push_macro("interface")
@@ -420,6 +421,7 @@ struct Stage
        std::map<std::string, FunctionDeclaration *> functions;
        std::map<std::string, unsigned> locations;
        Features required_features;
+       std::vector<Diagnostic> diagnostics;
 
        Stage(Type);
 
diff --git a/source/glsl/validate.cpp b/source/glsl/validate.cpp
new file mode 100644 (file)
index 0000000..6368c06
--- /dev/null
@@ -0,0 +1,83 @@
+#include <msp/core/raii.h>
+#include <msp/strings/format.h>
+#include "validate.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+namespace SL {
+
+Validator::Validator():
+       stage(0)
+{ }
+
+void Validator::diagnose(Statement *statement, Diagnostic::Severity severity, const string &message)
+{
+       Diagnostic diag;
+       diag.severity = severity;
+       if(statement)
+       {
+               diag.source = statement->source;
+               diag.line = statement->line;
+       }
+       diag.message = message;
+       stage->diagnostics.push_back(diag);
+}
+
+
+DeclarationValidator::DeclarationValidator():
+       anonymous_block(false)
+{ }
+
+Statement *DeclarationValidator::find_definition(const string &name)
+{
+       BlockDeclarationMap *decls = &declarations[current_block];
+       BlockDeclarationMap::const_iterator i = decls->find(name);
+       if(i==decls->end() && anonymous_block)
+       {
+               decls = &declarations[current_block->parent];
+               i = decls->find(name);
+       }
+       return (i!=decls->end() ? i->second : 0);
+}
+
+void DeclarationValidator::check_definition(const string &name, Statement &statement)
+{
+       if(Statement *previous = find_definition(name))
+       {
+               error(&statement, format("Multiple definition of '%s'", name));
+               diagnose(previous, Diagnostic::INFO, "Previous definition is here");
+               return;
+       }
+
+       declarations[current_block][name] = &statement;
+       if(anonymous_block)
+               declarations[current_block->parent][name] = &statement;
+}
+
+void DeclarationValidator::visit(VariableDeclaration &var)
+{
+       check_definition(var.name, var);
+       TraversingVisitor::visit(var);
+}
+
+void DeclarationValidator::visit(InterfaceBlock &iface)
+{
+       check_definition(iface.name, iface);
+       if(!iface.instance_name.empty())
+               check_definition(iface.instance_name, iface);
+       SetFlag set_anon(anonymous_block, iface.instance_name.empty());
+       TraversingVisitor::visit(iface);
+}
+
+void DeclarationValidator::visit(FunctionDeclaration &func)
+{
+       if(func.definition==&func)
+               check_definition(func.name, func);
+       TraversingVisitor::visit(func);
+}
+
+} // namespace SL
+} // namespace GL
+} // namespace Msp
diff --git a/source/glsl/validate.h b/source/glsl/validate.h
new file mode 100644 (file)
index 0000000..f784ea4
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef MSP_GL_SL_VALIDATE_H_
+#define MSP_GL_SL_VALIDATE_H_
+
+#include <string>
+#include <vector>
+#include "glsl_error.h"
+#include "visitor.h"
+
+namespace Msp {
+namespace GL {
+namespace SL {
+
+class Validator: protected TraversingVisitor
+{
+protected:
+       Stage *stage;
+
+       Validator();
+
+       void diagnose(Statement *, Diagnostic::Severity, const std::string &);
+       void error(Statement *s, const std::string &m) { diagnose(s, Diagnostic::ERR, m); }
+};
+
+class DeclarationValidator: private Validator
+{
+private:
+       typedef std::map<std::string, Statement *> BlockDeclarationMap;
+
+       std::map<Block *, BlockDeclarationMap> declarations;
+       bool anonymous_block;
+
+public:
+       DeclarationValidator();
+
+       void apply(Stage &s) { stage = &s; s.content.visit(*this); }
+
+private:
+       Statement *find_definition(const std::string &);
+       void check_definition(const std::string &, Statement &);
+
+       virtual void visit(VariableDeclaration &);
+       virtual void visit(InterfaceBlock &);
+       virtual void visit(FunctionDeclaration &);
+};
+
+} // namespace SL
+} // namespace GL
+} // namespace Msp
+
+#endif