From a77a8da5b4eaa80763293e1b1b5b3d673503a0d8 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Fri, 9 Aug 2019 11:39:20 -0400 Subject: [PATCH] Replace OpKill With function call. We are no able to inline OpKill instructions into a continue construct. See #2433. However, we have to be able to inline to correctly do legalization. This commit creates a pass that will wrap OpKill instructions into a function of its own. That way we are able to inline the rest of the code. The follow up to this will be to not inline any function that contains an OpKill. Fixes #2726 --- Android.mk | 1 + BUILD.gn | 2 + include/spirv-tools/optimizer.hpp | 4 + source/opt/CMakeLists.txt | 2 + source/opt/ir_builder.h | 18 +++ source/opt/optimizer.cpp | 18 ++- source/opt/passes.h | 1 + source/opt/types.cpp | 4 +- source/opt/types.h | 4 +- source/opt/wrap_opkill.cpp | 124 +++++++++++++++++++ source/opt/wrap_opkill.h | 69 +++++++++++ test/opt/CMakeLists.txt | 1 + test/opt/wrap_opkill_test.cpp | 198 ++++++++++++++++++++++++++++++ test/tools/opt/flags.py | 6 +- 14 files changed, 443 insertions(+), 9 deletions(-) create mode 100644 source/opt/wrap_opkill.cpp create mode 100644 source/opt/wrap_opkill.h create mode 100644 test/opt/wrap_opkill_test.cpp diff --git a/Android.mk b/Android.mk index a6278af0d57..8fce8ffdaba 100644 --- a/Android.mk +++ b/Android.mk @@ -172,6 +172,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/value_number_table.cpp \ source/opt/vector_dce.cpp \ source/opt/workaround1209.cpp + source/opt/wrap_opkill.cpp # Locations of grammar files. # diff --git a/BUILD.gn b/BUILD.gn index 84b21e13b34..8e4dd915d99 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -650,6 +650,8 @@ static_library("spvtools_opt") { "source/opt/vector_dce.h", "source/opt/workaround1209.cpp", "source/opt/workaround1209.h", + "source/opt/wrap_opkill.cpp", + "source/opt/wrap_opkill.h", ] deps = [ diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index d442b97f525..22f21155498 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -795,6 +795,10 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass(); // for the first index. Optimizer::PassToken CreateDescriptorScalarReplacementPass(); +// Create a pass to replace all OpKill instruction with a function call to a +// function that has a single OpKill. This allows more code to be inlined. +Optimizer::PassToken CreateWrapOpKillPass(); + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 2ebad512af5..6a3d7021118 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -113,6 +113,7 @@ set(SPIRV_TOOLS_OPT_SOURCES value_number_table.h vector_dce.h workaround1209.h + wrap_opkill.h aggressive_dead_code_elim_pass.cpp basic_block.cpp @@ -212,6 +213,7 @@ set(SPIRV_TOOLS_OPT_SOURCES value_number_table.cpp vector_dce.cpp workaround1209.cpp + wrap_opkill.cpp ) if(MSVC) diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h index da740551221..5514e2dd4e9 100644 --- a/source/opt/ir_builder.h +++ b/source/opt/ir_builder.h @@ -465,6 +465,20 @@ class InstructionBuilder { return AddInstruction(std::move(new_inst)); } + Instruction* AddFunctionCall(uint32_t result_type, uint32_t function, + const std::vector& parameters) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {function}}); + for (uint32_t id : parameters) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + std::unique_ptr new_inst( + new Instruction(GetContext(), SpvOpFunctionCall, result_type, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + // Inserts the new instruction before the insertion point. Instruction* AddInstruction(std::unique_ptr&& insn) { Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn)); @@ -512,6 +526,10 @@ class InstructionBuilder { // Returns true if the users requested to update |analysis|. inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const { + if (!GetContext()->AreAnalysesValid(analysis)) { + // Do not try to update something that is not built. + return false; + } return preserved_analyses_ & analysis; } diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index f3b56c314eb..0881afc74d8 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -107,8 +107,10 @@ Optimizer& Optimizer::RegisterPass(PassToken&& p) { // or enable more copy propagation. Optimizer& Optimizer::RegisterLegalizationPasses() { return - // Remove unreachable block so that merge return works. - RegisterPass(CreateDeadBranchElimPass()) + // Wrap OpKill instructions so all other code can be inlined. + RegisterPass(CreateWrapOpKillPass()) + // Remove unreachable block so that merge return works. + .RegisterPass(CreateDeadBranchElimPass()) // Merge the returns so we can inline. .RegisterPass(CreateMergeReturnPass()) // Make sure uses and definitions are in the same function. @@ -154,7 +156,8 @@ Optimizer& Optimizer::RegisterLegalizationPasses() { } Optimizer& Optimizer::RegisterPerformancePasses() { - return RegisterPass(CreateDeadBranchElimPass()) + return RegisterPass(CreateWrapOpKillPass()) + .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateMergeReturnPass()) .RegisterPass(CreateInlineExhaustivePass()) .RegisterPass(CreateAggressiveDCEPass()) @@ -190,7 +193,8 @@ Optimizer& Optimizer::RegisterPerformancePasses() { } Optimizer& Optimizer::RegisterSizePasses() { - return RegisterPass(CreateDeadBranchElimPass()) + return RegisterPass(CreateWrapOpKillPass()) + .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateMergeReturnPass()) .RegisterPass(CreateInlineExhaustivePass()) .RegisterPass(CreateAggressiveDCEPass()) @@ -477,6 +481,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateDecomposeInitializedVariablesPass()); } else if (pass_name == "graphics-robust-access") { RegisterPass(CreateGraphicsRobustAccessPass()); + } else if (pass_name == "wrap-opkill") { + RegisterPass(CreateWrapOpKillPass()); } else { Errorf(consumer(), nullptr, {}, "Unknown flag '--%s'. Use --help for a list of valid flags", @@ -893,4 +899,8 @@ Optimizer::PassToken CreateDescriptorScalarReplacementPass() { MakeUnique()); } +Optimizer::PassToken CreateWrapOpKillPass() { + return MakeUnique(MakeUnique()); +} + } // namespace spvtools diff --git a/source/opt/passes.h b/source/opt/passes.h index 86588f7ebce..5476e88b2ec 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -76,5 +76,6 @@ #include "source/opt/upgrade_memory_model.h" #include "source/opt/vector_dce.h" #include "source/opt/workaround1209.h" +#include "source/opt/wrap_opkill.h" #endif // SOURCE_OPT_PASSES_H_ diff --git a/source/opt/types.cpp b/source/opt/types.cpp index 8e202385667..4f7150fc3c6 100644 --- a/source/opt/types.cpp +++ b/source/opt/types.cpp @@ -571,10 +571,10 @@ void Pointer::GetExtraHashWords(std::vector* words, void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; } -Function::Function(Type* ret_type, const std::vector& params) +Function::Function(const Type* ret_type, const std::vector& params) : Type(kFunction), return_type_(ret_type), param_types_(params) {} -Function::Function(Type* ret_type, std::vector& params) +Function::Function(const Type* ret_type, std::vector& params) : Type(kFunction), return_type_(ret_type), param_types_(params) {} bool Function::IsSameImpl(const Type* that, IsSameCache* seen) const { diff --git a/source/opt/types.h b/source/opt/types.h index 1d3552ac339..57920df962c 100644 --- a/source/opt/types.h +++ b/source/opt/types.h @@ -520,8 +520,8 @@ class Pointer : public Type { class Function : public Type { public: - Function(Type* ret_type, const std::vector& params); - Function(Type* ret_type, std::vector& params); + Function(const Type* ret_type, const std::vector& params); + Function(const Type* ret_type, std::vector& params); Function(const Function&) = default; std::string str() const override; diff --git a/source/opt/wrap_opkill.cpp b/source/opt/wrap_opkill.cpp new file mode 100644 index 00000000000..2e12ab77885 --- /dev/null +++ b/source/opt/wrap_opkill.cpp @@ -0,0 +1,124 @@ +// Copyright (c) 2019 Google LLC +// +// 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 "source/opt/wrap_opkill.h" + +#include "ir_builder.h" + +namespace spvtools { +namespace opt { + +Pass::Status WrapOpKill::Process() { + bool modified = false; + + for (auto& func : *get_module()) { + func.ForEachInst([this, &modified](Instruction* inst) { + if (inst->opcode() == SpvOpKill) { + modified = true; + ReplaceWithFunctionCall(inst); + } + }); + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) { + assert(inst->opcode() == SpvOpKill && + "|inst| must be an OpKill instruction."); + InstructionBuilder ir_builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + ir_builder.AddFunctionCall(GetVoidTypeId(), GetOpKillFuncId(), {}); + ir_builder.AddUnreachable(); + context()->KillInst(inst); +} + +uint32_t WrapOpKill::GetVoidTypeId() { + if (void_type_id_ != 0) { + return void_type_id_; + } + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_type; + void_type_id_ = type_mgr->GetTypeInstruction(&void_type); + return void_type_id_; +} + +uint32_t WrapOpKill::GetVoidFunctionTypeId() { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_type; + const analysis::Type* registered_void_type = + type_mgr->GetRegisteredType(&void_type); + + analysis::Function func_type(registered_void_type, {}); + return type_mgr->GetTypeInstruction(&func_type); +} + +uint32_t WrapOpKill::GetOpKillFuncId() { + if (opkill_func_id_ != 0) { + return opkill_func_id_; + } + + opkill_func_id_ = TakeNextId(); + + // Generate the function start instruction + std::unique_ptr func_start(new Instruction( + context(), SpvOpFunction, GetVoidTypeId(), opkill_func_id_, {})); + func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}}); + func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}}); + std::unique_ptr opkill_function( + new Function(std::move(func_start))); + + // Generate the function end instruction + std::unique_ptr func_end( + new Instruction(context(), SpvOpFunctionEnd, 0, 0, {})); + opkill_function->SetFunctionEnd(std::move(func_end)); + + // Create the one basic block for the function. + std::unique_ptr label_inst( + new Instruction(context(), SpvOpLabel, 0, TakeNextId(), {})); + std::unique_ptr bb(new BasicBlock(std::move(label_inst))); + + // Add the OpKill to the basic block + std::unique_ptr kill_inst( + new Instruction(context(), SpvOpKill, 0, 0, {})); + bb->AddInstruction(std::move(kill_inst)); + + // Add the bb to the function + opkill_function->AddBasicBlock(std::move(bb)); + + // Add the function to the module. + auto func = opkill_function.get(); + context()->AddFunction(std::move(opkill_function)); + + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { + func->ForEachInst( + [this](Instruction* inst) { context()->AnalyzeDefUse(inst); }); + } + + if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { + for (BasicBlock& basic_block : *func) { + context()->set_instr_block(basic_block.GetLabelInst(), &basic_block); + for (Instruction& inst : basic_block) { + context()->set_instr_block(&inst, &basic_block); + } + } + } + + return opkill_func_id_; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/wrap_opkill.h b/source/opt/wrap_opkill.h new file mode 100644 index 00000000000..e918c3932c1 --- /dev/null +++ b/source/opt/wrap_opkill.h @@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// 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 SOURCE_OPT_WRAP_OPKILL_H_ +#define SOURCE_OPT_WRAP_OPKILL_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class WrapOpKill : public Pass { + public: + WrapOpKill() : opkill_func_id_(0), void_type_id_(0) {} + + const char* name() const override { return "wrap-opkill"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Replaces the OpKill instruction |inst| with a function call to a function + // that contains a single instruction, which is OpKill. An OpUnreachable + // instruction will be placed after the function call. + void ReplaceWithFunctionCall(Instruction* inst); + + // Returns the id of the void type. + uint32_t GetVoidTypeId(); + + // Returns the id of the function type for a void function with no parameters. + uint32_t GetVoidFunctionTypeId(); + + // Return the id of a function that has return type void, no no parameters, + // and contains a single instruction, which is an OpKill. + uint32_t GetOpKillFuncId(); + + // The id of the function whose body is a single OpKill instruction. If the + // id is 0, then the function has not been generated yet. + uint32_t opkill_func_id_; + + // The id of the void type. If its value is 0, then the void type has not + // been found or created yet. + uint32_t void_type_id_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_WRAP_OPKILL_H_ diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 366a61f68d0..d723dc3142b 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -97,6 +97,7 @@ add_spvtools_unittest(TARGET opt value_table_test.cpp vector_dce_test.cpp workaround1209_test.cpp + wrap_opkill_test.cpp LIBS SPIRV-Tools-opt PCH_FILE pch_test_opt ) diff --git a/test/opt/wrap_opkill_test.cpp b/test/opt/wrap_opkill_test.cpp new file mode 100644 index 00000000000..a0314adc477 --- /dev/null +++ b/test/opt/wrap_opkill_test.cpp @@ -0,0 +1,198 @@ +// Copyright (c) 2019 Google LLC +// +// 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 "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using WrapOpKillTest = PassTest<::testing::Test>; + +TEST_F(WrapOpKillTest, SingleOpKill) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpUnreachable +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, MultipleOpKillInSameFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpUnreachable +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill]] +; CHECK-NEXT: OpUnreachable +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %true %17 %18 + %17 = OpLabel + OpKill + %18 = OpLabel + OpKill + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, MultipleOpKillInDifferentFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]] +; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]] +; CHECK: [[orig_kill1]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpUnreachable +; CHECK: [[orig_kill2]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill]] +; CHECK-NEXT: OpUnreachable +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpBranchConditional %true %12 %9 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + %13 = OpFunctionCall %void %14 + %15 = OpFunctionCall %void %16 + OpBranch %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %void None %4 + %17 = OpLabel + OpKill + OpFunctionEnd + %16 = OpFunction %void None %4 + %18 = OpLabel + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py index a89477cc311..49e2cabc548 100644 --- a/test/tools/opt/flags.py +++ b/test/tools/opt/flags.py @@ -57,7 +57,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4, """Tests that spirv-opt accepts all valid optimization flags.""" flags = [ - '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids', + '--wrap-opkill', '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids', '--convert-local-access-chains', '--copy-propagate-arrays', '--eliminate-dead-branches', '--eliminate-dead-code-aggressive', '--eliminate-dead-const', @@ -76,6 +76,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4, '--unify-const' ] expected_passes = [ + 'wrap-opkill', 'ccp', 'cfg-cleanup', 'combine-access-chains', @@ -134,6 +135,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_4, flags = ['-O'] expected_passes = [ + 'wrap-opkill', 'eliminate-dead-branches', 'merge-return', 'inline-entry-points-exhaustive', @@ -181,6 +183,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_4, flags = ['-Os'] expected_passes = [ + 'wrap-opkill', 'eliminate-dead-branches', 'merge-return', 'inline-entry-points-exhaustive', @@ -221,6 +224,7 @@ class TestLegalizationPasses(expect.ValidObjectFile1_4, flags = ['--legalize-hlsl'] expected_passes = [ + 'wrap-opkill', 'eliminate-dead-branches', 'merge-return', 'inline-entry-points-exhaustive',