Coverage Report

Created: 2025-11-11 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/glslang/SPIRV/SpvTools.cpp
Line
Count
Source
1
//
2
// Copyright (C) 2014-2016 LunarG, Inc.
3
// Copyright (C) 2018-2020 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 "glslang/MachineIndependent/localintermediate.h"
48
49
namespace glslang {
50
51
// Translate glslang's view of target versioning to what SPIRV-Tools uses.
52
spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger)
53
0
{
54
0
    switch (spvVersion.vulkan) {
55
0
    case glslang::EShTargetVulkan_1_0:
56
0
        return spv_target_env::SPV_ENV_VULKAN_1_0;
57
0
    case glslang::EShTargetVulkan_1_1:
58
0
        switch (spvVersion.spv) {
59
0
        case EShTargetSpv_1_0:
60
0
        case EShTargetSpv_1_1:
61
0
        case EShTargetSpv_1_2:
62
0
        case EShTargetSpv_1_3:
63
0
            return spv_target_env::SPV_ENV_VULKAN_1_1;
64
0
        case EShTargetSpv_1_4:
65
0
            return spv_target_env::SPV_ENV_VULKAN_1_1_SPIRV_1_4;
66
0
        default:
67
0
            logger->missingFunctionality("Target version for SPIRV-Tools validator");
68
0
            return spv_target_env::SPV_ENV_VULKAN_1_1;
69
0
        }
70
0
    case glslang::EShTargetVulkan_1_2:
71
0
        return spv_target_env::SPV_ENV_VULKAN_1_2;
72
0
    case glslang::EShTargetVulkan_1_3:
73
0
        return spv_target_env::SPV_ENV_VULKAN_1_3;
74
0
    case glslang::EShTargetVulkan_1_4:
75
0
        return spv_target_env::SPV_ENV_VULKAN_1_4;
76
0
    default:
77
0
        break;
78
0
    }
79
80
0
    if (spvVersion.openGl > 0)
81
0
        return spv_target_env::SPV_ENV_OPENGL_4_5;
82
83
0
    logger->missingFunctionality("Target version for SPIRV-Tools validator");
84
0
    return spv_target_env::SPV_ENV_UNIVERSAL_1_0;
85
0
}
86
87
spv_target_env MapToSpirvToolsEnv(const glslang::TIntermediate& intermediate, spv::SpvBuildLogger* logger)
88
0
{
89
0
    return MapToSpirvToolsEnv(intermediate.getSpv(), logger);
90
0
}
91
92
// Callback passed to spvtools::Optimizer::SetMessageConsumer
93
void OptimizerMesssageConsumer(spv_message_level_t level, const char *source,
94
        const spv_position_t &position, const char *message)
95
0
{
96
0
    auto &out = std::cerr;
97
0
    switch (level)
98
0
    {
99
0
    case SPV_MSG_FATAL:
100
0
    case SPV_MSG_INTERNAL_ERROR:
101
0
    case SPV_MSG_ERROR:
102
0
        out << "error: ";
103
0
        break;
104
0
    case SPV_MSG_WARNING:
105
0
        out << "warning: ";
106
0
        break;
107
0
    case SPV_MSG_INFO:
108
0
    case SPV_MSG_DEBUG:
109
0
        out << "info: ";
110
0
        break;
111
0
    default:
112
0
        break;
113
0
    }
114
0
    if (source)
115
0
    {
116
0
        out << source << ":";
117
0
    }
118
0
    out << position.line << ":" << position.column << ":" << position.index << ":";
119
0
    if (message)
120
0
    {
121
0
        out << " " << message;
122
0
    }
123
0
    out << std::endl;
124
0
}
125
126
// Use the SPIRV-Tools disassembler to print SPIR-V using a SPV_ENV_UNIVERSAL_1_3 environment.
127
void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv)
128
0
{
129
0
    SpirvToolsDisassemble(out, spirv, spv_target_env::SPV_ENV_UNIVERSAL_1_3);
130
0
}
131
132
// Use the SPIRV-Tools disassembler to print SPIR-V with a provided SPIR-V environment.
133
void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv,
134
                           spv_target_env requested_context)
135
0
{
136
    // disassemble
137
0
    spv_context context = spvContextCreate(requested_context);
138
0
    spv_text text;
139
0
    spv_diagnostic diagnostic = nullptr;
140
0
    spvBinaryToText(context, spirv.data(), spirv.size(),
141
0
        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT,
142
0
        &text, &diagnostic);
143
144
    // dump
145
0
    if (diagnostic == nullptr)
146
0
        out << text->str;
147
0
    else
148
0
        spvDiagnosticPrint(diagnostic);
149
150
    // teardown
151
0
    spvDiagnosticDestroy(diagnostic);
152
0
    spvContextDestroy(context);
153
0
}
154
155
// Apply the SPIRV-Tools validator to generated SPIR-V.
156
void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
157
                        spv::SpvBuildLogger* logger, bool prelegalization)
158
0
{
159
    // validate
160
0
    spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
161
0
    spv_const_binary_t binary = { spirv.data(), spirv.size() };
162
0
    spv_diagnostic diagnostic = nullptr;
163
0
    spv_validator_options options = spvValidatorOptionsCreate();
164
0
    spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets());
165
0
    spvValidatorOptionsSetBeforeHlslLegalization(options, prelegalization);
166
0
    spvValidatorOptionsSetScalarBlockLayout(options, intermediate.usingScalarBlockLayout());
167
0
    spvValidatorOptionsSetWorkgroupScalarBlockLayout(options, intermediate.usingScalarBlockLayout());
168
0
    spvValidatorOptionsSetAllowOffsetTextureOperand(options, intermediate.usingTextureOffsetNonConst());
169
0
    spvValidateWithOptions(context, options, &binary, &diagnostic);
170
171
    // report
172
0
    if (diagnostic != nullptr) {
173
0
        logger->error("SPIRV-Tools Validation Errors");
174
0
        logger->error(diagnostic->error);
175
0
    }
176
177
    // tear down
178
0
    spvValidatorOptionsDestroy(options);
179
0
    spvDiagnosticDestroy(diagnostic);
180
0
    spvContextDestroy(context);
181
0
}
182
183
// Apply the SPIRV-Tools optimizer to generated SPIR-V.  HLSL SPIR-V is legalized in the process.
184
void SpirvToolsTransform(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
185
                         spv::SpvBuildLogger* logger, const SpvOptions* options)
186
0
{
187
0
    spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger);
188
189
0
    spvtools::Optimizer optimizer(target_env);
190
0
    optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
191
192
    // If debug (specifically source line info) is being generated, propagate
193
    // line information into all SPIR-V instructions. This avoids loss of
194
    // information when instructions are deleted or moved. Later, remove
195
    // redundant information to minimize final SPRIR-V size.
196
0
    if (options->stripDebugInfo) {
197
0
        optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass());
198
0
    }
199
0
    optimizer.RegisterPass(spvtools::CreateWrapOpKillPass());
200
0
    optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
201
0
    optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
202
0
    optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
203
0
    optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass());
204
0
    optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
205
0
    optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
206
0
    optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
207
0
    optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
208
0
    optimizer.RegisterPass(spvtools::CreateSimplificationPass());
209
0
    optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
210
0
    optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
211
0
    optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
212
0
    optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
213
0
    optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
214
0
    optimizer.RegisterPass(spvtools::CreateBlockMergePass());
215
0
    optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
216
0
    optimizer.RegisterPass(spvtools::CreateIfConversionPass());
217
0
    optimizer.RegisterPass(spvtools::CreateSimplificationPass());
218
0
    optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
219
0
    optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
220
0
    optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
221
0
    optimizer.RegisterPass(spvtools::CreateInterpolateFixupPass());
222
0
    if (options->optimizeSize) {
223
0
        optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
224
0
        optimizer.RegisterPass(spvtools::CreateEliminateDeadInputComponentsSafePass());
225
0
    }
226
0
    optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
227
0
    optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
228
229
0
    spvtools::OptimizerOptions spvOptOptions;
230
0
    if (options->optimizerAllowExpandedIDBound)
231
0
        spvOptOptions.set_max_id_bound(0x3FFFFFFF);
232
0
    optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
233
0
    spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on
234
0
    optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
235
236
0
    if (options->optimizerAllowExpandedIDBound) {
237
0
        if (spirv.size() > 3 && spirv[3] > kDefaultMaxIdBound) {
238
0
            spvtools::Optimizer optimizer2(target_env);
239
0
            optimizer2.SetMessageConsumer(OptimizerMesssageConsumer);
240
0
            optimizer2.RegisterPass(spvtools::CreateCompactIdsPass());
241
0
            optimizer2.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
242
0
        }
243
0
    }
244
0
}
245
246
bool SpirvToolsAnalyzeDeadOutputStores(spv_target_env target_env, std::vector<unsigned int>& spirv,
247
                                       std::unordered_set<uint32_t>* live_locs,
248
                                       std::unordered_set<uint32_t>* live_builtins,
249
                                       spv::SpvBuildLogger*)
250
0
{
251
0
  spvtools::Optimizer optimizer(target_env);
252
0
  optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
253
254
0
  optimizer.RegisterPass(spvtools::CreateAnalyzeLiveInputPass(live_locs, live_builtins));
255
256
0
  spvtools::OptimizerOptions spvOptOptions;
257
0
  optimizer.SetTargetEnv(target_env);
258
0
  spvOptOptions.set_run_validator(false);
259
0
  return optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
260
0
}
261
262
void SpirvToolsEliminateDeadOutputStores(spv_target_env target_env, std::vector<unsigned int>& spirv,
263
                                         std::unordered_set<uint32_t>* live_locs,
264
                                         std::unordered_set<uint32_t>* live_builtins,
265
                                         spv::SpvBuildLogger*)
266
0
{
267
0
  spvtools::Optimizer optimizer(target_env);
268
0
  optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
269
270
0
  optimizer.RegisterPass(spvtools::CreateEliminateDeadOutputStoresPass(live_locs, live_builtins));
271
0
  optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass(false, true));
272
0
  optimizer.RegisterPass(spvtools::CreateEliminateDeadOutputComponentsPass());
273
0
  optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass(false, true));
274
275
0
  spvtools::OptimizerOptions spvOptOptions;
276
0
  optimizer.SetTargetEnv(target_env);
277
0
  spvOptOptions.set_run_validator(false);
278
0
  optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
279
0
}
280
281
void SpirvToolsEliminateDeadInputComponents(spv_target_env target_env, std::vector<unsigned int>& spirv,
282
                                            spv::SpvBuildLogger*)
283
0
{
284
0
  spvtools::Optimizer optimizer(target_env);
285
0
  optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
286
287
0
  optimizer.RegisterPass(spvtools::CreateEliminateDeadInputComponentsPass());
288
0
  optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
289
290
0
  spvtools::OptimizerOptions spvOptOptions;
291
0
  optimizer.SetTargetEnv(target_env);
292
0
  spvOptOptions.set_run_validator(false);
293
0
  optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
294
0
}
295
296
// Apply the SPIRV-Tools optimizer to strip debug info from SPIR-V.  This is implicitly done by
297
// SpirvToolsTransform if spvOptions->stripDebugInfo is set, but can be called separately if
298
// optimization is disabled.
299
void SpirvToolsStripDebugInfo(const glslang::TIntermediate& intermediate,
300
        std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger)
301
0
{
302
0
    spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger);
303
304
0
    spvtools::Optimizer optimizer(target_env);
305
0
    optimizer.SetMessageConsumer(OptimizerMesssageConsumer);
306
307
0
    optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass());
308
309
0
    spvtools::OptimizerOptions spvOptOptions;
310
0
    optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
311
0
    spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on
312
0
    optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
313
0
}
314
315
} // end namespace glslang
316
317
#endif