+static unsigned
+compute_variable_location_slot(ir_variable *var, gl_shader_stage stage)
+{
+ unsigned location_start = VARYING_SLOT_VAR0;
+
+ switch (stage) {
+ case MESA_SHADER_VERTEX:
+ if (var->data.mode == ir_var_shader_in)
+ location_start = VERT_ATTRIB_GENERIC0;
+ break;
+ case MESA_SHADER_TESS_CTRL:
+ case MESA_SHADER_TESS_EVAL:
+ if (var->data.patch)
+ location_start = VARYING_SLOT_PATCH0;
+ break;
+ case MESA_SHADER_FRAGMENT:
+ if (var->data.mode == ir_var_shader_out)
+ location_start = FRAG_RESULT_DATA0;
+ break;
+ default:
+ break;
+ }
+
+ return var->data.location - location_start;
+}
+
+struct explicit_location_info {
+ ir_variable *var;
+ bool base_type_is_integer;
+ unsigned base_type_bit_size;
+ unsigned interpolation;
+ bool centroid;
+ bool sample;
+ bool patch;
+};
+
+static bool
+check_location_aliasing(struct explicit_location_info explicit_locations[][4],
+ ir_variable *var,
+ unsigned location,
+ unsigned component,
+ unsigned location_limit,
+ const glsl_type *type,
+ unsigned interpolation,
+ bool centroid,
+ bool sample,
+ bool patch,
+ gl_shader_program *prog,
+ gl_shader_stage stage)
+{
+ unsigned last_comp;
+ unsigned base_type_bit_size;
+ const glsl_type *type_without_array = type->without_array();
+ const bool base_type_is_integer =
+ glsl_base_type_is_integer(type_without_array->base_type);
+ const bool is_struct = type_without_array->is_struct();
+ if (is_struct) {
+ /* structs don't have a defined underlying base type so just treat all
+ * component slots as used and set the bit size to 0. If there is
+ * location aliasing, we'll fail anyway later.
+ */
+ last_comp = 4;
+ base_type_bit_size = 0;
+ } else {
+ unsigned dmul = type_without_array->is_64bit() ? 2 : 1;
+ last_comp = component + type_without_array->vector_elements * dmul;
+ base_type_bit_size =
+ glsl_base_type_get_bit_size(type_without_array->base_type);
+ }
+
+ while (location < location_limit) {
+ unsigned comp = 0;
+ while (comp < 4) {
+ struct explicit_location_info *info =
+ &explicit_locations[location][comp];
+
+ if (info->var) {
+ if (info->var->type->without_array()->is_struct() || is_struct) {
+ /* Structs cannot share location since they are incompatible
+ * with any other underlying numerical type.
+ */
+ linker_error(prog,
+ "%s shader has multiple %sputs sharing the "
+ "same location that don't have the same "
+ "underlying numerical type. Struct variable '%s', "
+ "location %u\n",
+ _mesa_shader_stage_to_string(stage),
+ var->data.mode == ir_var_shader_in ? "in" : "out",
+ is_struct ? var->name : info->var->name,
+ location);
+ return false;
+ } else if (comp >= component && comp < last_comp) {
+ /* Component aliasing is not allowed */
+ linker_error(prog,
+ "%s shader has multiple %sputs explicitly "
+ "assigned to location %d and component %d\n",
+ _mesa_shader_stage_to_string(stage),
+ var->data.mode == ir_var_shader_in ? "in" : "out",
+ location, comp);
+ return false;
+ } else {
+ /* From the OpenGL 4.60.5 spec, section 4.4.1 Input Layout
+ * Qualifiers, Page 67, (Location aliasing):
+ *
+ * " Further, when location aliasing, the aliases sharing the
+ * location must have the same underlying numerical type
+ * and bit width (floating-point or integer, 32-bit versus
+ * 64-bit, etc.) and the same auxiliary storage and
+ * interpolation qualification."
+ */
+
+ /* If the underlying numerical type isn't integer, implicitly
+ * it will be float or else we would have failed by now.
+ */
+ if (info->base_type_is_integer != base_type_is_integer) {
+ linker_error(prog,
+ "%s shader has multiple %sputs sharing the "
+ "same location that don't have the same "
+ "underlying numerical type. Location %u "
+ "component %u.\n",
+ _mesa_shader_stage_to_string(stage),
+ var->data.mode == ir_var_shader_in ?
+ "in" : "out", location, comp);
+ return false;
+ }
+
+ if (info->base_type_bit_size != base_type_bit_size) {
+ linker_error(prog,
+ "%s shader has multiple %sputs sharing the "
+ "same location that don't have the same "
+ "underlying numerical bit size. Location %u "
+ "component %u.\n",
+ _mesa_shader_stage_to_string(stage),
+ var->data.mode == ir_var_shader_in ?
+ "in" : "out", location, comp);
+ return false;
+ }
+
+ if (info->interpolation != interpolation) {
+ linker_error(prog,
+ "%s shader has multiple %sputs sharing the "
+ "same location that don't have the same "
+ "interpolation qualification. Location %u "
+ "component %u.\n",
+ _mesa_shader_stage_to_string(stage),
+ var->data.mode == ir_var_shader_in ?
+ "in" : "out", location, comp);
+ return false;
+ }
+
+ if (info->centroid != centroid ||
+ info->sample != sample ||
+ info->patch != patch) {
+ linker_error(prog,
+ "%s shader has multiple %sputs sharing the "
+ "same location that don't have the same "
+ "auxiliary storage qualification. Location %u "
+ "component %u.\n",
+ _mesa_shader_stage_to_string(stage),
+ var->data.mode == ir_var_shader_in ?
+ "in" : "out", location, comp);
+ return false;
+ }
+ }
+ } else if (comp >= component && comp < last_comp) {
+ info->var = var;
+ info->base_type_is_integer = base_type_is_integer;
+ info->base_type_bit_size = base_type_bit_size;
+ info->interpolation = interpolation;
+ info->centroid = centroid;
+ info->sample = sample;
+ info->patch = patch;
+ }
+
+ comp++;
+
+ /* We need to do some special handling for doubles as dvec3 and
+ * dvec4 consume two consecutive locations. We don't need to
+ * worry about components beginning at anything other than 0 as
+ * the spec does not allow this for dvec3 and dvec4.
+ */
+ if (comp == 4 && last_comp > 4) {
+ last_comp = last_comp - 4;
+ /* Bump location index and reset the component index */
+ location++;
+ comp = 0;
+ component = 0;
+ }
+ }
+
+ location++;
+ }
+
+ return true;
+}
+
+static bool
+validate_explicit_variable_location(struct gl_context *ctx,
+ struct explicit_location_info explicit_locations[][4],
+ ir_variable *var,
+ gl_shader_program *prog,
+ gl_linked_shader *sh)
+{
+ const glsl_type *type = get_varying_type(var, sh->Stage);
+ unsigned num_elements = type->count_attribute_slots(false);
+ unsigned idx = compute_variable_location_slot(var, sh->Stage);
+ unsigned slot_limit = idx + num_elements;
+
+ /* Vertex shader inputs and fragment shader outputs are validated in
+ * assign_attribute_or_color_locations() so we should not attempt to
+ * validate them again here.
+ */
+ unsigned slot_max;
+ if (var->data.mode == ir_var_shader_out) {
+ assert(sh->Stage != MESA_SHADER_FRAGMENT);
+ slot_max =
+ ctx->Const.Program[sh->Stage].MaxOutputComponents / 4;
+ } else {
+ assert(var->data.mode == ir_var_shader_in);
+ assert(sh->Stage != MESA_SHADER_VERTEX);
+ slot_max =
+ ctx->Const.Program[sh->Stage].MaxInputComponents / 4;
+ }
+
+ if (slot_limit > slot_max) {
+ linker_error(prog,
+ "Invalid location %u in %s shader\n",
+ idx, _mesa_shader_stage_to_string(sh->Stage));
+ return false;
+ }
+
+ const glsl_type *type_without_array = type->without_array();
+ if (type_without_array->is_interface()) {
+ for (unsigned i = 0; i < type_without_array->length; i++) {
+ glsl_struct_field *field = &type_without_array->fields.structure[i];
+ unsigned field_location = field->location -
+ (field->patch ? VARYING_SLOT_PATCH0 : VARYING_SLOT_VAR0);
+ if (!check_location_aliasing(explicit_locations, var,
+ field_location,
+ 0, field_location + 1,
+ field->type,
+ field->interpolation,
+ field->centroid,
+ field->sample,
+ field->patch,
+ prog, sh->Stage)) {
+ return false;
+ }
+ }
+ } else if (!check_location_aliasing(explicit_locations, var,
+ idx, var->data.location_frac,
+ slot_limit, type,
+ var->data.interpolation,
+ var->data.centroid,
+ var->data.sample,
+ var->data.patch,
+ prog, sh->Stage)) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Validate explicit locations for the inputs to the first stage and the
+ * outputs of the last stage in a program, if those are not the VS and FS
+ * shaders.
+ */
+void
+validate_first_and_last_interface_explicit_locations(struct gl_context *ctx,
+ struct gl_shader_program *prog,
+ gl_shader_stage first_stage,
+ gl_shader_stage last_stage)
+{
+ /* VS inputs and FS outputs are validated in
+ * assign_attribute_or_color_locations()
+ */
+ bool validate_first_stage = first_stage != MESA_SHADER_VERTEX;
+ bool validate_last_stage = last_stage != MESA_SHADER_FRAGMENT;
+ if (!validate_first_stage && !validate_last_stage)
+ return;
+
+ struct explicit_location_info explicit_locations[MAX_VARYING][4];
+
+ gl_shader_stage stages[2] = { first_stage, last_stage };
+ bool validate_stage[2] = { validate_first_stage, validate_last_stage };
+ ir_variable_mode var_direction[2] = { ir_var_shader_in, ir_var_shader_out };
+
+ for (unsigned i = 0; i < 2; i++) {
+ if (!validate_stage[i])
+ continue;
+
+ gl_shader_stage stage = stages[i];
+
+ gl_linked_shader *sh = prog->_LinkedShaders[stage];
+ assert(sh);
+
+ memset(explicit_locations, 0, sizeof(explicit_locations));
+
+ foreach_in_list(ir_instruction, node, sh->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var == NULL ||
+ !var->data.explicit_location ||
+ var->data.location < VARYING_SLOT_VAR0 ||
+ var->data.mode != var_direction[i])
+ continue;
+
+ if (!validate_explicit_variable_location(
+ ctx, explicit_locations, var, prog, sh)) {
+ return;
+ }
+ }
+ }
+}
+