blocks_.push(dest_bb);
}
-void SSAPropagator::AddSSAEdges(uint32_t id) {
- get_def_use_mgr()->ForEachUser(id, [this](ir::Instruction* instr) {
- // If the basic block for |instr| has not been simulated yet, do nothing.
- if (!BlockHasBeenSimulated(ctx_->get_instr_block(instr))) {
- return;
- }
+void SSAPropagator::AddSSAEdges(ir::Instruction* instr) {
+ if (instr->result_id() == 0 || instr->opcode() == SpvOpPhi) {
+ // Ignore instructions that produce no result and Phi instructions. The SSA
+ // edges out of Phi instructions are never added. Phi instructions can
+ // produce cycles in the def-use web and they are always simulated when a
+ // block is visited.
+ return;
+ }
- if (ShouldSimulateAgain(instr)) {
- ssa_edge_uses_.push(instr);
- }
- });
+ get_def_use_mgr()->ForEachUser(
+ instr->result_id(), [this](ir::Instruction* use_instr) {
+ // If the basic block for |use_instr| has not been simulated yet, do
+ // nothing.
+ if (!BlockHasBeenSimulated(ctx_->get_instr_block(use_instr))) {
+ return;
+ }
+
+ if (ShouldSimulateAgain(use_instr)) {
+ ssa_edge_uses_.push(use_instr);
+ }
+ });
}
bool SSAPropagator::IsPhiArgExecutable(ir::Instruction* phi, uint32_t i) const {
// The statement produces a varying result, add it to the list of statements
// not to simulate anymore and add its SSA def-use edges for simulation.
DontSimulateAgain(instr);
- if (instr->result_id() > 0) {
- AddSSAEdges(instr->result_id());
- }
+ AddSSAEdges(instr);
// If |instr| is a block terminator, add all the control edges out of its
// block.
}
return false;
} else if (status == kInteresting) {
- // If the instruction produced a new interesting value, add the SSA edge
- // for its result ID.
- if (instr->result_id() > 0) {
- AddSSAEdges(instr->result_id());
- }
+ // Add the SSA edges coming out of this instruction.
+ AddSSAEdges(instr);
// If there are multiple outgoing control flow edges and we know which one
// will be taken, add the destination block to the CFG work list.
return ctx_->get_def_use_mgr();
}
- // If the CFG edge |e| has not been executed, add its destination block to the
- // work list.
+ // If the CFG edge |e| has not been executed, this function adds |e|'s
+ // destination block to the work list.
void AddControlEdge(const Edge& e);
- // Add all the instructions that use |id| to the SSA edges work list.
- void AddSSAEdges(uint32_t id);
+ // Adds all the instructions that use the result of |instr| to the SSA edges
+ // work list. If |instr| produces no result id, this does nothing. This also
+ // does nothing if |instr| is a Phi instruction. Phi instructions are treated
+ // specially because (a) they can be in def-use cycles with other
+ // instructions, and (b) they are always executed when a basic block is
+ // simulated (see the description of the Sparse Conditional Constant algorithm
+ // in the original paper).
+ void AddSSAEdges(ir::Instruction* instr);
// IR context to use.
ir::IRContext* ctx_;
SinglePassRunAndMatch<opt::CCPPass>(spv_asm, true);
}
+TEST_F(CCPTest, SSAWebCycles) {
+ // Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1159
+ // When there is a cycle in the SSA def-use web, the propagator was getting
+ // into an infinite loop. SSA edges for Phi instructions should not be
+ // added to the edges to simulate.
+ const std::string spv_asm = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %int_0 = OpConstant %int 0
+ %int_4 = OpConstant %int 4
+ %bool = OpTypeBool
+ %int_1 = OpConstant %int 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ %29 = OpPhi %int %int_0 %5 %22 %14
+ %30 = OpPhi %int %int_0 %5 %25 %14
+ OpLoopMerge %13 %14 None
+ OpBranch %15
+ %15 = OpLabel
+ %19 = OpSLessThan %bool %30 %int_4
+; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}}
+ OpBranchConditional %19 %12 %13
+ %12 = OpLabel
+; CHECK: OpIAdd %int %int_0 %int_0
+ %22 = OpIAdd %int %29 %30
+ OpBranch %14
+ %14 = OpLabel
+; CHECK: OpPhi %int %int_0 {{%\d+}} %int_0 {{%\d+}}
+ %25 = OpPhi %int %int_0 %5 %30 %14
+ OpBranch %11
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<opt::CCPPass>(spv_asm, true);
+}
#endif
} // namespace