mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
Added --compact-ids to /tools/opt
The pass remaps ids to a compact set which starts with %1 and has no gaps.
This commit is contained in:
parent
b173d1c3cd
commit
1e309af80a
@ -185,6 +185,10 @@ Optimizer::PassToken CreateEliminateDeadConstantPass();
|
||||
// points are not changed.
|
||||
Optimizer::PassToken CreateInlinePass();
|
||||
|
||||
// Creates a compact ids pass.
|
||||
// The pass remaps result ids to a compact and gapless range starting from %1.
|
||||
Optimizer::PassToken CreateCompactIdsPass();
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_OPTIMIZER_HPP_
|
||||
|
@ -14,6 +14,7 @@
|
||||
add_library(SPIRV-Tools-opt
|
||||
basic_block.h
|
||||
build_module.h
|
||||
compact_ids_pass.h
|
||||
constants.h
|
||||
def_use_manager.h
|
||||
eliminate_dead_constant_pass.h
|
||||
@ -39,6 +40,7 @@ add_library(SPIRV-Tools-opt
|
||||
|
||||
basic_block.cpp
|
||||
build_module.cpp
|
||||
compact_ids_pass.cpp
|
||||
def_use_manager.cpp
|
||||
eliminate_dead_constant_pass.cpp
|
||||
flatten_decoration_pass.cpp
|
||||
|
@ -64,11 +64,12 @@ std::unique_ptr<ir::Module> BuildModule(spv_target_env env,
|
||||
|
||||
std::unique_ptr<ir::Module> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const std::string& text) {
|
||||
const std::string& text,
|
||||
uint32_t assemble_options) {
|
||||
SpirvTools t(env);
|
||||
t.SetMessageConsumer(consumer);
|
||||
std::vector<uint32_t> binary;
|
||||
if (!t.Assemble(text, &binary)) return nullptr;
|
||||
if (!t.Assemble(text, &binary, assemble_options)) return nullptr;
|
||||
return BuildModule(env, consumer, binary.data(), binary.size());
|
||||
}
|
||||
|
||||
|
@ -27,16 +27,16 @@ namespace spvtools {
|
||||
// specifies number of words in |binary|. The |binary| will be decoded
|
||||
// according to the given target |env|. Returns nullptr if erors occur and
|
||||
// sends the errors to |consumer|.
|
||||
std::unique_ptr<ir::Module> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const uint32_t* binary, size_t size);
|
||||
std::unique_ptr<ir::Module> BuildModule(
|
||||
spv_target_env env, MessageConsumer consumer, const uint32_t* binary,
|
||||
size_t size);
|
||||
|
||||
// Builds and returns an ir::Module from the given SPIR-V assembly |text|.
|
||||
// The |text| will be encoded according to the given target |env|. Returns
|
||||
// nullptr if erors occur and sends the errors to |consumer|.
|
||||
std::unique_ptr<ir::Module> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const std::string& text);
|
||||
std::unique_ptr<ir::Module> BuildModule(
|
||||
spv_target_env env, MessageConsumer consumer, const std::string& text,
|
||||
uint32_t assemble_options = SpirvTools::kDefaultAssembleOption);
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
|
57
source/opt/compact_ids_pass.cpp
Normal file
57
source/opt/compact_ids_pass.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2017 Google 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 "compact_ids_pass.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
using ir::Instruction;
|
||||
using ir::Operand;
|
||||
|
||||
Pass::Status CompactIdsPass::Process(ir::Module* module) {
|
||||
bool modified = false;
|
||||
std::unordered_map<uint32_t, uint32_t> result_id_mapping;
|
||||
|
||||
module->ForEachInst([&result_id_mapping, &modified] (Instruction* inst) {
|
||||
auto operand = inst->begin();
|
||||
while (operand != inst->end()) {
|
||||
if (spvIsIdType(operand->type)) {
|
||||
assert(operand->words.size() == 1);
|
||||
uint32_t& id = operand->words[0];
|
||||
auto it = result_id_mapping.find(id);
|
||||
if (it == result_id_mapping.end()) {
|
||||
const uint32_t new_id =
|
||||
static_cast<uint32_t>(result_id_mapping.size()) + 1;
|
||||
const auto insertion_result = result_id_mapping.emplace(id, new_id);
|
||||
it = insertion_result.first;
|
||||
assert(insertion_result.second);
|
||||
}
|
||||
if (id != it->second) {
|
||||
modified = true;
|
||||
id = it->second;
|
||||
}
|
||||
}
|
||||
++operand;
|
||||
}
|
||||
}, true);
|
||||
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
34
source/opt/compact_ids_pass.h
Normal file
34
source/opt/compact_ids_pass.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2017 Google 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.
|
||||
|
||||
#ifndef LIBSPIRV_OPT_COMPACT_IDS_PASS_H_
|
||||
#define LIBSPIRV_OPT_COMPACT_IDS_PASS_H_
|
||||
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class CompactIdsPass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "compact-ids"; }
|
||||
Status Process(ir::Module*) override;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_COMPACT_IDS_PASS_H_
|
@ -130,4 +130,9 @@ Optimizer::PassToken CreateInlinePass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::InlinePass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateCompactIdsPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::CompactIdsPass>());
|
||||
}
|
||||
|
||||
} // namespace spvtools
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
// A single header to include all passes.
|
||||
|
||||
#include "compact_ids_pass.h"
|
||||
#include "eliminate_dead_constant_pass.h"
|
||||
#include "flatten_decoration_pass.h"
|
||||
#include "fold_spec_constant_op_and_composite_pass.h"
|
||||
|
@ -38,6 +38,11 @@ add_spvtools_unittest(TARGET pass_strip_debug_info
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET pass_compact_ids
|
||||
SRCS compact_ids_test.cpp pass_utils.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET pass_flatten_decoration
|
||||
SRCS flatten_decoration_test.cpp pass_utils.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
|
90
test/opt/compact_ids_test.cpp
Normal file
90
test/opt/compact_ids_test.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2017 Google 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 <gmock/gmock.h>
|
||||
|
||||
#include "pass_fixture.h"
|
||||
#include "pass_utils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace spvtools;
|
||||
|
||||
using CompactIdsTest = PassTest<::testing::Test>;
|
||||
|
||||
TEST_F(CompactIdsTest, PassOff) {
|
||||
const std::string before =
|
||||
R"(OpCapability Addresses
|
||||
OpCapability Kernel
|
||||
OpCapability GenericPointer
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Physical32 OpenCL
|
||||
%99 = OpTypeInt 32 0
|
||||
%10 = OpTypeVector %99 2
|
||||
%20 = OpConstant %99 2
|
||||
%30 = OpTypeArray %99 %20
|
||||
)";
|
||||
|
||||
const std::string after = before;
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
|
||||
SinglePassRunAndCheck<opt::NullPass>(before, after, false, false);
|
||||
}
|
||||
|
||||
TEST_F(CompactIdsTest, PassOn) {
|
||||
const std::string before =
|
||||
R"(OpCapability Addresses
|
||||
OpCapability Kernel
|
||||
OpCapability GenericPointer
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Physical32 OpenCL
|
||||
OpEntryPoint Kernel %3 "simple_kernel"
|
||||
%99 = OpTypeInt 32 0
|
||||
%10 = OpTypeVector %99 2
|
||||
%20 = OpConstant %99 2
|
||||
%30 = OpTypeArray %99 %20
|
||||
%40 = OpTypeVoid
|
||||
%50 = OpTypeFunction %40
|
||||
%3 = OpFunction %40 None %50
|
||||
%70 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after =
|
||||
R"(OpCapability Addresses
|
||||
OpCapability Kernel
|
||||
OpCapability GenericPointer
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Physical32 OpenCL
|
||||
OpEntryPoint Kernel %1 "simple_kernel"
|
||||
%2 = OpTypeInt 32 0
|
||||
%3 = OpTypeVector %2 2
|
||||
%4 = OpConstant %2 2
|
||||
%5 = OpTypeArray %2 %4
|
||||
%6 = OpTypeVoid
|
||||
%7 = OpTypeFunction %6
|
||||
%1 = OpFunction %6 None %7
|
||||
%8 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
|
||||
SinglePassRunAndCheck<opt::CompactIdsPass>(before, after, false, false);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
@ -43,15 +43,17 @@ class PassTest : public TestT {
|
||||
PassTest()
|
||||
: consumer_(nullptr),
|
||||
tools_(SPV_ENV_UNIVERSAL_1_1),
|
||||
manager_(new opt::PassManager()) {}
|
||||
manager_(new opt::PassManager()),
|
||||
assemble_options_(SpirvTools::kDefaultAssembleOption),
|
||||
disassemble_options_(SpirvTools::kDefaultDisassembleOption) {}
|
||||
|
||||
// Runs the given |pass| on the binary assembled from the |original|.
|
||||
// Returns a tuple of the optimized binary and the boolean value returned
|
||||
// from pass Process() function.
|
||||
std::tuple<std::vector<uint32_t>, opt::Pass::Status> OptimizeToBinary(
|
||||
opt::Pass* pass, const std::string& original, bool skip_nop) {
|
||||
std::unique_ptr<ir::Module> module =
|
||||
BuildModule(SPV_ENV_UNIVERSAL_1_1, consumer_, original);
|
||||
std::unique_ptr<ir::Module> module = BuildModule(
|
||||
SPV_ENV_UNIVERSAL_1_1, consumer_, original, assemble_options_);
|
||||
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
||||
<< original << std::endl;
|
||||
if (!module) {
|
||||
@ -88,7 +90,8 @@ class PassTest : public TestT {
|
||||
std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>(
|
||||
assembly, skip_nop, std::forward<Args>(args)...);
|
||||
std::string optimized_asm;
|
||||
EXPECT_TRUE(tools_.Disassemble(optimized_bin, &optimized_asm))
|
||||
EXPECT_TRUE(tools_.Disassemble(optimized_bin, &optimized_asm,
|
||||
disassemble_options_))
|
||||
<< "Disassembling failed for shader:\n"
|
||||
<< assembly << std::endl;
|
||||
return std::make_tuple(optimized_asm, status);
|
||||
@ -125,7 +128,8 @@ class PassTest : public TestT {
|
||||
spvContextDestroy(context);
|
||||
}
|
||||
std::string optimized_asm;
|
||||
EXPECT_TRUE(tools_.Disassemble(optimized_bin, &optimized_asm))
|
||||
EXPECT_TRUE(tools_.Disassemble(optimized_bin, &optimized_asm,
|
||||
disassemble_options_))
|
||||
<< "Disassembling failed for shader:\n"
|
||||
<< original << std::endl;
|
||||
EXPECT_EQ(expected, optimized_asm);
|
||||
@ -162,8 +166,8 @@ class PassTest : public TestT {
|
||||
void RunAndCheck(const std::string& original, const std::string& expected) {
|
||||
assert(manager_->NumPasses());
|
||||
|
||||
std::unique_ptr<ir::Module> module =
|
||||
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, original);
|
||||
std::unique_ptr<ir::Module> module = BuildModule(
|
||||
SPV_ENV_UNIVERSAL_1_1, nullptr, original, assemble_options_);
|
||||
ASSERT_NE(nullptr, module);
|
||||
|
||||
manager_->Run(module.get());
|
||||
@ -172,14 +176,25 @@ class PassTest : public TestT {
|
||||
module->ToBinary(&binary, /* skip_nop = */ false);
|
||||
|
||||
std::string optimized;
|
||||
EXPECT_TRUE(tools_.Disassemble(binary, &optimized));
|
||||
EXPECT_TRUE(tools_.Disassemble(binary, &optimized,
|
||||
disassemble_options_));
|
||||
EXPECT_EQ(expected, optimized);
|
||||
}
|
||||
|
||||
void SetAssembleOptions(uint32_t assemble_options) {
|
||||
assemble_options_ = assemble_options;
|
||||
}
|
||||
|
||||
void SetDisassembleOptions(uint32_t disassemble_options) {
|
||||
disassemble_options_ = disassemble_options;
|
||||
}
|
||||
|
||||
private:
|
||||
MessageConsumer consumer_; // Message consumer.
|
||||
SpirvTools tools_; // An instance for calling SPIRV-Tools functionalities.
|
||||
std::unique_ptr<opt::PassManager> manager_; // The pass manager.
|
||||
uint32_t assemble_options_;
|
||||
uint32_t disassemble_options_;
|
||||
};
|
||||
|
||||
} // namespace spvtools
|
||||
|
102
test/scripts/test_compact_ids.py
Normal file
102
test/scripts/test_compact_ids.py
Normal file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2017 Google 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.
|
||||
"""Tests correctness of opt pass tools/opt --compact-ids."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
def test_spirv_file(path, temp_dir):
|
||||
optimized_spv_path = os.path.join(temp_dir, 'optimized.spv')
|
||||
optimized_dis_path = os.path.join(temp_dir, 'optimized.dis')
|
||||
converted_spv_path = os.path.join(temp_dir, 'converted.spv')
|
||||
converted_dis_path = os.path.join(temp_dir, 'converted.dis')
|
||||
|
||||
os.system('tools/spirv-opt ' + path + ' -o ' + optimized_spv_path +
|
||||
' --compact-ids')
|
||||
os.system('tools/spirv-dis ' + optimized_spv_path + ' -o ' +
|
||||
optimized_dis_path)
|
||||
|
||||
os.system('tools/spirv-dis ' + path + ' -o ' + converted_dis_path)
|
||||
os.system('tools/spirv-as ' + converted_dis_path + ' -o ' +
|
||||
converted_spv_path)
|
||||
os.system('tools/spirv-dis ' + converted_spv_path + ' -o ' +
|
||||
converted_dis_path)
|
||||
|
||||
with open(converted_dis_path, 'r') as f:
|
||||
converted_dis = f.readlines()[3:]
|
||||
|
||||
with open(optimized_dis_path, 'r') as f:
|
||||
optimized_dis = f.readlines()[3:]
|
||||
|
||||
return converted_dis == optimized_dis
|
||||
|
||||
def print_usage():
|
||||
template= \
|
||||
"""{script} tests correctness of opt pass tools/opt --compact-ids
|
||||
|
||||
USAGE: python {script} [<spirv_files>]
|
||||
|
||||
Requires tools/spirv-dis, tools/spirv-as and tools/spirv-opt to be in path
|
||||
(call the script from the SPIRV-Tools build output directory).
|
||||
|
||||
TIP: In order to test all .spv files under current dir use
|
||||
find <path> -name "*.spv" -print0 | xargs -0 -s 2000000 python {script}
|
||||
"""
|
||||
print(template.format(script=sys.argv[0]));
|
||||
|
||||
def main():
|
||||
if not os.path.isfile('tools/spirv-dis'):
|
||||
print('error: tools/spirv-dis not found')
|
||||
print_usage()
|
||||
exit(1)
|
||||
|
||||
if not os.path.isfile('tools/spirv-as'):
|
||||
print('error: tools/spirv-as not found')
|
||||
print_usage()
|
||||
exit(1)
|
||||
|
||||
if not os.path.isfile('tools/spirv-opt'):
|
||||
print('error: tools/spirv-opt not found')
|
||||
print_usage()
|
||||
exit(1)
|
||||
|
||||
paths = sys.argv[1:]
|
||||
if not paths:
|
||||
print_usage()
|
||||
|
||||
num_failed = 0
|
||||
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
|
||||
for path in paths:
|
||||
success = test_spirv_file(path, temp_dir)
|
||||
if not success:
|
||||
print('Test failed for ' + path)
|
||||
num_failed += 1
|
||||
|
||||
print('Tested ' + str(len(paths)) + ' files')
|
||||
|
||||
if num_failed:
|
||||
print(str(num_failed) + ' tests failed')
|
||||
exit(1)
|
||||
else:
|
||||
print('All tests successful')
|
||||
exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -66,6 +66,9 @@ Options:
|
||||
--flatten-decorations
|
||||
Replace decoration groups with repeated OpDecorate and
|
||||
OpMemberDecorate instructions.
|
||||
--compact-ids
|
||||
Remap result ids to a compact range starting from %%1 and without
|
||||
any gaps.
|
||||
-h, --help Print this help.
|
||||
--version Display optimizer version information.
|
||||
)",
|
||||
@ -136,6 +139,8 @@ int main(int argc, char** argv) {
|
||||
optimizer.RegisterPass(CreateUnifyConstantPass());
|
||||
} else if (0 == strcmp(cur_arg, "--flatten-decorations")) {
|
||||
optimizer.RegisterPass(CreateFlattenDecorationPass());
|
||||
} else if (0 == strcmp(cur_arg, "--compact-ids")) {
|
||||
optimizer.RegisterPass(CreateCompactIdsPass());
|
||||
} else if ('\0' == cur_arg[1]) {
|
||||
// Setting a filename of "-" to indicate stdin.
|
||||
if (!in_file) {
|
||||
|
Loading…
Reference in New Issue
Block a user