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:
Andrey Tuganov 2017-04-11 15:11:04 -04:00 committed by David Neto
parent b173d1c3cd
commit 1e309af80a
13 changed files with 337 additions and 16 deletions

View File

@ -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_

View File

@ -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

View File

@ -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());
}

View File

@ -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

View 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

View 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_

View File

@ -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

View File

@ -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"

View File

@ -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

View 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

View File

@ -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

View 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()

View File

@ -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) {