/src/spirv-tools/source/opt/canonicalize_ids_pass.h
Line | Count | Source |
1 | | // Copyright (c) 2025 LunarG Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <algorithm> |
16 | | #include <map> |
17 | | #include <set> |
18 | | #include <vector> |
19 | | |
20 | | #include "source/opt/pass.h" |
21 | | |
22 | | namespace spvtools { |
23 | | namespace opt { |
24 | | |
25 | | // The canonicalize IDs pass is an optimization to improve compression of SPIR-V |
26 | | // binary files via entropy reduction. It transforms SPIR-V to SPIR-V, remapping |
27 | | // IDs. The resulting modules have an increased ID range (IDs are not as tightly |
28 | | // packed around zero), but will compress better when multiple modules are |
29 | | // compressed together, since the compressor's dictionary can find better cross |
30 | | // module commonality. Remapping is accomplished via canonicalization. Thus, |
31 | | // modules can be compressed one at a time with no loss of quality relative to |
32 | | // operating on many modules at once. |
33 | | |
34 | | // This pass should be run after most optimization passes except for |
35 | | // --strip-debug because this pass will use OpName to canonicalize IDs. i.e. Run |
36 | | // --strip-debug after this pass. |
37 | | |
38 | | // This is a port of remap utility in glslang. There are great deal of magic |
39 | | // numbers that are present throughout this code. The general goal is to replace |
40 | | // the IDs with a hash value such that the distribution of IDs is deterministic |
41 | | // and minimizes collisions. The magic numbers in the glslang version were |
42 | | // chosen semi-arbitrarily and have been preserved in this port in order to |
43 | | // maintain backward compatibility. |
44 | | |
45 | | class CanonicalizeIdsPass : public Pass { |
46 | | public: |
47 | 0 | CanonicalizeIdsPass() = default; |
48 | 0 | virtual ~CanonicalizeIdsPass() = default; |
49 | | |
50 | | Pass::Status Process() override; |
51 | | |
52 | 0 | const char* name() const override { return "canonicalize-ids"; } |
53 | | |
54 | | private: |
55 | | // Special values for IDs. |
56 | | static constexpr spv::Id unmapped_{spv::Id(-10000)}; |
57 | | static constexpr spv::Id unused_{spv::Id(-10001)}; |
58 | | |
59 | | // Scans the module for IDs and sets them to unmapped_. |
60 | | void ScanIds(); |
61 | | |
62 | | // Functions to compute new IDs. |
63 | | void CanonicalizeTypeAndConst(); |
64 | | spv::Id HashTypeAndConst( |
65 | | spv::Id const id) const; // Helper for CanonicalizeTypeAndConst. |
66 | | void CanonicalizeNames(); |
67 | | void CanonicalizeFunctions(); |
68 | | spv::Id HashOpCode(Instruction const* const inst) |
69 | | const; // Helper for CanonicalizeFunctions. |
70 | | void CanonicalizeRemainders(); |
71 | | |
72 | | // Applies the new IDs. |
73 | | bool ApplyMap(); |
74 | | |
75 | | // Methods to manage the bound field in header. |
76 | | spv::Id GetBound() const; // All IDs must satisfy 0 < ID < bound. |
77 | | void UpdateBound(); |
78 | | |
79 | | // Methods to map from old IDs to new IDs. |
80 | 0 | spv::Id GetNewId(spv::Id const old_id) const { return new_id_[old_id]; } |
81 | | spv::Id SetNewId(spv::Id const old_id, spv::Id new_id); |
82 | | |
83 | | // Methods to manage claimed IDs. |
84 | | spv::Id ClaimNewId(spv::Id new_id); |
85 | 0 | bool IsNewIdClaimed(spv::Id const new_id) const { |
86 | 0 | return claimed_new_ids_.find(new_id) != claimed_new_ids_.end(); |
87 | 0 | } |
88 | | |
89 | | // Queries for old IDs. |
90 | 0 | bool IsOldIdUnmapped(spv::Id const old_id) const { |
91 | 0 | return GetNewId(old_id) == unmapped_; |
92 | 0 | } |
93 | 0 | bool IsOldIdUnused(spv::Id const old_id) const { |
94 | 0 | return GetNewId(old_id) == unused_; |
95 | 0 | } |
96 | | |
97 | | // Container to map old IDs to new IDs. e.g. new_id_[old_id] = new_id |
98 | | std::vector<spv::Id> new_id_; |
99 | | |
100 | | // IDs from the new ID space that have been claimed (faster than searching |
101 | | // through new_id_). |
102 | | std::set<spv::Id> claimed_new_ids_; |
103 | | |
104 | | // Helper functions for printing IDs (useful for debugging). |
105 | | std::string IdAsString(spv::Id const id) const; |
106 | | void PrintNewIds() const; |
107 | | |
108 | | // Containers to track IDs we want to canonicalize. |
109 | | std::vector<spv::Id> type_and_const_ids_; |
110 | | std::map<std::string, spv::Id> name_ids_; |
111 | | std::vector<spv::Id> function_ids_; |
112 | | }; |
113 | | |
114 | | } // namespace opt |
115 | | } // namespace spvtools |