[Mesa-dev] [PATCH 3/4] glsl: Create a standalone executable for testing optimization passes.
Ian Romanick
idr at freedesktop.org
Tue Jul 19 13:15:35 PDT 2011
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 07/08/2011 03:30 PM, Paul Berry wrote:
> This patch adds a new build artifact, glsl_test, which can be used for
> testing optimization passes in isolation.
>
> I'm hoping that we will be able to add other useful standalone tests
> to this executable in the future. Accordingly, it is built in a
> modular fashion: the main() function uses its first argument to
> determine which test function to invoke, removes that argument from
> argv[], and then calls that function to interpret the rest of the
> command line arguments and perform the test. Currently the only test
> function is "optpass", which tests optimization passes.
A couple of comments...
We'll probably have to tweak the build a bit to be sure everything ends
up in the tarballs. Since José's changes recently this may not be as
much of a problem, but we'll at least want to test it. We can do that
on Wednesday.
There are a couple other comments below...
> ---
> src/glsl/.gitignore | 1 +
> src/glsl/Makefile | 23 +++-
> src/glsl/test.cpp | 78 ++++++++++++
> src/glsl/test_optpass.cpp | 289 +++++++++++++++++++++++++++++++++++++++++++++
> src/glsl/test_optpass.h | 30 +++++
> 5 files changed, 418 insertions(+), 3 deletions(-)
> create mode 100644 src/glsl/test.cpp
> create mode 100644 src/glsl/test_optpass.cpp
> create mode 100644 src/glsl/test_optpass.h
>
> diff --git a/src/glsl/.gitignore b/src/glsl/.gitignore
> index dfbd572..d26839a 100644
> --- a/src/glsl/.gitignore
> +++ b/src/glsl/.gitignore
> @@ -5,3 +5,4 @@ glsl_parser.h
> glsl_parser.output
> builtin_function.cpp
> builtin_compiler
> +glsl_test
> diff --git a/src/glsl/Makefile b/src/glsl/Makefile
> index e0776c1..f574e27 100644
> --- a/src/glsl/Makefile
> +++ b/src/glsl/Makefile
> @@ -88,7 +88,7 @@ CXX_SOURCES = \
> LIBS = \
> $(TOP)/src/glsl/libglsl.a
>
> -APPS = glsl_compiler glcpp/glcpp
> +APPS = glsl_compiler glsl_test glcpp/glcpp
>
> GLSL2_C_SOURCES = \
> ../mesa/program/hash_table.c \
> @@ -100,6 +100,18 @@ GLSL2_OBJECTS = \
> $(GLSL2_C_SOURCES:.c=.o) \
> $(GLSL2_CXX_SOURCES:.cpp=.o)
>
> +TEST_C_SOURCES = \
> + ../mesa/program/hash_table.c \
> + ../mesa/program/symbol_table.c
> +
> +TEST_CXX_SOURCES = \
> + test.cpp \
> + test_optpass.cpp
> +
> +TEST_OBJECTS = \
> + $(TEST_C_SOURCES:.c=.o) \
> + $(TEST_CXX_SOURCES:.cpp=.o)
> +
> ### Basic defines ###
>
> DEFINES += \
> @@ -128,7 +140,9 @@ ALL_SOURCES = \
> $(C_SOURCES) \
> $(CXX_SOURCES) \
> $(GLSL2_CXX_SOURCES) \
> - $(GLSL2_C_SOURCES)
> + $(GLSL2_C_SOURCES) \
> + $(TEST_CXX_SOURCES) \
> + $(TEST_C_SOURCES)
>
> ##### TARGETS #####
>
> @@ -150,7 +164,7 @@ depend: $(ALL_SOURCES) Makefile
>
> # Remove .o and backup files
> clean: clean-dricore
> - rm -f $(GLCPP_OBJECTS) $(GLSL2_OBJECTS) $(OBJECTS) lib$(LIBNAME).a depend depend.bak builtin_function.cpp builtin_function.o builtin_stubs.o builtin_compiler
> + rm -f $(GLCPP_OBJECTS) $(GLSL2_OBJECTS) $(TEST_OBJECTS) $(OBJECTS) lib$(LIBNAME).a depend depend.bak builtin_function.cpp builtin_function.o builtin_stubs.o builtin_compiler
> -rm -f $(APPS)
>
> clean-dricore:
> @@ -173,6 +187,9 @@ install-dricore: default
> glsl_compiler: $(GLSL2_OBJECTS) libglsl.a builtin_stubs.o
> $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLSL2_OBJECTS) builtin_stubs.o $(LIBS) -o $@
>
> +glsl_test: $(TEST_OBJECTS) libglsl.a builtin_stubs.o
> + $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(TEST_OBJECTS) builtin_stubs.o $(LIBS) -o $@
> +
> glcpp: glcpp/glcpp
> glcpp/glcpp: $(GLCPP_OBJECTS)
> $(APP_CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLCPP_OBJECTS) -o $@
> diff --git a/src/glsl/test.cpp b/src/glsl/test.cpp
> new file mode 100644
> index 0000000..b1ff92e
> --- /dev/null
> +++ b/src/glsl/test.cpp
> @@ -0,0 +1,78 @@
> +/*
> + * 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.
> + */
> +
> +/**
> + * \file test.cpp
> + *
> + * Standalone tests for the GLSL compiler.
> + *
> + * This file provides a standalone executable which can be used to
> + * test components of the GLSL.
> + *
> + * Each test is a function with the same signature as main(). The
> + * main function interprets its first argument as the name of the test
> + * to run, strips out that argument, and then calls the test function.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "test_optpass.h"
> +
> +/**
> + * Print proper usage and exit with failure.
> + */
> +static void
> +usage_fail(const char *name)
> +{
> + printf("*** usage: %s <command> <options>\n", name);
> + printf("\n");
> + printf("Possible commands are:\n");
> + printf(" optpass: test an optimization pass in isolation\n");
> + exit(EXIT_FAILURE);
> +}
> +
> +static const char *extract_command_from_argv(int *argc, char **argv)
> +{
> + if (*argc < 2) {
> + usage_fail(argv[0]);
> + }
> + const char *command = argv[1];
> + --*argc;
> + memmove(&argv[1], &argv[2], (*argc) * sizeof(argv[1]));
> + return command;
> +}
This feels like an odd re-invention of getopt. Why?
> +
> +int main(int argc, char **argv)
> +{
> + const char *command = extract_command_from_argv(&argc, argv);
> + if (strcmp(command, "optpass") == 0) {
> + return test_optpass(argc, argv);
> + } else {
> + usage_fail(argv[0]);
> + }
> +
> + /* Execution should never reach here. */
> + return EXIT_FAILURE;
> +}
> diff --git a/src/glsl/test_optpass.cpp b/src/glsl/test_optpass.cpp
> new file mode 100644
> index 0000000..8988af2
> --- /dev/null
> +++ b/src/glsl/test_optpass.cpp
> @@ -0,0 +1,289 @@
> +/*
> + * 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.
> + */
> +
> +/**
> + * \file test_optpass.cpp
> + *
> + * Standalone test for optimization passes.
> + *
> + * This file provides the "optpass" command for the standalone
> + * glsl_test app. It accepts either GLSL or high-level IR as input,
> + * and performs the optimiation passes specified on the command line.
> + * It outputs the IR, both before and after optimiations.
> + */
> +
> +#include <string>
> +#include <iostream>
> +#include <sstream>
> +#include <getopt.h>
> +
> +#include "ast.h"
> +#include "ir_optimization.h"
> +#include "ir_print_visitor.h"
> +#include "program.h"
> +#include "ir_reader.h"
> +
> +using namespace std;
> +
> +static struct gl_shader *
> +_mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type)
> +{
> + struct gl_shader *shader;
> +
> + (void) ctx;
> +
> + assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER);
> + shader = rzalloc(NULL, struct gl_shader);
> + if (shader) {
> + shader->Type = type;
> + shader->Name = name;
> + shader->RefCount = 1;
> + }
> + return shader;
> +}
Another candidate for refactoring. :) Since this can't go in
glsl_parser_utils.cpp, the common bits of scaffolding should probably go
in their own file. Perhaps src/glsl/standalone_scaffolding.cpp?
It looks like there's a bit of code below for reading the shader file
that could use the same treatment.
> +
> +static string read_stdin_to_eof()
> +{
> + stringbuf sb;
> + cin.get(sb, '\0');
> + return sb.str();
> +}
Why are you mixing C++ iostreams and C FILE I/O? That seems weird. I'd
prefer to stick with C FILE I/O because everybody on the project knows
that well, but the real preference is to not mix and match.
> +
> +static GLboolean
> +do_optimization(struct exec_list *ir, const char *optimization)
> +{
> + int int_0;
> + int int_1;
> + int int_2;
> + int int_3;
> + int int_4;
> +
> + if (sscanf(optimization, "do_common_optimization ( %d , %d ) ",
> + &int_0, &int_1) == 2) {
> + return do_common_optimization(ir, int_0 != 0, int_1);
> + } else if (strcmp(optimization, "do_algebraic") == 0) {
> + return do_algebraic(ir);
> + } else if (strcmp(optimization, "do_constant_folding") == 0) {
> + return do_constant_folding(ir);
> + } else if (strcmp(optimization, "do_constant_variable") == 0) {
> + return do_constant_variable(ir);
> + } else if (strcmp(optimization, "do_constant_variable_unlinked") == 0) {
> + return do_constant_variable_unlinked(ir);
> + } else if (strcmp(optimization, "do_copy_propagation") == 0) {
> + return do_copy_propagation(ir);
> + } else if (strcmp(optimization, "do_copy_propagation_elements") == 0) {
> + return do_copy_propagation_elements(ir);
> + } else if (strcmp(optimization, "do_constant_propagation") == 0) {
> + return do_constant_propagation(ir);
> + } else if (strcmp(optimization, "do_dead_code") == 0) {
> + return do_dead_code(ir);
> + } else if (strcmp(optimization, "do_dead_code_local") == 0) {
> + return do_dead_code_local(ir);
> + } else if (strcmp(optimization, "do_dead_code_unlinked") == 0) {
> + return do_dead_code_unlinked(ir);
> + } else if (strcmp(optimization, "do_dead_functions") == 0) {
> + return do_dead_functions(ir);
> + } else if (strcmp(optimization, "do_function_inlining") == 0) {
> + return do_function_inlining(ir);
> + } else if (sscanf(optimization,
> + "do_lower_jumps ( %d , %d , %d , %d , %d ) ",
> + &int_0, &int_1, &int_2, &int_3, &int_4) == 5) {
> + return do_lower_jumps(ir, int_0 != 0, int_1 != 0, int_2 != 0,
> + int_3 != 0, int_4 != 0);
> + } else if (strcmp(optimization, "do_lower_texture_projection") == 0) {
> + return do_lower_texture_projection(ir);
> + } else if (strcmp(optimization, "do_if_simplification") == 0) {
> + return do_if_simplification(ir);
> + } else if (strcmp(optimization, "do_discard_simplification") == 0) {
> + return do_discard_simplification(ir);
> + } else if (sscanf(optimization, "lower_if_to_cond_assign ( %d ) ",
> + &int_0) == 1) {
> + return lower_if_to_cond_assign(ir, int_0);
> + } else if (strcmp(optimization, "do_mat_op_to_vec") == 0) {
> + return do_mat_op_to_vec(ir);
> + } else if (strcmp(optimization, "do_noop_swizzle") == 0) {
> + return do_noop_swizzle(ir);
> + } else if (strcmp(optimization, "do_structure_splitting") == 0) {
> + return do_structure_splitting(ir);
> + } else if (strcmp(optimization, "do_swizzle_swizzle") == 0) {
> + return do_swizzle_swizzle(ir);
> + } else if (strcmp(optimization, "do_tree_grafting") == 0) {
> + return do_tree_grafting(ir);
> + } else if (strcmp(optimization, "do_vec_index_to_cond_assign") == 0) {
> + return do_vec_index_to_cond_assign(ir);
> + } else if (strcmp(optimization, "do_vec_index_to_swizzle") == 0) {
> + return do_vec_index_to_swizzle(ir);
> + } else if (strcmp(optimization, "lower_discard") == 0) {
> + return lower_discard(ir);
> + } else if (sscanf(optimization, "lower_instructions ( %d ) ",
> + &int_0) == 1) {
> + return lower_instructions(ir, int_0);
> + } else if (strcmp(optimization, "lower_noise") == 0) {
> + return lower_noise(ir);
> + } else if (sscanf(optimization, "lower_variable_index_to_cond_assign "
> + "( %d , %d , %d , %d ) ", &int_0, &int_1, &int_2,
> + &int_3) == 4) {
> + return lower_variable_index_to_cond_assign(ir, int_0 != 0, int_1 != 0,
> + int_2 != 0, int_3 != 0);
> + } else if (sscanf(optimization, "lower_quadop_vector ( %d ) ",
> + &int_0) == 1) {
> + return lower_quadop_vector(ir, int_0 != 0);
> + } else if (strcmp(optimization, "optimize_redundant_jumps") == 0) {
> + return optimize_redundant_jumps(ir);
> + } else {
> + printf("Unrecognized optimization %s\n", optimization);
> + exit(EXIT_FAILURE);
> + return false;
> + }
> +}
> +
> +static GLboolean
> +do_optimization_passes(struct exec_list *ir, char **optimizations,
> + int num_optimizations, bool quiet)
> +{
> + GLboolean overall_progress = false;
> +
> + for (int i = 0; i < num_optimizations; ++i) {
> + const char *optimization = optimizations[i];
> + if (!quiet) {
> + printf("*** Running optimization %s...", optimization);
> + }
> + GLboolean progress = do_optimization(ir, optimization);
> + if (!quiet) {
> + printf("%s\n", progress ? "progress" : "no progress");
> + }
> + validate_ir_tree(ir);
> +
> + overall_progress = overall_progress || progress;
> + }
> +
> + return overall_progress;
> +}
> +
> +int test_optpass(int argc, char **argv)
> +{
> + int input_format_ir = 0; /* 0=glsl, 1=ir */
> + int loop = 0;
> + int shader_type = GL_VERTEX_SHADER;
> + int quiet = 0;
> +
> + const struct option optpass_opts[] = {
> + { "input-ir", no_argument, &input_format_ir, 1 },
> + { "input-glsl", no_argument, &input_format_ir, 0 },
> + { "loop", no_argument, &loop, 1 },
> + { "vertex-shader", no_argument, &shader_type, GL_VERTEX_SHADER },
> + { "fragment-shader", no_argument, &shader_type, GL_FRAGMENT_SHADER },
> + { "quiet", no_argument, &quiet, 1 },
> + { NULL, 0, NULL, 0 }
> + };
> +
> + int idx = 0;
> + int c;
> + while ((c = getopt_long(argc, argv, "", optpass_opts, &idx)) != -1) {
> + if (c != 0) {
> + printf("*** usage: %s optpass <optimizations> <options>\n", argv[0]);
> + printf("\n");
> + printf("Possible options are:\n");
> + printf(" --input-ir: input format is IR\n");
> + printf(" --input-glsl: input format is GLSL (the default)\n");
> + printf(" --loop: run optimizations repeatedly until no progress\n");
> + printf(" --vertex-shader: test with a vertex shader (the default)\n");
> + printf(" --fragment-shader: test with a fragment shader\n");
> + exit(EXIT_FAILURE);
> + }
> + }
> +
> + struct gl_context local_ctx;
> + struct gl_context *ctx = &local_ctx;
> + initialize_context_to_defaults(ctx, API_OPENGL);
> +
> + ctx->Driver.NewShader = _mesa_new_shader;
> +
> + struct gl_shader *shader = rzalloc(NULL, struct gl_shader);
> + shader->Type = shader_type;
> +
> + string input = read_stdin_to_eof();
> +
> + struct _mesa_glsl_parse_state *state
> + = new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader);
> +
> + if (input_format_ir) {
> + shader->ir = new(shader) exec_list;
> + _mesa_glsl_initialize_types(state);
> + _mesa_glsl_read_ir(state, shader->ir, input.c_str(), true);
> + } else {
> + shader->Source = input.c_str();
> + const char *source = shader->Source;
> + state->error = preprocess(state, &source, &state->info_log,
> + state->extensions, ctx->API) != 0;
> +
> + if (!state->error) {
> + _mesa_glsl_lexer_ctor(state, source);
> + _mesa_glsl_parse(state);
> + _mesa_glsl_lexer_dtor(state);
> + }
> +
> + shader->ir = new(shader) exec_list;
> + if (!state->error && !state->translation_unit.is_empty())
> + _mesa_ast_to_hir(shader->ir, state);
> + }
> +
> + /* Print out the initial IR */
> + if (!state->error && !quiet) {
> + printf("*** pre-optimization IR:\n");
> + _mesa_print_ir(shader->ir, state);
> + printf("\n--\n");
> + }
> +
> + /* Optimization passes */
> + if (!state->error) {
> + GLboolean progress;
> + do {
> + progress = do_optimization_passes(shader->ir, &argv[optind],
> + argc - optind, quiet != 0);
> + } while (loop && progress);
> + }
> +
> + /* Print out the resulting IR */
> + if (!state->error) {
> + if (!quiet) {
> + printf("*** resulting IR:\n");
> + }
> + _mesa_print_ir(shader->ir, state);
> + if (!quiet) {
> + printf("\n--\n");
> + }
> + }
> +
> + if (state->error) {
> + printf("*** error(s) occurred:\n");
> + printf("%s\n", state->info_log);
> + printf("--\n");
> + }
> +
> + ralloc_free(state);
> + ralloc_free(shader);
> +
> + return state->error;
> +}
> +
> diff --git a/src/glsl/test_optpass.h b/src/glsl/test_optpass.h
> new file mode 100644
> index 0000000..923ccf3
> --- /dev/null
> +++ b/src/glsl/test_optpass.h
> @@ -0,0 +1,30 @@
> +/*
> + * 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.
> + */
> +
> +#pragma once
> +#ifndef TEST_OPTPASS_H
> +#define TEST_OPTPASS_H
> +
> +int test_optpass(int argc, char **argv);
> +
> +#endif /* TEST_OPTPASS_H */
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
iEYEARECAAYFAk4l5eYACgkQX1gOwKyEAw8heACfb7mxL+XlhIPMRqy3AWh+XOC2
UuoAn3c6gCQZzB8eH8XxOZ584DLdvqMN
=Mbws
-----END PGP SIGNATURE-----
More information about the mesa-dev
mailing list