1 //
2 // Copyright (C) 2014-2016 LunarG, Inc.
3 // Copyright (C) 2018 Google, Inc.
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 // Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //
14 // Redistributions in binary form must reproduce the above
15 // copyright notice, this list of conditions and the following
16 // disclaimer in the documentation and/or other materials provided
17 // with the distribution.
18 //
19 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
20 // contributors may be used to endorse or promote products derived
21 // from this software without specific prior written permission.
22 //
23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 // POSSIBILITY OF SUCH DAMAGE.
35
36 //
37 // Call into SPIRV-Tools to disassemble, validate, and optimize.
38 //
39
40 #if ENABLE_OPT
41
42 #include <cstdio>
43 #include <iostream>
44
45 #include "SpvTools.h"
46 #include "spirv-tools/optimizer.hpp"
47 #include "spirv-tools/libspirv.h"
48
49 namespace glslang {
50
51 // Translate glslang's view of target versioning to what SPIRV-Tools uses.
MapToSpirvToolsEnv(const SpvVersion & spvVersion,spv::SpvBuildLogger * logger)52 spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger)
53 {
54 switch (spvVersion.vulkan) {
55 case glslang::EShTargetVulkan_1_0: return spv_target_env::SPV_ENV_VULKAN_1_0;
56 case glslang::EShTargetVulkan_1_1: return spv_target_env::SPV_ENV_VULKAN_1_1;
57 default:
58 break;
59 }
60
61 if (spvVersion.openGl > 0)
62 return spv_target_env::SPV_ENV_OPENGL_4_5;
63
64 logger->missingFunctionality("Target version for SPIRV-Tools validator");
65 return spv_target_env::SPV_ENV_UNIVERSAL_1_0;
66 }
67
68
69 // Use the SPIRV-Tools disassembler to print SPIR-V.
SpirvToolsDisassemble(std::ostream & out,const std::vector<unsigned int> & spirv)70 void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv)
71 {
72 // disassemble
73 spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_3);
74 spv_text text;
75 spv_diagnostic diagnostic = nullptr;
76 spvBinaryToText(context, spirv.data(), spirv.size(),
77 SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT,
78 &text, &diagnostic);
79
80 // dump
81 if (diagnostic == nullptr)
82 out << text->str;
83 else
84 spvDiagnosticPrint(diagnostic);
85
86 // teardown
87 spvDiagnosticDestroy(diagnostic);
88 spvContextDestroy(context);
89 }
90
91 // Apply the SPIRV-Tools validator to generated SPIR-V.
SpirvToolsValidate(const glslang::TIntermediate & intermediate,std::vector<unsigned int> & spirv,spv::SpvBuildLogger * logger)92 void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
93 spv::SpvBuildLogger* logger)
94 {
95 // validate
96 spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
97 spv_const_binary_t binary = { spirv.data(), spirv.size() };
98 spv_diagnostic diagnostic = nullptr;
99 spv_validator_options options = spvValidatorOptionsCreate();
100 spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets());
101 spvValidateWithOptions(context, options, &binary, &diagnostic);
102
103 // report
104 if (diagnostic != nullptr) {
105 logger->error("SPIRV-Tools Validation Errors");
106 logger->error(diagnostic->error);
107 }
108
109 // tear down
110 spvValidatorOptionsDestroy(options);
111 spvDiagnosticDestroy(diagnostic);
112 spvContextDestroy(context);
113 }
114
115 // Apply the SPIRV-Tools optimizer to generated SPIR-V, for the purpose of
116 // legalizing HLSL SPIR-V.
SpirvToolsLegalize(const glslang::TIntermediate &,std::vector<unsigned int> & spirv,spv::SpvBuildLogger *,const SpvOptions * options)117 void SpirvToolsLegalize(const glslang::TIntermediate&, std::vector<unsigned int>& spirv,
118 spv::SpvBuildLogger*, const SpvOptions* options)
119 {
120 spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
121
122 spvtools::Optimizer optimizer(target_env);
123 optimizer.SetMessageConsumer(
124 [](spv_message_level_t level, const char *source, const spv_position_t &position, const char *message) {
125 auto &out = std::cerr;
126 switch (level)
127 {
128 case SPV_MSG_FATAL:
129 case SPV_MSG_INTERNAL_ERROR:
130 case SPV_MSG_ERROR:
131 out << "error: ";
132 break;
133 case SPV_MSG_WARNING:
134 out << "warning: ";
135 break;
136 case SPV_MSG_INFO:
137 case SPV_MSG_DEBUG:
138 out << "info: ";
139 break;
140 default:
141 break;
142 }
143 if (source)
144 {
145 out << source << ":";
146 }
147 out << position.line << ":" << position.column << ":" << position.index << ":";
148 if (message)
149 {
150 out << " " << message;
151 }
152 out << std::endl;
153 });
154
155 // If debug (specifically source line info) is being generated, propagate
156 // line information into all SPIR-V instructions. This avoids loss of
157 // information when instructions are deleted or moved. Later, remove
158 // redundant information to minimize final SPRIR-V size.
159 if (options->generateDebugInfo) {
160 optimizer.RegisterPass(spvtools::CreatePropagateLineInfoPass());
161 }
162 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
163 optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
164 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
165 optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass());
166 optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
167 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
168 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
169 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
170 optimizer.RegisterPass(spvtools::CreateSimplificationPass());
171 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
172 optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
173 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
174 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
175 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
176 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
177 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
178 optimizer.RegisterPass(spvtools::CreateIfConversionPass());
179 optimizer.RegisterPass(spvtools::CreateSimplificationPass());
180 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
181 optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
182 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
183 if (options->optimizeSize) {
184 optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
185 // TODO(greg-lunarg): Add this when AMD driver issues are resolved
186 // optimizer.RegisterPass(CreateCommonUniformElimPass());
187 }
188 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
189 optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
190 if (options->generateDebugInfo) {
191 optimizer.RegisterPass(spvtools::CreateRedundantLineInfoElimPass());
192 }
193
194 optimizer.Run(spirv.data(), spirv.size(), &spirv);
195 }
196
197 }; // end namespace glslang
198
199 #endif
200