Commit Graph

58 Commits

Author SHA1 Message Date
Daniel Thornburgh
3820c4f6e2
Qualify std::move. (#4741)
Clang added -Wunqualified-std-cast-call in
https://reviews.llvm.org/D119670, which warns on unqualified std::move
and std::forward calls. This change qualifies these calls to allow the
project to build on HEAD Clang -Werror.
2022-03-22 11:20:11 -04:00
Steven Perron
7c5b17d379
Update passes to handle function declarations (#4599)
Spirv-opt has not had to handle module with function declarations.  This
lead many passes to assume that every function has a body.  This is not
always true.  This commit will modify a number of passes to handle
function declarations.

Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4443
2021-10-28 11:54:37 -04:00
Steven Perron
b2ba019bf6
Delete decorations before replaces uses in dead branch elim (#4598)
If we do not delete the decoration before all ReplaceAllUses, the decorations will be transferred to the new id.  This can cause problems.

Fixes #4442.
2021-10-28 10:25:37 -04:00
Ehsan
a7112d544b
Eliminate branches with condition of OpConstantNull (#3438) 2020-06-16 13:31:03 -04:00
Steven Perron
97f1d485b7 Dead branch elim fix (#3160)
We must treat a branch to the merge node of a switch that is in the
header of a construct as a nested construced.  The original merge
instruction is still needed in that case.
2020-01-28 10:17:43 -05:00
Steven Perron
5ce8cf781f
Change the order branches are simplified in dead branch elim (#2728)
Dead branch elimination needs to know about the constructs that a block is contained it when determining what to do with its merge instruction.  We currently fold branches in block as we see them, which is parent constructs before their children.  This causes the struct cfg analysis to crash because it tries to get the parent construct for a block after the parent has been folded.

This can be fixed by folding the branch of the children before the parents.

Fixes #2667.
2019-07-10 14:59:44 -04:00
Steven Perron
6c7db9c630
Handle nested breaks from switches. (#2624)
* Handle nested breaks from switches.

There was a recent decision made to allow branches to the merge node of
a switch even if the switch is not the first enclosing construct.  They
can be generated by glslang from break statements in switches.

Dead branch elimination seems to be the only optimization that will
break because of this change, so I will update that optimizations.

The change made are:

- Track switches in structured cfg analysis.
- In Dead branch elimination:
  - Look for nested breaks that will require a switch instruction.
  - Rewrite, but don't delete, switchs that are required even if it
    could be replaced by an unconditional branch.
  - When looking for the first break, consider the merge of a switch
    as well.

See #2612.

* Fix variable names and comments.

* Add tests for the struct cfg analysis and switches.

* Fix typos in comments.
2019-05-27 16:28:14 -04:00
alan-baker
236bdc0065 Change prioritization of unreachable merge and continue (#2460)
Fixes #2452

Swaps priority of handling unreachable merge and continues so that the
back-edge is retained in the case a block is both a loop continue and
loop merge
2019-04-03 12:50:08 -04:00
Steven Perron
d800bbbac9
Handle back edges better in dead branch elim. (#2417)
* Handle back edges better in dead branch elim.

Loop header must have exactly one back edge.  Sometimes the branch
with the back edge can be folded.  However, it should not be folded
if it removes the back edge.

The code to check this simply avoids folding the branch in the
continue block.  That needs to be changed to not fold the back edge,
wherever it is.

At the same time, the branch can be folded if it folds to a branch to
the header, because the back edge will still exist.

Fixes #2391.
2019-02-26 09:06:51 -05:00
Steven Perron
2d2a512691
Don't inline recursive functions. (#2130)
* Move ProcessFunction* function from pass to the context.

There are a few functions that are used to traverse the call tree.
They currently live in the Pass class, but they have nothing to do with
a pass, and may be needed outside of a pass.  They would be better in
the ir context, or in a specific call tree class if we ever have a need
for it.

* Don't inline recursive functions.

Inlining does not check if a function is recursive or not.  This has
been fine as long as the shader was a Vulkan shader, which forbid
recursive functions.  However, not all shaders are vulkan, so either
we limit inlining to Vulkan shaders or we teach it to look for recursive
functions.

I prefer to keep the passes as general as is reasonable.  The change
does not require much new code in inlining and gives a reason to refactor
some other code.

The changes are to add a member function to the Function class that
checks if that function is recursive or not.

Then this is used in inlining to not inlining a function call if it calls
a recursive function.

* Add id to function analysis

There are a few places that build a map from ids to Function whose
result is that id.  I decided to add an analysis to the context for this
to reduce that code, and simplify some of the functions.

* Add missing file.
2018-11-29 14:24:58 -05:00
greg-lunarg
6721478ef1 Don't assume one return means function can be inlined. (#2018) (#2025)
If there is only 1 return and it is in a loop, then the function cannot be inlined.

Fix condition when inlined code needs one-trip loop wrapper.  The dummy loop is needed when there is a return inside a selection construct.  Even if there is only 1 return.
2018-11-08 09:11:20 -05:00
Steven Perron
18fe6d59e5
Fix dead branch elim infinite loop. (#2009)
When looking for a break from a selection construct, we do not realize
that a jump to the continue target of a loop containing the selection
is a break.  This causes and infinit loop, or possibly other failures.

Fixes #2004.
2018-10-24 09:10:30 -04:00
Steven Perron
0ba35798c3
Fix dead branch elim infinite loop. (#1997)
When looking for a break from a selection construct, we do not need to
look inside nested constructs.  However, if a loop header has an
unconditional branch, then we enter the loop.  Entering the loop causes
an infinite loop because we keep going through the loop.

The solution is to look for a merge block, if one exsits, even for block
terminated by an OpBranch.

Fixes #1979.
2018-10-22 13:59:20 -04:00
Steven Perron
82663f34c9
Check for unreachable blocks in merge-return. (#1966)
Merge return assumes that the only unreachable blocks are those needed
to keep the structured cfg valid.  Even those must be essentially empty
blocks.

If this is not the case, we get unpredictable behaviour.  This commit
add a check in merge return, and emits an error if it is not the case.

Added a pass of dead branch elimination before merge return in both the
performance and size passes.  It is a precondition of merge return.

Fixes #1962.
2018-10-10 15:18:15 -04:00
Steven Perron
ddc705933d
Analyze uses for all instructions. (#1937)
* Analyze uses for all instructions.

The def-use manager needs to fill in the `inst_to_used_ids_` field for
every instruction.  This means we have to analyze the uses for every
instruction, even if they do not have any uses.

This mistake was not found earlier because there was a typo in the
equality check for def-use managers.  No new tests are needed.

While looking into this I found redundant work in block merge.  Cleaning
that up at the same time.

* Fix other transformations

Aggressive dead code elimination did not update the OpGroupDecorate
and the OpGroupMemberDecorate instructions properly when they are
updated.  That is fixed.

Dead branch elimination did not analyze the OpUnreachable instructions
that is would add.  That is taken care of.
2018-09-28 14:39:06 -04:00
Steven Perron
5f599e700e
Fix infinite loop in dead-branch-elimination (#1891)
* Create structed cfg analysis.

There are lots of optimization that have to traverse the CFG in a
structured order just because it wants to know which constructs a
basic block in contained in.  This adds extra complexity to these
optimizations, for causes too much refactoring of older optimizations.

To help with this problem, I have written an analysis that can give this
information.

* Identify branches breaking from loops.

Dead branch elimination does a search for a conditional branch to the
end of the current selection construct.  This search assumes that the
only way to leave the construct is through the merge node.  But that is
not true.  The code can jump to the merge node of a loop that contains
the construct.

The search needs to take this into consideration.
2018-09-17 13:00:24 -04:00
Steven Perron
45c235d41f
Have dead-branch-elim handle conditional exits from selections. (#1850)
When dead-branch-elim folds a conditional branch, it also deletes the
OpSelectionMerge instruction.  If that construct contains a
conditional branch to the merge node, it will not have its own
OpSelectionMerge.  When the headers merge instruction is deleted, the
the inner conditional branch will no longer be legal.  It will be a
selection to a node that is not a merge node.

We fix this up by moving the OpSelectionMerge to a new location if it is
still needed.
2018-08-21 11:49:56 -04:00
dan sinclair
1553025f4c
Move make_unique to source/util. (#1836)
This MakeUnique code is used in places other then source/opt so move it
to source/utils.
2018-08-14 12:44:54 -04:00
dan sinclair
eda2cfbe12
Cleanup includes. (#1795)
This Cl cleans up the include paths to be relative to the top level
directory. Various include-what-you-use fixes have been added.
2018-08-03 15:06:09 -04:00
Alan Baker
28199b80b7 Fix block ordering in dead branch elim
Fixes #1727

* If the pass finds any dead branches it can optimize then at the end of
the pass it reorders basic blocks to ensure they satisfy block ordering
requirements
 * Added some new tests
* While investigating this issue, found and fixed a non-deterministic
ordering of dominators
 * Now the edges used to construct the dominator tree are sorted
 according to posorder traversal indices
2018-07-19 11:17:57 -04:00
dan sinclair
c7da51a085
Cleanup extraneous namespace qualifies in source/opt. (#1716)
This CL follows up on the opt namespacing CLs by removing the
unnecessary opt:: and opt::analysis:: namespace prefixes.
2018-07-12 15:14:43 -04:00
dan sinclair
f96b7f1cb9
use Pass::Run to set the context on each pass. (#1708)
Currently the IRContext is passed into the Pass::Process method. It is
then up to the individual pass to store the context into the context_
variable. This CL changes the Run method to store the context before
calling Process which no-longer receives the context as a parameter.
2018-07-12 09:08:45 -04:00
dan sinclair
e6b953361d
Move the ir namespace to opt. (#1680)
This CL moves the files in opt/ to consistenly be under the opt::
namespace. This frees up the ir:: namespace so it can be used to make a
shared ir represenation.
2018-07-09 11:32:29 -04:00
Steven Perron
a1f9e1342e Preserve inst-to-block and def-use in passes.
The following passes are updated to preserve the inst-to-block and
def-use analysies:

	private-to-local
	aggressive dead-code elimination
	dead branch elimination
	local-single-block elimination
	local-single-store elimination
	reduce load size
	compact ids (inst-to-block only)
	merge block
	dead-insert elimination
	ccp

The one execption is that compact ids still kills the def-use manager.
This is because it changes so many ids it is faster to kill and rebuild.

Does everything in
https://github.com/KhronosGroup/SPIRV-Tools/issues/1593 except for the
changes to merge return.
2018-06-04 13:48:30 -04:00
Eleni Maria Stea
045cc8f75b Fixes compile errors generated with -Wpedantic
This patch fixes the compile errors generated when the options
SPIRV_WARN_EVERYTHING and SPIRV_WERROR (that force -Wpedantic) are
set to cmake.
2018-03-22 09:40:11 -04:00
David Neto
340370eddb Remove extension whitelist from some transforms
Remove extension whitelists from transforms that are essentially
combinatorial (and avoiding pointers) or which affect only control flow.
It's very very unlikely an extension will add a new control flow construct.

Remove from:
- dead branch elimination
- dead insertion elimination
- insert extract elimination
- block merge

Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/1392
2018-03-08 12:25:49 -05:00
Rex Xu
314cfa29b2 Add missing SPV extension strings 2018-03-08 21:54:00 +08:00
Steven Perron
50f307f889 Simplify OpPhi instructions referencing unreachable continues
In dead branch elimination, we already recognize unreachable continue
blocks, and update OpPhi instruction accordingly.  This change adds an
extra check: if the head block has exactly 1 other incoming edge, then
replace the OpPhi with the value from that edge.

Fixes #1314.
2018-02-16 18:58:03 -05:00
David Neto
87f9cfaba3 Disambiguate between const and nonconst ForEachSuccessorLabel
This helps VisualStudio 2013 compile the code.

Contributes to #1262
2018-02-02 17:54:40 -05:00
Alan Baker
16949236fe Prevent unnecessary changes to the IR in dead branch elim
* When handling unreachable merges and continues, do not optimize to the
same IR
 * pass did not check whether the unreachable blocks were in the
 optimized form before transforming them
* added a test to catch this issue
2018-01-30 16:51:58 -05:00
Alan Baker
6704233d39 Fix dereference of possibly nullptr
* If the dead branch elim is performed on a module without structured
control flow, the OpSelectionMerge may not be present
 * Add a check for pointer validity before dereferencing
* Added a test to catch the bug
2018-01-30 10:15:43 -05:00
Alan Baker
6587d3f8a3 Adding early exit versions of several ForEach* methods
* Looked through code for instances where code would benefit from early
exit
 * Added a corresponding WhileEach* method and updated the code
2018-01-12 17:05:09 -05:00
Alan Baker
1b6cfd3409 Rewriting dead branch elimination.
Pass now paints live blocks and fixes constant branches and switches as
it goes. No longer requires structured control flow. It also removes
unreachable blocks as a side effect. It fixes the IR (phis) before doing
any code removal (other than terminator changes).

Added several unit tests for updated/new functionality.

Does not remove dead edge from a phi node:
* Checks that incoming edges are live in order to retain them
* Added BasicBlock::IsSuccessor
* added test

Fixing phi updates in the presence of extra backedge blocks

* Added tests to catch bug

Reworked how phis are updated

* Instead of creating a new Phi and RAUW'ing the old phi with it, I now
replace the phi operands, but maintain the def/use manager correctly.

For unreachable merge:

* When considering unreachable continue blocks the code now properly
checks whether the incoming edge will continue to be live.

Major refactoring for review

* Broke into 4 major functions
 * marking live blocks
 * marking structured targets
 * fixing phis
 * deleting blocks
2018-01-09 12:21:39 -05:00
David Neto
ac9a828e6e dead branch elim: Track killed backedges
When deleting branches and blocks, also remove them from
the backedges set, in case they were there.

This prevents us from keeping stale pointers to deleted Instruction
objects.  That memory could be used later by another instruction,
incorrectly signaling that something has a backedge reference, and
the dead branch eliminator could end up deleting live blocks.

Adds accessor method ir::BasicBlock::terminator

Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/1168
2018-01-04 19:06:55 -05:00
Steven Perron
756b277fb8 Store all enabled capabilities in the feature manger.
In order to keep track of all of the implicit capabilities as well as
the explicit ones, we will add them all to the feature manager.  That is
the object that needs to be queried when checking if a capability is
enabled.

The name of the "HasCapability" function in the module was changed to
make it more obvious that it does not check for implied capabilities.

Keep an spv_context and AssemblyGrammar in IRContext
2017-12-21 11:14:53 -05:00
Steven Perron
65046eca7c Change IRContext::KillInst to delete instructions.
The current method of removing an instruction is to call ToNop.  The
problem with this is that it leaves around an instruction that later
passes will look at.  We should just delete the instruction.

In MemPass there is a utility routine called DCEInst.  It can delete
essentially any instruction, which can invalidate pointers now that they
are actually deleted.  The interface was changed to add a call back that
can be used to update any local data structures that contain
ir::Intruction*.
2017-12-04 11:07:45 -05:00
Diego Novillo
83228137e1 Re-format source tree - NFC.
Re-formatted the source tree with the command:

$ /usr/bin/clang-format -style=file -i \
    $(find include source tools test utils -name '*.cpp' -or -name '*.h')

This required a fix to source/val/decoration.h.  It was not including
spirv.h, which broke builds when the #include headers were re-ordered by
clang-format.
2017-11-27 14:31:49 -05:00
Alan Baker
746bfd210a Adding new def -> use mapping container
Replaced representation of uses

* Changed uses from unordered_map<uint32_t, UseList> to
set<pairInstruction*, Instruction*>>
* Replaced GetUses with ForEachUser and ForEachUse functions
* updated passes to use new functions
* partially updated tests
* lots of cleanup still todo

Adding an unique id to Instruction generated by IRContext

Each instruction is given an unique id that can be used for ordering
purposes. The ids are generated via the IRContext.

Major changes:
* Instructions now contain a uint32_t for unique id and a cached context
pointer
 * Most constructors have been modified to take a context as input
 * unfortunately I cannot remove the default and copy constructors, but
 developers should avoid these
* Added accessors to parents of basic block and function
* Removed the copy constructors for BasicBlock and Function and replaced
them with Clone functions
* Reworked BuildModule to return an IRContext owning the built module
 * Since all instructions require a context, the context now becomes the
basic unit for IR
* Added a constructor to context to create an owned module internally
* Replaced uses of Instruction's copy constructor with Clone whereever I
found them
* Reworked the linker functionality to perform clones into a different
context instead of moves
* Updated many tests to be consistent with the above changes
 * Still need to add new tests to cover added functionality
* Added comparison operators to Instruction

Adding tests for Instruction, IRContext and IR loading

Fixed some header comments for BuildModule

Fixes to get tests passing again

* Reordered two linker steps to avoid use/def problems
* Fixed def/use manager uses in merge return pass
* Added early return for GetAnnotations
* Changed uses of Instruction::ToNop in passes to IRContext::KillInst

Simplifying the uses for some contexts in passes
2017-11-23 16:40:02 -05:00
Alan Baker
a771713e42 Adding an unique id to Instruction generated by IRContext
Each instruction is given an unique id that can be used for ordering
purposes. The ids are generated via the IRContext.

Major changes:
* Instructions now contain a uint32_t for unique id and a cached context
pointer
 * Most constructors have been modified to take a context as input
 * unfortunately I cannot remove the default and copy constructors, but
 developers should avoid these
* Added accessors to parents of basic block and function
* Removed the copy constructors for BasicBlock and Function and replaced
them with Clone functions
* Reworked BuildModule to return an IRContext owning the built module
 * Since all instructions require a context, the context now becomes the
basic unit for IR
* Added a constructor to context to create an owned module internally
* Replaced uses of Instruction's copy constructor with Clone whereever I
found them
* Reworked the linker functionality to perform clones into a different
context instead of moves
* Updated many tests to be consistent with the above changes
 * Still need to add new tests to cover added functionality
* Added comparison operators to Instruction
* Added an internal option to LinkerOptions to verify merged ids are
unique
* Added a test for the linker to verify merged ids are unique

* Updated MergeReturnPass to supply a context
* Updated DecorationManager to supply a context for cloned decorations

* Reworked several portions of the def use tests in anticipation of next
set of changes
2017-11-20 17:49:10 -05:00
Steven Perron
eb4653a67f Add the decoration manager to the IRContext.
To make the decoration manger available everywhere, and to reduce the
number of times it needs to be build, I add one the IRContext.

As the same time, I move code that modifies decoration instruction into
the IRContext from mempass and the decoration manager.  This will make
it easier to keep everything up to date.

This should take care of issue #928.
2017-11-15 12:48:03 -05:00
Diego Novillo
d2938e4842 Re-format files in source, source/opt, source/util, source/val and tools.
NFC. This just makes sure every file is formatted following the
formatting definition in .clang-format.

Re-formatted with:

$ clang-format -i $(find source tools include -name '*.cpp')
$ clang-format -i $(find source tools include -name '*.h')
2017-11-08 14:03:08 -05:00
Steven Perron
f32d11f74b Add the IRContext (part 2): Add def-use manager
This change will move the instances of the def-use manager to the
IRContext.  This allows it to persists across optimization, and does
not have to be rebuilt multiple times.

Added test to ensure that the IRContext is validating and invalidating
the analyses correctly.
2017-11-08 13:35:34 -05:00
Diego Novillo
fef669f30f Add a new class opt::CFG to represent the CFG for the module.
This class moves some of the CFG-related functionality into a new
class opt::CFG.  There is some other code related to the CFG in the
inliner and in opt::LocalSingleStoreElimPass that should also be moved,
but that require more changes than this pure restructuring.

I will move those bits in a follow-up PR.

Currently, the CFG is computed every time a pass is instantiated, but
this should be later moved to the new IRContext class that @s-perron is
working on.

Other re-factoring:

- Add BasicBlock::ContinueBlockIdIfAny. Re-factored out of MergeBlockIdIfAny
- Rewrite IsLoopHeader in terms of GetLoopMergeInst.
- Run clang-format on some files.
2017-11-02 10:37:03 -04:00
Steven Perron
476cae6f7d Add the IRContext (part 1)
This is the first part of adding the IRContext.  This class is meant to
hold the extra data that is build on top of the module that it
owns.

The first part will simply create the IRContext class and get it passed
to the passes in place of the module.  For now it does not have any
functionality of its own, but it acts more as a wrapper for the module.

The functions that I added to the IRContext are those that either
traverse the headers or add to them.  I did this because we may decide
to have other ways of dealing with these sections (for example adding a
type pool, or use the decoration manager).

I also added the function that add to the header because the IRContext
needs to know when an instruction is added to update other data
structures appropriately.

Note that there is still lots of work that needs to be done.  There are
still many places that change the module, and do not inform the context.
That will be the next step.
2017-10-31 13:46:05 -04:00
GregF
94bec26afe ADCE: Dead if elimination
Mark structured conditional branches live only if one or more instructions
in their associated construct is marked live. After closure, replace dead
structured conditional branches with a branch to its merge and remove
dead blocks.

ADCE: Dead If Elim: Remove duplicate StructuredOrder code

Also generalize ComputeStructuredOrder so that the caller can specify the
root block for the order. Phi insertion uses pseudo_entry_block and adce and
dead branch elim use the first block of the function.

ADCE: Dead If Elim: Pull redundant code out of InsertPhiInstructions

ADCE: Dead If Elim: Encapsulate CFG Cleanup Initialization

ADCE: Dead If Elim: Remove redundant code from ADCE initialization

ADCE: Dead If: Use CFGCleanup to eliminate newly dead blocks

Moved bulk of CFG Cleanup code into MemPass.
2017-10-31 11:51:30 -04:00
Diego Novillo
632e2068f3 More re-factoring to simplify pass initialization.
This implements two cleanups suggested by @s-perron
(https://github.com/KhronosGroup/SPIRV-Tools/pull/921):

- Move FindNamedOrDecoratedIds() into MemPass::InitializeProcessing().
- Remove FinalizeNextId(). Always call SetIdBound() from
  Pass::TakeNextId().
2017-10-30 09:06:17 -04:00
Diego Novillo
1040a95b3f Re-factor Phi insertion code out of LocalMultiStoreElimPass
Including a re-factor of common behaviour into class Pass:

The following functions are now in class Pass:

- IsLoopHeader.
- ComputeStructuredOrder
- ComputeStructuredSuccessors (annoyingly, I could not re-factor all
  instances of this function, the copy in common_uniform_elim_pass.cpp
  is slightly different and fails with the common implementation).
- GetPointeeTypeId
- TakeNextId
- FinalizeNextId
- MergeBlockIdIfAny

This is a NFC (non-functional change)
2017-10-27 15:28:08 -04:00
GregF
e3a7209330 DeadBranchElim: Fix dead block elimination
The previous algorithm would leave invalid code in the case of unreachable
blocks pointing into a dead branch. It would leave the unreachable blocks
branching to labels that no longer exist. The previous algorithm also left
unreachable blocks in some cases (a loop following an orphaned merge block).
This fix also addresses that.

This code will soon be replaced with the coming CFG cleanup.
2017-10-23 22:04:17 -04:00
David Neto
863578a38d DeadBranchElim: Slightly more defensive coding 2017-10-19 19:28:45 -04:00
GregF
1e7994c085 Opt: Move *NextId functionality into MemPass 2017-10-13 15:22:19 -04:00