-
Notifications
You must be signed in to change notification settings - Fork 786
[analysis] Simplify core analysis code #6034
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
|
||
#include "cfg.h" | ||
#include "lattice.h" | ||
#include "support/unique_deferring_queue.h" | ||
#include "wasm-traversal.h" | ||
|
||
namespace wasm::analysis { | ||
|
@@ -36,71 +37,47 @@ struct VisitorTransferFunc : public Visitor<SubType> { | |
// Returns an iterable to all the BasicBlocks which depend on currBlock for | ||
// information. | ||
BasicBlock::BasicBlockIterable | ||
getDependents(const BasicBlock* currBlock) noexcept { | ||
getDependents(const BasicBlock& currBlock) noexcept { | ||
if constexpr (Direction == AnalysisDirection::Backward) { | ||
return currBlock->preds(); | ||
return currBlock.preds(); | ||
} else { | ||
return currBlock->succs(); | ||
return currBlock.succs(); | ||
} | ||
} | ||
|
||
// Executes the transfer function on all the expressions of the corresponding | ||
// CFG node, starting with the node's input state, and changes the input state | ||
// to the final output state of the node in place. | ||
void transfer(const BasicBlock* cfgBlock, | ||
void transfer(const BasicBlock& bb, | ||
typename L::Element& inputState) noexcept { | ||
// If the block is empty, we propagate the state by inputState = | ||
// outputState. | ||
|
||
currState = &inputState; | ||
if constexpr (Direction == AnalysisDirection::Backward) { | ||
for (auto cfgIter = cfgBlock->rbegin(); cfgIter != cfgBlock->rend(); | ||
++cfgIter) { | ||
for (auto cfgIter = bb.rbegin(); cfgIter != bb.rend(); ++cfgIter) { | ||
static_cast<SubType*>(this)->visit(*cfgIter); | ||
} | ||
} else { | ||
for (auto cfgIter = cfgBlock->begin(); cfgIter != cfgBlock->end(); | ||
++cfgIter) { | ||
static_cast<SubType*>(this)->visit(*cfgIter); | ||
for (auto* inst : bb) { | ||
static_cast<SubType*>(this)->visit(inst); | ||
} | ||
} | ||
currState = nullptr; | ||
} | ||
|
||
// Enqueues the worklist before the worklist algorithm is run. We want to | ||
// evaluate the blocks in an order matching the "flow" of the analysis to | ||
// reduce the number of state propagations needed. Thus, for a forward | ||
// analysis, we push all the blocks in order, while for backward analysis, we | ||
// push them in reverse order, so that later blocks are evaluated before | ||
// earlier ones. | ||
void enqueueWorklist(const CFG& cfg, | ||
std::queue<const BasicBlock*>& worklist) noexcept { | ||
if constexpr (Direction == AnalysisDirection::Backward) { | ||
for (auto it = cfg.rbegin(); it != cfg.rend(); ++it) { | ||
worklist.push(&(*it)); | ||
} | ||
} else { | ||
for (auto it = cfg.begin(); it != cfg.end(); ++it) { | ||
worklist.push(&(*it)); | ||
} | ||
} | ||
} | ||
|
||
// This is for collecting results after solving an analysis. Implemented in | ||
// the same way as transfer(), but we also set the collectingResults flag. | ||
void collectResults(const BasicBlock* cfgBlock, | ||
typename L::Element& inputState) { | ||
void collectResults(const BasicBlock& bb, typename L::Element& inputState) { | ||
collectingResults = true; | ||
currState = &inputState; | ||
if constexpr (Direction == AnalysisDirection::Backward) { | ||
for (auto cfgIter = cfgBlock->rbegin(); cfgIter != cfgBlock->rend(); | ||
++cfgIter) { | ||
static_cast<SubType*>(this)->visit(*cfgIter); | ||
for (auto it = bb.rbegin(); it != bb.rend(); ++it) { | ||
static_cast<SubType*>(this)->visit(*it); | ||
} | ||
} else { | ||
for (auto cfgIter = cfgBlock->begin(); cfgIter != cfgBlock->end(); | ||
++cfgIter) { | ||
static_cast<SubType*>(this)->visit(*cfgIter); | ||
for (auto it = bb.begin(); it != bb.end(); ++it) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you using the .begin() .end() iterator pattern for symmetry with the If-true block above? Just wondering why not the range-based loop that's used on line 263 in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it's for symmetry. You're right that a range-based for loop would have worked here as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A range-based loop would be better imo, even if it looks more different than the other loop above. I see that as a benefit actually: the difference is more obvious, and separately this one becomes easier to read. |
||
static_cast<SubType*>(this)->visit(*it); | ||
} | ||
} | ||
currState = nullptr; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why this print function instead of a << overload? Does it have to do with Concepts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No particular reason. The original author probably thought
print
was a nicer interface than<<
, and that seems like a reasonable opinion to me. A<<
overload would have worked just as well. Print methods generally have a slight advantage that they are easier to call from a debugger, although I'm not sure whether it's easy to get the ostream to pass into this one from the debugger.toString
methods are generally the easiest for debugging.