From 1f09306906fbf57c05dccb27189264706cc64cfa Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 4 Mar 2021 12:50:52 +0200 Subject: [PATCH] Add basic validation to the GLSL compiler Currently the only thing validated is multiple definitions. --- source/glsl/compiler.cpp | 30 +++++++++++++++ source/glsl/compiler.h | 6 +++ source/glsl/glsl_error.h | 17 ++++++++ source/glsl/syntax.h | 2 + source/glsl/validate.cpp | 83 ++++++++++++++++++++++++++++++++++++++++ source/glsl/validate.h | 50 ++++++++++++++++++++++++ 6 files changed, 188 insertions(+) create mode 100644 source/glsl/validate.cpp create mode 100644 source/glsl/validate.h diff --git a/source/glsl/compiler.cpp b/source/glsl/compiler.cpp index 7fe608bf..803862cb 100644 --- a/source/glsl/compiler.cpp +++ b/source/glsl/compiler.cpp @@ -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::iterator i=module->stages.begin(); i!=module->stages.end(); ++i) generate(*i, mode); + + bool valid = true; + for(list::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::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::const_iterator i=module->stages.begin(); i!=module->stages.end(); ++i) + for(vector::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::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); diff --git a/source/glsl/compiler.h b/source/glsl/compiler.h index ec410c03..29561407 100644 --- a/source/glsl/compiler.h +++ b/source/glsl/compiler.h @@ -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 &); diff --git a/source/glsl/glsl_error.h b/source/glsl/glsl_error.h index 1b005e0f..1fd7169c 100644 --- a/source/glsl/glsl_error.h +++ b/source/glsl/glsl_error.h @@ -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 diff --git a/source/glsl/syntax.h b/source/glsl/syntax.h index 384bd60f..da7b0907 100644 --- a/source/glsl/syntax.h +++ b/source/glsl/syntax.h @@ -8,6 +8,7 @@ #include #include #include "features.h" +#include "glsl_error.h" #include "sourcemap.h" #pragma push_macro("interface") @@ -420,6 +421,7 @@ struct Stage std::map functions; std::map locations; Features required_features; + std::vector diagnostics; Stage(Type); diff --git a/source/glsl/validate.cpp b/source/glsl/validate.cpp new file mode 100644 index 00000000..6368c069 --- /dev/null +++ b/source/glsl/validate.cpp @@ -0,0 +1,83 @@ +#include +#include +#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 index 00000000..f784ea45 --- /dev/null +++ b/source/glsl/validate.h @@ -0,0 +1,50 @@ +#ifndef MSP_GL_SL_VALIDATE_H_ +#define MSP_GL_SL_VALIDATE_H_ + +#include +#include +#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 BlockDeclarationMap; + + std::map 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 -- 2.43.0