-
Notifications
You must be signed in to change notification settings - Fork 850
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
SPIR-V: prune unreachable merge block and continue target #1943
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 |
---|---|---|
|
@@ -226,6 +226,36 @@ class Block { | |
return nullptr; | ||
} | ||
|
||
// Change this block into a canonical dead merge block. Delete instructions | ||
// as necessary. A canonical dead merge block has only an OpLabel and an | ||
// OpUnreachable. | ||
void rewriteAsCanonicalUnreachableMerge() { | ||
assert(localVariables.empty()); | ||
// Delete all instructions except for the label. | ||
assert(instructions.size() > 0); | ||
instructions.resize(1); | ||
successors.clear(); | ||
Instruction* unreachable = new Instruction(OpUnreachable); | ||
addInstruction(std::unique_ptr<Instruction>(unreachable)); | ||
} | ||
// Change this block into a canonical dead continue target branching to the | ||
// given header ID. Delete instructions as necessary. A canonical dead continue | ||
// target has only an OpLabel and an unconditional branch back to the corresponding | ||
// header. | ||
void rewriteAsCanonicalUnreachableContinue(Block* header) { | ||
assert(localVariables.empty()); | ||
// Delete all instructions except for the label. | ||
assert(instructions.size() > 0); | ||
instructions.resize(1); | ||
successors.clear(); | ||
// Add OpBranch back to the header. | ||
assert(header != nullptr); | ||
Instruction* branch = new Instruction(OpBranch); | ||
branch->addIdOperand(header->getId()); | ||
addInstruction(std::move(std::unique_ptr<Instruction>(branch))); | ||
successors.push_back(header); | ||
} | ||
|
||
bool isTerminated() const | ||
{ | ||
switch (instructions.back()->getOpCode()) { | ||
|
@@ -235,6 +265,7 @@ class Block { | |
case OpKill: | ||
case OpReturn: | ||
case OpReturnValue: | ||
case OpUnreachable: | ||
return true; | ||
default: | ||
return false; | ||
|
@@ -268,10 +299,24 @@ class Block { | |
bool unreachable; | ||
}; | ||
|
||
// The different reasons for reaching a block in the inReadableOrder traversal. | ||
typedef enum ReachReason { | ||
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. What's the purpose of 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. Note, I removed the |
||
// Reachable from the entry block via transfers of control, i.e. branches. | ||
ReachViaControlFlow = 0, | ||
// A continue target that is not reachable via control flow. | ||
ReachDeadContinue, | ||
// A merge block that is not reachable via control flow. | ||
ReachDeadMerge | ||
}; | ||
|
||
// Traverses the control-flow graph rooted at root in an order suited for | ||
// readable code generation. Invokes callback at every node in the traversal | ||
// order. | ||
void inReadableOrder(Block* root, std::function<void(Block*)> callback); | ||
// order. The callback arguments are: | ||
// - the block, | ||
// - the reason we reached the block, | ||
// - if the reason was that block is an unreachable continue or unreachable merge block | ||
// then the last parameter is the corresponding header block. | ||
void inReadableOrder(Block* root, std::function<void(Block*, ReachReason, Block* header)> callback); | ||
|
||
// | ||
// SPIR-V IR Function. | ||
|
@@ -321,7 +366,7 @@ class Function { | |
parameterInstructions[p]->dump(out); | ||
|
||
// Blocks | ||
inReadableOrder(blocks[0], [&out](const Block* b) { b->dump(out); }); | ||
inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); }); | ||
Instruction end(0, 0, OpFunctionEnd); | ||
end.dump(out); | ||
} | ||
|
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.
This turned on lots of code having to do with types and capabilities that should not be relevant to WebGPU.
Don't we need to just turn on the CFG topology part?
I can look at that too.
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.
Well, just reply with the specific subset needed by WebGPU, and I'll do the experiment/pruning of other things and verify how size is impacted.
(First, I'll merge this, and then do that as a second step to get space back for WebGPU.)
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.
In principle, yes.
But when I looked more deeply at Builder::postProcess, I became worried that the WEB path was missing functionality, particularly around adding per-instruction capabilities and extensions.
Looking again, at the moment those additional things are only affecting:
Neither of these are in the WEB variant for now, so that could be saved.
I can take on getting that code space back.
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.
I posted #1968 to reclaim the codespace wasted by this part of the change.
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.
That looks like it solves it, thanks.