#include "ir_optimization.h"
#include "program.h"
#include "hash_table.h"
+#include "shader_api.h"
/**
* Visitor that determines whether or not a variable is ever written.
void
-linker_error_printf(glsl_program *prog, const char *fmt, ...)
+linker_error_printf(gl_shader_program *prog, const char *fmt, ...)
{
va_list ap;
void
-invalidate_variable_locations(glsl_shader *sh, enum ir_variable_mode mode,
+invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
int generic_base)
{
- foreach_list(node, &sh->ir) {
+ foreach_list(node, sh->ir) {
ir_variable *const var = ((ir_instruction *) node)->as_variable();
if ((var == NULL) || (var->mode != (unsigned) mode))
* \param shader Vertex shader executable to be verified
*/
bool
-validate_vertex_shader_executable(struct glsl_program *prog,
- struct glsl_shader *shader)
+validate_vertex_shader_executable(struct gl_shader_program *prog,
+ struct gl_shader *shader)
{
if (shader == NULL)
return true;
}
find_assignment_visitor find("gl_Position");
- find.run(&shader->ir);
+ find.run(shader->ir);
if (!find.variable_found()) {
linker_error_printf(prog,
"vertex shader does not write to `gl_Position'\n");
* \param shader Fragment shader executable to be verified
*/
bool
-validate_fragment_shader_executable(struct glsl_program *prog,
- struct glsl_shader *shader)
+validate_fragment_shader_executable(struct gl_shader_program *prog,
+ struct gl_shader *shader)
{
if (shader == NULL)
return true;
find_assignment_visitor frag_color("gl_FragColor");
find_assignment_visitor frag_data("gl_FragData");
- frag_color.run(&shader->ir);
- frag_data.run(&shader->ir);
-
- if (!frag_color.variable_found() && !frag_data.variable_found()) {
- linker_error_printf(prog, "fragment shader does not write to "
- "`gl_FragColor' or `gl_FragData'\n");
- return false;
- }
+ frag_color.run(shader->ir);
+ frag_data.run(shader->ir);
if (frag_color.variable_found() && frag_data.variable_found()) {
linker_error_printf(prog, "fragment shader writes to both "
/**
- * Perform validation of uniforms used across multiple shader stages
+ * Generate a string describing the mode of a variable
+ */
+static const char *
+mode_string(const ir_variable *var)
+{
+ switch (var->mode) {
+ case ir_var_auto:
+ return (var->read_only) ? "global constant" : "global variable";
+
+ case ir_var_uniform: return "uniform";
+ case ir_var_in: return "shader input";
+ case ir_var_out: return "shader output";
+ case ir_var_inout: return "shader inout";
+ default:
+ assert(!"Should not get here.");
+ return "invalid variable";
+ }
+}
+
+
+/**
+ * Perform validation of global variables used across multiple shaders
*/
bool
-cross_validate_uniforms(struct glsl_program *prog)
+cross_validate_globals(struct gl_shader_program *prog,
+ struct gl_shader **shader_list,
+ unsigned num_shaders,
+ bool uniforms_only)
{
/* Examine all of the uniforms in all of the shaders and cross validate
* them.
*/
- glsl_symbol_table uniforms;
- for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
- foreach_list(node, &prog->_LinkedShaders[i]->ir) {
+ glsl_symbol_table variables;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ foreach_list(node, shader_list[i]->ir) {
ir_variable *const var = ((ir_instruction *) node)->as_variable();
- if ((var == NULL) || (var->mode != ir_var_uniform))
+ if (var == NULL)
continue;
- /* If a uniform with this name has already been seen, verify that the
- * new instance has the same type. In addition, if the uniforms have
+ if (uniforms_only && (var->mode != ir_var_uniform))
+ continue;
+
+ /* If a global with this name has already been seen, verify that the
+ * new instance has the same type. In addition, if the globals have
* initializers, the values of the initializers must be the same.
*/
- ir_variable *const existing = uniforms.get_variable(var->name);
+ ir_variable *const existing = variables.get_variable(var->name);
if (existing != NULL) {
if (var->type != existing->type) {
- linker_error_printf(prog, "uniform `%s' declared as type "
+ linker_error_printf(prog, "%s `%s' declared as type "
"`%s' and type `%s'\n",
+ mode_string(var),
var->name, var->type->name,
existing->type->name);
return false;
}
+ /* FINISHME: Handle non-constant initializers.
+ */
if (var->constant_value != NULL) {
if (existing->constant_value != NULL) {
if (!var->constant_value->has_value(existing->constant_value)) {
- linker_error_printf(prog, "initializers for uniform "
+ linker_error_printf(prog, "initializers for %s "
"`%s' have differing values\n",
- var->name);
+ mode_string(var), var->name);
return false;
}
} else
* have an initializer but a later instance does, copy the
* initializer to the version stored in the symbol table.
*/
- existing->constant_value =
- (ir_constant *)var->constant_value->clone(NULL);
+ existing->constant_value = var->constant_value->clone(NULL);
}
} else
- uniforms.add_variable(var->name, var);
+ variables.add_variable(var->name, var);
}
}
}
+/**
+ * Perform validation of uniforms used across multiple shader stages
+ */
+bool
+cross_validate_uniforms(struct gl_shader_program *prog)
+{
+ return cross_validate_globals(prog, prog->_LinkedShaders,
+ prog->_NumLinkedShaders, true);
+}
+
+
/**
* Validate that outputs from one stage match inputs of another
*/
bool
-cross_validate_outputs_to_inputs(struct glsl_program *prog,
- glsl_shader *producer, glsl_shader *consumer)
+cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
+ gl_shader *producer, gl_shader *consumer)
{
glsl_symbol_table parameters;
/* FINISHME: Figure these out dynamically. */
/* Find all shader outputs in the "producer" stage.
*/
- foreach_list(node, &producer->ir) {
+ foreach_list(node, producer->ir) {
ir_variable *const var = ((ir_instruction *) node)->as_variable();
/* FINISHME: For geometry shaders, this should also look for inout
* matching outputs already in the symbol table must have the same type and
* qualifiers.
*/
- foreach_list(node, &consumer->ir) {
+ foreach_list(node, consumer->ir) {
ir_variable *const input = ((ir_instruction *) node)->as_variable();
/* FINISHME: For geometry shaders, this should also look for inout
}
+/**
+ * Populates a shaders symbol table with all global declarations
+ */
+static void
+populate_symbol_table(gl_shader *sh)
+{
+ sh->symbols = new(sh) glsl_symbol_table;
+
+ foreach_list(node, sh->ir) {
+ ir_instruction *const inst = (ir_instruction *) node;
+ ir_variable *var;
+ ir_function *func;
+
+ if ((func = inst->as_function()) != NULL) {
+ sh->symbols->add_function(func->name, func);
+ } else if ((var = inst->as_variable()) != NULL) {
+ sh->symbols->add_variable(var->name, var);
+ }
+ }
+}
+
+
+/**
+ * Remap variables referenced in an instruction tree
+ *
+ * This is used when instruction trees are cloned from one shader and placed in
+ * another. These trees will contain references to \c ir_variable nodes that
+ * do not exist in the target shader. This function finds these \c ir_variable
+ * references and replaces the references with matching variables in the target
+ * shader.
+ *
+ * If there is no matching variable in the target shader, a clone of the
+ * \c ir_variable is made and added to the target shader. The new variable is
+ * added to \b both the instruction stream and the symbol table.
+ *
+ * \param inst IR tree that is to be processed.
+ * \param symbols Symbol table containing global scope symbols in the
+ * linked shader.
+ * \param instructions Instruction stream where new variable declarations
+ * should be added.
+ */
+void
+remap_variables(ir_instruction *inst, glsl_symbol_table *symbols,
+ exec_list *instructions)
+{
+ class remap_visitor : public ir_hierarchical_visitor {
+ public:
+ remap_visitor(glsl_symbol_table *symbols, exec_list *instructions)
+ {
+ this->symbols = symbols;
+ this->instructions = instructions;
+ }
+
+ virtual ir_visitor_status visit(ir_dereference_variable *ir)
+ {
+ ir_variable *const existing =
+ this->symbols->get_variable(ir->var->name);
+ if (existing != NULL)
+ ir->var = existing;
+ else {
+ ir_variable *copy = ir->var->clone(NULL);
+
+ this->symbols->add_variable(copy->name, copy);
+ this->instructions->push_head(copy);
+ }
+
+ return visit_continue;
+ }
+
+ private:
+ glsl_symbol_table *symbols;
+ exec_list *instructions;
+ };
+
+ remap_visitor v(symbols, instructions);
+
+ inst->accept(&v);
+}
+
+
+/**
+ * Move non-declarations from one instruction stream to another
+ *
+ * The intended usage pattern of this function is to pass the pointer to the
+ * head sentinal of a list (i.e., a pointer to the list cast to an \c exec_node
+ * pointer) for \c last and \c false for \c make_copies on the first
+ * call. Successive calls pass the return value of the previous call for
+ * \c last and \c true for \c make_copies.
+ *
+ * \param instructions Source instruction stream
+ * \param last Instruction after which new instructions should be
+ * inserted in the target instruction stream
+ * \param make_copies Flag selecting whether instructions in \c instructions
+ * should be copied (via \c ir_instruction::clone) into the
+ * target list or moved.
+ *
+ * \return
+ * The new "last" instruction in the target instruction stream. This pointer
+ * is suitable for use as the \c last parameter of a later call to this
+ * function.
+ */
+exec_node *
+move_non_declarations(exec_list *instructions, exec_node *last,
+ bool make_copies, gl_shader *target)
+{
+ foreach_list(node, instructions) {
+ ir_instruction *inst = (ir_instruction *) node;
+
+ if (inst->as_variable() || inst->as_function())
+ continue;
+
+ assert(inst->as_assignment());
+
+ if (make_copies) {
+ inst = inst->clone(NULL);
+ remap_variables(inst, target->symbols, target->ir);
+ } else {
+ inst->remove();
+ }
+
+ last->insert_after(inst);
+ last = inst;
+ }
+
+ return last;
+}
+
+/**
+ * Get the function signature for main from a shader
+ */
+static ir_function_signature *
+get_main_function_signature(gl_shader *sh)
+{
+ ir_function *const f = sh->symbols->get_function("main");
+ if (f != NULL) {
+ exec_list void_parameters;
+
+ /* Look for the 'void main()' signature and ensure that it's defined.
+ * This keeps the linker from accidentally pick a shader that just
+ * contains a prototype for main.
+ *
+ * We don't have to check for multiple definitions of main (in multiple
+ * shaders) because that would have already been caught above.
+ */
+ ir_function_signature *sig = f->matching_signature(&void_parameters);
+ if ((sig != NULL) && sig->is_defined) {
+ return sig;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Combine a group of shaders for a single stage to generate a linked shader
+ *
+ * \note
+ * If this function is supplied a single shader, it is cloned, and the new
+ * shader is returned.
+ */
+static struct gl_shader *
+link_intrastage_shaders(struct gl_shader_program *prog,
+ struct gl_shader **shader_list,
+ unsigned num_shaders)
+{
+ /* Check that global variables defined in multiple shaders are consistent.
+ */
+ if (!cross_validate_globals(prog, shader_list, num_shaders, false))
+ return NULL;
+
+ /* Check that there is only a single definition of each function signature
+ * across all shaders.
+ */
+ for (unsigned i = 0; i < (num_shaders - 1); i++) {
+ foreach_list(node, shader_list[i]->ir) {
+ ir_function *const f = ((ir_instruction *) node)->as_function();
+
+ if (f == NULL)
+ continue;
+
+ for (unsigned j = i + 1; j < num_shaders; j++) {
+ ir_function *const other =
+ shader_list[j]->symbols->get_function(f->name);
+
+ /* If the other shader has no function (and therefore no function
+ * signatures) with the same name, skip to the next shader.
+ */
+ if (other == NULL)
+ continue;
+
+ foreach_iter (exec_list_iterator, iter, *f) {
+ ir_function_signature *sig =
+ (ir_function_signature *) iter.get();
+
+ if (!sig->is_defined || sig->is_built_in)
+ continue;
+
+ ir_function_signature *other_sig =
+ other->exact_matching_signature(& sig->parameters);
+
+ if ((other_sig != NULL) && other_sig->is_defined
+ && !other_sig->is_built_in) {
+ linker_error_printf(prog,
+ "function `%s' is multiply defined",
+ f->name);
+ return NULL;
+ }
+ }
+ }
+ }
+ }
+
+ /* Find the shader that defines main, and make a clone of it.
+ *
+ * Starting with the clone, search for undefined references. If one is
+ * found, find the shader that defines it. Clone the reference and add
+ * it to the shader. Repeat until there are no undefined references or
+ * until a reference cannot be resolved.
+ */
+ gl_shader *main = NULL;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ if (get_main_function_signature(shader_list[i]) != NULL) {
+ main = shader_list[i];
+ break;
+ }
+ }
+
+ if (main == NULL) {
+ linker_error_printf(prog, "%s shader lacks `main'\n",
+ (shader_list[0]->Type == GL_VERTEX_SHADER)
+ ? "vertex" : "fragment");
+ return NULL;
+ }
+
+ gl_shader *const linked = _mesa_new_shader(NULL, 0, main->Type);
+ linked->ir = new(linked) exec_list;
+ clone_ir_list(linked->ir, main->ir);
+
+ populate_symbol_table(linked);
+
+ /* The a pointer to the main function in the final linked shader (i.e., the
+ * copy of the original shader that contained the main function).
+ */
+ ir_function_signature *const main_sig = get_main_function_signature(linked);
+
+ /* Move any instructions other than variable declarations or function
+ * declarations into main.
+ */
+ exec_node *insertion_point = (exec_node *) &main_sig->body;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ insertion_point = move_non_declarations(shader_list[i]->ir,
+ insertion_point,
+ (shader_list[i] != main),
+ linked);
+ }
+
+ /* Resolve initializers for global variables in the linked shader.
+ */
+
+ return linked;
+}
+
+
struct uniform_node {
exec_node link;
struct gl_uniform *u;
};
void
-assign_uniform_locations(struct glsl_program *prog)
+assign_uniform_locations(struct gl_shader_program *prog)
{
/* */
exec_list uniforms;
for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
unsigned next_position = 0;
- foreach_list(node, &prog->_LinkedShaders[i]->ir) {
+ foreach_list(node, prog->_LinkedShaders[i]->ir) {
ir_variable *const var = ((ir_instruction *) node)->as_variable();
if ((var == NULL) || (var->mode != ir_var_uniform))
bool
-assign_attribute_locations(glsl_program *prog, unsigned max_attribute_index)
+assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index)
{
/* Mark invalid attribute locations as being used.
*/
unsigned used_locations = (max_attribute_index >= 32)
? ~0 : ~((1 << max_attribute_index) - 1);
- glsl_shader *const sh = prog->_LinkedShaders[0];
+ gl_shader *const sh = prog->_LinkedShaders[0];
assert(sh->Type == GL_VERTEX_SHADER);
/* Operate in a total of four passes.
unsigned num_attr = 0;
- foreach_list(node, &sh->ir) {
+ foreach_list(node, sh->ir) {
ir_variable *const var = ((ir_instruction *) node)->as_variable();
if ((var == NULL) || (var->mode != ir_var_in))
qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare);
+ /* VERT_ATTRIB_GENERIC0 is a psdueo-alias for VERT_ATTRIB_POS. It can only
+ * be explicitly assigned by via glBindAttribLocation. Mark it as reserved
+ * to prevent it from being automatically allocated below.
+ */
+ used_locations |= (1 << 0);
+
for (unsigned i = 0; i < num_attr; i++) {
/* Mask representing the contiguous slots that will be used by this
* attribute.
void
-assign_varying_locations(glsl_shader *producer, glsl_shader *consumer)
+assign_varying_locations(gl_shader *producer, gl_shader *consumer)
{
/* FINISHME: Set dynamically when geometry shader support is added. */
unsigned output_index = VERT_RESULT_VAR0;
invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0);
invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0);
- foreach_list(node, &producer->ir) {
+ foreach_list(node, producer->ir) {
ir_variable *const output_var = ((ir_instruction *) node)->as_variable();
if ((output_var == NULL) || (output_var->mode != ir_var_out)
input_index++;
}
- foreach_list(node, &producer->ir) {
+ foreach_list(node, producer->ir) {
ir_variable *const var = ((ir_instruction *) node)->as_variable();
if ((var == NULL) || (var->mode != ir_var_out))
/* An 'out' variable is only really a shader output if its value is read
* by the following stage.
*/
- var->shader_out = (var->location != -1);
+ if (var->location == -1) {
+ var->shader_out = false;
+ var->mode = ir_var_auto;
+ }
}
- foreach_list(node, &consumer->ir) {
+ foreach_list(node, consumer->ir) {
ir_variable *const var = ((ir_instruction *) node)->as_variable();
if ((var == NULL) || (var->mode != ir_var_in))
void
-link_shaders(struct glsl_program *prog)
+link_shaders(struct gl_shader_program *prog)
{
prog->LinkStatus = false;
prog->Validated = false;
/* Separate the shaders into groups based on their type.
*/
- struct glsl_shader **vert_shader_list;
+ struct gl_shader **vert_shader_list;
unsigned num_vert_shaders = 0;
- struct glsl_shader **frag_shader_list;
+ struct gl_shader **frag_shader_list;
unsigned num_frag_shaders = 0;
- vert_shader_list = (struct glsl_shader **)
- calloc(2 * prog->NumShaders, sizeof(struct glsl_shader *));
+ vert_shader_list = (struct gl_shader **)
+ calloc(2 * prog->NumShaders, sizeof(struct gl_shader *));
frag_shader_list = &vert_shader_list[prog->NumShaders];
for (unsigned i = 0; i < prog->NumShaders; i++) {
}
/* FINISHME: Implement intra-stage linking. */
- assert(num_vert_shaders <= 1);
- assert(num_frag_shaders <= 1);
-
- /* Verify that each of the per-target executables is valid.
- */
- if (!validate_vertex_shader_executable(prog, vert_shader_list[0])
- || !validate_fragment_shader_executable(prog, frag_shader_list[0]))
- goto done;
+ prog->_NumLinkedShaders = 0;
+ if (num_vert_shaders > 0) {
+ gl_shader *const sh =
+ link_intrastage_shaders(prog, vert_shader_list, num_vert_shaders);
+ if (sh == NULL)
+ goto done;
- prog->_LinkedShaders = (struct glsl_shader **)
- calloc(2, sizeof(struct glsl_shader *));
- prog->_NumLinkedShaders = 0;
+ if (!validate_vertex_shader_executable(prog, sh))
+ goto done;
- if (num_vert_shaders > 0) {
- prog->_LinkedShaders[prog->_NumLinkedShaders] = vert_shader_list[0];
+ prog->_LinkedShaders[prog->_NumLinkedShaders] = sh;
prog->_NumLinkedShaders++;
}
if (num_frag_shaders > 0) {
- prog->_LinkedShaders[prog->_NumLinkedShaders] = frag_shader_list[0];
+ gl_shader *const sh =
+ link_intrastage_shaders(prog, frag_shader_list, num_frag_shaders);
+
+ if (sh == NULL)
+ goto done;
+
+ if (!validate_fragment_shader_executable(prog, sh))
+ goto done;
+
+ prog->_LinkedShaders[prog->_NumLinkedShaders] = sh;
prog->_NumLinkedShaders++;
}