This subclass of SILArgument should be eliminated--it's not always a
phi, and whether it is a "phi argument" has nothing whatsoever to do
with the opcode. That is a property of a value's uses, not a property of the
value.
Until then, provide a logical and useful API within the type. This
often avoids the need to explicitly cast to a SILPhiArgument type and
avoids a lot of boilerplate in code that deals with phis.
Note: PhiOperand and PhiValue are improved abstractions on top of this
API. But the SILArgument-level API is still an important bridge
between SILArgument and other phi abstractions.
This is the initial version of a buildable SIL definition in libswift.
It defines an initial set of SIL classes, like Function, BasicBlock, Instruction, Argument, and a few instruction classes.
The interface between C++ and SIL is a bridging layer, implemented in C.
It contains all the required bridging data structures used to access various SIL data structures.
When an instruction is "deleted" from the SIL, it is put into the SILModule::scheduledForDeletion list.
The instructions in this list are eventually deleted for real in SILModule::flushDeletedInsts(), which is called by the pass manager after each pass run.
In other words: instruction deletion is deferred to the end of a pass.
This avoids dangling instruction pointers within the run of a pass and in analysis caches.
Note that the analysis invalidation mechanism ensures that analysis caches are invalidated before flushDeletedInsts().
This is necessitated by the SILArgument representation. It has the
tragic property that adding unrelated phis invalidates existing
phis. Therefore, the optimizer can't do book-keeping of phi values by
refering directly to SILValues or Operands. Instead, it must only
refer to SILBasicBlocks and argument indices.
* Instead of passing the vector type as template argument, use a SmallVector and just pass the inline size
* Increase the inline size to 32. Found by experiment, this fits 90% of all functions.
* add an API for getting data for newly created blocks.
It can be used by transforms to store temporary data per basic block.
It is very efficient: only a single memory allocation is needed and no maps are used to lookup data.
The main change is to detect infinite recursive calls under invariant conditions. For example:
func f() {
if #available(macOS 10.4.4, *) {
f()
}
}
or invariant conditions due to forwarded arguments:
func f(_ x: Int) {
if x > 0 {
f(x)
}
}
Also, improve the warning message. Instead of giving a warning at the function location
warning: all paths through this function will call itself
give a warning at the call location:
warning: function call causes an infinite recursion
Especially in case of multiple recursive calls, it makes it easier to locate the problem.
https://bugs.swift.org/browse/SR-11842
rdar://57460599
When merging many blocks to a single block (in the wrong order), instructions are getting moved over and over again.
This is quadratic and can result in very long compile times for large functions.
To fix this, always move the instruction to smaller block to the larger block.
rdar://problem/56268570
VS2015 had an issue with the deletion of an operator. Since VS2017 is
the minimum version that LLVM uses, we can assume that VS2017+ is in use
(_MSC_VER >= 1910). Clean up the now defunct workaround.
We have a lot of "transform a range" types already:
llvm::mapped_iterator, swift::TransformRange and
swift::TransformIterator, and swift::ArrayRefView for static
transformations. This gets rid of one more layer without losing
any real functionality.
We've been running doxygen with the autobrief option for a couple of
years now. This makes the \brief markers into our comments
redundant. Since they are a visual distraction and we don't want to
encourage more \brief markers in new code either, this patch removes
them all.
Patch produced by
for i in $(git grep -l '\\brief'); do perl -pi -e 's/\\brief //g' $i & done
* [SILOptimizer] Don't diagnose infinite recursion if a branch terminates the program
This patch augments the infinite recursion checker to not warn if a
branch terminates, but still warns if a branch calls into something with
@_semantics("programtermination_point"). This way, calling fatalError
doesn't disqualify you for the diagnostic, but calling exit does.
This also removes the warning workaround in the standard library, and
annotates the internal _assertionFailure functions as
programtermination_points, so they get this treatment too.
* Fix formatting in SILInstructions.cpp
* Re-add missing test
This patch augments the infinite recursion checker to not warn if a
branch terminates, but still warns if a branch calls into something with
`@_semantics("arc.programtermination_point")`. This way, calling `fatalError`
doesn't disqualify you for the diagnostic, but calling `exit` does.
This also removes the warning workaround in the standard library, and
annotates the internal _assertionFailure functions as
`programtermination_point`s, so they get this treatment too.
This convenience API enables one to iterate over the array of arguments of each
successor of the terminator.
To implement this I needed to implement SILBasicBlock::getPHIArguments(), so I
also implemented SILBasicBlock::getFunctionArguments().
rdar://44667493
In a non-rigorous test, this change shaves off 20% of the time spent
in SIL optimizations for the standard library in a +Asserts build.
(Admittedly the wins for a no-asserts build will be much lower because
SILInstructionResultArray has a bunch of assertions in its
constructor.)
I am doing this for a few different reasons:
1. The code for manipulating successors was partially in TermInst (with
SILBasicBlock delegating to TermInst) and partly in SILBasicBlock itself. It
makes more sense to just be consistent and move all said functionality into
TermInst and just always delegate to SILBasicBlock.
2. I am preparing an API around gathering all critical edges. All of the
critical edge breaking APIs to take a TermInst. I wanted to use some of the
successor APIs, only to discover that we were not delegating to TermInst. By
moving said functionality onto TermInst itself and delegating, we have it in
both places.
rdar://31521023
introduce a common superclass, SILNode.
This is in preparation for allowing instructions to have multiple
results. It is also a somewhat more elegant representation for
instructions that have zero results. Instructions that are known
to have exactly one result inherit from a class, SingleValueInstruction,
that subclasses both ValueBase and SILInstruction. Some care must be
taken when working with SILNode pointers and testing for equality;
please see the comment on SILNode for more information.
A number of SIL passes needed to be updated in order to handle this
new distinction between SIL values and SIL instructions.
Note that the SIL parser is now stricter about not trying to assign
a result value from an instruction (like 'return' or 'strong_retain')
that does not produce any.
Static initializers are now represented by a list of literal and aggregate instructions in a SILGlobalVariable.
For details see SIL.rst.
This representation is cleaner than what we did so far (point to the initializer function and do some pattern matching).
One implication of that change is that now (a subset of) instructions not necessarily have a parent function.
Regarding the generated code it's a NFC.
Also the swift module format didn't change because so far we don't serializer global variables.
addNodeToList only needs to be implemented to override the default callback
behavior of ilist_trait_defaults<SILBasicBlock>. This implmentation in
ilist_traits<SILBasicBlock> is exactly the same as the default callback so is
unnecessary.
This iterator is not actually a SILSuccessorIterator since it is not iterating
over the "successors" of a block. Instead it is used to given the head of a CFG
edge, iterate over the CFG edge's predecessors using the double linked list
stored inside SILSuccessor.
This rename/refactor ties SILSuccessor closer to SILSuccessorIterator and makes
it clear what we are actually iterating over.
I reversed this loop's direction over the instruction list and forgot to change
the order of erasing an instruction with respect to advancing the iterator.
Thankfully ASAN is far smarter than I.
Converting between forward/reverse iterators makes the loop unreadable.
Add an iterator return value to BasicBlock::erase(SILInstruction*).
This commit does a few things:
1. It uses SwitchEnumBuilder so we are not re-inventing any wheels.
2. Instead of hacking around not putting in a destroy for .None on the fail
pass, just *do the right thing* and recognize that we have a binary case enum
and in such a case, just emit code for the other case rather than use a default
case (meaning no cleanup on .none).
rdar://31145255
There are a few different use cases here:
1. In Raw SIL, no return folding may not have been run yet implying that a call
to a no-return function /can/ have arbitrary control flow after it (consider
mandatory inlined functions). We need to recognize that the region of code that
is strictly post dominated by the no-return function is "transitively
unreachable" and thus leaking is ok from that point. *Footnote 1*.
2. In Canonical and Raw SIL, we must recognize that unreachables and no-return
functions constitute places where we are allowed to leak.
rdar://29791263
----
*Footnote 1*: The reason why this is done is since we want to emit unreachable
code diagnostics when we run no-return folding. By leaving in the relevant code,
we have preserved all of the SILLocations on that code allowing us to create
really nice diagnostics.