Skip to content

Generic value propagation engine.#985

Merged
dnovillo merged 1 commit intoKhronosGroup:masterfrom
dnovillo:const-prop
Nov 28, 2017
Merged

Generic value propagation engine.#985
dnovillo merged 1 commit intoKhronosGroup:masterfrom
dnovillo:const-prop

Conversation

@dnovillo
Copy link
Copy Markdown
Contributor

NOTE: I am adding tests and debugging at the moment. I'm sending this initial version for review and discussion. This will be used as the basis for SSA-CCP, copy propagation and range propagation (if we decide to add it).

This class implements a generic value propagation algorithm based on the
conditional constant propagation algorithm proposed in

 Constant propagation with conditional branches,
 Wegman and Zadeck, ACM TOPLAS 13(2):181-210.

The implementation is based on

 A Propagation Engine for GCC
 Diego Novillo, GCC Summit 2005
 http://ols.fedoraproject.org/GCC/Reprints-2005/novillo-Reprint.pdf

The purpose of this implementation is to act as a common framework for any
transformation that needs to propagate values from statements producing new
values to statements using those values.

@dnovillo dnovillo self-assigned this Nov 20, 2017
#ifndef LIBSPIRV_OPT_INSTRUCTION_H_
#define LIBSPIRV_OPT_INSTRUCTION_H_

#include <opcode.h>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


inline void Instruction::AddOperand(Operand &&operand) {
inline void Instruction::AddOperand(Operand&& operand) {
operands_.push_back(operand);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably was supposed to be
operands_.push_back(std::move(operand));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


template <typename ValueType>
void SSAPropagator<ValueType>::AddControlEdge(const Edge& edge) {
const ir::BasicBlock *dest_bb = edge.dst;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const ir::BasicBlock*

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

namespace opt {

// Represents a CFG control edge. TODO(dnovillo): Move it to ir::CFG and always
// build it. Alternately, move it to IRContext and build it on-demand.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TODO is not clear what is being built. This is just a CFG edge.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the spot we actually build preds/succs.

// Hashing operator for Edge. Needed to instantiate unordered sets of Edges.
namespace std {
template <>
struct std::hash<spvtools::opt::Edge> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are already in the namespace.
std::hash -> hash

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Collaborator

@s-perron s-perron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of changes, but mostly in the comments. You seem to be mixing terminology from the paper with that of the specific implementation, which gets confusing at times.

explicit Edge(const ir::BasicBlock* b1, const ir::BasicBlock* b2)
: src(b1), dst(b2) {}
const ir::BasicBlock* src;
const ir::BasicBlock* dst;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For classes (or variables) that will be used in many places, are abbreviations like this a good idea? The style guide says not to abbreviate: https://google.github.io/styleguide/cppguide.html#Naming

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// Hashing operator for Edge. Needed to instantiate unordered sets of Edges.
namespace std {
template <>
struct std::hash<spvtools::opt::Edge> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://google.github.io/styleguide/cppguide.html#std_hash

Don't define specializations of std::hash.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've taken the easy way out and I'm using std::set for edges.

// at compile time. Further simulation of I is not required.
// If I is a conditional jump, all the outgoing edges for the
// block are considered executable and added to the work
// list.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not clear who is responsible for adding to the work list. This seems like |visit_fn| is doing this because you are describing the results of |vistit_fn| right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. The simulate_fn callback simply needs to return the indication that the instruction was interesting for the propagator to add SSA edges out of it. The actual tracking of values needs to be done by the simulate_fn itself. I've added this to the documentation.

// the instructions that feed from I. Furthermore, if I is a
// conditional jump, only the edge known to be taken is added
// to the work list. Edges that are known not to execute are
// never simulated.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified and added sample code.

// Blocks are added to this queue if their incoming edges are
// executable.
//
// Edges: contains the list of statements that we need to revisit.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This naming seem odd. "Edges" is a "list of statements"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. This was referring to SSA edges (def-use edges).

// Function that visit instructions during simulation. The output of this
// function is used to determine if the simulated instruction produced a value
// interesting for propagation. The actual value produced by the instruction
// must be stored in the |values_| map.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention "SetValue" at the method of storing the value?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed anymore. This was the only thing that was forcing the whole class to be a template. It's unnecessary, so I moved this as a requirement for the user-provided callback.


// SSA def-use edges to traverse. Each entry is a destination statement for an
// SSA def-use edge as returned by |def_use_manager_|.
std::queue<ir::Instruction*> ssa_edges_;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can a single instruction be an edge? I find this very confusing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to ssa_edge_uses_

AddSSAEdges(instr->result_id());
}

/* If STMT transfers control out of its basic block, add
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

STMT?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


ir::BasicBlock* dest_bb = nullptr;
uint32_t result_id = 0;
PropStatus status = visit_fn_(instr, &dest_bb, &result_id);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You pass in the address |result_id|, but |result_id| is not used afterward. Why do you need it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// before. We do this because Phi instructions receive their inputs from
// incoming edges. When those edges are marked executable, the corresponding
// operand can be evaluated.
block->ForEachPhiInst([this](ir::Instruction* instr) { Simulate(instr); });
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this is necessary. Won't the phi nodes get added to "sss_edges_" when its operands are simulated. So the phi nodes will get processed anyway if they need to. Is that right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phi nodes get operands enabled not only by SSA edges, a Phi operand becomes available also when the incoming CFG edge is marked executable. That's why this code checks which operands have an executable predecessor.

// Returns true if the instruction annotates an id with a decoration.
inline bool IsDecoration();

// Returns true if this instruction is a branch instruction (either
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention switch support.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// operand can be evaluated.
block->ForEachPhiInst([this](ir::Instruction* instr) { Simulate(instr); });

// If this is the frist time this block is being simulated, evaluate every
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frist -> first

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


using VisitFunction = std::function<PropStatus(ir::Instruction*)>;

SSAPropagator(ir::IRContext* context, ir::CFG* cfg, VisitFunction& visit_fn)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const VisitFunction&

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

std::unordered_set<ir::Instruction*> do_not_simulate_;

// Map between a basic block and its predecessor edges.
std::unordered_map<ir::BasicBlock*, std::list<Edge>> bb_preds_;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please repeat the todo about moving these into CFG (which is to be moved into IRContext).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Contributor

@alan-baker alan-baker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small changes.

#include <functional>
#include <unordered_map>
#include <unordered_set>
#include <queue>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please list headers in alphabetical order.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


using VisitFunction = std::function<PropStatus(ir::Instruction*)>;

SSAPropagator(ir::IRContext* context, ir::CFG* cfg, VisitFunction& visit_fn)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider passing the function by value, const-reference or rvalue reference. Passing by non-const reference will disallow in-place definition of a lambda.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

void SetValue(uint32_t id, ValueType value) { values_[id] = value; }

// Gets the value for |id|. If |id| has not been assigned a value, it
// throws a runtime assertion.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would raise SIGABRT since we build with -fno-exceptions.

}

// Add the edges out of the entry block to seed the propagator.
std::list<Edge> entry_succs = bb_succs_[fn->entry()];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const std::list&

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


// Add the edges out of the entry block to seed the propagator.
std::list<Edge> entry_succs = bb_succs_[fn->entry()];
for (const auto& e : bb_succs_[fn->entry()]) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for (const auto& e : entry_succs) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

struct std::hash<spvtools::opt::Edge> {
size_t operator()(const spvtools::opt::Edge& e) const {
return std::hash<const spvtools::ir::BasicBlock*>()(e.src) ^
std::hash<const spvtools::ir::BasicBlock*>()(e.dst);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless the hash is supposed to be symmetric I would prefer
hash1 * kConst + hash2.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. Using std::set.

Copy link
Copy Markdown
Contributor Author

@dnovillo dnovillo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I've addressed all the comments and added new tests. I'm adding more tests now, but the code is now ready for more reviewing.

PTAL. Thanks.

public:
// Lattice values used for propagation. See class documentation for
// a description.
enum PropStatus { NotInteresting, Interesting, Varying };
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// Initialize processing.
void Initialize(ir::Function* fn);

// Simulate the execution of |block| by evaluating every instruction in it.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are. Changed to use 'simulate' everywhere.

}

// Returns true if |block| has been simulated already.
bool IsBlockSimulated(ir::BasicBlock* block) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how else to say this. The predicate does precisely that. If every instruction has been simulated, it returns true.

}

// Returns true if |block| has been simulated already.
bool IsBlockSimulated(ir::BasicBlock* block) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be BlockHasBeenSimulated?


// Marks |edge| as executable. Returns false if the edge was already marked
// as executable. Otherwise, the edge is marked executable for the first time
// and it returns true.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

return executable_edges_.find(edge) != executable_edges_.end();
}

// Return a pointer to the def-use manager for this context.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

}

// Add the destination block of the CFG edge |e| to the work list. If |e|
// had already been executed, do nothing.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

void AddControlEdge(const Edge& e);

// Add all the use statements at the end of SSA edges coming out the
// definition for |id|.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paper terminology. Fixed.

// Function that visit instructions during simulation. The output of this
// function is used to determine if the simulated instruction produced a value
// interesting for propagation. The actual value produced by the instruction
// must be stored in the |values_| map.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed anymore. This was the only thing that was forcing the whole class to be a template. It's unnecessary, so I moved this as a requirement for the user-provided callback.

// Returns true if the instruction annotates an id with a decoration.
inline bool IsDecoration();

// Returns true if this instruction is a branch instruction (either
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

ir::BasicBlock* succ_bb =
ctx_->get_instr_block(get_def_use_mgr()->GetDef(label_id));
bb_succs_[&block].push_back(Edge(&block, succ_bb));
bb_preds_[succ_bb].push_back(Edge(succ_bb, &block));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see bb_preds_ being used anywhere. Should bb_preds_ also feature pseudo-blocks?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Added. Predecessors are not really used in the propagator, but I'm thinking of moving this into ir::CFG and I think I may need it in SSA-CCP itself.

@dnovillo
Copy link
Copy Markdown
Contributor Author

@s-perron, @alan-baker with the new tests, this PR is ready for more reviews. PTAL. Thanks.

for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
if (IsLive(&*ii)) continue;
if (IsBranch(ii->opcode()) &&
if (ii->IsBranch() &&
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've extended the functionality of this pass. You should test it with an OpSwitch.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend not changing this right now. Pottery barn rules...

In particular, the lines at 223 and 224 specifically check for OpBranch and OpBranchConditional. So who knows if that also needs OpSwitch to be there too if you change this line.

(Good catch @alan-baker )

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a new test, but it fails because ADCE does not seem to handle OpSwitch at all (#1021)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restored the original test. I had filed #1021 for lack of OpSwitch support. I've added an open-coded test for Branch and BranchConditional to avoid having two flavours of IsBranch().


// Returns true if this basic block exits this function and returns to its
// caller.
bool IsReturn() const { return insts_.back().IsReturn(); }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return tail()->IsReturn(). Fewer restrictions on the underlying implementation of insts_.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


// Returns true if this instruction is a basic block terminator.
bool IsBlockTerminator() const {
return IsBranch() || IsReturn() || opcode() == SpvOpKill ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making this congruent with the other tests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Contributor

@alan-baker alan-baker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most important is to extend the tests for adce.

//
// EXAMPLE: Basic constant store propagator.
//
// Suppose we want to propagate all constant assignments of the form "OpStore
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this is a good example. No propagating seems to be going on, and it is unclear what should happen if there are two store to the same variable with different constants. If I was trying to figure out how my |value_fn| function is suppose to work so I can use the propagator, I would be confused.

Copy link
Copy Markdown
Collaborator

@s-perron s-perron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still unclear about the requirements that the visit function has the meet. For example, what happens if it returns that all branch instructions are not interesting? Looking at the implementation of the propagator, it seems like a whole lot of blocks will not be visited. Is that intended? If so, more explanation is needed. If not it needs to be fixed up.

@dnovillo
Copy link
Copy Markdown
Contributor Author

dnovillo commented Nov 23, 2017 via email

@dnovillo
Copy link
Copy Markdown
Contributor Author

dnovillo commented Nov 23, 2017 via email

@dnovillo
Copy link
Copy Markdown
Contributor Author

@s-perron added more details on the user-provided visit function. I decided against creating a more detailed example in the docs. I prefer to have the tests be that example. The tests have the advantage that they will be updated with any API changes.

Will address the comments from @alan-baker

@s-perron
Copy link
Copy Markdown
Collaborator

s-perron commented Nov 24, 2017

Need to make the build bots happy, but otherwise LGTM.

@dnovillo dnovillo force-pushed the const-prop branch 2 times, most recently from c15be81 to e6d2efd Compare November 27, 2017 22:01
This class implements a generic value propagation algorithm based on the
conditional constant propagation algorithm proposed in

     Constant propagation with conditional branches,
     Wegman and Zadeck, ACM TOPLAS 13(2):181-210.

The implementation is based on

     A Propagation Engine for GCC
     Diego Novillo, GCC Summit 2005
     http://ols.fedoraproject.org/GCC/Reprints-2005/novillo-Reprint.pdf

The purpose of this implementation is to act as a common framework for any
transformation that needs to propagate values from statements producing new
values to statements using those values.
@dnovillo dnovillo merged commit 7432784 into KhronosGroup:master Nov 28, 2017
@dnovillo
Copy link
Copy Markdown
Contributor Author

Re-based, re-tested and pushed to master as 7432784

@dneto0
Copy link
Copy Markdown
Collaborator

dneto0 commented Nov 29, 2017

This MR is proximate cause of Windows debug build failure. PTAL @dnovillo

Copy link
Copy Markdown
Collaborator

@dneto0 dneto0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh. I reviewed this last year but failed to hit "submit review". :-(
Looks like the OpKill issue came up as #1153

for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
if (IsLive(&*ii)) continue;
if (IsBranch(ii->opcode()) &&
if (ii->IsBranch() &&
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend not changing this right now. Pottery barn rules...

In particular, the lines at 223 and 224 specifically check for OpBranch and OpBranchConditional. So who knows if that also needs OpSwitch to be there too if you change this line.

(Good catch @alan-baker )

// this block, if any. If none, returns zero.
uint32_t ContinueBlockIdIfAny() const;

// Returns true if this basic block exits this function and returns to its
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This begs the question: Do you need to do something special for OpKill or OpUnreachable?
(OpKill is weird...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed with #1156 which added IsReturnOrAbort.


// Represents a CFG control edge.
struct Edge {
explicit Edge(ir::BasicBlock* b1, ir::BasicBlock* b2)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does explicit mean anything when the constructor has two parameters?

Also, are you requiring both blocks to be null? If not, then would it be better to make the members references?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use BasicBlock pointers all over the place (particularly when doing things like testing whether a block is one of the pseudos in the CFG). I've added a test for non-null in the constructor.

dnovillo added a commit to dnovillo/SPIRV-Tools that referenced this pull request Jan 4, 2018
Copy link
Copy Markdown
Contributor Author

@dnovillo dnovillo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I've created a new PR to handle them (#1163).

for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
if (IsLive(&*ii)) continue;
if (IsBranch(ii->opcode()) &&
if (ii->IsBranch() &&
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restored the original test. I had filed #1021 for lack of OpSwitch support. I've added an open-coded test for Branch and BranchConditional to avoid having two flavours of IsBranch().

// this block, if any. If none, returns zero.
uint32_t ContinueBlockIdIfAny() const;

// Returns true if this basic block exits this function and returns to its
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed with #1156 which added IsReturnOrAbort.


// Represents a CFG control edge.
struct Edge {
explicit Edge(ir::BasicBlock* b1, ir::BasicBlock* b2)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use BasicBlock pointers all over the place (particularly when doing things like testing whether a block is one of the pseudos in the CFG). I've added a test for non-null in the constructor.

nhaehnle pushed a commit to nhaehnle/llpc that referenced this pull request Apr 7, 2020
Changelist: ce5941a

ce5941a Fixes GPUOpen-Drivers#1357. Support null constants better in folding
bdaf8d5 Opt: Add constant folding for FToI and IToF
9457cab Fixes GPUOpen-Drivers#1354. Do not merge integer division.
588f4fc Add more folding rules for vector shuffle.
90e1637 Remove Function::GetBlocks pushed by accident
2cb589c Remove uses DCEInst and call ADCE
0c13467 Consistently include latest spirv.h header file.
802cf05 Merge arithmetic with non-trivial constant operands
20b8cdb Make IR builder use the type manager for constants
9394272 linker: merge debug annotations from category c)
bdd6617 linker: Allow modules to be partially linked
64298bf tools/linker: Allow setting --verify-ids on the command line
b08b94e Update CHANGES
3497a94 Add loop unswitch pass.
e354984 Unroller support for multiple induction variables
94af58a Clean up variables before sroa
3f19c20 Preserve analysies in the simplification pass
432dc40 Appveyor: remove VS2015 configuration to reduce build time
46a9ec9 Opt: Check for side-effects in DCEInst()
01760d2 Fixes GPUOpen-Drivers#1338. Handle OpConstantNull in branch/switch conditions
51ecc73 Reduce instruction create and deletion during inlining.
c1b9366 Add Insert-extract elimination back into legalization passes.
309be42 Add folding for redundant add/sub/mul/div/mix operations
fa3ac3c Revert "Preserve analysies in the simplification pass"
ec3bbf0 Preserve analysies in the simplification pass
6c75050 Speed up Phi insertion.
ac9ab0d Travis: require MacOS to build and test again
9d95a91 Fix folding insert feeding extract
c3f34d8 Fixes GPUOpen-Drivers#1300. Adding checks for bad CCP transitions and unsettled values
e543b19 Removed warnings from hex_float.h
04cd63e Make better use of simplification pass
1054413 Add constant folding rules for floating-point comparison
27d23a9 Remove constants from constant manager in KillInst
50f307f Simplify OpPhi instructions referencing unreachable continues
3756b38 Get CCP to use the constant floating point rules.
efe286c SubgroupBallotKHR can enable SubgroupSize & SubgroupLocalInvocationId
5d442fa fix typo
d2c0fce Invoke cmake via CMAKE_COMMAND variable
f3a1047 Avoid using static unordered_map (GPUOpen-Drivers#1304)
32a8e04 Add folding of redundant OpSelect insns
0e9f2f9 Add id to name map
6669d81 Fold binary floating point operators.
dd8400e Initial support for loop unrolling.
229ebc0 Fixes GPUOpen-Drivers#1295. Mark undef values as varying in ccp.
0869992 Cleanup.  Use proper #include guard.  NFC.
06b437d Avoid using the def-use manager during inlining.
70bf351 Fix spirv.h include to rely on include paths
1d7b142 Add folding of OpCompositeExtract and OpConstantComposite constant instructions.
8868591 Fix generation of Vim syntax file
4e4a254 Do not hardcode libdir and includedir in pkg config files
1a849ff Add header files missing from CMakeLists.txt
84ccd0b Loop invariant code motion initial implementation
ca4457b SROA: Do replacement on structs with no partial references.
06cdb96 Make use of the instruction folder.
a61e4c1 Disable check which fails Vulkan CTS
2f0c3aa Add Vulkan-specific validation rules for atomics
3013897 Build SPIRV-Tools as shared library
b1c9c4e Enable Visual Studio 2013 again
e7fafda Fix test inclusion when Effcee is absent
c452bfd Update CHANGES
8710227 Registering a type now rebuilds it out of memory owned by the manager.
860b2ee ADCE: Fix combinator initialization
9e19fc0 VS2013: LoopDescriptor LoopContainerType can't contain unique_ptr
12e6860 Add barrier instructions validation pass
3ef4bb6 Avoid vector copies in range-for loops in opt/types.cpp
87f9cfa Disambiguate between const and nonconst ForEachSuccessorLabel
bc1ec94 Add general folding infrastructure.
1c0056c Start v2018.1-dev
c430a41 Finalize v2018.0
bcd4f23 Update CHANGES
abe1132 Reordering performance passes ordering to produce better opts
50e85c8 Add LoopUtils class to gather some loop transformation support.
61d8c03 Add pass to reaplce invalid opcodes
d37869c Added OpenCL ExtInst validation rules
cd68f2b Add adjacency validation pass
905536c Fixed harmless uninit var warning
ac537c7 Use SPIR-V headers from "unified1" directory
2735e08 Remove constexpr from Analysis operators
0aa0ac5 Opt: Add ScalarReplacement to RegisterSizePasses
44d88c8 Add memory semantics checks to validate atomics
38f297c Update CHANGES
1694923 Prevent unnecessary changes to the IR in dead branch elim
c86cb76 Improved error message in val capabilities
e661da7 Enhancements to block merging
6704233 Fix dereference of possibly nullptr
f28b106 InsertExtractElim: Split out DeadInsertElim as separate pass
1b46f7e Fixes in CCP for GPUOpen-Drivers#1228
6018de8 Add LoopDescriptor as an IRContext analysis.
684997e DeadInsertElim: Detect and DCE dead Inserts
2e93e80 Initial implementation of if conversion
b2eb840 Validator: restricted some atomic ops for shaders
bdc7837 Added Vulkan-specifc checks to image validation
c4835e1 Use id_map in Fold*ToConstant
6c409e3 Add generic folding function and use in CCP
3b780db Fixes infinite loop in ADCE
cf3b2a5 Introduce an instruction builder helper class.
73940ab Simplifying code for adding instructions to worklist
34d4294 Create a pass to work around a driver bug related to OpUnreachable.
1861806 Adding testcase for GPUOpen-Drivers#1210
0b1372a CFG: force the creation of a predecessor entry for all basic block.
3604c0b spirv-dis: Add --color option to force color disassembly
5e70d20 Fixing missing early exit from break identification
80b743a Adding support for switch removal in ADCE
3a0eb44 Capturing value table by reference in local redundancy elim
ae6daee Update CHANGES
5ffe862 Fixes missing increment in common uniform elim
6cc772c Skip SpecConstants in CCP.
ba017f7 Update CHANGES
c2aadb0 Add MatrixConstant
8cb0aec Remove redundant passes from legalization passes
6587d3f Adding early exit versions of several ForEach* methods
24f9947 Move initialization of the const mgr to the constructor.
86dec64 Start v2018.0-dev
902ed46 Finalize v2017.3
672494d Adding ostream operators for IR structures
eb0c73d Maintain instruction to block mapping in phi insertion
5eafc00 InsertExtractElim: Optimize through VectorShuffle, Mix
1ebd860 Add generic folding function and use in CCP
3a054e1 Adding additional functionality to ADCE.
d54a286 Fix validation rules for GLSL pack/unpack 2x32
1b6cfd3 Rewriting dead branch elimination.
e5560d6 Fix constant propagation of induction variables.
a82a0ea Fix method comment for BasicBlock::MegeBlockIdIfAny
44f27f9 Allow relaxing validation of pointers in logical addressing mode
e8ad02f Add loop descriptors and some required dominator tree extensions.
6e9ea2e AnalyzeInstUse: Reuse the instruction lookup
3fbbd3c Remove CCP from size and performance recipes, pending bugfixes
7183ad5 Linker code cleanups
ccb921d Allow getting the base pointer of an image load/store.
716718a Fix infinite simulation cycles in SSA propagator.
120ddff Ignore clang-format-diff.py from copyrights check
ac9a828 dead branch elim: Track killed backedges
c32e79e Add --print-all optimizer option
702852b Opt: Make DecorationManager::HaveTheSameDecorations symmetric
a376b19 Validator checks out of bounds composite access
5b52626 Address review comments from KhronosGroup/SPIRV-Tools#985.
7834bee Update legalization passes
e8f2890 Replace calls to `ToNop` by `KillInst`.
5f10078 Handle execution termination instructions when building edges.
135150a Do not insert Phi nodes in CCP propagator.
25d396b Add ExtInst validation pass (GLSL only for now)
226f263 Test: Fix linux/gcc defined-but-not-used warnings/errors
b185a3d utils/generate_grammar_tables.py: use spaces for indentation
1acce99 Fix KhronosGroup/SPIRV-Tools#1130
a91aa53 Disallow Dim=SubpassData for OpImageSparseRead
59de610 Add asm, dis support for DebugInfo extended instruction set
a27d673 CONTRIBUTING: If you fixed a bug, say so
4ba9dcc Implement SSA CCP (SSA Conditional Constant Propagation).
756b277 Store all enabled capabilities in the feature manger.
8e0051c Update CHANGES
1ab8ad6 Fixing bugs in type manager memory management
7505d24 Update the legalization passes.
c9a881e Make a string parameter const ref
88409b7 Fix CHANGES: there is no OpImageSparseWrite
2e5672d Update CHANGES for recent changes
424f744 Opt: Fix implementation and comment of AreDecorationsTheSame Target should not be ignored when comparing decorations in RemoveDuplicates Opt: Remove unused code in RemoveDuplicateDecorations
79a0064 Allow pointers to pointers in logical addressing mode.
b86eb68 Convert private variables to function scope.
8135dd6 More validation on primitive instructions
4dbcef6 validate & test of literal's upper bits
f359635 Opt: Remove commented out duplicated type_id function
0d8ea48 Fix comment in primitives validation
dbc3a66 Image Operand Sample allows sparse image opcodes
8c05012 Export a config file for pkg-config
0dbe184 Remove concept of FIRST_CONCRETE_* operand types
6169085 Improving the usability of the type manager. The type manager hashes types. This allows the lookup of type declaration ids from arbitrarily constructed types. Users should be cautious when dealing with non-unique types (structs and potentially pointers) to get the exact id if necessary.
7361034 Start v2017.3-dev
1ccfb58 Finalize v2017.2
0f80406 ADCE: Only mark true breaks and continues of live loops
cdfbf26 Add primitive instruction validation pass
af7d579 Refactor include of latest spir-v header versions
532b327 Add validation rules for atomic instructions
853a3d6 Fix uninitialized warning at -Os.
22faa2b ADCE: Empty Loop Elimination
07ce16d Set the parent for basic blocks during inlining.
2fc1221 Appveyor: stop testing on VS 2013
c520d43 Add validator checks for sparse image opcodes
12447d8 Support OpenCL 1.2 and 2.0 target environments
059fe08 AppVeyor: Put VS 2017 first
7ba59ac Force gtest to expose ::testing::Combine
dbd8d0e Reenable OpCopyObject validation rules
867451f Add scalar replacement
78c025a MultiStore: Support OpVariable Initialization
70f88f5 Add external/SPIRV-Headers to .gitignore
c6fdf68 SingleStore: Support OpVariable Initialization
a05ad01 Add option to spirv-opt to skip the validator.
241dcac Add a new constant manager class.
5d602ab Add global redundancy elimination
851e1ad Kill names and decoration in inlining.
731d189 Add depth first iterator for trees
0c2396d Revert extraneous changes from commit 8ec62deb2.
8ba68fa Dominator Tree Analysis (GPUOpen-Drivers#3)
692a22c Sort flags in help message - NFC.
b93c066 CMake: allow both SPIRV-Headers and spirv-headers
94e3e7b Add composite instruction validation pass
3b6c4f1 Do not zip and deploy for check formatting
adb9396 Go back to root directory
235ef34 Travis CI: add clang-format check
1af6c4a Update .appveyor.yml
bf18431 Fix some of the known issues in image validation
fd3a220 DCEInst kill the same instruction twice.
726573a Change Release builds to Release with debug info on Linux
e9ecc0c Remove cfg_ field from SSAPropagator class - NFC.
65046ec Change IRContext::KillInst to delete instructions.
b35b52f Compute value number when the value table is constructed.
b98254b Fixed typo that leaked to the binary
9996173 Add simplified instructions for pushing a reviewed PR.
22582fa Travis: mark MacOS builds as optional
2885c65 Re-format CONTRIBUTING.md - NFC.
0dd4ee2 Fix Dref type check in validator
b8aeab8 Appveyor: use ninja instead of MSBuild
6904396 Opt: Remove unused lambda captures
1379535 Support outputting ANSI color escape sequences in library
188cd37 Erase decorations removed from internal collections
3c2e4c7 Fix validation of group ops in SPV_AMD_shader_ballot
8cfa0c4 Fix GPUOpen-Drivers#1034 - Give Edge::operator<() weak ordering semantics.
e1ceff9 Validate OpTypeImage and OpTypeSampleImage
4c58086 Linker: Fix incorrect exit status.
d78e11c Create CONTRIBUTING.md
8dd3d93 AggressiveDCE: Add merge and continue branches for live loop.
5f2589f Update README about the automatic master-tot relase
9f20799 Convert the CFG to an on-demand analysis - NFC.
8ffed97 Fix windows build. unsigned vs signed comparison in EXPECT_EQ.
7432784 Generic value propagation engine.
491b112 Fix windows build.
8322813 Re-format source tree - NFC.
d8b2013 Derivative opcodes require Fragment exec model
c170afd Relaxed OpImageWrite texel type check
e1a6f8d Update CHANGES to mention fixes for 1007 and 1009
f84f266 Relaxed OpImageRead validation rules
0cae89e Notify the context of instructions that are being erased.
3e08a3f Add validation checks for Execution Model
d9129f0 Test for pollution of the global namespace
0b1cb27 Remove derivative instructions from the list of combinators.
b0a7037 Start v2017.2-dev
493c088 Finalize 2017.1
aec60b8 Add RegisterLegalizationPasses() into the interface
746bfd2 Adding new def -> use mapping container
b02c9a5 Allow derived access chain without uses in access chain conversion
ab892f7 Add derivatives validation pass
c299927 Move SetContextMessageConsumer into libspirv namespace
28c4155 Create a local value numbering pass
f407ae2 Validator pass for image instructions
e28edd4 Optimize loads/stores on nested structs
b142915 Fix move semantics in iterator make_range
250a235 Add new compression algorithm and models
a771713 Adding an unique id to Instruction generated by IRContext
3214c3b Add dead function elimination to -O and -Os
4019bcf Fix hard-coded header path
dbf9b37 Git should ignore external repos
eb4653a Add the decoration manager to the IRContext.
a92d69b Initial implementation of merge return pass.
0126ad9 Appveyor: skip building tags
919c990 Appveyor: Stop testing VS 2013/2015 Debug
98281ed Add analysis to compute mappings between instructions and basic blocks.
a76d097 Fix decorations of inlined functions.
76555bd Tests: Add optional dependency on Effcee stateful matcher
efe12ff Have all MemPasses preserve the def-use manager.
039c12f Travis: auto deploy build artifacts to GitHub Releases
651ba75 Appveyor: auto deploy build artifacts to GitHub Releases
d2938e4 Re-format files in source, source/opt, source/util, source/val and tools.
f32d11f Add the IRContext (part 2): Add def-use manager
ac04b2f Opt: Fix HasLoads to not report decoration as load.
d86c7ce Opt: Remove CommonUniformElimination from -O and -Os (for now)
1affe5a Describe public_spirv_tools_dev@khronos.org mailing list
2dddb81 Validate storage class of target pointer for OpStore
9d6cc26 Move class CFG from namespace opt to namespace ir.
fef669f Add a new class opt::CFG to represent the CFG for the module.
476cae6 Add the IRContext (part 1)
d861cef Add validation for OpBranchConditional
7299fb5 Lowered initial capacity of move-to-front sequence
b1b4948 Fix copyright check when the build directory is in the source directory.
94bec26 ADCE: Dead if elimination
632e206 More re-factoring to simplify pass initialization.
716138e Add option to relax validation of store types.
6724c27 Compression: removed 'presumed index' feature
f063f91 Use std::lower_bound for opcode lookup
1040a95 Re-factor Phi insertion code out of LocalMultiStoreElimPass
94dc66b Change the sections in the module to use the InstructionList class.
063dbea Turn all function static non-POD variables into global POD variables

git-pf-change: stg@1521268
dneto0 pushed a commit to dneto0/SPIRV-Tools that referenced this pull request Sep 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants