| // Copyright (c) 2025 LunarG Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <algorithm> |
| #include <map> |
| #include <set> |
| #include <vector> |
| |
| #include "source/opt/pass.h" |
| |
| namespace spvtools { |
| namespace opt { |
| |
| // The canonicalize IDs pass is an optimization to improve compression of SPIR-V |
| // binary files via entropy reduction. It transforms SPIR-V to SPIR-V, remapping |
| // IDs. The resulting modules have an increased ID range (IDs are not as tightly |
| // packed around zero), but will compress better when multiple modules are |
| // compressed together, since the compressor's dictionary can find better cross |
| // module commonality. Remapping is accomplished via canonicalization. Thus, |
| // modules can be compressed one at a time with no loss of quality relative to |
| // operating on many modules at once. |
| |
| // This pass should be run after most optimization passes except for |
| // --strip-debug because this pass will use OpName to canonicalize IDs. i.e. Run |
| // --strip-debug after this pass. |
| |
| // This is a port of remap utility in glslang. There are great deal of magic |
| // numbers that are present throughout this code. The general goal is to replace |
| // the IDs with a hash value such that the distribution of IDs is deterministic |
| // and minimizes collisions. The magic numbers in the glslang version were |
| // chosen semi-arbitrarily and have been preserved in this port in order to |
| // maintain backward compatibility. |
| |
| class CanonicalizeIdsPass : public Pass { |
| public: |
| CanonicalizeIdsPass() = default; |
| virtual ~CanonicalizeIdsPass() = default; |
| |
| Pass::Status Process() override; |
| |
| const char* name() const override { return "canonicalize-ids"; } |
| |
| private: |
| // Special values for IDs. |
| static constexpr spv::Id unmapped_{spv::Id(-10000)}; |
| static constexpr spv::Id unused_{spv::Id(-10001)}; |
| |
| // Scans the module for IDs and sets them to unmapped_. |
| void ScanIds(); |
| |
| // Functions to compute new IDs. |
| void CanonicalizeTypeAndConst(); |
| spv::Id HashTypeAndConst( |
| spv::Id const id) const; // Helper for CanonicalizeTypeAndConst. |
| void CanonicalizeNames(); |
| void CanonicalizeFunctions(); |
| spv::Id HashOpCode(Instruction const* const inst) |
| const; // Helper for CanonicalizeFunctions. |
| void CanonicalizeRemainders(); |
| |
| // Applies the new IDs. |
| bool ApplyMap(); |
| |
| // Methods to manage the bound field in header. |
| spv::Id GetBound() const; // All IDs must satisfy 0 < ID < bound. |
| void UpdateBound(); |
| |
| // Methods to map from old IDs to new IDs. |
| spv::Id GetNewId(spv::Id const old_id) const { return new_id_[old_id]; } |
| spv::Id SetNewId(spv::Id const old_id, spv::Id new_id); |
| |
| // Methods to manage claimed IDs. |
| spv::Id ClaimNewId(spv::Id new_id); |
| bool IsNewIdClaimed(spv::Id const new_id) const { |
| return claimed_new_ids_.find(new_id) != claimed_new_ids_.end(); |
| } |
| |
| // Queries for old IDs. |
| bool IsOldIdUnmapped(spv::Id const old_id) const { |
| return GetNewId(old_id) == unmapped_; |
| } |
| bool IsOldIdUnused(spv::Id const old_id) const { |
| return GetNewId(old_id) == unused_; |
| } |
| |
| // Container to map old IDs to new IDs. e.g. new_id_[old_id] = new_id |
| std::vector<spv::Id> new_id_; |
| |
| // IDs from the new ID space that have been claimed (faster than searching |
| // through new_id_). |
| std::set<spv::Id> claimed_new_ids_; |
| |
| // Helper functions for printing IDs (useful for debugging). |
| std::string IdAsString(spv::Id const id) const; |
| void PrintNewIds() const; |
| |
| // Containers to track IDs we want to canonicalize. |
| std::vector<spv::Id> type_and_const_ids_; |
| std::map<std::string, spv::Id> name_ids_; |
| std::vector<spv::Id> function_ids_; |
| }; |
| |
| } // namespace opt |
| } // namespace spvtools |