summaryrefslogtreecommitdiffstats
path: root/src/compiler/glsl/tests
diff options
context:
space:
mode:
authorEmil Velikov <emil.velikov@collabora.com>2016-01-18 12:16:48 +0200
committerEmil Velikov <emil.l.velikov@gmail.com>2016-01-26 16:08:33 +0000
commiteb63640c1d38a200a7b1540405051d3ff79d0d8a (patch)
treeda46321a41f309b1d02aeb14d5d5487791c45aeb /src/compiler/glsl/tests
parenta39a8fbbaa129f4e52f2a3ad2747182e9a74d910 (diff)
glsl: move to compiler/
Signed-off-by: Emil Velikov <emil.velikov@collabora.com> Acked-by: Matt Turner <mattst88@gmail.com> Acked-by: Jose Fonseca <jfonseca@vmware.com>
Diffstat (limited to 'src/compiler/glsl/tests')
-rw-r--r--src/compiler/glsl/tests/.gitignore5
-rw-r--r--src/compiler/glsl/tests/blob_test.c320
-rw-r--r--src/compiler/glsl/tests/builtin_variable_test.cpp393
-rwxr-xr-xsrc/compiler/glsl/tests/compare_ir59
-rw-r--r--src/compiler/glsl/tests/copy_constant_to_storage_tests.cpp300
-rw-r--r--src/compiler/glsl/tests/general_ir_test.cpp80
-rw-r--r--src/compiler/glsl/tests/invalidate_locations_test.cpp196
-rw-r--r--src/compiler/glsl/tests/lower_jumps/.gitignore3
-rw-r--r--src/compiler/glsl/tests/lower_jumps/create_test_cases.py643
-rwxr-xr-xsrc/compiler/glsl/tests/optimization-test42
-rw-r--r--src/compiler/glsl/tests/sampler_types_test.cpp100
-rw-r--r--src/compiler/glsl/tests/set_uniform_initializer_tests.cpp594
-rw-r--r--src/compiler/glsl/tests/sexps.py103
-rw-r--r--src/compiler/glsl/tests/uniform_initializer_utils.cpp255
-rw-r--r--src/compiler/glsl/tests/uniform_initializer_utils.h48
-rw-r--r--src/compiler/glsl/tests/varyings_test.cpp349
16 files changed, 3490 insertions, 0 deletions
diff --git a/src/compiler/glsl/tests/.gitignore b/src/compiler/glsl/tests/.gitignore
new file mode 100644
index 00000000000..13dcdc4ab73
--- /dev/null
+++ b/src/compiler/glsl/tests/.gitignore
@@ -0,0 +1,5 @@
+blob-test
+ralloc-test
+uniform-initializer-test
+sampler-types-test
+general-ir-test
diff --git a/src/compiler/glsl/tests/blob_test.c b/src/compiler/glsl/tests/blob_test.c
new file mode 100644
index 00000000000..4806029bca6
--- /dev/null
+++ b/src/compiler/glsl/tests/blob_test.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* A collection of unit tests for blob.c */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "util/ralloc.h"
+#include "blob.h"
+
+#define bytes_test_str "bytes_test"
+#define reserve_test_str "reserve_test"
+
+/* This placeholder must be the same length as the next overwrite_test_str */
+#define placeholder_str "XXXXXXXXXXXXXX"
+#define overwrite_test_str "overwrite_test"
+#define uint32_test 0x12345678
+#define uint32_placeholder 0xDEADBEEF
+#define uint32_overwrite 0xA1B2C3D4
+#define uint64_test 0x1234567890ABCDEF
+#define string_test_str "string_test"
+
+bool error = false;
+
+static void
+expect_equal(uint64_t expected, uint64_t actual, const char *test)
+{
+ if (actual != expected) {
+ fprintf (stderr, "Error: Test '%s' failed: Expected=%ld, Actual=%ld\n",
+ test, expected, actual);
+ error = true;
+ }
+}
+
+static void
+expect_unequal(uint64_t expected, uint64_t actual, const char *test)
+{
+ if (actual == expected) {
+ fprintf (stderr, "Error: Test '%s' failed: Result=%ld, but expected something different.\n",
+ test, actual);
+ error = true;
+ }
+}
+
+static void
+expect_equal_str(const char *expected, const char *actual, const char *test)
+{
+ if (strcmp(expected, actual)) {
+ fprintf (stderr, "Error: Test '%s' failed:\n\t"
+ "Expected=\"%s\", Actual=\"%s\"\n",
+ test, expected, actual);
+ error = true;
+ }
+}
+
+static void
+expect_equal_bytes(uint8_t *expected, uint8_t *actual,
+ size_t num_bytes, const char *test)
+{
+ size_t i;
+
+ if (memcmp(expected, actual, num_bytes)) {
+ fprintf (stderr, "Error: Test '%s' failed:\n\t", test);
+
+ fprintf (stderr, "Expected=[");
+ for (i = 0; i < num_bytes; i++) {
+ if (i != 0)
+ fprintf(stderr, ", ");
+ fprintf(stderr, "0x%02x", expected[i]);
+ }
+ fprintf (stderr, "]");
+
+ fprintf (stderr, "Actual=[");
+ for (i = 0; i < num_bytes; i++) {
+ if (i != 0)
+ fprintf(stderr, ", ");
+ fprintf(stderr, "0x%02x", actual[i]);
+ }
+ fprintf (stderr, "]\n");
+
+ error = true;
+ }
+}
+
+/* Test at least one call of each blob_write_foo and blob_read_foo function,
+ * verifying that we read out everything we wrote, that every bytes is
+ * consumed, and that the overrun bit is not set.
+ */
+static void
+test_write_and_read_functions (void)
+{
+ void *ctx = ralloc_context(NULL);
+ struct blob *blob;
+ struct blob_reader reader;
+ uint8_t *reserved;
+ size_t str_offset, uint_offset;
+ uint8_t reserve_buf[sizeof(reserve_test_str)];
+
+ blob = blob_create(ctx);
+
+ /*** Test blob by writing one of every possible kind of value. */
+
+ blob_write_bytes(blob, bytes_test_str, sizeof(bytes_test_str));
+
+ reserved = blob_reserve_bytes(blob, sizeof(reserve_test_str));
+ memcpy(reserved, reserve_test_str, sizeof(reserve_test_str));
+
+ /* Write a placeholder, (to be replaced later via overwrite_bytes) */
+ str_offset = blob->size;
+ blob_write_bytes(blob, placeholder_str, sizeof(placeholder_str));
+
+ blob_write_uint32(blob, uint32_test);
+
+ /* Write a placeholder, (to be replaced later via overwrite_uint32) */
+ uint_offset = blob->size;
+ blob_write_uint32(blob, uint32_placeholder);
+
+ blob_write_uint64(blob, uint64_test);
+
+ blob_write_intptr(blob, (intptr_t) blob);
+
+ blob_write_string(blob, string_test_str);
+
+ /* Finally, overwrite our placeholders. */
+ blob_overwrite_bytes(blob, str_offset, overwrite_test_str,
+ sizeof(overwrite_test_str));
+ blob_overwrite_uint32(blob, uint_offset, uint32_overwrite);
+
+ /*** Now read each value and verify. */
+ blob_reader_init(&reader, blob->data, blob->size);
+
+ expect_equal_str(bytes_test_str,
+ blob_read_bytes(&reader, sizeof(bytes_test_str)),
+ "blob_write/read_bytes");
+
+ blob_copy_bytes(&reader, reserve_buf, sizeof(reserve_buf));
+ expect_equal_str(reserve_test_str, (char *) reserve_buf,
+ "blob_reserve_bytes/blob_copy_bytes");
+
+ expect_equal_str(overwrite_test_str,
+ blob_read_bytes(&reader, sizeof(overwrite_test_str)),
+ "blob_overwrite_bytes");
+
+ expect_equal(uint32_test, blob_read_uint32(&reader),
+ "blob_write/read_uint32");
+ expect_equal(uint32_overwrite, blob_read_uint32(&reader),
+ "blob_overwrite_uint32");
+ expect_equal(uint64_test, blob_read_uint64(&reader),
+ "blob_write/read_uint64");
+ expect_equal((intptr_t) blob, blob_read_intptr(&reader),
+ "blob_write/read_intptr");
+ expect_equal_str(string_test_str, blob_read_string(&reader),
+ "blob_write/read_string");
+
+ expect_equal(reader.end - reader.data, reader.current - reader.data,
+ "read_consumes_all_bytes");
+ expect_equal(false, reader.overrun, "read_does_not_overrun");
+
+ ralloc_free(ctx);
+}
+
+/* Test that data values are written and read with proper alignment. */
+static void
+test_alignment(void)
+{
+ void *ctx = ralloc_context(NULL);
+ struct blob *blob;
+ struct blob_reader reader;
+ uint8_t bytes[] = "ABCDEFGHIJKLMNOP";
+ size_t delta, last, num_bytes;
+
+ blob = blob_create(ctx);
+
+ /* First, write an intptr value to the blob and capture that size. This is
+ * the expected offset between any pair of intptr values (if written with
+ * alignment).
+ */
+ blob_write_intptr(blob, (intptr_t) blob);
+
+ delta = blob->size;
+ last = blob->size;
+
+ /* Then loop doing the following:
+ *
+ * 1. Write an unaligned number of bytes
+ * 2. Verify that write results in an unaligned size
+ * 3. Write an intptr_t value
+ * 2. Verify that that write results in an aligned size
+ */
+ for (num_bytes = 1; num_bytes < sizeof(intptr_t); num_bytes++) {
+ blob_write_bytes(blob, bytes, num_bytes);
+
+ expect_unequal(delta, blob->size - last, "unaligned write of bytes");
+
+ blob_write_intptr(blob, (intptr_t) blob);
+
+ expect_equal(2 * delta, blob->size - last, "aligned write of intptr");
+
+ last = blob->size;
+ }
+
+ /* Finally, test that reading also does proper alignment. Since we know
+ * that values were written with all the right alignment, all we have to do
+ * here is verify that correct values are read.
+ */
+ blob_reader_init(&reader, blob->data, blob->size);
+
+ expect_equal((intptr_t) blob, blob_read_intptr(&reader),
+ "read of initial, aligned intptr_t");
+
+ for (num_bytes = 1; num_bytes < sizeof(intptr_t); num_bytes++) {
+ expect_equal_bytes(bytes, blob_read_bytes(&reader, num_bytes),
+ num_bytes, "unaligned read of bytes");
+ expect_equal((intptr_t) blob, blob_read_intptr(&reader),
+ "aligned read of intptr_t");
+ }
+
+ ralloc_free(ctx);
+}
+
+/* Test that we detect overrun. */
+static void
+test_overrun(void)
+{
+ void *ctx =ralloc_context(NULL);
+ struct blob *blob;
+ struct blob_reader reader;
+ uint32_t value = 0xdeadbeef;
+
+ blob = blob_create(ctx);
+
+ blob_write_uint32(blob, value);
+
+ blob_reader_init(&reader, blob->data, blob->size);
+
+ expect_equal(value, blob_read_uint32(&reader), "read before overrun");
+ expect_equal(false, reader.overrun, "overrun flag not set");
+ expect_equal(0, blob_read_uint32(&reader), "read at overrun");
+ expect_equal(true, reader.overrun, "overrun flag set");
+
+ ralloc_free(ctx);
+}
+
+/* Test that we can read and write some large objects, (exercising the code in
+ * the blob_write functions to realloc blob->data.
+ */
+static void
+test_big_objects(void)
+{
+ void *ctx = ralloc_context(NULL);
+ struct blob *blob;
+ struct blob_reader reader;
+ int size = 1000;
+ int count = 1000;
+ size_t i;
+ char *buf;
+
+ blob = blob_create(ctx);
+
+ /* Initialize our buffer. */
+ buf = ralloc_size(ctx, size);
+ for (i = 0; i < size; i++) {
+ buf[i] = i % 256;
+ }
+
+ /* Write it many times. */
+ for (i = 0; i < count; i++) {
+ blob_write_bytes(blob, buf, size);
+ }
+
+ blob_reader_init(&reader, blob->data, blob->size);
+
+ /* Read and verify it many times. */
+ for (i = 0; i < count; i++) {
+ expect_equal_bytes((uint8_t *) buf, blob_read_bytes(&reader, size), size,
+ "read of large objects");
+ }
+
+ expect_equal(reader.end - reader.data, reader.current - reader.data,
+ "number of bytes read reading large objects");
+
+ expect_equal(false, reader.overrun,
+ "overrun flag not set reading large objects");
+
+ ralloc_free(ctx);
+}
+
+int
+main (void)
+{
+ test_write_and_read_functions ();
+ test_alignment ();
+ test_overrun ();
+ test_big_objects ();
+
+ return error ? 1 : 0;
+}
diff --git a/src/compiler/glsl/tests/builtin_variable_test.cpp b/src/compiler/glsl/tests/builtin_variable_test.cpp
new file mode 100644
index 00000000000..11e384a0722
--- /dev/null
+++ b/src/compiler/glsl/tests/builtin_variable_test.cpp
@@ -0,0 +1,393 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <gtest/gtest.h>
+#include "standalone_scaffolding.h"
+#include "main/compiler.h"
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "ir.h"
+#include "glsl_parser_extras.h"
+#include "glsl_symbol_table.h"
+
+class common_builtin : public ::testing::Test {
+public:
+ common_builtin(GLenum shader_type)
+ : shader_type(shader_type)
+ {
+ /* empty */
+ }
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ void string_starts_with_prefix(const char *str, const char *prefix);
+ void names_start_with_gl();
+ void uniforms_and_system_values_dont_have_explicit_location();
+ void constants_are_constant();
+ void no_invalid_variable_modes();
+
+ GLenum shader_type;
+ struct _mesa_glsl_parse_state *state;
+ struct gl_shader *shader;
+ void *mem_ctx;
+ gl_context ctx;
+ exec_list ir;
+};
+
+void
+common_builtin::SetUp()
+{
+ this->mem_ctx = ralloc_context(NULL);
+ this->ir.make_empty();
+
+ initialize_context_to_defaults(&this->ctx, API_OPENGL_COMPAT);
+
+ this->shader = rzalloc(this->mem_ctx, gl_shader);
+ this->shader->Type = this->shader_type;
+ this->shader->Stage = _mesa_shader_enum_to_shader_stage(this->shader_type);
+
+ this->state =
+ new(mem_ctx) _mesa_glsl_parse_state(&this->ctx, this->shader->Stage,
+ this->shader);
+
+ _mesa_glsl_initialize_types(this->state);
+ _mesa_glsl_initialize_variables(&this->ir, this->state);
+}
+
+void
+common_builtin::TearDown()
+{
+ ralloc_free(this->mem_ctx);
+ this->mem_ctx = NULL;
+}
+
+void
+common_builtin::string_starts_with_prefix(const char *str, const char *prefix)
+{
+ const size_t len = strlen(prefix);
+ char *const name_prefix = new char[len + 1];
+
+ strncpy(name_prefix, str, len);
+ name_prefix[len] = '\0';
+ EXPECT_STREQ(prefix, name_prefix) << "Bad name " << str;
+
+ delete [] name_prefix;
+}
+
+void
+common_builtin::names_start_with_gl()
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ string_starts_with_prefix(var->name, "gl_");
+ }
+}
+
+void
+common_builtin::uniforms_and_system_values_dont_have_explicit_location()
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var->data.mode != ir_var_uniform && var->data.mode != ir_var_system_value)
+ continue;
+
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+ }
+}
+
+void
+common_builtin::constants_are_constant()
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var->data.mode != ir_var_auto)
+ continue;
+
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+ EXPECT_TRUE(var->data.read_only);
+ }
+}
+
+void
+common_builtin::no_invalid_variable_modes()
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ switch (var->data.mode) {
+ case ir_var_auto:
+ case ir_var_uniform:
+ case ir_var_shader_in:
+ case ir_var_shader_out:
+ case ir_var_system_value:
+ break;
+
+ default:
+ ADD_FAILURE() << "Built-in variable " << var->name
+ << " has an invalid mode " << int(var->data.mode);
+ break;
+ }
+ }
+}
+
+/************************************************************/
+
+class vertex_builtin : public common_builtin {
+public:
+ vertex_builtin()
+ : common_builtin(GL_VERTEX_SHADER)
+ {
+ /* empty */
+ }
+};
+
+TEST_F(vertex_builtin, names_start_with_gl)
+{
+ common_builtin::names_start_with_gl();
+}
+
+TEST_F(vertex_builtin, inputs_have_explicit_location)
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var->data.mode != ir_var_shader_in)
+ continue;
+
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_NE(-1, var->data.location);
+ EXPECT_GT(VERT_ATTRIB_GENERIC0, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+ }
+}
+
+TEST_F(vertex_builtin, outputs_have_explicit_location)
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var->data.mode != ir_var_shader_out)
+ continue;
+
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_NE(-1, var->data.location);
+ EXPECT_GT(VARYING_SLOT_VAR0, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+
+ /* Several varyings only exist in the fragment shader. Be sure that no
+ * outputs with these locations exist.
+ */
+ EXPECT_NE(VARYING_SLOT_PNTC, var->data.location);
+ EXPECT_NE(VARYING_SLOT_FACE, var->data.location);
+ EXPECT_NE(VARYING_SLOT_PRIMITIVE_ID, var->data.location);
+ }
+}
+
+TEST_F(vertex_builtin, uniforms_and_system_values_dont_have_explicit_location)
+{
+ common_builtin::uniforms_and_system_values_dont_have_explicit_location();
+}
+
+TEST_F(vertex_builtin, constants_are_constant)
+{
+ common_builtin::constants_are_constant();
+}
+
+TEST_F(vertex_builtin, no_invalid_variable_modes)
+{
+ common_builtin::no_invalid_variable_modes();
+}
+
+/********************************************************************/
+
+class fragment_builtin : public common_builtin {
+public:
+ fragment_builtin()
+ : common_builtin(GL_FRAGMENT_SHADER)
+ {
+ /* empty */
+ }
+};
+
+TEST_F(fragment_builtin, names_start_with_gl)
+{
+ common_builtin::names_start_with_gl();
+}
+
+TEST_F(fragment_builtin, inputs_have_explicit_location)
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var->data.mode != ir_var_shader_in)
+ continue;
+
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_NE(-1, var->data.location);
+ EXPECT_GT(VARYING_SLOT_VAR0, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+
+ /* Several varyings only exist in the vertex / geometry shader. Be sure
+ * that no inputs with these locations exist.
+ */
+ EXPECT_TRUE(_mesa_varying_slot_in_fs((gl_varying_slot) var->data.location));
+ }
+}
+
+TEST_F(fragment_builtin, outputs_have_explicit_location)
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var->data.mode != ir_var_shader_out)
+ continue;
+
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_NE(-1, var->data.location);
+
+ /* gl_FragData[] has location FRAG_RESULT_DATA0. Locations beyond that
+ * are invalid.
+ */
+ EXPECT_GE(FRAG_RESULT_DATA0, var->data.location);
+
+ EXPECT_EQ(0u, var->data.location_frac);
+ }
+}
+
+TEST_F(fragment_builtin, uniforms_and_system_values_dont_have_explicit_location)
+{
+ common_builtin::uniforms_and_system_values_dont_have_explicit_location();
+}
+
+TEST_F(fragment_builtin, constants_are_constant)
+{
+ common_builtin::constants_are_constant();
+}
+
+TEST_F(fragment_builtin, no_invalid_variable_modes)
+{
+ common_builtin::no_invalid_variable_modes();
+}
+
+/********************************************************************/
+
+class geometry_builtin : public common_builtin {
+public:
+ geometry_builtin()
+ : common_builtin(GL_GEOMETRY_SHADER)
+ {
+ /* empty */
+ }
+};
+
+TEST_F(geometry_builtin, names_start_with_gl)
+{
+ common_builtin::names_start_with_gl();
+}
+
+TEST_F(geometry_builtin, inputs_have_explicit_location)
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var->data.mode != ir_var_shader_in)
+ continue;
+
+ if (var->is_interface_instance()) {
+ EXPECT_STREQ("gl_in", var->name);
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+
+ ASSERT_TRUE(var->type->is_array());
+
+ const glsl_type *const instance_type = var->type->fields.array;
+
+ for (unsigned i = 0; i < instance_type->length; i++) {
+ const glsl_struct_field *const input =
+ &instance_type->fields.structure[i];
+
+ string_starts_with_prefix(input->name, "gl_");
+ EXPECT_NE(-1, input->location);
+ EXPECT_GT(VARYING_SLOT_VAR0, input->location);
+
+ /* Several varyings only exist in the fragment shader. Be sure
+ * that no inputs with these locations exist.
+ */
+ EXPECT_NE(VARYING_SLOT_PNTC, input->location);
+ EXPECT_NE(VARYING_SLOT_FACE, input->location);
+ }
+ } else {
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_NE(-1, var->data.location);
+ EXPECT_GT(VARYING_SLOT_VAR0, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+ }
+
+ /* Several varyings only exist in the fragment shader. Be sure that no
+ * inputs with these locations exist.
+ */
+ EXPECT_NE(VARYING_SLOT_PNTC, var->data.location);
+ EXPECT_NE(VARYING_SLOT_FACE, var->data.location);
+ }
+}
+
+TEST_F(geometry_builtin, outputs_have_explicit_location)
+{
+ foreach_in_list(ir_instruction, node, &this->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var->data.mode != ir_var_shader_out)
+ continue;
+
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_NE(-1, var->data.location);
+ EXPECT_GT(VARYING_SLOT_VAR0, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+
+ /* Several varyings only exist in the fragment shader. Be sure that no
+ * outputs with these locations exist.
+ */
+ EXPECT_NE(VARYING_SLOT_PNTC, var->data.location);
+ EXPECT_NE(VARYING_SLOT_FACE, var->data.location);
+ }
+}
+
+TEST_F(geometry_builtin, uniforms_and_system_values_dont_have_explicit_location)
+{
+ common_builtin::uniforms_and_system_values_dont_have_explicit_location();
+}
+
+TEST_F(geometry_builtin, constants_are_constant)
+{
+ common_builtin::constants_are_constant();
+}
+
+TEST_F(geometry_builtin, no_invalid_variable_modes)
+{
+ common_builtin::no_invalid_variable_modes();
+}
diff --git a/src/compiler/glsl/tests/compare_ir b/src/compiler/glsl/tests/compare_ir
new file mode 100755
index 00000000000..a40fc810cf3
--- /dev/null
+++ b/src/compiler/glsl/tests/compare_ir
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# coding=utf-8
+#
+# Copyright © 2011 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+# Compare two files containing IR code. Ignore formatting differences
+# and declaration order.
+
+import os
+import os.path
+import subprocess
+import sys
+import tempfile
+
+from sexps import *
+
+if len(sys.argv) != 3:
+ print 'Usage: compare_ir <file1> <file2>'
+ exit(1)
+
+with open(sys.argv[1]) as f:
+ ir1 = sort_decls(parse_sexp(f.read()))
+with open(sys.argv[2]) as f:
+ ir2 = sort_decls(parse_sexp(f.read()))
+
+if ir1 == ir2:
+ exit(0)
+else:
+ file1, path1 = tempfile.mkstemp(os.path.basename(sys.argv[1]))
+ file2, path2 = tempfile.mkstemp(os.path.basename(sys.argv[2]))
+ try:
+ os.write(file1, '{0}\n'.format(sexp_to_string(ir1)))
+ os.close(file1)
+ os.write(file2, '{0}\n'.format(sexp_to_string(ir2)))
+ os.close(file2)
+ subprocess.call(['diff', '-u', path1, path2])
+ finally:
+ os.remove(path1)
+ os.remove(path2)
+ exit(1)
diff --git a/src/compiler/glsl/tests/copy_constant_to_storage_tests.cpp b/src/compiler/glsl/tests/copy_constant_to_storage_tests.cpp
new file mode 100644
index 00000000000..cd48bc523c1
--- /dev/null
+++ b/src/compiler/glsl/tests/copy_constant_to_storage_tests.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <gtest/gtest.h>
+#include "main/compiler.h"
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "util/ralloc.h"
+#include "uniform_initializer_utils.h"
+
+namespace linker {
+extern void
+copy_constant_to_storage(union gl_constant_value *storage,
+ const ir_constant *val,
+ const enum glsl_base_type base_type,
+ const unsigned int elements,
+ unsigned int boolean_true);
+}
+
+class copy_constant_to_storage : public ::testing::Test {
+public:
+ void int_test(unsigned rows);
+ void uint_test(unsigned rows);
+ void bool_test(unsigned rows);
+ void sampler_test();
+ void float_test(unsigned columns, unsigned rows);
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ gl_constant_value storage[17];
+ void *mem_ctx;
+};
+
+void
+copy_constant_to_storage::SetUp()
+{
+ this->mem_ctx = ralloc_context(NULL);
+}
+
+void
+copy_constant_to_storage::TearDown()
+{
+ ralloc_free(this->mem_ctx);
+ this->mem_ctx = NULL;
+}
+
+void
+copy_constant_to_storage::int_test(unsigned rows)
+{
+ ir_constant *val;
+ generate_data(mem_ctx, GLSL_TYPE_INT, 1, rows, val);
+
+ const unsigned red_zone_size = ARRAY_SIZE(storage) - val->type->components();
+ fill_storage_array_with_sentinels(storage,
+ val->type->components(),
+ red_zone_size);
+
+ linker::copy_constant_to_storage(storage,
+ val,
+ val->type->base_type,
+ val->type->components(),
+ 0xF00F);
+
+ verify_data(storage, 0, val, red_zone_size, 0xF00F);
+}
+
+void
+copy_constant_to_storage::uint_test(unsigned rows)
+{
+ ir_constant *val;
+ generate_data(mem_ctx, GLSL_TYPE_UINT, 1, rows, val);
+
+ const unsigned red_zone_size = ARRAY_SIZE(storage) - val->type->components();
+ fill_storage_array_with_sentinels(storage,
+ val->type->components(),
+ red_zone_size);
+
+ linker::copy_constant_to_storage(storage,
+ val,
+ val->type->base_type,
+ val->type->components(),
+ 0xF00F);
+
+ verify_data(storage, 0, val, red_zone_size, 0xF00F);
+}
+
+void
+copy_constant_to_storage::float_test(unsigned columns, unsigned rows)
+{
+ ir_constant *val;
+ generate_data(mem_ctx, GLSL_TYPE_FLOAT, columns, rows, val);
+
+ const unsigned red_zone_size = ARRAY_SIZE(storage) - val->type->components();
+ fill_storage_array_with_sentinels(storage,
+ val->type->components(),
+ red_zone_size);
+
+ linker::copy_constant_to_storage(storage,
+ val,
+ val->type->base_type,
+ val->type->components(),
+ 0xF00F);
+
+ verify_data(storage, 0, val, red_zone_size, 0xF00F);
+}
+
+void
+copy_constant_to_storage::bool_test(unsigned rows)
+{
+ ir_constant *val;
+ generate_data(mem_ctx, GLSL_TYPE_BOOL, 1, rows, val);
+
+ const unsigned red_zone_size = ARRAY_SIZE(storage) - val->type->components();
+ fill_storage_array_with_sentinels(storage,
+ val->type->components(),
+ red_zone_size);
+
+ linker::copy_constant_to_storage(storage,
+ val,
+ val->type->base_type,
+ val->type->components(),
+ 0xF00F);
+
+ verify_data(storage, 0, val, red_zone_size, 0xF00F);
+}
+
+/**
+ * The only difference between this test and int_test is that the base type
+ * passed to \c linker::copy_constant_to_storage is hard-coded to \c
+ * GLSL_TYPE_SAMPLER instead of using the base type from the constant.
+ */
+void
+copy_constant_to_storage::sampler_test(void)
+{
+ ir_constant *val;
+ generate_data(mem_ctx, GLSL_TYPE_INT, 1, 1, val);
+
+ const unsigned red_zone_size = ARRAY_SIZE(storage) - val->type->components();
+ fill_storage_array_with_sentinels(storage,
+ val->type->components(),
+ red_zone_size);
+
+ linker::copy_constant_to_storage(storage,
+ val,
+ GLSL_TYPE_SAMPLER,
+ val->type->components(),
+ 0xF00F);
+
+ verify_data(storage, 0, val, red_zone_size, 0xF00F);
+}
+
+TEST_F(copy_constant_to_storage, bool_uniform)
+{
+ bool_test(1);
+}
+
+TEST_F(copy_constant_to_storage, bvec2_uniform)
+{
+ bool_test(2);
+}
+
+TEST_F(copy_constant_to_storage, bvec3_uniform)
+{
+ bool_test(3);
+}
+
+TEST_F(copy_constant_to_storage, bvec4_uniform)
+{
+ bool_test(4);
+}
+
+TEST_F(copy_constant_to_storage, int_uniform)
+{
+ int_test(1);
+}
+
+TEST_F(copy_constant_to_storage, ivec2_uniform)
+{
+ int_test(2);
+}
+
+TEST_F(copy_constant_to_storage, ivec3_uniform)
+{
+ int_test(3);
+}
+
+TEST_F(copy_constant_to_storage, ivec4_uniform)
+{
+ int_test(4);
+}
+
+TEST_F(copy_constant_to_storage, uint_uniform)
+{
+ uint_test(1);
+}
+
+TEST_F(copy_constant_to_storage, uvec2_uniform)
+{
+ uint_test(2);
+}
+
+TEST_F(copy_constant_to_storage, uvec3_uniform)
+{
+ uint_test(3);
+}
+
+TEST_F(copy_constant_to_storage, uvec4_uniform)
+{
+ uint_test(4);
+}
+
+TEST_F(copy_constant_to_storage, float_uniform)
+{
+ float_test(1, 1);
+}
+
+TEST_F(copy_constant_to_storage, vec2_uniform)
+{
+ float_test(1, 2);
+}
+
+TEST_F(copy_constant_to_storage, vec3_uniform)
+{
+ float_test(1, 3);
+}
+
+TEST_F(copy_constant_to_storage, vec4_uniform)
+{
+ float_test(1, 4);
+}
+
+TEST_F(copy_constant_to_storage, mat2x2_uniform)
+{
+ float_test(2, 2);
+}
+
+TEST_F(copy_constant_to_storage, mat2x3_uniform)
+{
+ float_test(2, 3);
+}
+
+TEST_F(copy_constant_to_storage, mat2x4_uniform)
+{
+ float_test(2, 4);
+}
+
+TEST_F(copy_constant_to_storage, mat3x2_uniform)
+{
+ float_test(3, 2);
+}
+
+TEST_F(copy_constant_to_storage, mat3x3_uniform)
+{
+ float_test(3, 3);
+}
+
+TEST_F(copy_constant_to_storage, mat3x4_uniform)
+{
+ float_test(3, 4);
+}
+
+TEST_F(copy_constant_to_storage, mat4x2_uniform)
+{
+ float_test(4, 2);
+}
+
+TEST_F(copy_constant_to_storage, mat4x3_uniform)
+{
+ float_test(4, 3);
+}
+
+TEST_F(copy_constant_to_storage, mat4x4_uniform)
+{
+ float_test(4, 4);
+}
+
+TEST_F(copy_constant_to_storage, sampler_uniform)
+{
+ sampler_test();
+}
diff --git a/src/compiler/glsl/tests/general_ir_test.cpp b/src/compiler/glsl/tests/general_ir_test.cpp
new file mode 100644
index 00000000000..217305bf847
--- /dev/null
+++ b/src/compiler/glsl/tests/general_ir_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <gtest/gtest.h>
+#include "main/compiler.h"
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "ir.h"
+
+TEST(ir_variable_constructor, interface)
+{
+ void *mem_ctx = ralloc_context(NULL);
+
+ static const glsl_struct_field f[] = {
+ glsl_struct_field(glsl_type::vec(4), "v")
+ };
+
+ const glsl_type *const interface =
+ glsl_type::get_interface_instance(f,
+ ARRAY_SIZE(f),
+ GLSL_INTERFACE_PACKING_STD140,
+ "simple_interface");
+
+ static const char name[] = "named_instance";
+
+ ir_variable *const v =
+ new(mem_ctx) ir_variable(interface, name, ir_var_uniform);
+
+ EXPECT_STREQ(name, v->name);
+ EXPECT_NE(name, v->name);
+ EXPECT_EQ(interface, v->type);
+ EXPECT_EQ(interface, v->get_interface_type());
+}
+
+TEST(ir_variable_constructor, interface_array)
+{
+ void *mem_ctx = ralloc_context(NULL);
+
+ static const glsl_struct_field f[] = {
+ glsl_struct_field(glsl_type::vec(4), "v")
+ };
+
+ const glsl_type *const interface =
+ glsl_type::get_interface_instance(f,
+ ARRAY_SIZE(f),
+ GLSL_INTERFACE_PACKING_STD140,
+ "simple_interface");
+
+ const glsl_type *const interface_array =
+ glsl_type::get_array_instance(interface, 2);
+
+ static const char name[] = "array_instance";
+
+ ir_variable *const v =
+ new(mem_ctx) ir_variable(interface_array, name, ir_var_uniform);
+
+ EXPECT_STREQ(name, v->name);
+ EXPECT_NE(name, v->name);
+ EXPECT_EQ(interface_array, v->type);
+ EXPECT_EQ(interface, v->get_interface_type());
+}
diff --git a/src/compiler/glsl/tests/invalidate_locations_test.cpp b/src/compiler/glsl/tests/invalidate_locations_test.cpp
new file mode 100644
index 00000000000..ba94d7e3a21
--- /dev/null
+++ b/src/compiler/glsl/tests/invalidate_locations_test.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <gtest/gtest.h>
+#include "main/compiler.h"
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "util/ralloc.h"
+#include "ir.h"
+#include "linker.h"
+
+/**
+ * \file varyings_test.cpp
+ *
+ * Test various aspects of linking shader stage inputs and outputs.
+ */
+
+class invalidate_locations : public ::testing::Test {
+public:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ void *mem_ctx;
+ exec_list ir;
+};
+
+void
+invalidate_locations::SetUp()
+{
+ this->mem_ctx = ralloc_context(NULL);
+ this->ir.make_empty();
+}
+
+void
+invalidate_locations::TearDown()
+{
+ ralloc_free(this->mem_ctx);
+ this->mem_ctx = NULL;
+}
+
+TEST_F(invalidate_locations, simple_vertex_in_generic)
+{
+ ir_variable *const var =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ "a",
+ ir_var_shader_in);
+
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+
+ var->data.location = VERT_ATTRIB_GENERIC0;
+ var->data.location_frac = 2;
+
+ ir.push_tail(var);
+
+ link_invalidate_variable_locations(&ir);
+
+ EXPECT_EQ(-1, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_TRUE(var->data.is_unmatched_generic_inout);
+}
+
+TEST_F(invalidate_locations, explicit_location_vertex_in_generic)
+{
+ ir_variable *const var =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ "a",
+ ir_var_shader_in);
+
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+
+ var->data.location = VERT_ATTRIB_GENERIC0;
+ var->data.explicit_location = true;
+
+ ir.push_tail(var);
+
+ link_invalidate_variable_locations(&ir);
+
+ EXPECT_EQ(VERT_ATTRIB_GENERIC0, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_FALSE(var->data.is_unmatched_generic_inout);
+}
+
+TEST_F(invalidate_locations, explicit_location_frac_vertex_in_generic)
+{
+ ir_variable *const var =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ "a",
+ ir_var_shader_in);
+
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+
+ var->data.location = VERT_ATTRIB_GENERIC0;
+ var->data.location_frac = 2;
+ var->data.explicit_location = true;
+
+ ir.push_tail(var);
+
+ link_invalidate_variable_locations(&ir);
+
+ EXPECT_EQ(VERT_ATTRIB_GENERIC0, var->data.location);
+ EXPECT_EQ(2u, var->data.location_frac);
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_FALSE(var->data.is_unmatched_generic_inout);
+}
+
+TEST_F(invalidate_locations, vertex_in_builtin)
+{
+ ir_variable *const var =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ "gl_Vertex",
+ ir_var_shader_in);
+
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+
+ var->data.location = VERT_ATTRIB_POS;
+ var->data.explicit_location = true;
+
+ ir.push_tail(var);
+
+ link_invalidate_variable_locations(&ir);
+
+ EXPECT_EQ(VERT_ATTRIB_POS, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_FALSE(var->data.is_unmatched_generic_inout);
+}
+
+TEST_F(invalidate_locations, simple_vertex_out_generic)
+{
+ ir_variable *const var =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ "a",
+ ir_var_shader_out);
+
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+
+ var->data.location = VARYING_SLOT_VAR0;
+
+ ir.push_tail(var);
+
+ link_invalidate_variable_locations(&ir);
+
+ EXPECT_EQ(-1, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_TRUE(var->data.is_unmatched_generic_inout);
+}
+
+TEST_F(invalidate_locations, vertex_out_builtin)
+{
+ ir_variable *const var =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ "gl_FrontColor",
+ ir_var_shader_out);
+
+ EXPECT_FALSE(var->data.explicit_location);
+ EXPECT_EQ(-1, var->data.location);
+
+ var->data.location = VARYING_SLOT_COL0;
+ var->data.explicit_location = true;
+
+ ir.push_tail(var);
+
+ link_invalidate_variable_locations(&ir);
+
+ EXPECT_EQ(VARYING_SLOT_COL0, var->data.location);
+ EXPECT_EQ(0u, var->data.location_frac);
+ EXPECT_TRUE(var->data.explicit_location);
+ EXPECT_FALSE(var->data.is_unmatched_generic_inout);
+}
diff --git a/src/compiler/glsl/tests/lower_jumps/.gitignore b/src/compiler/glsl/tests/lower_jumps/.gitignore
new file mode 100644
index 00000000000..e98df627fd8
--- /dev/null
+++ b/src/compiler/glsl/tests/lower_jumps/.gitignore
@@ -0,0 +1,3 @@
+*.opt_test
+*.expected
+*.out
diff --git a/src/compiler/glsl/tests/lower_jumps/create_test_cases.py b/src/compiler/glsl/tests/lower_jumps/create_test_cases.py
new file mode 100644
index 00000000000..3be1079bc14
--- /dev/null
+++ b/src/compiler/glsl/tests/lower_jumps/create_test_cases.py
@@ -0,0 +1,643 @@
+# coding=utf-8
+#
+# Copyright © 2011 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import os
+import os.path
+import re
+import subprocess
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # For access to sexps.py, which is in parent dir
+from sexps import *
+
+def make_test_case(f_name, ret_type, body):
+ """Create a simple optimization test case consisting of a single
+ function with the given name, return type, and body.
+
+ Global declarations are automatically created for any undeclared
+ variables that are referenced by the function. All undeclared
+ variables are assumed to be floats.
+ """
+ check_sexp(body)
+ declarations = {}
+ def make_declarations(sexp, already_declared = ()):
+ if isinstance(sexp, list):
+ if len(sexp) == 2 and sexp[0] == 'var_ref':
+ if sexp[1] not in already_declared:
+ declarations[sexp[1]] = [
+ 'declare', ['in'], 'float', sexp[1]]
+ elif len(sexp) == 4 and sexp[0] == 'assign':
+ assert sexp[2][0] == 'var_ref'
+ if sexp[2][1] not in already_declared:
+ declarations[sexp[2][1]] = [
+ 'declare', ['out'], 'float', sexp[2][1]]
+ make_declarations(sexp[3], already_declared)
+ else:
+ already_declared = set(already_declared)
+ for s in sexp:
+ if isinstance(s, list) and len(s) >= 4 and \
+ s[0] == 'declare':
+ already_declared.add(s[3])
+ else:
+ make_declarations(s, already_declared)
+ make_declarations(body)
+ return declarations.values() + \
+ [['function', f_name, ['signature', ret_type, ['parameters'], body]]]
+
+
+# The following functions can be used to build expressions.
+
+def const_float(value):
+ """Create an expression representing the given floating point value."""
+ return ['constant', 'float', ['{0:.6f}'.format(value)]]
+
+def const_bool(value):
+ """Create an expression representing the given boolean value.
+
+ If value is not a boolean, it is converted to a boolean. So, for
+ instance, const_bool(1) is equivalent to const_bool(True).
+ """
+ return ['constant', 'bool', ['{0}'.format(1 if value else 0)]]
+
+def gt_zero(var_name):
+ """Create Construct the expression var_name > 0"""
+ return ['expression', 'bool', '>', ['var_ref', var_name], const_float(0)]
+
+
+# The following functions can be used to build complex control flow
+# statements. All of these functions return statement lists (even
+# those which only create a single statement), so that statements can
+# be sequenced together using the '+' operator.
+
+def return_(value = None):
+ """Create a return statement."""
+ if value is not None:
+ return [['return', value]]
+ else:
+ return [['return']]
+
+def break_():
+ """Create a break statement."""
+ return ['break']
+
+def continue_():
+ """Create a continue statement."""
+ return ['continue']
+
+def simple_if(var_name, then_statements, else_statements = None):
+ """Create a statement of the form
+
+ if (var_name > 0.0) {
+ <then_statements>
+ } else {
+ <else_statements>
+ }
+
+ else_statements may be omitted.
+ """
+ if else_statements is None:
+ else_statements = []
+ check_sexp(then_statements)
+ check_sexp(else_statements)
+ return [['if', gt_zero(var_name), then_statements, else_statements]]
+
+def loop(statements):
+ """Create a loop containing the given statements as its loop
+ body.
+ """
+ check_sexp(statements)
+ return [['loop', statements]]
+
+def declare_temp(var_type, var_name):
+ """Create a declaration of the form
+
+ (declare (temporary) <var_type> <var_name)
+ """
+ return [['declare', ['temporary'], var_type, var_name]]
+
+def assign_x(var_name, value):
+ """Create a statement that assigns <value> to the variable
+ <var_name>. The assignment uses the mask (x).
+ """
+ check_sexp(value)
+ return [['assign', ['x'], ['var_ref', var_name], value]]
+
+def complex_if(var_prefix, statements):
+ """Create a statement of the form
+
+ if (<var_prefix>a > 0.0) {
+ if (<var_prefix>b > 0.0) {
+ <statements>
+ }
+ }
+
+ This is useful in testing jump lowering, because if <statements>
+ ends in a jump, lower_jumps.cpp won't try to combine this
+ construct with the code that follows it, as it might do for a
+ simple if.
+
+ All variables used in the if statement are prefixed with
+ var_prefix. This can be used to ensure uniqueness.
+ """
+ check_sexp(statements)
+ return simple_if(var_prefix + 'a', simple_if(var_prefix + 'b', statements))
+
+def declare_execute_flag():
+ """Create the statements that lower_jumps.cpp uses to declare and
+ initialize the temporary boolean execute_flag.
+ """
+ return declare_temp('bool', 'execute_flag') + \
+ assign_x('execute_flag', const_bool(True))
+
+def declare_return_flag():
+ """Create the statements that lower_jumps.cpp uses to declare and
+ initialize the temporary boolean return_flag.
+ """
+ return declare_temp('bool', 'return_flag') + \
+ assign_x('return_flag', const_bool(False))
+
+def declare_return_value():
+ """Create the statements that lower_jumps.cpp uses to declare and
+ initialize the temporary variable return_value. Assume that
+ return_value is a float.
+ """
+ return declare_temp('float', 'return_value')
+
+def declare_break_flag():
+ """Create the statements that lower_jumps.cpp uses to declare and
+ initialize the temporary boolean break_flag.
+ """
+ return declare_temp('bool', 'break_flag') + \
+ assign_x('break_flag', const_bool(False))
+
+def lowered_return_simple(value = None):
+ """Create the statements that lower_jumps.cpp lowers a return
+ statement to, in situations where it does not need to clear the
+ execute flag.
+ """
+ if value:
+ result = assign_x('return_value', value)
+ else:
+ result = []
+ return result + assign_x('return_flag', const_bool(True))
+
+def lowered_return(value = None):
+ """Create the statements that lower_jumps.cpp lowers a return
+ statement to, in situations where it needs to clear the execute
+ flag.
+ """
+ return lowered_return_simple(value) + \
+ assign_x('execute_flag', const_bool(False))
+
+def lowered_continue():
+ """Create the statement that lower_jumps.cpp lowers a continue
+ statement to.
+ """
+ return assign_x('execute_flag', const_bool(False))
+
+def lowered_break_simple():
+ """Create the statement that lower_jumps.cpp lowers a break
+ statement to, in situations where it does not need to clear the
+ execute flag.
+ """
+ return assign_x('break_flag', const_bool(True))
+
+def lowered_break():
+ """Create the statement that lower_jumps.cpp lowers a break
+ statement to, in situations where it needs to clear the execute
+ flag.
+ """
+ return lowered_break_simple() + assign_x('execute_flag', const_bool(False))
+
+def if_execute_flag(statements):
+ """Wrap statements in an if test so that they will only execute if
+ execute_flag is True.
+ """
+ check_sexp(statements)
+ return [['if', ['var_ref', 'execute_flag'], statements, []]]
+
+def if_not_return_flag(statements):
+ """Wrap statements in an if test so that they will only execute if
+ return_flag is False.
+ """
+ check_sexp(statements)
+ return [['if', ['var_ref', 'return_flag'], [], statements]]
+
+def final_return():
+ """Create the return statement that lower_jumps.cpp places at the
+ end of a function when lowering returns.
+ """
+ return [['return', ['var_ref', 'return_value']]]
+
+def final_break():
+ """Create the conditional break statement that lower_jumps.cpp
+ places at the end of a function when lowering breaks.
+ """
+ return [['if', ['var_ref', 'break_flag'], break_(), []]]
+
+def bash_quote(*args):
+ """Quote the arguments appropriately so that bash will understand
+ each argument as a single word.
+ """
+ def quote_word(word):
+ for c in word:
+ if not (c.isalpha() or c.isdigit() or c in '@%_-+=:,./'):
+ break
+ else:
+ if not word:
+ return "''"
+ return word
+ return "'{0}'".format(word.replace("'", "'\"'\"'"))
+ return ' '.join(quote_word(word) for word in args)
+
+def create_test_case(doc_string, input_sexp, expected_sexp, test_name,
+ pull_out_jumps=False, lower_sub_return=False,
+ lower_main_return=False, lower_continue=False,
+ lower_break=False):
+ """Create a test case that verifies that do_lower_jumps transforms
+ the given code in the expected way.
+ """
+ doc_lines = [line.strip() for line in doc_string.splitlines()]
+ doc_string = ''.join('# {0}\n'.format(line) for line in doc_lines if line != '')
+ check_sexp(input_sexp)
+ check_sexp(expected_sexp)
+ input_str = sexp_to_string(sort_decls(input_sexp))
+ expected_output = sexp_to_string(sort_decls(expected_sexp))
+
+ optimization = (
+ 'do_lower_jumps({0:d}, {1:d}, {2:d}, {3:d}, {4:d})'.format(
+ pull_out_jumps, lower_sub_return, lower_main_return,
+ lower_continue, lower_break))
+ args = ['../../glsl_test', 'optpass', '--quiet', '--input-ir', optimization]
+ test_file = '{0}.opt_test'.format(test_name)
+ with open(test_file, 'w') as f:
+ f.write('#!/usr/bin/env bash\n#\n# This file was generated by create_test_cases.py.\n#\n')
+ f.write(doc_string)
+ f.write('{0} <<EOF\n'.format(bash_quote(*args)))
+ f.write('{0}\nEOF\n'.format(input_str))
+ os.chmod(test_file, 0774)
+ expected_file = '{0}.opt_test.expected'.format(test_name)
+ with open(expected_file, 'w') as f:
+ f.write('{0}\n'.format(expected_output))
+
+def test_lower_returns_main():
+ doc_string = """Test that do_lower_jumps respects the lower_main_return
+ flag in deciding whether to lower returns in the main
+ function.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ complex_if('', return_())
+ ))
+ expected_sexp = make_test_case('main', 'void', (
+ declare_execute_flag() +
+ declare_return_flag() +
+ complex_if('', lowered_return())
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_main_true',
+ lower_main_return=True)
+ create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_main_false',
+ lower_main_return=False)
+
+def test_lower_returns_sub():
+ doc_string = """Test that do_lower_jumps respects the lower_sub_return flag
+ in deciding whether to lower returns in subroutines.
+ """
+ input_sexp = make_test_case('sub', 'void', (
+ complex_if('', return_())
+ ))
+ expected_sexp = make_test_case('sub', 'void', (
+ declare_execute_flag() +
+ declare_return_flag() +
+ complex_if('', lowered_return())
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_sub_true',
+ lower_sub_return=True)
+ create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_sub_false',
+ lower_sub_return=False)
+
+def test_lower_returns_1():
+ doc_string = """Test that a void return at the end of a function is
+ eliminated.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ assign_x('a', const_float(1)) +
+ return_()
+ ))
+ expected_sexp = make_test_case('main', 'void', (
+ assign_x('a', const_float(1))
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_1',
+ lower_main_return=True)
+
+def test_lower_returns_2():
+ doc_string = """Test that lowering is not performed on a non-void return at
+ the end of subroutine.
+ """
+ input_sexp = make_test_case('sub', 'float', (
+ assign_x('a', const_float(1)) +
+ return_(const_float(1))
+ ))
+ create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_2',
+ lower_sub_return=True)
+
+def test_lower_returns_3():
+ doc_string = """Test lowering of returns when there is one nested inside a
+ complex structure of ifs, and one at the end of a function.
+
+ In this case, the latter return needs to be lowered because it
+ will not be at the end of the function once the final return
+ is inserted.
+ """
+ input_sexp = make_test_case('sub', 'float', (
+ complex_if('', return_(const_float(1))) +
+ return_(const_float(2))
+ ))
+ expected_sexp = make_test_case('sub', 'float', (
+ declare_execute_flag() +
+ declare_return_value() +
+ declare_return_flag() +
+ complex_if('', lowered_return(const_float(1))) +
+ if_execute_flag(lowered_return(const_float(2))) +
+ final_return()
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_3',
+ lower_sub_return=True)
+
+def test_lower_returns_4():
+ doc_string = """Test that returns are properly lowered when they occur in
+ both branches of an if-statement.
+ """
+ input_sexp = make_test_case('sub', 'float', (
+ simple_if('a', return_(const_float(1)),
+ return_(const_float(2)))
+ ))
+ expected_sexp = make_test_case('sub', 'float', (
+ declare_execute_flag() +
+ declare_return_value() +
+ declare_return_flag() +
+ simple_if('a', lowered_return(const_float(1)),
+ lowered_return(const_float(2))) +
+ final_return()
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_4',
+ lower_sub_return=True)
+
+def test_lower_unified_returns():
+ doc_string = """If both branches of an if statement end in a return, and
+ pull_out_jumps is True, then those returns should be lifted
+ outside the if and then properly lowered.
+
+ Verify that this lowering occurs during the same pass as the
+ lowering of other returns by checking that extra temporary
+ variables aren't generated.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ complex_if('a', return_()) +
+ simple_if('b', simple_if('c', return_(), return_()))
+ ))
+ expected_sexp = make_test_case('main', 'void', (
+ declare_execute_flag() +
+ declare_return_flag() +
+ complex_if('a', lowered_return()) +
+ if_execute_flag(simple_if('b', (simple_if('c', [], []) +
+ lowered_return())))
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_unified_returns',
+ lower_main_return=True, pull_out_jumps=True)
+
+def test_lower_pulled_out_jump():
+ doc_string = """If one branch of an if ends in a jump, and control cannot
+ fall out the bottom of the other branch, and pull_out_jumps is
+ True, then the jump is lifted outside the if.
+
+ Verify that this lowering occurs during the same pass as the
+ lowering of other jumps by checking that extra temporary
+ variables aren't generated.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ complex_if('a', return_()) +
+ loop(simple_if('b', simple_if('c', break_(), continue_()),
+ return_())) +
+ assign_x('d', const_float(1))
+ ))
+ # Note: optimization produces two other effects: the break
+ # gets lifted out of the if statements, and the code after the
+ # loop gets guarded so that it only executes if the return
+ # flag is clear.
+ expected_sexp = make_test_case('main', 'void', (
+ declare_execute_flag() +
+ declare_return_flag() +
+ complex_if('a', lowered_return()) +
+ if_execute_flag(
+ loop(simple_if('b', simple_if('c', [], continue_()),
+ lowered_return_simple()) +
+ break_()) +
+ if_not_return_flag(assign_x('d', const_float(1))))
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_pulled_out_jump',
+ lower_main_return=True, pull_out_jumps=True)
+
+def test_lower_breaks_1():
+ doc_string = """If a loop contains an unconditional break at the bottom of
+ it, it should not be lowered."""
+ input_sexp = make_test_case('main', 'void', (
+ loop(assign_x('a', const_float(1)) +
+ break_())
+ ))
+ expected_sexp = input_sexp
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_1', lower_break=True)
+
+def test_lower_breaks_2():
+ doc_string = """If a loop contains a conditional break at the bottom of it,
+ it should not be lowered if it is in the then-clause.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ loop(assign_x('a', const_float(1)) +
+ simple_if('b', break_()))
+ ))
+ expected_sexp = input_sexp
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_2', lower_break=True)
+
+def test_lower_breaks_3():
+ doc_string = """If a loop contains a conditional break at the bottom of it,
+ it should not be lowered if it is in the then-clause, even if
+ there are statements preceding the break.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ loop(assign_x('a', const_float(1)) +
+ simple_if('b', (assign_x('c', const_float(1)) +
+ break_())))
+ ))
+ expected_sexp = input_sexp
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_3', lower_break=True)
+
+def test_lower_breaks_4():
+ doc_string = """If a loop contains a conditional break at the bottom of it,
+ it should not be lowered if it is in the else-clause.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ loop(assign_x('a', const_float(1)) +
+ simple_if('b', [], break_()))
+ ))
+ expected_sexp = input_sexp
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_4', lower_break=True)
+
+def test_lower_breaks_5():
+ doc_string = """If a loop contains a conditional break at the bottom of it,
+ it should not be lowered if it is in the else-clause, even if
+ there are statements preceding the break.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ loop(assign_x('a', const_float(1)) +
+ simple_if('b', [], (assign_x('c', const_float(1)) +
+ break_())))
+ ))
+ expected_sexp = input_sexp
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_5', lower_break=True)
+
+def test_lower_breaks_6():
+ doc_string = """If a loop contains conditional breaks and continues, and
+ ends in an unconditional break, then the unconditional break
+ needs to be lowered, because it will no longer be at the end
+ of the loop after the final break is added.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ loop(simple_if('a', (complex_if('b', continue_()) +
+ complex_if('c', break_()))) +
+ break_())
+ ))
+ expected_sexp = make_test_case('main', 'void', (
+ declare_break_flag() +
+ loop(declare_execute_flag() +
+ simple_if(
+ 'a',
+ (complex_if('b', lowered_continue()) +
+ if_execute_flag(
+ complex_if('c', lowered_break())))) +
+ if_execute_flag(lowered_break_simple()) +
+ final_break())
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_6',
+ lower_break=True, lower_continue=True)
+
+def test_lower_guarded_conditional_break():
+ doc_string = """Normally a conditional break at the end of a loop isn't
+ lowered, however if the conditional break gets placed inside
+ an if(execute_flag) because of earlier lowering of continues,
+ then the break needs to be lowered.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ loop(complex_if('a', continue_()) +
+ simple_if('b', break_()))
+ ))
+ expected_sexp = make_test_case('main', 'void', (
+ declare_break_flag() +
+ loop(declare_execute_flag() +
+ complex_if('a', lowered_continue()) +
+ if_execute_flag(simple_if('b', lowered_break())) +
+ final_break())
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'lower_guarded_conditional_break',
+ lower_break=True, lower_continue=True)
+
+def test_remove_continue_at_end_of_loop():
+ doc_string = """Test that a redundant continue-statement at the end of a
+ loop is removed.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ loop(assign_x('a', const_float(1)) +
+ continue_())
+ ))
+ expected_sexp = make_test_case('main', 'void', (
+ loop(assign_x('a', const_float(1)))
+ ))
+ create_test_case(doc_string, input_sexp, expected_sexp, 'remove_continue_at_end_of_loop')
+
+def test_lower_return_void_at_end_of_loop():
+ doc_string = """Test that a return of void at the end of a loop is properly
+ lowered.
+ """
+ input_sexp = make_test_case('main', 'void', (
+ loop(assign_x('a', const_float(1)) +
+ return_()) +
+ assign_x('b', const_float(2))
+ ))
+ expected_sexp = make_test_case('main', 'void', (
+ declare_return_flag() +
+ loop(assign_x('a', const_float(1)) +
+ lowered_return_simple() +
+ break_()) +
+ if_not_return_flag(assign_x('b', const_float(2)))
+ ))
+ create_test_case(doc_string, input_sexp, input_sexp, 'return_void_at_end_of_loop_lower_nothing')
+ create_test_case(doc_string, input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return',
+ lower_main_return=True)
+ create_test_case(doc_string, input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return_and_break',
+ lower_main_return=True, lower_break=True)
+
+def test_lower_return_non_void_at_end_of_loop():
+ doc_string = """Test that a non-void return at the end of a loop is
+ properly lowered.
+ """
+ input_sexp = make_test_case('sub', 'float', (
+ loop(assign_x('a', const_float(1)) +
+ return_(const_float(2))) +
+ assign_x('b', const_float(3)) +
+ return_(const_float(4))
+ ))
+ expected_sexp = make_test_case('sub', 'float', (
+ declare_execute_flag() +
+ declare_return_value() +
+ declare_return_flag() +
+ loop(assign_x('a', const_float(1)) +
+ lowered_return_simple(const_float(2)) +
+ break_()) +
+ if_not_return_flag(assign_x('b', const_float(3)) +
+ lowered_return(const_float(4))) +
+ final_return()
+ ))
+ create_test_case(doc_string, input_sexp, input_sexp, 'return_non_void_at_end_of_loop_lower_nothing')
+ create_test_case(doc_string, input_sexp, expected_sexp, 'return_non_void_at_end_of_loop_lower_return',
+ lower_sub_return=True)
+ create_test_case(doc_string, input_sexp, expected_sexp, 'return_non_void_at_end_of_loop_lower_return_and_break',
+ lower_sub_return=True, lower_break=True)
+
+if __name__ == '__main__':
+ test_lower_returns_main()
+ test_lower_returns_sub()
+ test_lower_returns_1()
+ test_lower_returns_2()
+ test_lower_returns_3()
+ test_lower_returns_4()
+ test_lower_unified_returns()
+ test_lower_pulled_out_jump()
+ test_lower_breaks_1()
+ test_lower_breaks_2()
+ test_lower_breaks_3()
+ test_lower_breaks_4()
+ test_lower_breaks_5()
+ test_lower_breaks_6()
+ test_lower_guarded_conditional_break()
+ test_remove_continue_at_end_of_loop()
+ test_lower_return_void_at_end_of_loop()
+ test_lower_return_non_void_at_end_of_loop()
diff --git a/src/compiler/glsl/tests/optimization-test b/src/compiler/glsl/tests/optimization-test
new file mode 100755
index 00000000000..26a51be6980
--- /dev/null
+++ b/src/compiler/glsl/tests/optimization-test
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+if [ ! -z "$srcdir" ]; then
+ compare_ir=`pwd`/tests/compare_ir
+else
+ compare_ir=./compare_ir
+fi
+
+total=0
+pass=0
+
+echo "====== Generating tests ======"
+for dir in tests/*/; do
+ if [ -e "${dir}create_test_cases.py" ]; then
+ cd $dir; $PYTHON2 create_test_cases.py; cd ..
+ fi
+ echo "$dir"
+done
+
+echo "====== Testing optimization passes ======"
+for test in `find . -iname '*.opt_test'`; do
+ echo -n "Testing $test..."
+ (cd `dirname "$test"`; ./`basename "$test"`) > "$test.out" 2>&1
+ total=$((total+1))
+ if $PYTHON2 $PYTHON_FLAGS $compare_ir "$test.expected" "$test.out" >/dev/null 2>&1; then
+ echo "PASS"
+ pass=$((pass+1))
+ else
+ echo "FAIL"
+ $PYTHON2 $PYTHON_FLAGS $compare_ir "$test.expected" "$test.out"
+ fi
+done
+
+echo ""
+echo "$pass/$total tests returned correct results"
+echo ""
+
+if [[ $pass == $total ]]; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/src/compiler/glsl/tests/sampler_types_test.cpp b/src/compiler/glsl/tests/sampler_types_test.cpp
new file mode 100644
index 00000000000..04dd65e6e8d
--- /dev/null
+++ b/src/compiler/glsl/tests/sampler_types_test.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <gtest/gtest.h>
+#include "main/compiler.h"
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "ir.h"
+
+/**
+ * \file sampler_types_test.cpp
+ *
+ * Test that built-in sampler types have the right properties.
+ */
+
+#define ARRAY EXPECT_TRUE(type->sampler_array);
+#define NONARRAY EXPECT_FALSE(type->sampler_array);
+#define SHADOW EXPECT_TRUE(type->sampler_shadow);
+#define COLOR EXPECT_FALSE(type->sampler_shadow);
+
+#define T(TYPE, DIM, DATA_TYPE, ARR, SHAD, COMPS) \
+TEST(sampler_types, TYPE) \
+{ \
+ const glsl_type *type = glsl_type::TYPE##_type; \
+ EXPECT_EQ(GLSL_TYPE_SAMPLER, type->base_type); \
+ EXPECT_EQ(DIM, type->sampler_dimensionality); \
+ EXPECT_EQ(DATA_TYPE, type->sampler_type); \
+ ARR; \
+ SHAD; \
+ EXPECT_EQ(COMPS, type->coordinate_components()); \
+}
+
+T( sampler1D, GLSL_SAMPLER_DIM_1D, GLSL_TYPE_FLOAT, NONARRAY, COLOR, 1)
+T( sampler2D, GLSL_SAMPLER_DIM_2D, GLSL_TYPE_FLOAT, NONARRAY, COLOR, 2)
+T( sampler3D, GLSL_SAMPLER_DIM_3D, GLSL_TYPE_FLOAT, NONARRAY, COLOR, 3)
+T( samplerCube, GLSL_SAMPLER_DIM_CUBE, GLSL_TYPE_FLOAT, NONARRAY, COLOR, 3)
+T( sampler1DArray, GLSL_SAMPLER_DIM_1D, GLSL_TYPE_FLOAT, ARRAY, COLOR, 2)
+T( sampler2DArray, GLSL_SAMPLER_DIM_2D, GLSL_TYPE_FLOAT, ARRAY, COLOR, 3)
+T( samplerCubeArray, GLSL_SAMPLER_DIM_CUBE, GLSL_TYPE_FLOAT, ARRAY, COLOR, 4)
+T( sampler2DRect, GLSL_SAMPLER_DIM_RECT, GLSL_TYPE_FLOAT, NONARRAY, COLOR, 2)
+T( samplerBuffer, GLSL_SAMPLER_DIM_BUF, GLSL_TYPE_FLOAT, NONARRAY, COLOR, 1)
+T( sampler2DMS, GLSL_SAMPLER_DIM_MS, GLSL_TYPE_FLOAT, NONARRAY, COLOR, 2)
+T( sampler2DMSArray, GLSL_SAMPLER_DIM_MS, GLSL_TYPE_FLOAT, ARRAY, COLOR, 3)
+T(isampler1D, GLSL_SAMPLER_DIM_1D, GLSL_TYPE_INT, NONARRAY, COLOR, 1)
+T(isampler2D, GLSL_SAMPLER_DIM_2D, GLSL_TYPE_INT, NONARRAY, COLOR, 2)
+T(isampler3D, GLSL_SAMPLER_DIM_3D, GLSL_TYPE_INT, NONARRAY, COLOR, 3)
+T(isamplerCube, GLSL_SAMPLER_DIM_CUBE, GLSL_TYPE_INT, NONARRAY, COLOR, 3)
+T(isampler1DArray, GLSL_SAMPLER_DIM_1D, GLSL_TYPE_INT, ARRAY, COLOR, 2)
+T(isampler2DArray, GLSL_SAMPLER_DIM_2D, GLSL_TYPE_INT, ARRAY, COLOR, 3)
+T(isamplerCubeArray, GLSL_SAMPLER_DIM_CUBE, GLSL_TYPE_INT, ARRAY, COLOR, 4)
+T(isampler2DRect, GLSL_SAMPLER_DIM_RECT, GLSL_TYPE_INT, NONARRAY, COLOR, 2)
+T(isamplerBuffer, GLSL_SAMPLER_DIM_BUF, GLSL_TYPE_INT, NONARRAY, COLOR, 1)
+T(isampler2DMS, GLSL_SAMPLER_DIM_MS, GLSL_TYPE_INT, NONARRAY, COLOR, 2)
+T(isampler2DMSArray, GLSL_SAMPLER_DIM_MS, GLSL_TYPE_INT, ARRAY, COLOR, 3)
+T(usampler1D, GLSL_SAMPLER_DIM_1D, GLSL_TYPE_UINT, NONARRAY, COLOR, 1)
+T(usampler2D, GLSL_SAMPLER_DIM_2D, GLSL_TYPE_UINT, NONARRAY, COLOR, 2)
+T(usampler3D, GLSL_SAMPLER_DIM_3D, GLSL_TYPE_UINT, NONARRAY, COLOR, 3)
+T(usamplerCube, GLSL_SAMPLER_DIM_CUBE, GLSL_TYPE_UINT, NONARRAY, COLOR, 3)
+T(usampler1DArray, GLSL_SAMPLER_DIM_1D, GLSL_TYPE_UINT, ARRAY, COLOR, 2)
+T(usampler2DArray, GLSL_SAMPLER_DIM_2D, GLSL_TYPE_UINT, ARRAY, COLOR, 3)
+T(usamplerCubeArray, GLSL_SAMPLER_DIM_CUBE, GLSL_TYPE_UINT, ARRAY, COLOR, 4)
+T(usampler2DRect, GLSL_SAMPLER_DIM_RECT, GLSL_TYPE_UINT, NONARRAY, COLOR, 2)
+T(usamplerBuffer, GLSL_SAMPLER_DIM_BUF, GLSL_TYPE_UINT, NONARRAY, COLOR, 1)
+T(usampler2DMS, GLSL_SAMPLER_DIM_MS, GLSL_TYPE_UINT, NONARRAY, COLOR, 2)
+T(usampler2DMSArray, GLSL_SAMPLER_DIM_MS, GLSL_TYPE_UINT, ARRAY, COLOR, 3)
+
+T(sampler1DShadow, GLSL_SAMPLER_DIM_1D, GLSL_TYPE_FLOAT, NONARRAY, SHADOW, 1)
+T(sampler2DShadow, GLSL_SAMPLER_DIM_2D, GLSL_TYPE_FLOAT, NONARRAY, SHADOW, 2)
+T(samplerCubeShadow, GLSL_SAMPLER_DIM_CUBE, GLSL_TYPE_FLOAT, NONARRAY, SHADOW, 3)
+
+T(sampler1DArrayShadow,
+ GLSL_SAMPLER_DIM_1D, GLSL_TYPE_FLOAT, ARRAY, SHADOW, 2)
+T(sampler2DArrayShadow,
+ GLSL_SAMPLER_DIM_2D, GLSL_TYPE_FLOAT, ARRAY, SHADOW, 3)
+T(samplerCubeArrayShadow,
+ GLSL_SAMPLER_DIM_CUBE, GLSL_TYPE_FLOAT, ARRAY, SHADOW, 4)
+T(sampler2DRectShadow,
+ GLSL_SAMPLER_DIM_RECT, GLSL_TYPE_FLOAT, NONARRAY, SHADOW, 2)
+
+T(samplerExternalOES,
+ GLSL_SAMPLER_DIM_EXTERNAL, GLSL_TYPE_FLOAT, NONARRAY, COLOR, 2)
diff --git a/src/compiler/glsl/tests/set_uniform_initializer_tests.cpp b/src/compiler/glsl/tests/set_uniform_initializer_tests.cpp
new file mode 100644
index 00000000000..0b1f66cb342
--- /dev/null
+++ b/src/compiler/glsl/tests/set_uniform_initializer_tests.cpp
@@ -0,0 +1,594 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <gtest/gtest.h>
+#include "main/compiler.h"
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "util/ralloc.h"
+#include "uniform_initializer_utils.h"
+
+namespace linker {
+extern void
+set_uniform_initializer(void *mem_ctx, gl_shader_program *prog,
+ const char *name, const glsl_type *type,
+ ir_constant *val, unsigned int boolean_true);
+}
+
+class set_uniform_initializer : public ::testing::Test {
+public:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ /**
+ * Index of the uniform to be tested.
+ *
+ * All of the \c set_uniform_initializer tests create several slots for
+ * unifroms. All but one of the slots is fake. This field holds the index
+ * of the slot for the uniform being tested.
+ */
+ unsigned actual_index;
+
+ /**
+ * Name of the uniform to be tested.
+ */
+ const char *name;
+
+ /**
+ * Shader program used in the test.
+ */
+ struct gl_shader_program *prog;
+
+ /**
+ * Ralloc memory context used for all temporary allocations.
+ */
+ void *mem_ctx;
+};
+
+void
+set_uniform_initializer::SetUp()
+{
+ this->mem_ctx = ralloc_context(NULL);
+ this->prog = rzalloc(NULL, struct gl_shader_program);
+
+ /* Set default values used by the test cases.
+ */
+ this->actual_index = 1;
+ this->name = "i";
+}
+
+void
+set_uniform_initializer::TearDown()
+{
+ ralloc_free(this->mem_ctx);
+ this->mem_ctx = NULL;
+
+ ralloc_free(this->prog);
+ this->prog = NULL;
+}
+
+/**
+ * Create some uniform storage for a program.
+ *
+ * \param prog Program to get some storage
+ * \param num_storage Total number of storage slots
+ * \param index_to_set Storage slot that will actually get a value
+ * \param name Name for the actual storage slot
+ * \param type Type for the elements of the actual storage slot
+ * \param array_size Size for the array of the actual storage slot. This
+ * should be zero for non-arrays.
+ */
+static unsigned
+establish_uniform_storage(struct gl_shader_program *prog, unsigned num_storage,
+ unsigned index_to_set, const char *name,
+ const glsl_type *type, unsigned array_size)
+{
+ const unsigned elements = MAX2(1, array_size);
+ const unsigned data_components = elements * type->components();
+ const unsigned total_components = MAX2(17, (data_components
+ + type->components()));
+ const unsigned red_zone_components = total_components - data_components;
+
+ prog->UniformStorage = rzalloc_array(prog, struct gl_uniform_storage,
+ num_storage);
+ prog->NumUniformStorage = num_storage;
+
+ prog->UniformStorage[index_to_set].name = (char *) name;
+ prog->UniformStorage[index_to_set].type = type;
+ prog->UniformStorage[index_to_set].array_elements = array_size;
+ prog->UniformStorage[index_to_set].initialized = false;
+ for (int sh = 0; sh < MESA_SHADER_STAGES; sh++) {
+ prog->UniformStorage[index_to_set].opaque[sh].index = ~0;
+ prog->UniformStorage[index_to_set].opaque[sh].active = false;
+ }
+ prog->UniformStorage[index_to_set].num_driver_storage = 0;
+ prog->UniformStorage[index_to_set].driver_storage = NULL;
+ prog->UniformStorage[index_to_set].storage =
+ rzalloc_array(prog, union gl_constant_value, total_components);
+
+ fill_storage_array_with_sentinels(prog->UniformStorage[index_to_set].storage,
+ data_components,
+ red_zone_components);
+
+ for (unsigned i = 0; i < num_storage; i++) {
+ if (i == index_to_set)
+ continue;
+
+ prog->UniformStorage[i].name = (char *) "invalid slot";
+ prog->UniformStorage[i].type = glsl_type::void_type;
+ prog->UniformStorage[i].array_elements = 0;
+ prog->UniformStorage[i].initialized = false;
+ for (int sh = 0; sh < MESA_SHADER_STAGES; sh++) {
+ prog->UniformStorage[i].opaque[sh].index = ~0;
+ prog->UniformStorage[i].opaque[sh].active = false;
+ }
+ prog->UniformStorage[i].num_driver_storage = 0;
+ prog->UniformStorage[i].driver_storage = NULL;
+ prog->UniformStorage[i].storage = NULL;
+ }
+
+ return red_zone_components;
+}
+
+/**
+ * Verify that the correct uniform is marked as having been initialized.
+ */
+static void
+verify_initialization(struct gl_shader_program *prog, unsigned actual_index)
+{
+ for (unsigned i = 0; i < prog->NumUniformStorage; i++) {
+ if (i == actual_index) {
+ EXPECT_TRUE(prog->UniformStorage[actual_index].initialized);
+ } else {
+ EXPECT_FALSE(prog->UniformStorage[i].initialized);
+ }
+ }
+}
+
+static void
+non_array_test(void *mem_ctx, struct gl_shader_program *prog,
+ unsigned actual_index, const char *name,
+ enum glsl_base_type base_type,
+ unsigned columns, unsigned rows)
+{
+ const glsl_type *const type =
+ glsl_type::get_instance(base_type, rows, columns);
+
+ unsigned red_zone_components =
+ establish_uniform_storage(prog, 3, actual_index, name, type, 0);
+
+ ir_constant *val;
+ generate_data(mem_ctx, base_type, columns, rows, val);
+
+ linker::set_uniform_initializer(mem_ctx, prog, name, type, val, 0xF00F);
+
+ verify_initialization(prog, actual_index);
+ verify_data(prog->UniformStorage[actual_index].storage, 0, val,
+ red_zone_components, 0xF00F);
+}
+
+TEST_F(set_uniform_initializer, int_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 1);
+}
+
+TEST_F(set_uniform_initializer, ivec2_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 2);
+}
+
+TEST_F(set_uniform_initializer, ivec3_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 3);
+}
+
+TEST_F(set_uniform_initializer, ivec4_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 4);
+}
+
+TEST_F(set_uniform_initializer, uint_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 1);
+}
+
+TEST_F(set_uniform_initializer, uvec2_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 2);
+}
+
+TEST_F(set_uniform_initializer, uvec3_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 3);
+}
+
+TEST_F(set_uniform_initializer, uvec4_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 4);
+}
+
+TEST_F(set_uniform_initializer, bool_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 1);
+}
+
+TEST_F(set_uniform_initializer, bvec2_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 2);
+}
+
+TEST_F(set_uniform_initializer, bvec3_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 3);
+}
+
+TEST_F(set_uniform_initializer, bvec4_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 4);
+}
+
+TEST_F(set_uniform_initializer, float_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 2);
+}
+
+TEST_F(set_uniform_initializer, vec2_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 2);
+}
+
+TEST_F(set_uniform_initializer, vec3_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 3);
+}
+
+TEST_F(set_uniform_initializer, vec4_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 4);
+}
+
+TEST_F(set_uniform_initializer, mat2x2_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 2);
+}
+
+TEST_F(set_uniform_initializer, mat2x3_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 3);
+}
+
+TEST_F(set_uniform_initializer, mat2x4_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 4);
+}
+
+TEST_F(set_uniform_initializer, mat3x2_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 2);
+}
+
+TEST_F(set_uniform_initializer, mat3x3_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 3);
+}
+
+TEST_F(set_uniform_initializer, mat3x4_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 4);
+}
+
+TEST_F(set_uniform_initializer, mat4x2_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 2);
+}
+
+TEST_F(set_uniform_initializer, mat4x3_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 3);
+}
+
+TEST_F(set_uniform_initializer, mat4x4_uniform)
+{
+ non_array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 4);
+}
+
+static void
+array_test(void *mem_ctx, struct gl_shader_program *prog,
+ unsigned actual_index, const char *name,
+ enum glsl_base_type base_type,
+ unsigned columns, unsigned rows, unsigned array_size,
+ unsigned excess_data_size)
+{
+ const glsl_type *const element_type =
+ glsl_type::get_instance(base_type, rows, columns);
+
+ const unsigned red_zone_components =
+ establish_uniform_storage(prog, 3, actual_index, name, element_type,
+ array_size);
+
+ /* The constant value generated may have more array elements than the
+ * uniform that it initializes. In the real compiler and linker this can
+ * happen when a uniform array is compacted because some of the tail
+ * elements are not used. In this case, the type of the uniform will be
+ * modified, but the initializer will not.
+ */
+ ir_constant *val;
+ generate_array_data(mem_ctx, base_type, columns, rows,
+ array_size + excess_data_size, val);
+
+ linker::set_uniform_initializer(mem_ctx, prog, name, element_type, val,
+ 0xF00F);
+
+ verify_initialization(prog, actual_index);
+ verify_data(prog->UniformStorage[actual_index].storage, array_size,
+ val, red_zone_components, 0xF00F);
+}
+
+TEST_F(set_uniform_initializer, int_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 1, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, ivec2_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 2, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, ivec3_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 3, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, ivec4_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 4, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, uint_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 1, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, uvec2_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 2, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, uvec3_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 3, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, uvec4_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 4, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, bool_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 1, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, bvec2_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 2, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, bvec3_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 3, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, bvec4_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 4, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, float_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 1, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, vec2_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 2, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, vec3_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 3, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, vec4_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 4, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat2x2_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 2, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat2x3_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 3, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat2x4_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 4, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat3x2_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 2, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat3x3_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 3, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat3x4_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 4, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat4x2_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 2, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat4x3_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 3, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, mat4x4_array_uniform)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 4, 4, 0);
+}
+
+TEST_F(set_uniform_initializer, int_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 1, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, ivec2_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 2, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, ivec3_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 3, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, ivec4_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_INT, 1, 4, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, uint_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 1, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, uvec2_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 2, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, uvec3_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 3, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, uvec4_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_UINT, 1, 4, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, bool_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 1, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, bvec2_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 2, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, bvec3_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 3, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, bvec4_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_BOOL, 1, 4, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, float_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 1, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, vec2_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 2, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, vec3_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 3, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, vec4_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 1, 4, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat2x2_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 2, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat2x3_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 3, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat2x4_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 2, 4, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat3x2_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 2, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat3x3_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 3, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat3x4_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 3, 4, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat4x2_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 2, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat4x3_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 3, 4, 5);
+}
+
+TEST_F(set_uniform_initializer, mat4x4_array_uniform_excess_initializer)
+{
+ array_test(mem_ctx, prog, actual_index, name, GLSL_TYPE_FLOAT, 4, 4, 4, 5);
+}
diff --git a/src/compiler/glsl/tests/sexps.py b/src/compiler/glsl/tests/sexps.py
new file mode 100644
index 00000000000..a714af8d236
--- /dev/null
+++ b/src/compiler/glsl/tests/sexps.py
@@ -0,0 +1,103 @@
+# coding=utf-8
+#
+# Copyright © 2011 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+# This file contains helper functions for manipulating sexps in Python.
+#
+# We represent a sexp in Python using nested lists containing strings.
+# So, for example, the sexp (constant float (1.000000)) is represented
+# as ['constant', 'float', ['1.000000']].
+
+import re
+
+def check_sexp(sexp):
+ """Verify that the argument is a proper sexp.
+
+ That is, raise an exception if the argument is not a string or a
+ list, or if it contains anything that is not a string or a list at
+ any nesting level.
+ """
+ if isinstance(sexp, list):
+ for s in sexp:
+ check_sexp(s)
+ elif not isinstance(sexp, basestring):
+ raise Exception('Not a sexp: {0!r}'.format(sexp))
+
+def parse_sexp(sexp):
+ """Convert a string, of the form that would be output by mesa,
+ into a sexp represented as nested lists containing strings.
+ """
+ sexp_token_regexp = re.compile(
+ '[a-zA-Z_]+(@[0-9]+)?|[0-9]+(\\.[0-9]+)?|[^ \n]')
+ stack = [[]]
+ for match in sexp_token_regexp.finditer(sexp):
+ token = match.group(0)
+ if token == '(':
+ stack.append([])
+ elif token == ')':
+ if len(stack) == 1:
+ raise Exception('Unmatched )')
+ sexp = stack.pop()
+ stack[-1].append(sexp)
+ else:
+ stack[-1].append(token)
+ if len(stack) != 1:
+ raise Exception('Unmatched (')
+ if len(stack[0]) != 1:
+ raise Exception('Multiple sexps')
+ return stack[0][0]
+
+def sexp_to_string(sexp):
+ """Convert a sexp, represented as nested lists containing strings,
+ into a single string of the form parseable by mesa.
+ """
+ if isinstance(sexp, basestring):
+ return sexp
+ assert isinstance(sexp, list)
+ result = ''
+ for s in sexp:
+ sub_result = sexp_to_string(s)
+ if result == '':
+ result = sub_result
+ elif '\n' not in result and '\n' not in sub_result and \
+ len(result) + len(sub_result) + 1 <= 70:
+ result += ' ' + sub_result
+ else:
+ result += '\n' + sub_result
+ return '({0})'.format(result.replace('\n', '\n '))
+
+def sort_decls(sexp):
+ """Sort all toplevel variable declarations in sexp.
+
+ This is used to work around the fact that
+ ir_reader::read_instructions reorders declarations.
+ """
+ assert isinstance(sexp, list)
+ decls = []
+ other_code = []
+ for s in sexp:
+ if isinstance(s, list) and len(s) >= 4 and s[0] == 'declare':
+ decls.append(s)
+ else:
+ other_code.append(s)
+ return sorted(decls) + other_code
+
diff --git a/src/compiler/glsl/tests/uniform_initializer_utils.cpp b/src/compiler/glsl/tests/uniform_initializer_utils.cpp
new file mode 100644
index 00000000000..5006387036f
--- /dev/null
+++ b/src/compiler/glsl/tests/uniform_initializer_utils.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <gtest/gtest.h>
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "util/ralloc.h"
+#include "uniform_initializer_utils.h"
+#include <stdio.h>
+
+void
+fill_storage_array_with_sentinels(gl_constant_value *storage,
+ unsigned data_size,
+ unsigned red_zone_size)
+{
+ for (unsigned i = 0; i < data_size; i++)
+ storage[i].u = 0xDEADBEEF;
+
+ for (unsigned i = 0; i < red_zone_size; i++)
+ storage[data_size + i].u = 0xBADDC0DE;
+}
+
+/**
+ * Verfiy that markers past the end of the real uniform are unmodified
+ */
+static ::testing::AssertionResult
+red_zone_is_intact(gl_constant_value *storage,
+ unsigned data_size,
+ unsigned red_zone_size)
+{
+ for (unsigned i = 0; i < red_zone_size; i++) {
+ const unsigned idx = data_size + i;
+
+ if (storage[idx].u != 0xBADDC0DE)
+ return ::testing::AssertionFailure()
+ << "storage[" << idx << "].u = " << storage[idx].u
+ << ", exepected data values = " << data_size
+ << ", red-zone size = " << red_zone_size;
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+static const int values[] = {
+ 2, 0, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53
+};
+
+/**
+ * Generate a single data element.
+ *
+ * This is by both \c generate_data and \c generate_array_data to create the
+ * data.
+ */
+static void
+generate_data_element(void *mem_ctx, const glsl_type *type,
+ ir_constant *&val, unsigned data_index_base)
+{
+ /* Set the initial data values for the generated constant.
+ */
+ ir_constant_data data;
+ memset(&data, 0, sizeof(data));
+ for (unsigned i = 0; i < type->components(); i++) {
+ const unsigned idx = (i + data_index_base) % ARRAY_SIZE(values);
+ switch (type->base_type) {
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT:
+ case GLSL_TYPE_SAMPLER:
+ case GLSL_TYPE_IMAGE:
+ data.i[i] = values[idx];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[i] = float(values[idx]);
+ break;
+ case GLSL_TYPE_BOOL:
+ data.b[i] = bool(values[idx]);
+ break;
+ case GLSL_TYPE_DOUBLE:
+ data.d[i] = double(values[idx]);
+ break;
+ case GLSL_TYPE_ATOMIC_UINT:
+ case GLSL_TYPE_STRUCT:
+ case GLSL_TYPE_ARRAY:
+ case GLSL_TYPE_VOID:
+ case GLSL_TYPE_ERROR:
+ case GLSL_TYPE_INTERFACE:
+ case GLSL_TYPE_SUBROUTINE:
+ ASSERT_TRUE(false);
+ break;
+ }
+ }
+
+ /* Generate and verify the constant.
+ */
+ val = new(mem_ctx) ir_constant(type, &data);
+
+ for (unsigned i = 0; i < type->components(); i++) {
+ switch (type->base_type) {
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT:
+ case GLSL_TYPE_SAMPLER:
+ case GLSL_TYPE_IMAGE:
+ ASSERT_EQ(data.i[i], val->value.i[i]);
+ break;
+ case GLSL_TYPE_FLOAT:
+ ASSERT_EQ(data.f[i], val->value.f[i]);
+ break;
+ case GLSL_TYPE_BOOL:
+ ASSERT_EQ(data.b[i], val->value.b[i]);
+ break;
+ case GLSL_TYPE_DOUBLE:
+ ASSERT_EQ(data.d[i], val->value.d[i]);
+ break;
+ case GLSL_TYPE_ATOMIC_UINT:
+ case GLSL_TYPE_STRUCT:
+ case GLSL_TYPE_ARRAY:
+ case GLSL_TYPE_VOID:
+ case GLSL_TYPE_ERROR:
+ case GLSL_TYPE_INTERFACE:
+ case GLSL_TYPE_SUBROUTINE:
+ ASSERT_TRUE(false);
+ break;
+ }
+ }
+}
+
+void
+generate_data(void *mem_ctx, enum glsl_base_type base_type,
+ unsigned columns, unsigned rows,
+ ir_constant *&val)
+{
+ /* Determine what the type of the generated constant should be.
+ */
+ const glsl_type *const type =
+ glsl_type::get_instance(base_type, rows, columns);
+ ASSERT_FALSE(type->is_error());
+
+ generate_data_element(mem_ctx, type, val, 0);
+}
+
+void
+generate_array_data(void *mem_ctx, enum glsl_base_type base_type,
+ unsigned columns, unsigned rows, unsigned array_size,
+ ir_constant *&val)
+{
+ /* Determine what the type of the generated constant should be.
+ */
+ const glsl_type *const element_type =
+ glsl_type::get_instance(base_type, rows, columns);
+ ASSERT_FALSE(element_type->is_error());
+
+ const glsl_type *const array_type =
+ glsl_type::get_array_instance(element_type, array_size);
+ ASSERT_FALSE(array_type->is_error());
+
+ /* Set the initial data values for the generated constant.
+ */
+ exec_list values_for_array;
+ for (unsigned i = 0; i < array_size; i++) {
+ ir_constant *element;
+
+ generate_data_element(mem_ctx, element_type, element, i);
+ values_for_array.push_tail(element);
+ }
+
+ val = new(mem_ctx) ir_constant(array_type, &values_for_array);
+}
+
+/**
+ * Verify that the data stored for the uniform matches the initializer
+ *
+ * \param storage Backing storage for the uniform
+ * \param storage_array_size Array size of the backing storage. This must be
+ * less than or equal to the array size of the type
+ * of \c val. If \c val is not an array, this must
+ * be zero.
+ * \param val Value of the initializer for the unifrom.
+ * \param red_zone
+ */
+void
+verify_data(gl_constant_value *storage, unsigned storage_array_size,
+ ir_constant *val, unsigned red_zone_size,
+ unsigned int boolean_true)
+{
+ if (val->type->base_type == GLSL_TYPE_ARRAY) {
+ const glsl_type *const element_type = val->array_elements[0]->type;
+
+ for (unsigned i = 0; i < storage_array_size; i++) {
+ verify_data(storage + (i * element_type->components()), 0,
+ val->array_elements[i], 0, boolean_true);
+ }
+
+ const unsigned components = element_type->components();
+
+ if (red_zone_size > 0) {
+ EXPECT_TRUE(red_zone_is_intact(storage,
+ storage_array_size * components,
+ red_zone_size));
+ }
+ } else {
+ ASSERT_EQ(0u, storage_array_size);
+ for (unsigned i = 0; i < val->type->components(); i++) {
+ switch (val->type->base_type) {
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT:
+ case GLSL_TYPE_SAMPLER:
+ case GLSL_TYPE_IMAGE:
+ EXPECT_EQ(val->value.i[i], storage[i].i);
+ break;
+ case GLSL_TYPE_FLOAT:
+ EXPECT_EQ(val->value.f[i], storage[i].f);
+ break;
+ case GLSL_TYPE_BOOL:
+ EXPECT_EQ(val->value.b[i] ? boolean_true : 0, storage[i].i);
+ break;
+ case GLSL_TYPE_DOUBLE:
+ EXPECT_EQ(val->value.d[i], *(double *)&storage[i*2].i);
+ break;
+ case GLSL_TYPE_ATOMIC_UINT:
+ case GLSL_TYPE_STRUCT:
+ case GLSL_TYPE_ARRAY:
+ case GLSL_TYPE_VOID:
+ case GLSL_TYPE_ERROR:
+ case GLSL_TYPE_INTERFACE:
+ case GLSL_TYPE_SUBROUTINE:
+ ASSERT_TRUE(false);
+ break;
+ }
+ }
+
+ if (red_zone_size > 0) {
+ EXPECT_TRUE(red_zone_is_intact(storage,
+ val->type->components(),
+ red_zone_size));
+ }
+ }
+}
diff --git a/src/compiler/glsl/tests/uniform_initializer_utils.h b/src/compiler/glsl/tests/uniform_initializer_utils.h
new file mode 100644
index 00000000000..b4d0c10220f
--- /dev/null
+++ b/src/compiler/glsl/tests/uniform_initializer_utils.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "program/prog_parameter.h"
+#include "ir.h"
+#include "ir_uniform.h"
+
+extern void
+fill_storage_array_with_sentinels(gl_constant_value *storage,
+ unsigned data_size,
+ unsigned red_zone_size);
+
+extern void
+generate_data(void *mem_ctx, enum glsl_base_type base_type,
+ unsigned columns, unsigned rows,
+ ir_constant *&val);
+
+extern void
+generate_array_data(void *mem_ctx, enum glsl_base_type base_type,
+ unsigned columns, unsigned rows, unsigned array_size,
+ ir_constant *&val);
+
+extern void
+verify_data(gl_constant_value *storage, unsigned storage_array_size,
+ ir_constant *val, unsigned red_zone_size,
+ unsigned int boolean_true);
diff --git a/src/compiler/glsl/tests/varyings_test.cpp b/src/compiler/glsl/tests/varyings_test.cpp
new file mode 100644
index 00000000000..0c4e0a471b8
--- /dev/null
+++ b/src/compiler/glsl/tests/varyings_test.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <gtest/gtest.h>
+#include "main/compiler.h"
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "util/ralloc.h"
+#include "ir.h"
+#include "program/hash_table.h"
+
+/**
+ * \file varyings_test.cpp
+ *
+ * Test various aspects of linking shader stage inputs and outputs.
+ */
+
+namespace linker {
+bool
+populate_consumer_input_sets(void *mem_ctx, exec_list *ir,
+ hash_table *consumer_inputs,
+ hash_table *consumer_interface_inputs,
+ ir_variable *consumer_inputs_with_locations[VARYING_SLOT_MAX]);
+
+ir_variable *
+get_matching_input(void *mem_ctx,
+ const ir_variable *output_var,
+ hash_table *consumer_inputs,
+ hash_table *consumer_interface_inputs,
+ ir_variable *consumer_inputs_with_locations[VARYING_SLOT_MAX]);
+}
+
+class link_varyings : public ::testing::Test {
+public:
+ link_varyings();
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ char *interface_field_name(const glsl_type *iface, unsigned field = 0)
+ {
+ return ralloc_asprintf(mem_ctx,
+ "%s.%s",
+ iface->name,
+ iface->fields.structure[field].name);
+ }
+
+ void *mem_ctx;
+ exec_list ir;
+ hash_table *consumer_inputs;
+ hash_table *consumer_interface_inputs;
+
+ const glsl_type *simple_interface;
+ ir_variable *junk[VARYING_SLOT_TESS_MAX];
+};
+
+link_varyings::link_varyings()
+{
+ static const glsl_struct_field f[] = {
+ glsl_struct_field(glsl_type::vec(4), "v")
+ };
+
+ this->simple_interface =
+ glsl_type::get_interface_instance(f,
+ ARRAY_SIZE(f),
+ GLSL_INTERFACE_PACKING_STD140,
+ "simple_interface");
+}
+
+void
+link_varyings::SetUp()
+{
+ this->mem_ctx = ralloc_context(NULL);
+ this->ir.make_empty();
+
+ this->consumer_inputs
+ = hash_table_ctor(0, hash_table_string_hash, hash_table_string_compare);
+
+ this->consumer_interface_inputs
+ = hash_table_ctor(0, hash_table_string_hash, hash_table_string_compare);
+}
+
+void
+link_varyings::TearDown()
+{
+ ralloc_free(this->mem_ctx);
+ this->mem_ctx = NULL;
+
+ hash_table_dtor(this->consumer_inputs);
+ this->consumer_inputs = NULL;
+ hash_table_dtor(this->consumer_interface_inputs);
+ this->consumer_interface_inputs = NULL;
+}
+
+/**
+ * Hash table callback function that counts the elements in the table
+ *
+ * \sa num_elements
+ */
+static void
+ht_count_callback(const void *, void *, void *closure)
+{
+ unsigned int *counter = (unsigned int *) closure;
+
+ (*counter)++;
+}
+
+/**
+ * Helper function to count the number of elements in a hash table.
+ */
+static unsigned
+num_elements(hash_table *ht)
+{
+ unsigned int counter = 0;
+
+ hash_table_call_foreach(ht, ht_count_callback, (void *) &counter);
+
+ return counter;
+}
+
+/**
+ * Helper function to determine whether a hash table is empty.
+ */
+static bool
+is_empty(hash_table *ht)
+{
+ return num_elements(ht) == 0;
+}
+
+TEST_F(link_varyings, single_simple_input)
+{
+ ir_variable *const v =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ "a",
+ ir_var_shader_in);
+
+
+ ir.push_tail(v);
+
+ ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
+ &ir,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk));
+
+ EXPECT_EQ((void *) v, hash_table_find(consumer_inputs, "a"));
+ EXPECT_EQ(1u, num_elements(consumer_inputs));
+ EXPECT_TRUE(is_empty(consumer_interface_inputs));
+}
+
+TEST_F(link_varyings, gl_ClipDistance)
+{
+ const glsl_type *const array_8_of_float =
+ glsl_type::get_array_instance(glsl_type::vec(1), 8);
+
+ ir_variable *const clipdistance =
+ new(mem_ctx) ir_variable(array_8_of_float,
+ "gl_ClipDistance",
+ ir_var_shader_in);
+
+ clipdistance->data.explicit_location = true;
+ clipdistance->data.location = VARYING_SLOT_CLIP_DIST0;
+ clipdistance->data.explicit_index = 0;
+
+ ir.push_tail(clipdistance);
+
+ ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
+ &ir,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk));
+
+ EXPECT_EQ(clipdistance, junk[VARYING_SLOT_CLIP_DIST0]);
+ EXPECT_TRUE(is_empty(consumer_inputs));
+ EXPECT_TRUE(is_empty(consumer_interface_inputs));
+}
+
+TEST_F(link_varyings, single_interface_input)
+{
+ ir_variable *const v =
+ new(mem_ctx) ir_variable(simple_interface->fields.structure[0].type,
+ simple_interface->fields.structure[0].name,
+ ir_var_shader_in);
+
+ v->init_interface_type(simple_interface);
+
+ ir.push_tail(v);
+
+ ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
+ &ir,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk));
+ char *const full_name = interface_field_name(simple_interface);
+
+ EXPECT_EQ((void *) v, hash_table_find(consumer_interface_inputs, full_name));
+ EXPECT_EQ(1u, num_elements(consumer_interface_inputs));
+ EXPECT_TRUE(is_empty(consumer_inputs));
+}
+
+TEST_F(link_varyings, one_interface_and_one_simple_input)
+{
+ ir_variable *const v =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ "a",
+ ir_var_shader_in);
+
+
+ ir.push_tail(v);
+
+ ir_variable *const iface =
+ new(mem_ctx) ir_variable(simple_interface->fields.structure[0].type,
+ simple_interface->fields.structure[0].name,
+ ir_var_shader_in);
+
+ iface->init_interface_type(simple_interface);
+
+ ir.push_tail(iface);
+
+ ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
+ &ir,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk));
+
+ char *const iface_field_name = interface_field_name(simple_interface);
+
+ EXPECT_EQ((void *) iface, hash_table_find(consumer_interface_inputs,
+ iface_field_name));
+ EXPECT_EQ(1u, num_elements(consumer_interface_inputs));
+
+ EXPECT_EQ((void *) v, hash_table_find(consumer_inputs, "a"));
+ EXPECT_EQ(1u, num_elements(consumer_inputs));
+}
+
+TEST_F(link_varyings, invalid_interface_input)
+{
+ ir_variable *const v =
+ new(mem_ctx) ir_variable(simple_interface,
+ "named_interface",
+ ir_var_shader_in);
+
+ ASSERT_EQ(simple_interface, v->get_interface_type());
+
+ ir.push_tail(v);
+
+ EXPECT_FALSE(linker::populate_consumer_input_sets(mem_ctx,
+ &ir,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk));
+}
+
+TEST_F(link_varyings, interface_field_doesnt_match_noninterface)
+{
+ char *const iface_field_name = interface_field_name(simple_interface);
+
+ /* The input shader has a single input variable name "a.v"
+ */
+ ir_variable *const in_v =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ iface_field_name,
+ ir_var_shader_in);
+
+ ir.push_tail(in_v);
+
+ ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
+ &ir,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk));
+
+ /* Create an output variable, "v", that is part of an interface block named
+ * "a". They should not match.
+ */
+ ir_variable *const out_v =
+ new(mem_ctx) ir_variable(simple_interface->fields.structure[0].type,
+ simple_interface->fields.structure[0].name,
+ ir_var_shader_in);
+
+ out_v->init_interface_type(simple_interface);
+
+ ir_variable *const match =
+ linker::get_matching_input(mem_ctx,
+ out_v,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk);
+
+ EXPECT_EQ(NULL, match);
+}
+
+TEST_F(link_varyings, interface_field_doesnt_match_noninterface_vice_versa)
+{
+ char *const iface_field_name = interface_field_name(simple_interface);
+
+ /* In input shader has a single variable, "v", that is part of an interface
+ * block named "a".
+ */
+ ir_variable *const in_v =
+ new(mem_ctx) ir_variable(simple_interface->fields.structure[0].type,
+ simple_interface->fields.structure[0].name,
+ ir_var_shader_in);
+
+ in_v->init_interface_type(simple_interface);
+
+ ir.push_tail(in_v);
+
+ ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
+ &ir,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk));
+
+ /* Create an output variable "a.v". They should not match.
+ */
+ ir_variable *const out_v =
+ new(mem_ctx) ir_variable(glsl_type::vec(4),
+ iface_field_name,
+ ir_var_shader_out);
+
+ ir_variable *const match =
+ linker::get_matching_input(mem_ctx,
+ out_v,
+ consumer_inputs,
+ consumer_interface_inputs,
+ junk);
+
+ EXPECT_EQ(NULL, match);
+}