namespace GL {
namespace SL {
+namespace {
+
+enum ResolveFlags
+{
+ RESOLVE_NONE = 0,
+ RESOLVE_BLOCKS = 1,
+ RESOLVE_TYPES = 2,
+ RESOLVE_VARIABLES = 4,
+ RESOLVE_EXPRESSIONS = 8,
+ RESOLVE_FUNCTIONS = 16,
+ RESOLVE_ALL = 31
+};
+
+template<typename T, typename... Args>
+auto run(Args &&... args)
+{
+ return [&args...](Stage &s){ T().apply(s, args...); };
+}
+
+template<typename T>
+bool resolve(Stage &stage, unsigned &flags, unsigned bit)
+{
+ if(!(flags&bit))
+ return false;
+
+ flags &= ~bit;
+ return T().apply(stage);
+}
+
+/** Resolves various references between nodes. Flags can be specified to
+request resolving particular aspects. Resolving may ripple into other
+aspects as necessary. */
+void resolve(Stage &stage, unsigned flags)
+{
+ while(flags)
+ {
+ if(resolve<BlockHierarchyResolver>(stage, flags, RESOLVE_BLOCKS))
+ ;
+ else if(resolve<TypeResolver>(stage, flags, RESOLVE_TYPES))
+ flags |= RESOLVE_BLOCKS|RESOLVE_VARIABLES|RESOLVE_EXPRESSIONS;
+ else if(resolve<VariableResolver>(stage, flags, RESOLVE_VARIABLES))
+ flags |= RESOLVE_EXPRESSIONS;
+ else if(resolve<FunctionResolver>(stage, flags, RESOLVE_FUNCTIONS))
+ flags |= RESOLVE_EXPRESSIONS;
+ else if(resolve<ExpressionResolver>(stage, flags, RESOLVE_EXPRESSIONS))
+ flags |= RESOLVE_VARIABLES|RESOLVE_FUNCTIONS;
+ }
+}
+
+auto resolve(unsigned flags)
+{
+ return [flags](Stage &s){ resolve(s, flags); };
+}
+
+}
+
+
Compiler::Compiler(const Features &f):
features(f)
{ }
if(specialized && mode!=PROGRAM)
throw invalid_operation("Compiler::compile");
- for(Stage &s: module->stages)
- generate(s);
+ // Generate any implicitly defined syntactic structures
+ for_stages([this](Stage &s){ compose(s); },
+ resolve(RESOLVE_ALL),
+ run<InterfaceGenerator>(),
+ resolve(RESOLVE_BLOCKS|RESOLVE_TYPES|RESOLVE_VARIABLES),
+ run<LayoutDefaulter>(),
+ run<ArraySizer>(),
+ resolve(RESOLVE_EXPRESSIONS));
ConstantIdAssigner().apply(*module, features);
LocationAllocator().apply(*module, features, false);
- for(Stage &s: module->stages)
- validate(s);
+ // Run validators, recording diagnostic messages in each stage
+ for_stages(run<DeclarationValidator>(features),
+ run<IdentifierValidator>(),
+ run<ReferenceValidator>(),
+ run<ExpressionValidator>(),
+ run<FlowControlValidator>(),
+ run<StageInterfaceValidator>());
GlobalInterfaceValidator().apply(*module);
+ // Abort compilation if any stage had errors in it
bool valid = true;
for(Stage &s: module->stages)
if(!check_errors(s))
if(!valid)
throw invalid_shader_source(get_diagnostics());
+ // Apply specializations and target-specific code generation
if(specialized)
- {
- for(Stage &s: module->stages)
- ConstantSpecializer().apply(s, spec_values);
- }
+ for_stages(run<ConstantSpecializer>(spec_values));
if(mode==PROGRAM)
DepthRangeConverter().apply(*module, features);
+
+ // Optimize
for(auto i=module->stages.begin(); i!=module->stages.end(); )
{
- OptimizeResult result = optimize(*i);
+ StageResult result = optimize(*i);
if(result==REDO_PREVIOUS)
i = module->stages.begin();
else if(result!=REDO_STAGE)
++i;
}
+ // Remove any stages from which all functions were optimized out
Stage *prev_stage = nullptr;
for(auto i=module->stages.begin(); i!=module->stages.end(); )
{
}
}
+ // Perform final adjustments based on target features
if(mode!=MODULE)
- {
- for(Stage &s: module->stages)
- {
- StructuralFeatureConverter().apply(s, features);
- resolve(s, RESOLVE_VARIABLES|RESOLVE_FUNCTIONS);
- }
- }
+ for_stages(run<StructuralFeatureConverter>(features),
+ resolve(RESOLVE_VARIABLES|RESOLVE_FUNCTIONS));
LocationAllocator().apply(*module, features);
- for(Stage &s: module->stages)
- finalize(s, mode);
+ if(mode!=MODULE)
+ for_stages(run<QualifierConverter>(features),
+ run<PrecisionConverter>());
+ if(mode==SPIRV)
+ for_stages(run<StructOrganizer>());
+
+ // Collect bindings from all stages into the shared stage's maps
+ for_stages([&shared = module->shared](Stage &s){
+ shared.texture_bindings.insert(s.texture_bindings.begin(), s.texture_bindings.end());
+ shared.uniform_block_bindings.insert(s.uniform_block_bindings.begin(), s.uniform_block_bindings.end());
+ });
compiled = true;
}
return combined;
}
+template<typename... Fs>
+void Compiler::for_stages(const Fs &... funcs)
+{
+ for(Stage &s: module->stages)
+ (funcs(s), ...);
+}
+
void Compiler::append_module(const Module &mod, ModuleCache &mod_cache)
{
module->source_map.merge_from(mod.source_map);
append_module(mod_cache.get_module(name), mod_cache);
}
-void Compiler::generate(Stage &stage)
+void Compiler::compose(Stage &stage)
{
stage.required_features.target_api = features.target_api;
if(stage.required_features.glsl_version<module->shared.required_features.glsl_version)
inject_block(stage.content, builtins->content);
if(const Stage *builtins = get_builtins(Stage::SHARED))
inject_block(stage.content, builtins->content);
-
- // Initial resolving pass
- resolve(stage);
-
- /* All variables local to a stage have been resolved. Resolve non-local
- variables through interfaces. */
- InterfaceGenerator().apply(stage);
- resolve(stage, RESOLVE_BLOCKS|RESOLVE_TYPES|RESOLVE_VARIABLES);
-
- LayoutDefaulter().apply(stage);
- ArraySizer().apply(stage);
- resolve(stage, RESOLVE_EXPRESSIONS);
-}
-
-template<typename T>
-bool Compiler::resolve(Stage &stage, unsigned &flags, unsigned bit)
-{
- if(!(flags&bit))
- return false;
-
- flags &= ~bit;
- return T().apply(stage);
-}
-
-void Compiler::resolve(Stage &stage, unsigned flags)
-{
- while(flags)
- {
- if(resolve<BlockHierarchyResolver>(stage, flags, RESOLVE_BLOCKS))
- ;
- else if(resolve<TypeResolver>(stage, flags, RESOLVE_TYPES))
- flags |= RESOLVE_BLOCKS|RESOLVE_VARIABLES|RESOLVE_EXPRESSIONS;
- else if(resolve<VariableResolver>(stage, flags, RESOLVE_VARIABLES))
- flags |= RESOLVE_EXPRESSIONS;
- else if(resolve<FunctionResolver>(stage, flags, RESOLVE_FUNCTIONS))
- flags |= RESOLVE_EXPRESSIONS;
- else if(resolve<ExpressionResolver>(stage, flags, RESOLVE_EXPRESSIONS))
- flags |= RESOLVE_VARIABLES|RESOLVE_FUNCTIONS;
- }
-}
-
-void Compiler::validate(Stage &stage)
-{
- DeclarationValidator().apply(stage, features);
- IdentifierValidator().apply(stage);
- ReferenceValidator().apply(stage);
- ExpressionValidator().apply(stage);
- FlowControlValidator().apply(stage);
- StageInterfaceValidator().apply(stage);
}
bool Compiler::check_errors(Stage &stage)
return any_removed ? REDO_PREVIOUS : any_inlined ? REDO_STAGE : NEXT_STAGE;
}
-void Compiler::finalize(Stage &stage, Mode mode)
-{
- if(mode!=MODULE)
- {
- QualifierConverter().apply(stage, features);
- PrecisionConverter().apply(stage);
- }
- if(mode==SPIRV)
- StructOrganizer().apply(stage);
-
- // Collect bindings from all stages into the shared stage's maps
- module->shared.texture_bindings.insert(stage.texture_bindings.begin(), stage.texture_bindings.end());
- module->shared.uniform_block_bindings.insert(stage.uniform_block_bindings.begin(), stage.uniform_block_bindings.end());
-}
-
void Compiler::inject_block(Block &target, const Block &source)
{
auto insert_point = target.body.begin();
REDO_PREVIOUS
};
- enum ResolveFlags
- {
- RESOLVE_BLOCKS = 1,
- RESOLVE_TYPES = 2,
- RESOLVE_VARIABLES = 4,
- RESOLVE_EXPRESSIONS = 8,
- RESOLVE_FUNCTIONS = 16,
- RESOLVE_ALL = 31
- };
-
Features features;
Module *module = nullptr;
std::vector<std::string> imported_names;
std::string get_diagnostics() const;
private:
+ template<typename... Fs>
+ void for_stages(const Fs &... funcs);
+
/** Appends a module to the target, processing any imports found in it. */
void append_module(const Module &, ModuleCache &);
/** Imports a module by name and appends it to the target. */
void import(ModuleCache &, const std::string &);
- /** Generates any implicitly defines syntactic structures and resolves
- variables. */
- void generate(Stage &);
-
- template<typename T>
- bool resolve(Stage &, unsigned &, unsigned);
-
- /** Resolves various references between nodes. Flags can be specified to
- request resolving particular aspects. Resolving may ripple into other
- aspects as necessary. */
- void resolve(Stage &, unsigned = RESOLVE_ALL);
-
- /** Runs validators on a stage. Diagnostic messages are recorded in the
- stage for later inspection. */
- void validate(Stage &);
+ /** Adds the shared part and builtins to each stage. */
+ void compose(Stage &);
/** Checks a stage's recorded diagnostics for errors. If any are found,
returns true. */
stage should be optimized next. */
OptimizeResult optimize(Stage &);
- /** Performs final adjustments on a stage after compilation. */
- void finalize(Stage &, Mode);
-
static void inject_block(Block &, const Block &);
};