This patch changes link_shaders() so that it sets prog->LinkStatus to
true when it starts, and then relies on linker_error() to set it to
false if a link failure occurs.
Previously, link_shaders() would set prog->LinkStatus to true halfway
through its execution; as a result, linker functions that executed
during the first half of link_shaders() would have to do their own
success/failure tracking; if they didn't, then calling linker_error()
would add an error message to the log, but not cause the link to fail.
Since it wasn't always obvious from looking at a linker function
whether it was called before or after link_shaders() set
prog->LinkStatus to true, this carried a high risk of bugs.
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
#include "linker.h"
#include "main/macros.h"
#include "linker.h"
#include "main/macros.h"
validate_intrastage_interface_blocks(struct gl_shader_program *prog,
const gl_shader **shader_list,
unsigned num_shaders)
validate_intrastage_interface_blocks(struct gl_shader_program *prog,
const gl_shader **shader_list,
unsigned num_shaders)
} else if (old_iface_type != iface_type) {
linker_error(prog, "definitions of interface block `%s' do not"
" match\n", iface_type->name);
} else if (old_iface_type != iface_type) {
linker_error(prog, "definitions of interface block `%s' do not"
" match\n", iface_type->name);
-bool
-validate_interstage_interface_blocks(const gl_shader *producer,
+void
+validate_interstage_interface_blocks(struct gl_shader_program *prog,
+ const gl_shader *producer,
const gl_shader *consumer)
{
glsl_symbol_table interfaces;
const gl_shader *consumer)
{
glsl_symbol_table interfaces;
if (expected_type == NULL)
continue;
if (expected_type == NULL)
continue;
- if (var->interface_type != expected_type)
- return false;
+ if (var->interface_type != expected_type) {
+ linker_error(prog, "interface block mismatch between shader stages\n");
+ return;
+ }
/**
* Validate that outputs from one stage match inputs of another
*/
/**
* Validate that outputs from one stage match inputs of another
*/
cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
gl_shader *producer, gl_shader *consumer)
{
cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
gl_shader *producer, gl_shader *consumer)
{
producer_stage, output->name,
output->type->name,
consumer_stage, input->type->name);
producer_stage, output->name,
output->type->name,
consumer_stage, input->type->name);
(output->centroid) ? "has" : "lacks",
consumer_stage,
(input->centroid) ? "has" : "lacks");
(output->centroid) ? "has" : "lacks",
consumer_stage,
(input->centroid) ? "has" : "lacks");
}
if (input->invariant != output->invariant) {
}
if (input->invariant != output->invariant) {
(output->invariant) ? "has" : "lacks",
consumer_stage,
(input->invariant) ? "has" : "lacks");
(output->invariant) ? "has" : "lacks",
consumer_stage,
(input->invariant) ? "has" : "lacks");
}
if (input->interpolation != output->interpolation) {
}
if (input->interpolation != output->interpolation) {
output->interpolation_string(),
consumer_stage,
input->interpolation_string());
output->interpolation_string(),
consumer_stage,
input->interpolation_string());
cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
gl_shader *producer, gl_shader *consumer);
cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
gl_shader *producer, gl_shader *consumer);
*
* \param shader Vertex shader executable to be verified
*/
*
* \param shader Vertex shader executable to be verified
*/
validate_vertex_shader_executable(struct gl_shader_program *prog,
struct gl_shader *shader)
{
if (shader == NULL)
validate_vertex_shader_executable(struct gl_shader_program *prog,
struct gl_shader *shader)
{
if (shader == NULL)
/* From the GLSL 1.10 spec, page 48:
*
/* From the GLSL 1.10 spec, page 48:
*
find.run(shader->ir);
if (!find.variable_found()) {
linker_error(prog, "vertex shader does not write to `gl_Position'\n");
find.run(shader->ir);
if (!find.variable_found()) {
linker_error(prog, "vertex shader does not write to `gl_Position'\n");
if (clip_vertex.variable_found() && clip_distance.variable_found()) {
linker_error(prog, "vertex shader writes to both `gl_ClipVertex' "
"and `gl_ClipDistance'\n");
if (clip_vertex.variable_found() && clip_distance.variable_found()) {
linker_error(prog, "vertex shader writes to both `gl_ClipVertex' "
"and `gl_ClipDistance'\n");
}
prog->Vert.UsesClipDistance = clip_distance.variable_found();
ir_variable *clip_distance_var =
}
prog->Vert.UsesClipDistance = clip_distance.variable_found();
ir_variable *clip_distance_var =
if (clip_distance_var)
prog->Vert.ClipDistanceArraySize = clip_distance_var->type->length;
}
if (clip_distance_var)
prog->Vert.ClipDistanceArraySize = clip_distance_var->type->length;
}
*
* \param shader Fragment shader executable to be verified
*/
*
* \param shader Fragment shader executable to be verified
*/
validate_fragment_shader_executable(struct gl_shader_program *prog,
struct gl_shader *shader)
{
if (shader == NULL)
validate_fragment_shader_executable(struct gl_shader_program *prog,
struct gl_shader *shader)
{
if (shader == NULL)
find_assignment_visitor frag_color("gl_FragColor");
find_assignment_visitor frag_data("gl_FragData");
find_assignment_visitor frag_color("gl_FragColor");
find_assignment_visitor frag_data("gl_FragData");
if (frag_color.variable_found() && frag_data.variable_found()) {
linker_error(prog, "fragment shader writes to both "
"`gl_FragColor' and `gl_FragData'\n");
if (frag_color.variable_found() && frag_data.variable_found()) {
linker_error(prog, "fragment shader writes to both "
"`gl_FragColor' and `gl_FragData'\n");
/**
* Perform validation of global variables used across multiple shaders
*/
/**
* Perform validation of global variables used across multiple shaders
*/
cross_validate_globals(struct gl_shader_program *prog,
struct gl_shader **shader_list,
unsigned num_shaders,
cross_validate_globals(struct gl_shader_program *prog,
struct gl_shader **shader_list,
unsigned num_shaders,
mode_string(var),
var->name, var->type->name,
existing->type->name);
mode_string(var),
var->name, var->type->name,
existing->type->name);
linker_error(prog, "explicit locations for %s "
"`%s' have differing values\n",
mode_string(var), var->name);
linker_error(prog, "explicit locations for %s "
"`%s' have differing values\n",
mode_string(var), var->name);
}
existing->location = var->location;
}
existing->location = var->location;
linker_error(prog, "explicit bindings for %s "
"`%s' have differing values\n",
mode_string(var), var->name);
linker_error(prog, "explicit bindings for %s "
"`%s' have differing values\n",
mode_string(var), var->name);
}
existing->binding = var->binding;
}
existing->binding = var->binding;
linker_error(prog, "initializers for %s "
"`%s' have differing values\n",
mode_string(var), var->name);
linker_error(prog, "initializers for %s "
"`%s' have differing values\n",
mode_string(var), var->name);
}
} else {
/* If the first-seen instance of a particular uniform did not
}
} else {
/* If the first-seen instance of a particular uniform did not
"shared global variable `%s' has multiple "
"non-constant initializers.\n",
var->name);
"shared global variable `%s' has multiple "
"non-constant initializers.\n",
var->name);
}
/* Some instance had an initializer, so keep track of that. In
}
/* Some instance had an initializer, so keep track of that. In
linker_error(prog, "declarations for %s `%s' have "
"mismatching invariant qualifiers\n",
mode_string(var), var->name);
linker_error(prog, "declarations for %s `%s' have "
"mismatching invariant qualifiers\n",
mode_string(var), var->name);
}
if (existing->centroid != var->centroid) {
linker_error(prog, "declarations for %s `%s' have "
"mismatching centroid qualifiers\n",
mode_string(var), var->name);
}
if (existing->centroid != var->centroid) {
linker_error(prog, "declarations for %s `%s' have "
"mismatching centroid qualifiers\n",
mode_string(var), var->name);
}
} else
variables.add_variable(var);
}
}
}
} else
variables.add_variable(var);
}
}
}
/**
* Perform validation of uniforms used across multiple shader stages
*/
}
/**
* Perform validation of uniforms used across multiple shader stages
*/
cross_validate_uniforms(struct gl_shader_program *prog)
{
cross_validate_uniforms(struct gl_shader_program *prog)
{
- return cross_validate_globals(prog, prog->_LinkedShaders,
- MESA_SHADER_TYPES, true);
+ cross_validate_globals(prog, prog->_LinkedShaders,
+ MESA_SHADER_TYPES, true);
/* Check that global variables defined in multiple shaders are consistent.
*/
/* Check that global variables defined in multiple shaders are consistent.
*/
- if (!cross_validate_globals(prog, shader_list, num_shaders, false))
+ cross_validate_globals(prog, shader_list, num_shaders, false);
+ if (!prog->LinkStatus)
return NULL;
/* Check that interface blocks defined in multiple shaders are consistent.
*/
return NULL;
/* Check that interface blocks defined in multiple shaders are consistent.
*/
- if (!validate_intrastage_interface_blocks(prog,
- (const gl_shader **)shader_list,
- num_shaders))
+ validate_intrastage_interface_blocks(prog, (const gl_shader **)shader_list,
+ num_shaders);
+ if (!prog->LinkStatus)
return NULL;
/* Link up uniform blocks defined within this stage. */
return NULL;
/* Link up uniform blocks defined within this stage. */
/**
* Validate the resources used by a program versus the implementation limits
*/
/**
* Validate the resources used by a program versus the implementation limits
*/
check_resources(struct gl_context *ctx, struct gl_shader_program *prog)
{
static const char *const shader_names[MESA_SHADER_TYPES] = {
check_resources(struct gl_context *ctx, struct gl_shader_program *prog)
{
static const char *const shader_names[MESA_SHADER_TYPES] = {
-
- return prog->LinkStatus;
void *mem_ctx = ralloc_context(NULL); // temporary linker context
void *mem_ctx = ralloc_context(NULL); // temporary linker context
- prog->LinkStatus = false;
+ prog->LinkStatus = true; /* All error paths will set this to false */
prog->Validated = false;
prog->_Used = false;
prog->Validated = false;
prog->_Used = false;
link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list,
num_vert_shaders);
link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list,
num_vert_shaders);
- if (!validate_vertex_shader_executable(prog, sh))
+ validate_vertex_shader_executable(prog, sh);
+ if (!prog->LinkStatus)
goto done;
_mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX],
goto done;
_mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX],
link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list,
num_frag_shaders);
link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list,
num_frag_shaders);
- if (!validate_fragment_shader_executable(prog, sh))
+ validate_fragment_shader_executable(prog, sh);
+ if (!prog->LinkStatus)
goto done;
_mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT],
goto done;
_mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT],
* performed, then locations are assigned for uniforms, attributes, and
* varyings.
*/
* performed, then locations are assigned for uniforms, attributes, and
* varyings.
*/
- if (cross_validate_uniforms(prog)) {
- unsigned prev;
+ cross_validate_uniforms(prog);
+ if (!prog->LinkStatus)
+ goto done;
- for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
- if (prog->_LinkedShaders[prev] != NULL)
- break;
- }
- /* Validate the inputs of each stage with the output of the preceding
- * stage.
- */
- for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
- if (prog->_LinkedShaders[i] == NULL)
- continue;
+ for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
+ if (prog->_LinkedShaders[prev] != NULL)
+ break;
+ }
- if (!validate_interstage_interface_blocks(prog->_LinkedShaders[prev],
- prog->_LinkedShaders[i])) {
- linker_error(prog, "interface block mismatch between shader stages\n");
- goto done;
- }
+ /* Validate the inputs of each stage with the output of the preceding
+ * stage.
+ */
+ for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
- if (!cross_validate_outputs_to_inputs(prog,
- prog->_LinkedShaders[prev],
- prog->_LinkedShaders[i]))
- goto done;
+ validate_interstage_interface_blocks(prog, prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]);
+ if (!prog->LinkStatus)
+ goto done;
+ cross_validate_outputs_to_inputs(prog,
+ prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]);
+ if (!prog->LinkStatus)
+ goto done;
- prog->LinkStatus = true;
link_assign_uniform_locations(prog);
store_fragdepth_layout(prog);
link_assign_uniform_locations(prog);
store_fragdepth_layout(prog);
- if (!check_resources(ctx, prog))
+ check_resources(ctx, prog);
+ if (!prog->LinkStatus)
goto done;
/* OpenGL ES requires that a vertex shader and a fragment shader both be
goto done;
/* OpenGL ES requires that a vertex shader and a fragment shader both be
unsigned num_shaders,
struct gl_uniform_block **blocks_ret);
unsigned num_shaders,
struct gl_uniform_block **blocks_ret);
validate_intrastage_interface_blocks(struct gl_shader_program *prog,
const gl_shader **shader_list,
unsigned num_shaders);
validate_intrastage_interface_blocks(struct gl_shader_program *prog,
const gl_shader **shader_list,
unsigned num_shaders);
-bool
-validate_interstage_interface_blocks(const gl_shader *producer,
+void
+validate_interstage_interface_blocks(struct gl_shader_program *prog,
+ const gl_shader *producer,
const gl_shader *consumer);
/**
const gl_shader *consumer);
/**