From 7505d2422551e5dbc2bf4e0ffccfdc5ca5ce1925 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 19 Dec 2017 15:08:54 -0500 Subject: [PATCH] Update the legalization passes. Changes the set of optimizations done for legalization. While doing this, I added documentation to explain why we want each optimization. A new option "--legalize-hlsl" is added so the legalization passes can be easily run from the command line. The legalize option implies skip-validation. --- source/opt/optimizer.cpp | 55 +++++++++++++++++++++++++++++++--------- tools/opt/opt.cpp | 21 +++++++++++++-- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index fc652ffc0..57d5c7250 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -66,19 +66,50 @@ Optimizer& Optimizer::RegisterPass(PassToken&& p) { return *this; } +// The legalization passes take a spir-v shader generated by an HLSL front-end +// and turn it into a valid vulkan spir-v shader. There are two ways in which +// the code will be invalid at the start: +// +// 1) There will be opaque objects, like images, which will be passed around +// in intermediate objects. Valid spir-v will have to replace the use of +// the opaque object with an intermediate object that is the result of the +// load of the global opaque object. +// +// 2) There will be variables that contain pointers to structured or uniform +// buffers. It be legal, the variables must be eliminated, and the +// references to the structured buffers must use the result of OpVariable +// in the Uniform storage class. +// +// Optimization in this list must accept shaders with these relaxation of the +// rules. There is not guarantee that this list of optimizations is able to +// legalize all inputs, but it is on a best effort basis. +// +// The legalization problem is essentially a very general copy propagation +// problem. The optimization we use are all used to either do copy propagation +// or enable more copy propagation. Optimizer& Optimizer::RegisterLegalizationPasses() { - return RegisterPass(CreateInlineExhaustivePass()) - .RegisterPass(CreateLocalAccessChainConvertPass()) - .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) - .RegisterPass(CreateLocalSingleStoreElimPass()) - .RegisterPass(CreateInsertExtractElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) - .RegisterPass(CreateDeadBranchElimPass()) - .RegisterPass(CreateCFGCleanupPass()) - .RegisterPass(CreateBlockMergePass()) - .RegisterPass(CreateLocalMultiStoreElimPass()) - .RegisterPass(CreateInsertExtractElimPass()) - .RegisterPass(CreateAggressiveDCEPass()); + return + // Make sure uses and definitions are in the same function. + RegisterPass(CreateInlineExhaustivePass()) + // Make private variable function scope + .RegisterPass(CreateEliminateDeadFunctionsPass()) + .RegisterPass(CreatePrivateToLocalPass()) + // Split up aggragates so they are easier to deal with. + .RegisterPass(CreateScalarReplacementPass()) + // Remove loads and stores so everything is in intermediate values. + // Takes care of copy propagation of non-members. + .RegisterPass(CreateLocalMultiStoreElimPass()) + // Copy propagate members. Cleans up code sequences generated by + // scalar replacement. + .RegisterPass(CreateInsertExtractElimPass()) + // TODO: Add constant propagation here + // May need loop unrolling here see + // https://github.com/Microsoft/DirectXShaderCompiler/pull/930 + .RegisterPass(CreateCFGCleanupPass()) + // Get rid of unused code that leave traces of the illegal code. + .RegisterPass(CreateAggressiveDCEPass()) + // TODO: Remove this once ADCE can do it. + .RegisterPass(CreateDeadVariableEliminationPass()); } Optimizer& Optimizer::RegisterPerformancePasses() { diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 34ab4cb1e..1eab922e9 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -48,6 +48,12 @@ std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) { return ss.str(); } +std::string GetLegalizationPasses() { + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_2); + optimizer.RegisterLegalizationPasses(); + return GetListOfPassesAsString(optimizer); +} + std::string GetOptimizationPasses() { spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_2); optimizer.RegisterPerformancePasses(); @@ -140,6 +146,14 @@ Options (in lexicographical order): Exhaustively inline all function calls in entry point call tree functions. Currently does not inline calls to functions with early return in a loop. + --legalize-hlsl + Runs a series of optimizations that attempts to take SPIR-V + generated by and HLSL front-end and generate legal Vulkan SPIR-V. + The optimizations are: + %s + + Note this does not guarantee legal code. This option implies + --skip-validation. --local-redundancy-elimination Looks for instructions in the same basic block that compute the same value, and deletes the redundant ones. @@ -234,8 +248,8 @@ Options (in lexicographical order): --version Display optimizer version information. )", - program, program, GetOptimizationPasses().c_str(), - GetSizePasses().c_str()); + program, program, GetLegalizationPasses().c_str(), + GetOptimizationPasses().c_str(), GetSizePasses().c_str()); } // Reads command-line flags the file specified in |oconfig_flag|. This string @@ -421,6 +435,9 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer, optimizer->RegisterPerformancePasses(); } else if (0 == strcmp(cur_arg, "-Os")) { optimizer->RegisterSizePasses(); + } else if (0 == strcmp(cur_arg, "--legalize-hlsl")) { + *skip_validator = true; + optimizer->RegisterLegalizationPasses(); } else if (0 == strncmp(cur_arg, "-Oconfig=", sizeof("-Oconfig=") - 1)) { OptStatus status = ParseOconfigFlag(argv[0], cur_arg, optimizer, in_file, out_file);