live_local_vars_.insert(varId);
}
-bool AggressiveDCEPass::IsStructuredIfHeader(ir::BasicBlock* bp,
- ir::Instruction** mergeInst,
- ir::Instruction** branchInst,
- uint32_t* mergeBlockId) {
- auto ii = bp->end();
- --ii;
- if (ii->opcode() != SpvOpBranchConditional) return false;
- if (ii == bp->begin()) return false;
- if (branchInst != nullptr) *branchInst = &*ii;
- --ii;
- if (ii->opcode() != SpvOpSelectionMerge) return false;
- if (mergeInst != nullptr) *mergeInst = &*ii;
- if (mergeBlockId != nullptr)
- *mergeBlockId =
- ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
+bool AggressiveDCEPass::IsStructuredIfOrLoopHeader(ir::BasicBlock* bp,
+ ir::Instruction** mergeInst,
+ ir::Instruction** branchInst,
+ uint32_t* mergeBlockId) {
+ ir::Instruction* mi = bp->GetMergeInst();
+ if (mi == nullptr) return false;
+ ir::Instruction* bri = &*bp->tail();
+ // Make sure it is not a Switch
+ if (mi->opcode() == SpvOpSelectionMerge &&
+ bri->opcode() != SpvOpBranchConditional)
+ return false;
+ if (branchInst != nullptr) *branchInst = bri;
+ if (mergeInst != nullptr) *mergeInst = mi;
+ if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0);
return true;
}
currentBranchInst.push(nullptr);
currentMergeBlockId.push(0);
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
+ // If leaving an if or loop, update stacks
if ((*bi)->id() == currentMergeBlockId.top()) {
currentMergeBlockId.pop();
currentMergeInst.pop();
currentBranchInst.pop();
}
- block2headerMerge_[*bi] = currentMergeInst.top();
- block2headerBranch_[*bi] = currentBranchInst.top();
ir::Instruction* mergeInst;
ir::Instruction* branchInst;
uint32_t mergeBlockId;
- if (IsStructuredIfHeader(*bi, &mergeInst, &branchInst, &mergeBlockId)) {
+ bool is_header =
+ IsStructuredIfOrLoopHeader(*bi, &mergeInst, &branchInst, &mergeBlockId);
+ // If there is live code in loop header, the loop is live
+ if (is_header && mergeInst->opcode() == SpvOpLoopMerge) {
+ currentMergeBlockId.push(mergeBlockId);
+ currentMergeInst.push(mergeInst);
+ currentBranchInst.push(branchInst);
+ }
+ block2headerMerge_[*bi] = currentMergeInst.top();
+ block2headerBranch_[*bi] = currentBranchInst.top();
+ // If there is live code following if header, the if is live
+ if (is_header && mergeInst->opcode() == SpvOpSelectionMerge) {
currentMergeBlockId.push(mergeBlockId);
currentMergeInst.push(mergeInst);
currentBranchInst.push(branchInst);
ComputeBlock2HeaderMaps(structuredOrder);
bool modified = false;
// Add instructions with external side effects to worklist. Also add branches
- // EXCEPT those immediately contained in an "if" selection construct.
+ // EXCEPT those immediately contained in an "if" selection construct or a loop
+ // or continue construct.
// TODO(greg-lunarg): Handle Frexp, Modf more optimally
call_in_func_ = false;
func_is_entry_point_ = false;
private_stores_.clear();
- // Stacks to keep track of when we are inside an if-construct. When not
- // immediately inside an in-construct, we must assume all branches are live.
+ // Stacks to keep track of when we are inside an if- or loop-construct.
+ // When not immediately inside an if- or loop-construct, we must assume
+ // all branches are live as we may be inside of a control construct (ie
+ // switch) which is not part of the ADCE analysis.
std::stack<bool> assume_branches_live;
std::stack<uint32_t> currentMergeBlockId;
// Push sentinel values on stack for when outside of any control flow.
assume_branches_live.push(true);
currentMergeBlockId.push(0);
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
+ // If exiting if or loop, update stacks
if ((*bi)->id() == currentMergeBlockId.top()) {
assume_branches_live.pop();
currentMergeBlockId.pop();
AddToWorklist(&*ii);
} break;
case SpvOpLoopMerge: {
- // Assume loops live (for now)
- // TODO(greg-lunarg): Add dead loop elimination
- assume_branches_live.push(true);
+ assume_branches_live.push(false);
currentMergeBlockId.push(
ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx));
- AddToWorklist(&*ii);
} break;
case SpvOpSelectionMerge: {
auto brii = ii;
while (!worklist_.empty()) {
ir::Instruction* liveInst = worklist_.front();
// Add all operand instructions if not already live
- liveInst->ForEachInId([this](const uint32_t* iid) {
+ liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) {
ir::Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
+ // Do not add label if an operand of a branch. This is not needed
+ // as part of live code discovery and can create false live code,
+ // for example, the branch to a header of a loop.
+ if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return;
if (!IsLive(inInst)) AddToWorklist(inInst);
});
- // If in a structured if construct, add the controlling conditional branch
- // and its merge. Any containing if construct is marked live when the
- // the merge and branch are processed out of the worklist.
+ // If in a structured if or loop construct, add the controlling
+ // conditional branch and its merge. Any containing control construct
+ // is marked live when the merge and branch are processed out of the
+ // worklist.
ir::BasicBlock* blk = inst2block_[liveInst];
ir::Instruction* branchInst = block2headerBranch_[blk];
if (branchInst != nullptr && !IsLive(branchInst)) {
AddToWorklist(branchInst);
- AddToWorklist(block2headerMerge_[blk]);
+ ir::Instruction* mergeInst = block2headerMerge_[blk];
+ AddToWorklist(mergeInst);
+ // If in a loop, mark all branches targeting merge block
+ // and continue block as live.
+ if (mergeInst->opcode() == SpvOpLoopMerge) {
+ AddBranchesToWorklist(
+ mergeInst->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx));
+ AddBranchesToWorklist(
+ mergeInst->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx));
+ }
}
// If local load, add all variable's stores if variable not already live
if (liveInst->opcode() == SpvOpLoad) {
ProcessLoad(varId);
});
}
- // If loop merge, add all branches to continue and merge blocks
- // to worklist
- else if (liveInst->opcode() == SpvOpLoopMerge) {
- AddBranchesToWorklist(
- liveInst->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx));
- AddBranchesToWorklist(
- liveInst->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx));
- }
// If function parameter, treat as if it's result id is loaded from
else if (liveInst->opcode() == SpvOpFunctionParameter) {
ProcessLoad(liveInst->result_id());
}
worklist_.pop();
}
- // Mark all non-live instructions dead, except branches which are not
- // at the end of an if-header, which indicate a dead if.
+ // Mark all non-live instructions dead except non-structured branches, which
+ // now should be considered live unless their block is deleted.
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
if (IsLive(&*ii)) continue;
if (ii->IsBranch() &&
- !IsStructuredIfHeader(*bi, nullptr, nullptr, nullptr))
+ !IsStructuredIfOrLoopHeader(*bi, nullptr, nullptr, nullptr))
continue;
dead_insts_.insert(&*ii);
}
if (dead_insts_.find(inst) == dead_insts_.end()) return;
// If dead instruction is selection merge, remember merge block
// for new branch at end of block
- if (inst->opcode() == SpvOpSelectionMerge)
- mergeBlockId =
- inst->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
+ if (inst->opcode() == SpvOpSelectionMerge ||
+ inst->opcode() == SpvOpLoopMerge)
+ mergeBlockId = inst->GetSingleWordInOperand(0);
context()->KillInst(inst);
modified = true;
});
- // If a structured if was deleted, add a branch to its merge block,
- // and traverse to the merge block, continuing processing there.
- // The block still exists as the OpLabel at least is still intact.
+ // If a structured if or loop was deleted, add a branch to its merge
+ // block, and traverse to the merge block and continue processing there.
+ // We know the block still exists because the label is not deleted.
if (mergeBlockId != 0) {
AddBranch(mergeBlockId, *bi);
for (++bi; (*bi)->id() != mergeBlockId; ++bi) {
R"(%main = OpFunction %void None %9
%15 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
-%dv = OpVariable %_ptr_Function_v4float Function
+%dv = OpVariable %_ptr_Function_v4float Function
%16 = OpLoad %v4float %BaseColor
OpStore %v %16
%17 = OpLoad %v4float %Dead
-%18 = OpExtInst %v4float %1 Sqrt %17
+%18 = OpExtInst %v4float %1 Sqrt %17
OpStore %dv %18
%19 = OpLoad %v4float %v
OpStore %gl_FragColor %19
R"(%main = OpFunction %void None %11
%20 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
-%dv = OpVariable %_ptr_Function_v4float Function
+%dv = OpVariable %_ptr_Function_v4float Function
%21 = OpLoad %v4float %BaseColor
OpStore %v %21
%22 = OpLoad %v4float %Dead
R"(%main = OpFunction %void None %10
%17 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
-%dv = OpVariable %_ptr_Function_v4float Function
+%dv = OpVariable %_ptr_Function_v4float Function
%18 = OpLoad %v4float %BaseColor
OpStore %v %18
%19 = OpLoad %v4float %Dead
R"(%main = OpFunction %void None %9
%15 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
-%dv = OpVariable %_ptr_Function_v4float Function
+%dv = OpVariable %_ptr_Function_v4float Function
%16 = OpLoad %v4float %BaseColor
OpStore %v %16
%17 = OpLoad %v4float %Dead
predefs1 + names_after + predefs2 + func_after, true, true);
}
-TEST_F(AggressiveDCETest, DeadCycle) {
- // #version 140
- // in vec4 BaseColor;
- //
- // layout(std140) uniform U_t
- // {
- // int g_I ;
- // } ;
- //
- // void main()
- // {
- // vec4 v = BaseColor;
- // float df = 0.0;
- // int i = 0;
- // while (i < g_I) {
- // df = df * 0.5;
- // i = i + 1;
- // }
- // gl_FragColor = v;
- // }
-
- const std::string predefs1 =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-)";
-
- const std::string names_before =
- R"(OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %df "df"
-OpName %i "i"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_I"
-OpName %_ ""
-OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string names_after =
- R"(OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %i "i"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_I"
-OpName %_ ""
-OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string predefs2 =
- R"(OpMemberDecorate %U_t 0 Offset 0
-OpDecorate %U_t Block
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%11 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Function_float = OpTypePointer Function %float
-%float_0 = OpConstant %float 0
-%int = OpTypeInt 32 1
-%_ptr_Function_int = OpTypePointer Function %int
-%int_0 = OpConstant %int 0
-%U_t = OpTypeStruct %int
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%_ptr_Uniform_int = OpTypePointer Uniform %int
-%bool = OpTypeBool
-%float_0_5 = OpConstant %float 0.5
-%int_1 = OpConstant %int 1
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %11
-%27 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%df = OpVariable %_ptr_Function_float Function
-%i = OpVariable %_ptr_Function_int Function
-%28 = OpLoad %v4float %BaseColor
-OpStore %v %28
-OpStore %df %float_0
-OpStore %i %int_0
-OpBranch %29
-%29 = OpLabel
-OpLoopMerge %30 %31 None
-OpBranch %32
-%32 = OpLabel
-%33 = OpLoad %int %i
-%34 = OpAccessChain %_ptr_Uniform_int %_ %int_0
-%35 = OpLoad %int %34
-%36 = OpSLessThan %bool %33 %35
-OpBranchConditional %36 %37 %30
-%37 = OpLabel
-%38 = OpLoad %float %df
-%39 = OpFMul %float %38 %float_0_5
-OpStore %df %39
-%40 = OpLoad %int %i
-%41 = OpIAdd %int %40 %int_1
-OpStore %i %41
-OpBranch %31
-%31 = OpLabel
-OpBranch %29
-%30 = OpLabel
-%42 = OpLoad %v4float %v
-OpStore %gl_FragColor %42
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string func_after =
- R"(%main = OpFunction %void None %11
-%27 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%i = OpVariable %_ptr_Function_int Function
-%28 = OpLoad %v4float %BaseColor
-OpStore %v %28
-OpStore %i %int_0
-OpBranch %29
-%29 = OpLabel
-OpLoopMerge %30 %31 None
-OpBranch %32
-%32 = OpLabel
-%33 = OpLoad %int %i
-%34 = OpAccessChain %_ptr_Uniform_int %_ %int_0
-%35 = OpLoad %int %34
-%36 = OpSLessThan %bool %33 %35
-OpBranchConditional %36 %37 %30
-%37 = OpLabel
-%40 = OpLoad %int %i
-%41 = OpIAdd %int %40 %int_1
-OpStore %i %41
-OpBranch %31
-%31 = OpLabel
-OpBranch %29
-%30 = OpLabel
-%42 = OpLoad %v4float %v
-OpStore %gl_FragColor %42
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<opt::AggressiveDCEPass>(
- predefs1 + names_before + predefs2 + func_before,
- predefs1 + names_after + predefs2 + func_after, true, true);
-}
-
TEST_F(AggressiveDCETest, OptWhitelistExtension) {
// #version 140
//
R"(%main = OpFunction %void None %9
%15 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
-%dv = OpVariable %_ptr_Function_v4float Function
+%dv = OpVariable %_ptr_Function_v4float Function
%16 = OpLoad %v4float %BaseColor
OpStore %v %16
%17 = OpLoad %v4float %Dead
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
-OpName %nothing_vf4_ "nothing(vf4;"
+OpName %nothing_vf4_ "nothing(vf4;"
OpName %v "v"
OpName %v1 "v1"
OpName %i1 "i1"
OpName %v2 "v2"
OpName %i2 "i2"
-OpName %param "param"
-OpName %gl_FragColor "gl_FragColor"
+OpName %param "param"
+OpName %gl_FragColor "gl_FragColor"
%void = OpTypeVoid
%12 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%float_0 = OpConstant %float 0
-%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
)";
const std::string defs_after =
const std::string func_before =
R"(%main = OpFunction %void None %12
%21 = OpLabel
-%v1 = OpVariable %_ptr_Function_v4float Function
-%v2 = OpVariable %_ptr_Function_v4float Function
+%v1 = OpVariable %_ptr_Function_v4float Function
+%v2 = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%22 = OpLoad %v4float %i1
OpStore %v1 %22
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
-OpName %foo_vf4_vf4_ "foo(vf4;vf4;"
+OpName %foo_vf4_vf4_ "foo(vf4;vf4;"
OpName %v1 "v1"
OpName %v2 "v2"
OpName %t "t"
-OpName %gl_FragColor "gl_FragColor"
+OpName %gl_FragColor "gl_FragColor"
OpName %dead "dead"
OpName %BaseColor "BaseColor"
-OpName %param "param"
+OpName %param "param"
OpName %param_0 "param"
%void = OpTypeVoid
%13 = OpTypeFunction %void
%v2 = OpFunctionParameter %_ptr_Function_v4float
%24 = OpLabel
%t = OpVariable %_ptr_Function_v4float Function
-%25 = OpLoad %v4float %v1
+%25 = OpLoad %v4float %v1
%26 = OpFNegate %v4float %25
-OpStore %t %26
-%27 = OpLoad %v4float %v2
+OpStore %t %26
+%27 = OpLoad %v4float %v2
OpReturnValue %27
OpFunctionEnd
)";
const std::string func_before =
R"(%main = OpFunction %void None %9
%25 = OpLabel
-%s0 = OpVariable %_ptr_Function_S_t Function
+%s0 = OpVariable %_ptr_Function_S_t Function
%26 = OpLoad %v2float %texCoords
-%27 = OpLoad %S_t %s0
+%27 = OpLoad %S_t %s0
%28 = OpCompositeInsert %S_t %26 %27 0
%29 = OpLoad %15 %sampler15
%30 = OpCompositeInsert %S_t %29 %28 2
%d = OpVariable %_ptr_Function_float Function
%23 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0
%24 = OpLoad %float %23
-%25 = OpFOrdEqual %bool %24 %float_0
+%25 = OpFOrdEqual %bool %24 %float_0
OpSelectionMerge %26 None
OpBranchConditional %25 %27 %28
%27 = OpLabel
%d = OpVariable %_ptr_Function_float Function
%22 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0
%23 = OpLoad %float %22
-%24 = OpFOrdEqual %bool %23 %float_0
+%24 = OpFOrdEqual %bool %23 %float_0
OpSelectionMerge %25 None
OpBranchConditional %24 %26 %25
%26 = OpLabel
%d = OpVariable %_ptr_Function_float Function
%25 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0
%26 = OpLoad %float %25
-%27 = OpFOrdEqual %bool %26 %float_0
+%27 = OpFOrdEqual %bool %26 %float_0
OpSelectionMerge %28 None
OpBranchConditional %27 %29 %30
%29 = OpLabel
%31 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1
%32 = OpLoad %float %31
-%33 = OpFOrdEqual %bool %32 %float_0
+%33 = OpFOrdEqual %bool %32 %float_0
OpSelectionMerge %34 None
OpBranchConditional %33 %35 %36
%35 = OpLabel
OpStore %d %float_0
OpBranch %34
%36 = OpLabel
-OpStore %d %float_0_25
+OpStore %d %float_0_25
OpBranch %34
%34 = OpLabel
OpBranch %28
%30 = OpLabel
%37 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1
%38 = OpLoad %float %37
-%39 = OpFOrdEqual %bool %38 %float_0
+%39 = OpFOrdEqual %bool %38 %float_0
OpSelectionMerge %40 None
OpBranchConditional %39 %41 %42
%41 = OpLabel
OpStore %d %float_0_5
OpBranch %40
%42 = OpLabel
-OpStore %d %float_0_75
+OpStore %d %float_0_75
OpBranch %40
%40 = OpLabel
OpBranch %28
TEST_F(AggressiveDCETest, NoEliminateIfBreak) {
// Note: Assembly optimized from GLSL
//
- // #version 450
- //
+ // #version 450
+ //
// layout(location=0) in vec4 InColor;
// layout(location=0) out vec4 OutColor;
- //
+ //
// void main()
// {
// float f = 0.0;
// if (f > 20.0)
// break;
// }
- //
+ //
// OutColor = InColor / f;
// }
SinglePassRunAndCheck<opt::AggressiveDCEPass>(assembly, assembly, true, true);
}
-TEST_F(AggressiveDCETest, EliminateEntireFunctionBody) {
- // #version 450
+TEST_F(AggressiveDCETest, EliminateEntireUselessLoop) {
+ // #version 140
+ // in vec4 BaseColor;
//
- // layout(location = 0) in vec4 BaseColor;
- // layout(location = 0) out vec4 OutColor;
+ // layout(std140) uniform U_t
+ // {
+ // int g_I ;
+ // } ;
//
// void main()
// {
- // float d;
- // if (BaseColor.x == 0)
- // d = BaseColor.y;
- // else
- // d = BaseColor.z;
+ // vec4 v = BaseColor;
+ // float df = 0.0;
+ // int i = 0;
+ // while (i < g_I) {
+ // df = df * 0.5;
+ // i = i + 1;
+ // }
+ // gl_FragColor = v;
// }
- const std::string predefs_before =
+ const std::string predefs1 =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %OutColor
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
+OpSource GLSL 140
+)";
+
+ const std::string names_before =
+ R"(OpName %main "main"
+OpName %v "v"
OpName %BaseColor "BaseColor"
-OpName %d "d"
-OpName %OutColor "OutColor"
-OpDecorate %BaseColor Location 0
-OpDecorate %OutColor Location 0
+OpName %df "df"
+OpName %i "i"
+OpName %U_t "U_t"
+OpMemberName %U_t 0 "g_I"
+OpName %_ ""
+OpName %gl_FragColor "gl_FragColor"
+)";
+
+ const std::string names_after =
+ R"(OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %U_t "U_t"
+OpMemberName %U_t 0 "g_I"
+OpName %_ ""
+OpName %gl_FragColor "gl_FragColor"
+)";
+
+ const std::string predefs2 =
+ R"(OpMemberDecorate %U_t 0 Offset 0
+OpDecorate %U_t Block
+OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
-%7 = OpTypeFunction %void
+%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Function_float = OpTypePointer Function %float
%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%U_t = OpTypeStruct %int
+%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
+%_ = OpVariable %_ptr_Uniform_U_t Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
%bool = OpTypeBool
-%_ptr_Function_float = OpTypePointer Function %float
-%uint_1 = OpConstant %uint 1
-%uint_2 = OpConstant %uint 2
+%float_0_5 = OpConstant %float 0.5
+%int_1 = OpConstant %int 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
- const std::string predefs_after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
+ const std::string func_before =
+ R"(%main = OpFunction %void None %11
+%27 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%df = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+%28 = OpLoad %v4float %BaseColor
+OpStore %v %28
+OpStore %df %float_0
+OpStore %i %int_0
+OpBranch %29
+%29 = OpLabel
+OpLoopMerge %30 %31 None
+OpBranch %32
+%32 = OpLabel
+%33 = OpLoad %int %i
+%34 = OpAccessChain %_ptr_Uniform_int %_ %int_0
+%35 = OpLoad %int %34
+%36 = OpSLessThan %bool %33 %35
+OpBranchConditional %36 %37 %30
+%37 = OpLabel
+%38 = OpLoad %float %df
+%39 = OpFMul %float %38 %float_0_5
+OpStore %df %39
+%40 = OpLoad %int %i
+%41 = OpIAdd %int %40 %int_1
+OpStore %i %41
+OpBranch %31
+%31 = OpLabel
+OpBranch %29
+%30 = OpLabel
+%42 = OpLoad %v4float %v
+OpStore %gl_FragColor %42
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string func_after =
+ R"(%main = OpFunction %void None %11
+%27 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%28 = OpLoad %v4float %BaseColor
+OpStore %v %28
+OpBranch %29
+%29 = OpLabel
+OpBranch %30
+%30 = OpLabel
+%42 = OpLoad %v4float %v
+OpStore %gl_FragColor %42
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::AggressiveDCEPass>(
+ predefs1 + names_before + predefs2 + func_before,
+ predefs1 + names_after + predefs2 + func_after, true, true);
+}
+
+TEST_F(AggressiveDCETest, NoEliminateBusyLoop) {
+ // Note: SPIR-V edited to replace AtomicAdd(i,0) with AtomicLoad(i)
+ //
+ // #version 450
+ //
+ // layout(std430) buffer I_t
+ // {
+ // int g_I;
+ // int g_I2;
+ // };
+ //
+ // layout(location = 0) out int o;
+ //
+ // void main(void)
+ // {
+ // while (atomicAdd(g_I, 0) == 0) {}
+ // o = g_I2;
+ // }
+
+ const std::string assembly =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %o
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %I_t "I_t"
+OpMemberName %I_t 0 "g_I"
+OpMemberName %I_t 1 "g_I2"
+OpName %_ ""
+OpName %o "o"
+OpMemberDecorate %I_t 0 Offset 0
+OpMemberDecorate %I_t 1 Offset 4
+OpDecorate %I_t BufferBlock
+OpDecorate %_ DescriptorSet 0
+OpDecorate %o Location 0
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%I_t = OpTypeStruct %int %int
+%_ptr_Uniform_I_t = OpTypePointer Uniform %I_t
+%_ = OpVariable %_ptr_Uniform_I_t Uniform
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%uint_0 = OpConstant %uint 0
+%bool = OpTypeBool
+%_ptr_Output_int = OpTypePointer Output %int
+%o = OpVariable %_ptr_Output_int Output
+%main = OpFunction %void None %7
+%18 = OpLabel
+OpBranch %19
+%19 = OpLabel
+OpLoopMerge %20 %21 None
+OpBranch %22
+%22 = OpLabel
+%23 = OpAccessChain %_ptr_Uniform_int %_ %int_0
+%24 = OpAtomicLoad %int %23 %uint_1 %uint_0
+%25 = OpIEqual %bool %24 %int_0
+OpBranchConditional %25 %26 %20
+%26 = OpLabel
+OpBranch %21
+%21 = OpLabel
+OpBranch %19
+%20 = OpLabel
+%27 = OpAccessChain %_ptr_Uniform_int %_ %int_1
+%28 = OpLoad %int %27
+OpStore %o %28
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::AggressiveDCEPass>(assembly, assembly, true, true);
+}
+
+TEST_F(AggressiveDCETest, NoEliminateLiveLoop) {
+ // Note: SPIR-V optimized
+ //
+ // #version 430
+ //
+ // layout(std430) buffer U_t
+ // {
+ // float g_F[10];
+ // };
+ //
+ // layout(location = 0)out float o;
+ //
+ // void main(void)
+ // {
+ // float s = 0.0;
+ // for (int i=0; i<10; i++)
+ // s += g_F[i];
+ // o = s;
+ // }
+
+ const std::string assembly =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %o
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 430
+OpName %main "main"
+OpName %U_t "U_t"
+OpMemberName %U_t 0 "g_F"
+OpName %_ ""
+OpName %o "o"
+OpDecorate %_arr_float_uint_10 ArrayStride 4
+OpMemberDecorate %U_t 0 Offset 0
+OpDecorate %U_t BufferBlock
+OpDecorate %_ DescriptorSet 0
+OpDecorate %o Location 0
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_10 = OpConstant %int 10
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_10 = OpConstant %uint 10
+%_arr_float_uint_10 = OpTypeArray %float %uint_10
+%U_t = OpTypeStruct %_arr_float_uint_10
+%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
+%_ = OpVariable %_ptr_Uniform_U_t Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%o = OpVariable %_ptr_Output_float Output
+%main = OpFunction %void None %8
+%23 = OpLabel
+OpBranch %24
+%24 = OpLabel
+%25 = OpPhi %float %float_0 %23 %26 %27
+%28 = OpPhi %int %int_0 %23 %29 %27
+OpLoopMerge %30 %27 None
+OpBranch %31
+%31 = OpLabel
+%32 = OpSLessThan %bool %28 %int_10
+OpBranchConditional %32 %33 %30
+%33 = OpLabel
+%34 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %28
+%35 = OpLoad %float %34
+%26 = OpFAdd %float %25 %35
+OpBranch %27
+%27 = OpLabel
+%29 = OpIAdd %int %28 %int_1
+OpBranch %24
+%30 = OpLabel
+OpStore %o %25
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::AggressiveDCEPass>(assembly, assembly, true, true);
+}
+
+TEST_F(AggressiveDCETest, EliminateEntireFunctionBody) {
+ // #version 450
+ //
+ // layout(location = 0) in vec4 BaseColor;
+ // layout(location = 0) out vec4 OutColor;
+ //
+ // void main()
+ // {
+ // float d;
+ // if (BaseColor.x == 0)
+ // d = BaseColor.y;
+ // else
+ // d = BaseColor.z;
+ // }
+
+ const std::string predefs_before =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %d "d"
+OpName %OutColor "OutColor"
+OpDecorate %BaseColor Location 0
+OpDecorate %OutColor Location 0
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Input_float = OpTypePointer Input %float
+%float_0 = OpConstant %float 0
+%bool = OpTypeBool
+%_ptr_Function_float = OpTypePointer Function %float
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+)";
+
+ const std::string predefs_after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %OutColor
OpExecutionMode %main OriginUpperLeft
%d = OpVariable %_ptr_Function_float Function
%21 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0
%22 = OpLoad %float %21
-%23 = OpFOrdEqual %bool %22 %float_0
+%23 = OpFOrdEqual %bool %22 %float_0
OpSelectionMerge %24 None
OpBranchConditional %23 %25 %26
%25 = OpLabel
predefs_before + func_before, predefs_after + func_after, true, true);
}
+TEST_F(AggressiveDCETest, EliminateUselessInnerLoop) {
+ // #version 430
+ //
+ // layout(std430) buffer U_t
+ // {
+ // float g_F[10];
+ // };
+ //
+ // layout(location = 0)out float o;
+ //
+ // void main(void)
+ // {
+ // float s = 0.0;
+ // for (int i=0; i<10; i++) {
+ // for (int j=0; j<10; j++) {
+ // }
+ // s += g_F[i];
+ // }
+ // o = s;
+ // }
+
+ const std::string predefs_before =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %o
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 430
+OpName %main "main"
+OpName %s "s"
+OpName %i "i"
+OpName %j "j"
+OpName %U_t "U_t"
+OpMemberName %U_t 0 "g_F"
+OpName %_ ""
+OpName %o "o"
+OpDecorate %_arr_float_uint_10 ArrayStride 4
+OpMemberDecorate %U_t 0 Offset 0
+OpDecorate %U_t BufferBlock
+OpDecorate %_ DescriptorSet 0
+OpDecorate %o Location 0
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_10 = OpConstant %int 10
+%bool = OpTypeBool
+%int_1 = OpConstant %int 1
+%uint = OpTypeInt 32 0
+%uint_10 = OpConstant %uint 10
+%_arr_float_uint_10 = OpTypeArray %float %uint_10
+%U_t = OpTypeStruct %_arr_float_uint_10
+%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
+%_ = OpVariable %_ptr_Uniform_U_t Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%_ptr_Output_float = OpTypePointer Output %float
+%o = OpVariable %_ptr_Output_float Output
+)";
+
+ const std::string predefs_after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %o
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 430
+OpName %main "main"
+OpName %s "s"
+OpName %i "i"
+OpName %U_t "U_t"
+OpMemberName %U_t 0 "g_F"
+OpName %_ ""
+OpName %o "o"
+OpDecorate %_arr_float_uint_10 ArrayStride 4
+OpMemberDecorate %U_t 0 Offset 0
+OpDecorate %U_t BufferBlock
+OpDecorate %_ DescriptorSet 0
+OpDecorate %o Location 0
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_10 = OpConstant %int 10
+%bool = OpTypeBool
+%int_1 = OpConstant %int 1
+%uint = OpTypeInt 32 0
+%uint_10 = OpConstant %uint 10
+%_arr_float_uint_10 = OpTypeArray %float %uint_10
+%U_t = OpTypeStruct %_arr_float_uint_10
+%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
+%_ = OpVariable %_ptr_Uniform_U_t Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%_ptr_Output_float = OpTypePointer Output %float
+%o = OpVariable %_ptr_Output_float Output
+)";
+
+ const std::string func_before =
+ R"(%main = OpFunction %void None %11
+%26 = OpLabel
+%s = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+%j = OpVariable %_ptr_Function_int Function
+OpStore %s %float_0
+OpStore %i %int_0
+OpBranch %27
+%27 = OpLabel
+OpLoopMerge %28 %29 None
+OpBranch %30
+%30 = OpLabel
+%31 = OpLoad %int %i
+%32 = OpSLessThan %bool %31 %int_10
+OpBranchConditional %32 %33 %28
+%33 = OpLabel
+OpStore %j %int_0
+OpBranch %34
+%34 = OpLabel
+OpLoopMerge %35 %36 None
+OpBranch %37
+%37 = OpLabel
+%38 = OpLoad %int %j
+%39 = OpSLessThan %bool %38 %int_10
+OpBranchConditional %39 %40 %35
+%40 = OpLabel
+OpBranch %36
+%36 = OpLabel
+%41 = OpLoad %int %j
+%42 = OpIAdd %int %41 %int_1
+OpStore %j %42
+OpBranch %34
+%35 = OpLabel
+%43 = OpLoad %int %i
+%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %43
+%45 = OpLoad %float %44
+%46 = OpLoad %float %s
+%47 = OpFAdd %float %46 %45
+OpStore %s %47
+OpBranch %29
+%29 = OpLabel
+%48 = OpLoad %int %i
+%49 = OpIAdd %int %48 %int_1
+OpStore %i %49
+OpBranch %27
+%28 = OpLabel
+%50 = OpLoad %float %s
+OpStore %o %50
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string func_after =
+ R"(%main = OpFunction %void None %11
+%26 = OpLabel
+%s = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %s %float_0
+OpStore %i %int_0
+OpBranch %27
+%27 = OpLabel
+OpLoopMerge %28 %29 None
+OpBranch %30
+%30 = OpLabel
+%31 = OpLoad %int %i
+%32 = OpSLessThan %bool %31 %int_10
+OpBranchConditional %32 %33 %28
+%33 = OpLabel
+OpBranch %34
+%34 = OpLabel
+OpBranch %35
+%35 = OpLabel
+%43 = OpLoad %int %i
+%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %43
+%45 = OpLoad %float %44
+%46 = OpLoad %float %s
+%47 = OpFAdd %float %46 %45
+OpStore %s %47
+OpBranch %29
+%29 = OpLabel
+%48 = OpLoad %int %i
+%49 = OpIAdd %int %48 %int_1
+OpStore %i %49
+OpBranch %27
+%28 = OpLabel
+%50 = OpLoad %float %s
+OpStore %o %50
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::AggressiveDCEPass>(
+ predefs_before + func_before, predefs_after + func_after, true, true);
+}
+
+TEST_F(AggressiveDCETest, EliminateUselessNestedLoopWithIf) {
+ // #version 430
+ //
+ // layout(std430) buffer U_t
+ // {
+ // float g_F[10][10];
+ // };
+ //
+ // layout(location = 0)out float o;
+ //
+ // void main(void)
+ // {
+ // float s = 0.0;
+ // for (int i=0; i<10; i++) {
+ // for (int j=0; j<10; j++) {
+ // float t = g_F[i][j];
+ // if (t > 0.0)
+ // s += t;
+ // }
+ // }
+ // o = 0.0;
+ // }
+
+ const std::string predefs_before =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %o
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 430
+OpName %main "main"
+OpName %s "s"
+OpName %i "i"
+OpName %j "j"
+OpName %U_t "U_t"
+OpMemberName %U_t 0 "g_F"
+OpName %_ ""
+OpName %o "o"
+OpDecorate %_arr_float_uint_10 ArrayStride 4
+OpDecorate %_arr__arr_float_uint_10_uint_10 ArrayStride 40
+OpMemberDecorate %U_t 0 Offset 0
+OpDecorate %U_t BufferBlock
+OpDecorate %_ DescriptorSet 0
+OpDecorate %o Location 0
+%void = OpTypeVoid
+%12 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_10 = OpConstant %int 10
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_10 = OpConstant %uint 10
+%_arr_float_uint_10 = OpTypeArray %float %uint_10
+%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
+%U_t = OpTypeStruct %_arr__arr_float_uint_10_uint_10
+%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
+%_ = OpVariable %_ptr_Uniform_U_t Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%o = OpVariable %_ptr_Output_float Output
+)";
+
+ const std::string predefs_after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %o
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 430
+OpName %main "main"
+OpName %U_t "U_t"
+OpMemberName %U_t 0 "g_F"
+OpName %_ ""
+OpName %o "o"
+OpDecorate %_arr_float_uint_10 ArrayStride 4
+OpDecorate %_arr__arr_float_uint_10_uint_10 ArrayStride 40
+OpMemberDecorate %U_t 0 Offset 0
+OpDecorate %U_t BufferBlock
+OpDecorate %_ DescriptorSet 0
+OpDecorate %o Location 0
+%void = OpTypeVoid
+%12 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_10 = OpConstant %int 10
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_10 = OpConstant %uint 10
+%_arr_float_uint_10 = OpTypeArray %float %uint_10
+%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
+%U_t = OpTypeStruct %_arr__arr_float_uint_10_uint_10
+%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
+%_ = OpVariable %_ptr_Uniform_U_t Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%o = OpVariable %_ptr_Output_float Output
+)";
+
+ const std::string func_before =
+ R"(%main = OpFunction %void None %12
+%27 = OpLabel
+%s = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+%j = OpVariable %_ptr_Function_int Function
+OpStore %s %float_0
+OpStore %i %int_0
+OpBranch %28
+%28 = OpLabel
+OpLoopMerge %29 %30 None
+OpBranch %31
+%31 = OpLabel
+%32 = OpLoad %int %i
+%33 = OpSLessThan %bool %32 %int_10
+OpBranchConditional %33 %34 %29
+%34 = OpLabel
+OpStore %j %int_0
+OpBranch %35
+%35 = OpLabel
+OpLoopMerge %36 %37 None
+OpBranch %38
+%38 = OpLabel
+%39 = OpLoad %int %j
+%40 = OpSLessThan %bool %39 %int_10
+OpBranchConditional %40 %41 %36
+%41 = OpLabel
+%42 = OpLoad %int %i
+%43 = OpLoad %int %j
+%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %42 %43
+%45 = OpLoad %float %44
+%46 = OpFOrdGreaterThan %bool %45 %float_0
+OpSelectionMerge %47 None
+OpBranchConditional %46 %48 %47
+%48 = OpLabel
+%49 = OpLoad %float %s
+%50 = OpFAdd %float %49 %45
+OpStore %s %50
+OpBranch %47
+%47 = OpLabel
+OpBranch %37
+%37 = OpLabel
+%51 = OpLoad %int %j
+%52 = OpIAdd %int %51 %int_1
+OpStore %j %52
+OpBranch %35
+%36 = OpLabel
+OpBranch %30
+%30 = OpLabel
+%53 = OpLoad %int %i
+%54 = OpIAdd %int %53 %int_1
+OpStore %i %54
+OpBranch %28
+%29 = OpLabel
+OpStore %o %float_0
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string func_after =
+ R"(%main = OpFunction %void None %12
+%27 = OpLabel
+OpBranch %28
+%28 = OpLabel
+OpBranch %29
+%29 = OpLabel
+OpStore %o %float_0
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::AggressiveDCEPass>(
+ predefs_before + func_before, predefs_after + func_after, true, true);
+}
+
+TEST_F(AggressiveDCETest, NoEliminateLiveNestedLoopWithIf) {
+ // Note: SPIR-V optimized
+ //
+ // #version 430
+ //
+ // layout(std430) buffer U_t
+ // {
+ // float g_F[10][10];
+ // };
+ //
+ // layout(location = 0)out float o;
+ //
+ // void main(void)
+ // {
+ // float s = 0.0;
+ // for (int i=0; i<10; i++) {
+ // for (int j=0; j<10; j++) {
+ // float t = g_F[i][j];
+ // if (t > 0.0)
+ // s += t;
+ // }
+ // }
+ // o = s;
+ // }
+
+ const std::string assembly =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %o
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 430
+OpName %main "main"
+OpName %s "s"
+OpName %i "i"
+OpName %j "j"
+OpName %U_t "U_t"
+OpMemberName %U_t 0 "g_F"
+OpName %_ ""
+OpName %o "o"
+OpDecorate %_arr_float_uint_10 ArrayStride 4
+OpDecorate %_arr__arr_float_uint_10_uint_10 ArrayStride 40
+OpMemberDecorate %U_t 0 Offset 0
+OpDecorate %U_t BufferBlock
+OpDecorate %_ DescriptorSet 0
+OpDecorate %o Location 0
+%void = OpTypeVoid
+%12 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_10 = OpConstant %int 10
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_10 = OpConstant %uint 10
+%_arr_float_uint_10 = OpTypeArray %float %uint_10
+%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
+%U_t = OpTypeStruct %_arr__arr_float_uint_10_uint_10
+%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
+%_ = OpVariable %_ptr_Uniform_U_t Uniform
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%o = OpVariable %_ptr_Output_float Output
+%main = OpFunction %void None %12
+%27 = OpLabel
+%s = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+%j = OpVariable %_ptr_Function_int Function
+OpStore %s %float_0
+OpStore %i %int_0
+OpBranch %28
+%28 = OpLabel
+OpLoopMerge %29 %30 None
+OpBranch %31
+%31 = OpLabel
+%32 = OpLoad %int %i
+%33 = OpSLessThan %bool %32 %int_10
+OpBranchConditional %33 %34 %29
+%34 = OpLabel
+OpStore %j %int_0
+OpBranch %35
+%35 = OpLabel
+OpLoopMerge %36 %37 None
+OpBranch %38
+%38 = OpLabel
+%39 = OpLoad %int %j
+%40 = OpSLessThan %bool %39 %int_10
+OpBranchConditional %40 %41 %36
+%41 = OpLabel
+%42 = OpLoad %int %i
+%43 = OpLoad %int %j
+%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %42 %43
+%45 = OpLoad %float %44
+%46 = OpFOrdGreaterThan %bool %45 %float_0
+OpSelectionMerge %47 None
+OpBranchConditional %46 %48 %47
+%48 = OpLabel
+%49 = OpLoad %float %s
+%50 = OpFAdd %float %49 %45
+OpStore %s %50
+OpBranch %47
+%47 = OpLabel
+OpBranch %37
+%37 = OpLabel
+%51 = OpLoad %int %j
+%52 = OpIAdd %int %51 %int_1
+OpStore %j %52
+OpBranch %35
+%36 = OpLabel
+OpBranch %30
+%30 = OpLabel
+%53 = OpLoad %int %i
+%54 = OpIAdd %int %53 %int_1
+OpStore %i %54
+OpBranch %28
+%29 = OpLabel
+%55 = OpLoad %float %s
+OpStore %o %55
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::AggressiveDCEPass>(assembly, assembly, true, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Check that logical addressing required