Handle decoration groups with no decorations. (#1921)

In DecorationManager::RemoveDecorationsFrom, we do not remove the id
from a decoration group if the group has no decorations.  This causes
problems because KillNamesAndDecorates is suppose to remove all
references to the id, but in this case, there is still a reference.

This is fixed by adding a special case.

Also, there is the possibility of a double free because
RemoveDecorationsFrom will delete the instructions defining |id| when
|id| is a decoration group.  Later, KillInst would later write to memory
that has been deleted when trying to turn it into a Nop.  To fix this,
we will only remove the decorations that use |id| and not its definition
in RemoveDecorationsFrom.
This commit is contained in:
Steven Perron 2018-09-28 14:16:04 -04:00 committed by GitHub
parent f0aa6f4e3a
commit 32381e30ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 116 additions and 11 deletions

View File

@ -29,7 +29,9 @@ namespace analysis {
void DecorationManager::RemoveDecorationsFrom( void DecorationManager::RemoveDecorationsFrom(
uint32_t id, std::function<bool(const Instruction&)> pred) { uint32_t id, std::function<bool(const Instruction&)> pred) {
const auto ids_iter = id_to_decoration_insts_.find(id); const auto ids_iter = id_to_decoration_insts_.find(id);
if (ids_iter == id_to_decoration_insts_.end()) return; if (ids_iter == id_to_decoration_insts_.end()) {
return;
}
TargetData& decorations_info = ids_iter->second; TargetData& decorations_info = ids_iter->second;
auto context = module_->context(); auto context = module_->context();
@ -59,8 +61,14 @@ void DecorationManager::RemoveDecorationsFrom(
if (!pred(*decoration)) group_decorations_to_keep.push_back(decoration); if (!pred(*decoration)) group_decorations_to_keep.push_back(decoration);
} }
// If all decorations should be kept, move to the next group // If all decorations should be kept, then we can keep |id| part of the
if (group_decorations_to_keep.size() == group_decorations.size()) continue; // group. However, if the group itself has no decorations, we should remove
// the id from the group. This is needed to make |KillNameAndDecorate| work
// correctly when a decoration group has no decorations.
if (group_decorations_to_keep.size() == group_decorations.size() &&
group_decorations.size() != 0) {
continue;
}
// Otherwise, remove |id| from the targets of |group_id| // Otherwise, remove |id| from the targets of |group_id|
const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u; const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u;
@ -136,9 +144,6 @@ void DecorationManager::RemoveDecorationsFrom(
decorations_info.indirect_decorations.empty() && decorations_info.indirect_decorations.empty() &&
decorations_info.decorate_insts.empty()) { decorations_info.decorate_insts.empty()) {
id_to_decoration_insts_.erase(ids_iter); id_to_decoration_insts_.erase(ids_iter);
// Remove the OpDecorationGroup defining this group.
if (is_group) context->KillInst(context->get_def_use_mgr()->GetDef(id));
} }
} }

View File

@ -36,11 +36,22 @@ class DecorationManager {
} }
DecorationManager() = delete; DecorationManager() = delete;
// Removes all decorations from |id| (either directly or indirectly) for // Changes all of the decorations (direct and through groups) where |pred| is
// which |pred| returns true. // true and that apply to |id| so that they no longer apply to |id|.
// If |id| is a group ID, OpGroupDecorate and OpGroupMemberDecorate will be //
// removed if they have no targets left, and OpDecorationGroup will be // If |id| is part of a group, it will be removed from the group if it
// removed if the group is not applied to anyone and contains no decorations. // does not use all of the group's decorations, or, if there are no
// decorations that apply to the group.
//
// If decoration groups become empty, the |OpGroupDecorate| and
// |OpGroupMemberDecorate| instructions will be killed.
//
// Decoration instructions that apply directly to |id| will be killed.
//
// If |id| is a decoration group and all of the group's decorations are
// removed, then the |OpGroupDecorate| and
// |OpGroupMemberDecorate| for the group will be killed, but not the defining
// |OpDecorationGroup| instruction.
void RemoveDecorationsFrom(uint32_t id, void RemoveDecorationsFrom(uint32_t id,
std::function<bool(const Instruction&)> pred = std::function<bool(const Instruction&)> pred =
[](const Instruction&) { return true; }); [](const Instruction&) { return true; });

View File

@ -422,6 +422,7 @@ OpGroupDecorate %2 %1 %3
OpCapability Linkage OpCapability Linkage
OpMemoryModel Logical GLSL450 OpMemoryModel Logical GLSL450
OpDecorate %1 Constant OpDecorate %1 Constant
%2 = OpDecorationGroup
%4 = OpTypeInt 32 0 %4 = OpTypeInt 32 0
%1 = OpVariable %4 Uniform %1 = OpVariable %4 Uniform
%3 = OpVariable %4 Uniform %3 = OpVariable %4 Uniform

View File

@ -282,6 +282,94 @@ TEST_F(IRContextTest, TakeNextUniqueIdIncrementing) {
EXPECT_EQ(i, localContext.TakeNextUniqueId()); EXPECT_EQ(i, localContext.TakeNextUniqueId());
} }
TEST_F(IRContextTest, KillGroupDecorationWitNoDecorations) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 430
%3 = OpDecorationGroup
OpGroupDecorate %3 %4 %5
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%8 = OpTypeStruct %6
%9 = OpTypeVoid
%10 = OpTypeFunction %9
%2 = OpFunction %9 None %10
%11 = OpLabel
%4 = OpVariable %7 Function
%5 = OpVariable %7 Function
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
// Build the decoration manager.
context->get_decoration_mgr();
// Delete the second variable.
context->KillDef(5);
// The two decoration instructions should still be there. The first one
// should be the same, but the second should have %5 removed.
// Check the OpDecorationGroup Instruction
auto inst = context->annotation_begin();
EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup);
EXPECT_EQ(inst->result_id(), 3);
// Check that %5 is no longer part of the group.
++inst;
EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate);
EXPECT_EQ(inst->NumInOperands(), 2);
EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
EXPECT_EQ(inst->GetSingleWordInOperand(1), 4);
// Check that we are at the end.
++inst;
EXPECT_EQ(inst, context->annotation_end());
}
TEST_F(IRContextTest, KillDecorationGroup) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 430
%3 = OpDecorationGroup
OpGroupDecorate %3 %4 %5
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%8 = OpTypeStruct %6
%9 = OpTypeVoid
%10 = OpTypeFunction %9
%2 = OpFunction %9 None %10
%11 = OpLabel
%4 = OpVariable %7 Function
%5 = OpVariable %7 Function
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
// Build the decoration manager.
context->get_decoration_mgr();
// Delete the second variable.
context->KillDef(3);
// Check the OpDecorationGroup Instruction is still there.
EXPECT_TRUE(context->annotations().empty());
}
} // namespace } // namespace
} // namespace opt } // namespace opt
} // namespace spvtools } // namespace spvtools