Commit Graph

107 Commits

Author SHA1 Message Date
Erik Eckstein
bfdeb57863 LICM: fix handling of stores of Optional.none in OSSA
Currently we don't support hoisting ownership instructions.
But the check was missing a store of Optional.none because such a value has no ownership even if the optional is not trivial.

Fixes a SIL verifier crash.
https://github.com/swiftlang/swift/issues/79491
2025-02-25 09:34:59 +01:00
Meghana Gupta
6a47193e0c Fix LICM for begin_access/end_access in OSSA
LICM in OSSA is enabled only for instructions involving trvial values.

begin_access/end_access is eligible for LICM in OSSA. However we need to
collect applies to check for conflicts.

rdar://143835241
2025-01-29 12:54:35 -08:00
Erik Eckstein
48b913af4b Optimizer: make the hasOwnershipOperandsOrResults utility available in OwnershipOptUtils 2025-01-02 10:42:01 +01:00
Erik Eckstein
1bd74d1fc3 LICM: (limited) support for OSSA
The first step: allow hoisting instructions which only have trivial operands and results.
2024-12-11 12:32:33 +01:00
Meghana Gupta
49bb19d27e Fix debug info in LICM
Use RegularLocation::getAutoGeneratedLocation() while hoisting a load.
Previously the source location of the preheader's terminator was used,
this need not be a RegularLocationKind triggering verification errors.
2024-08-14 01:49:58 +05:30
Erik Eckstein
8f2531348d LICM: make sure that getMemoryBehavior is only called for address values
It was called for non-address operands of fix_lifetime.
Just exclude such fix_lifetime instructions from moving. It's not important anyway.
2024-07-29 17:33:44 +02:00
Tim Kientzle
1d961ba22d Add #include "swift/Basic/Assertions.h" to a lot of source files
Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
2024-06-05 19:37:30 -07:00
Nate Chandler
7753b0fa78 [LICM] Bail on unrefable storage.
When computing an access path, if a struct with unreferenceable storage
is encountered, bail out.  Otherwise, an attempt will be made to
construct such a struct via the struct instruction which isn't possible.

rdar://127013278
2024-04-24 18:04:21 -07:00
Michael Gottesman
11f0ff6e32 [sil] Ensure that all SILValues have a parent function by making it so that SILUndef is uniqued at the function instead of module level.
For years, optimizer engineers have been hitting a common bug caused by passes
assuming all SILValues have a parent function only to be surprised by SILUndef.
Generally we see SILUndef not that often so we see this come up later in
testing. This patch eliminates that problem by making SILUndef uniqued at the
function level instead of the module level. This ensures that it makes sense for
SILUndef to have a parent function, eliminating this possibility since we can
define an API to get its parent function.

rdar://123484595
2024-02-27 13:14:47 -08:00
Ben Barham
ef8825bfe6 Migrate llvm::Optional to std::optional
LLVM has removed llvm::Optional, move over to std::optional. Also
clang-format to fix up all the renamed #includes.
2024-02-21 11:20:06 -08:00
Erik Eckstein
7c46a4d9e2 LICM: don't let cond_fail prevent hoisting initializations of global variables
And fix the corresponding test file
2024-01-02 09:42:59 +01:00
Saleem Abdulrasool
55531ea5d9 Merge pull request #67085 from hjyamauchi/licm
Don't try to hoist/sink {begin,end}_access when the loop has no exits
2023-07-05 20:58:05 -07:00
Hiroshi Yamauchi
cd5275a12f Don't try to hoist/sink {begin,end}_access when the loop has no exits
This fixes compiler crash with message `LICM: Could not perform must-sink instruction`.

Fixes: #67084
2023-07-02 13:04:14 -07:00
Evan Wilde
f3ff561c6f [NFC] add llvm namespace to Optional and None
This is phase-1 of switching from llvm::Optional to std::optional in the
next rebranch. llvm::Optional was removed from upstream LLVM, so we need
to migrate off rather soon. On Darwin, std::optional, and llvm::Optional
have the same layout, so we don't need to be as concerned about ABI
beyond the name mangling. `llvm::Optional` is only returned from one
function in
```
getStandardTypeSubst(StringRef TypeName,
                     bool allowConcurrencyManglings);
```
It's the return value, so it should not impact the mangling of the
function, and the layout is the same as `std::optional`, so it should be
mostly okay. This function doesn't appear to have users, and the ABI was
already broken 2 years ago for concurrency and no one seemed to notice
so this should be "okay".

I'm doing the migration incrementally so that folks working on main can
cherry-pick back to the release/5.9 branch. Once 5.9 is done and locked
away, then we can go through and finish the replacement. Since `None`
and `Optional` show up in contexts where they are not `llvm::None` and
`llvm::Optional`, I'm preparing the work now by going through and
removing the namespace unwrapping and making the `llvm` namespace
explicit. This should make it fairly mechanical to go through and
replace llvm::Optional with std::optional, and llvm::None with
std::nullopt. It's also a change that can be brought onto the
release/5.9 with minimal impact. This should be an NFC change.
2023-06-27 09:03:52 -07:00
Erik Eckstein
47a7acd4a7 LICM: hoist builtin "once" calls out of loops
`builtin "once"` calls are a result of inlining global accessors
2023-05-08 21:23:36 +02:00
Erik Eckstein
8c05024ea6 SIL: move the SILInstruction::MemoryBehavior enum out of SILInstruction into the swift namespace 2023-03-21 15:33:09 +01:00
Erik Eckstein
7d8bf37e5e change to the new llvm::Optional APIs
This is a follow-up of https://github.com/apple/swift/pull/62217
2023-01-25 09:18:36 +01:00
Erik Eckstein
ae7e5b5d90 LICM: add limits for the number of alias checks to avoid large compile times in corner cases
Avoid quadratic complexity in corner cases. Usually, these limits will not be exceeded.
2023-01-04 13:36:44 +01:00
Erik Eckstein
814d890946 Use the new side effects in LICM 2022-12-21 17:41:46 +01:00
Erik Eckstein
ab1b343dad use new llvm::Optional API
`getValue` -> `value`
`getValueOr` -> `value_or`
`hasValue` -> `has_value`
`map` -> `transform`

The old API will be deprecated in the rebranch.
To avoid merge conflicts, use the new API already in the main branch.

rdar://102362022
2022-11-21 19:44:24 +01:00
Josh Soref
730b16c569 Spelling siloptimizer
* access
* accessed
* accesses
* accessor
* acquiring
* across
* activated
* additive
* address
* addresses'
* aggregated
* analysis
* and
* appropriately
* archetype
* argument
* associated
* availability
* barriers
* because
* been
* beginning
* belongs
* beneficial
* blocks
* borrow
* builtin
* cannot
* canonical
* canonicalize
* clazz
* cleanup
* coalesceable
* coalesced
* comparisons
* completely
* component
* computed
* concrete
* conjunction
* conservatively
* constituent
* construct
* consuming
* containing
* covered
* creates
* critical
* dataflow
* declaration
* defined
* defining
* definition
* deinitialization
* deliberately
* dependencies
* dependent
* deserialized
* destroy
* deterministic
* deterministically
* devirtualizes
* diagnostic
* diagnostics
* differentiation
* disable
* discipline
* dominate
* dominates
* don't
* element
* eliminate
* eliminating
* elimination
* embedded
* encounter
* epilogue
* epsilon
* escape
* escaping
* essential
* evaluating
* evaluation
* evaluator
* executing
* existential
* existentials
* explicit
* expression
* extended
* extension
* extract
* for
* from
* function
* generic
* guarantee
* guaranteed
* happened
* heuristic
* however
* identifiable
* immediately
* implementation
* improper
* include
* infinite
* initialize
* initialized
* initializer
* inside
* instruction
* interference
* interferes
* interleaved
* internal
* intersection
* intractable
* intrinsic
* invalidates
* irreducible
* irrelevant
* language
* lifetime
* literal
* looks
* materialize
* meaning
* mergeable
* might
* mimics
* modification
* modifies
* multiple
* mutating
* necessarily
* necessary
* needsmultiplecopies
* nonetheless
* nothing
* occurred
* occurs
* optimization
* optimizing
* original
* outside
* overflow
* overlapping
* overridden
* owned
* ownership
* parallel
* parameter
* paths
* patterns
* pipeline
* plottable
* possible
* potentially
* practically
* preamble
* precede
* preceding
* predecessor
* preferable
* preparation
* probably
* projection
* properties
* property
* protocol
* reabstraction
* reachable
* recognized
* recursive
* recursively
* redundant
* reentrancy
* referenced
* registry
* reinitialization
* reload
* represent
* requires
* response
* responsible
* retrieving
* returned
* returning
* returns
* rewriting
* rewritten
* sample
* scenarios
* scope
* should
* sideeffects
* similar
* simplify
* simplifycfg
* somewhat
* spaghetti
* specialization
* specializations
* specialized
* specially
* statistically
* substitute
* substitution
* succeeds
* successful
* successfully
* successor
* superfluous
* surprisingly
* suspension
* swift
* targeted
* that
* that our
* the
* therefore
* this
* those
* threshold
* through
* transform
* transformation
* truncated
* ultimate
* unchecked
* uninitialized
* unlikely
* unmanaged
* unoptimized key
* updataflow
* usefulness
* utilities
* villain
* whenever
* writes

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2022-10-03 18:31:33 -04:00
Michael Gottesman
1e6187c4f4 [sil] Update all usages of old API SILValue::getOwnershipKind() in favor of new ValueBase::getOwnershipKind().
Andy some time ago already created the new API but didn't go through and update
the old occurences. I did that in this PR and then deprecated the old API. The
tree is clean, so I could just remove it, but I decided to be nicer to
downstream people by deprecating it first.
2022-07-26 11:46:23 -07:00
Andrew Trick
c4079179ee Trivial fix for an LICM assertion in projectLoadValue
This seems to compile correctly in release builds. But it does go
through an llvm_unreachable path, so really isn't safe to leave unfixed.

When the accessPath has an offset, propagate it through the recursive
calls. This may have happened when the offset was moved outside of the
sequence of path indices. The code rebuilds a path from the indices
without adding back the offset.

LICM asserts during projectLoadValue when it needs to rematerialize a
loaded value within the loop using projections and the loop-invariant
address is an index_addr.

Basically:

  %a = index_addr %4 : $*Wrapper, %1 : $Builtin.Word
  store %_ to %a : $*Wrapper
  br loop:

loop:
  %f = struct_element_addr %a
  %v = load %f : $Value
  %s = struct $Wrapper (%v : $Value)
  store %s to %a : $*Wrapper

Where the store inside the loop is deleted. And where the load is
hoisted out of the loop, but now loads $Wrapper instead of $Value.

Fixes rdar://92191909 (LICM assertion: isSubObjectProjection(), MemAccessUtils.h, line 1069)
2022-04-22 16:07:12 -07:00
Andrew Trick
e85228491d Rename AccessedStorage to AccessStorage
to be consistent with AccessPath and AccessBase.

Otherwise, the arbitrary name difference adds constant friction.
2021-09-21 23:18:24 -07:00
Meghana Gupta
64095a6d6b Fix use of MemAcessUtils in LICM (#38784)
AccessPathWithBase::compute can return a valid access path with unidentified base.
In such cases, we cannot LICM stores, because there is no base address to check if it is invariant
2021-08-06 18:34:55 -07:00
Erik Eckstein
d2fc6eb3b5 AliasAnalysis: make AliasAnalysis a function analysis and simplify the cache keys
Instead of caching alias results globally for the module, make AliasAnalysis a FunctionAnalysisBase which caches the alias results per function.
Why?
* So far the result caches could only grow. They were reset when they reached a certain size. This was not ideal. Now, they are invalidated whenever the function changes.
* It was not possible to actually invalidate an alias analysis result. This is required, for example in TempRValueOpt and TempLValueOpt (so far it was done manually with invalidateInstruction).
* Type based alias analysis results were also cached for the whole module, while it is actually dependent on the function, because it depends on the function's resilience expansion. This was a potential bug.

I also added a new PassManager API to directly get a function-base analysis:
    getAnalysis(SILFunction *f)

The second change of this commit is the removal of the instruction-index indirection for the cache keys. Now the cache keys directly work on instruction pointers instead of instruction indices. This reduces the number of hash table lookups for a cache lookup from 3 to 1.
This indirection was needed to avoid dangling instruction pointers in the cache keys. But this is not needed anymore, because of the new delayed instruction deletion mechanism.
2021-05-26 21:57:54 +02:00
Andrew Trick
66752e9724 OSSA: Rewrite address cloning code to fix issues.
Generalize the AccessUseDefChainCloner in MemAccessUtils. It was
always meant to work this way, just needed a client.

Add a new API AccessUseDefChainCloner::canCloneUseDefChain().

Add a bailout for begin_borrow and mark_dependence. Those
projections may appear on an access path, but they can't be
individually cloned without compensating.

Delete InteriorPointerAddressRebaseUseDefChainCloner.

Add a check in OwnershipRAUWHelper for canCloneUseDefChain.

Add test cases for begin_borrow and mark_dependence.
2021-02-24 22:18:21 -08:00
Erik Eckstein
fe10f98cf0 SIL: rename the SILBitfield.h header file to BasicBlockBits.h
NFC
2021-02-12 11:15:55 +01:00
Erik Eckstein
f48191966c SILOptimizer: use BasicBlockSet instead of SmallPtrSet in various transformations.
It reduces compile time.
2021-01-27 10:31:17 +01:00
Meghana Gupta
845e63f901 Support ownershipKind in SILSSAUpdater 2021-01-21 16:27:50 -08:00
Andrew Trick
437765e7e1 LICM: split loads that are wider than the loop-stored value.
For combined load-store hoisting, split loads that contain the
loop-stored value into a single load from the same address as the
loop-stores, and a set of loads disjoint from the loop-stores. The
single load will be hoisted while sinking the stores to the same
address. The disjoint loads will be hoisted normally in a subsequent
iteration on the same loop.

loop:
  load %outer
  store %inner1
exit:

Will be split into

loop:
  load %inner1
  load %inner2
  store %inner1
exit:

Then, combined load/store hoisting will produce:

  load %inner1
loop:
  load %inner2
exit:
  store %inner1
2020-11-12 13:59:43 -08:00
Andrew Trick
d86099f05f Use AccessPath in LICM.
The LICM algorithm was not robust with respect to address projection
because it identifies a projected address by its SILValue. This should
never be done! Use AccessPath instead.

Fixes regressions caused by rdar://66791257 (Print statement provokes
"Can't unsafeBitCast between types of different sizes" when
optimizations enabled)
2020-11-10 12:19:18 -08:00
Andrew Trick
6f2cda1390 Add AccessUseVisitor and cleanup related APIs.
Add AccesssedStorage::compute and computeInScope to mirror AccessPath.

Allow recovering the begin_access for Nested storage.

Adds AccessedStorage.visitRoots().
2020-10-16 15:00:10 -07:00
Andrew Trick
cc0aa2f8b8 Add an AccessPath abstraction and formalize memory access
Things that have come up recently but are somewhat blocked on this:

- Moving AccessMarkerElimination down in the pipeline
- SemanticARCOpts correctness and improvements
- AliasAnalysis improvements
- LICM performance regressions
- RLE/DSE improvements

Begin to formalize the model for valid memory access in SIL. Ignoring
ownership, every access is a def-use chain in three parts:

object root -> formal access base -> memory operation address

AccessPath abstracts over this path and standardizes the identity of a
memory access throughout the optimizer. This abstraction is the basis
for a new AccessPathVerification.

With that verification, we now have all the properties we need for the
type of analysis requires for exclusivity enforcement, but now
generalized for any memory analysis. This is suitable for an extremely
lightweight analysis with no side data structures. We currently have a
massive amount of ad-hoc memory analysis throughout SIL, which is
incredibly unmaintainable, bug-prone, and not performance-robust. We
can begin taking advantage of this verifably complete model to solve
that problem.

The properties this gives us are:

Access analysis must be complete over memory operations: every memory
operation needs a recognizable valid access. An access can be
unidentified only to the extent that it is rooted in some non-address
type and we can prove that it is at least *not* part of an access to a
nominal class or global property. Pointer provenance is also required
for future IRGen-level bitfield optimizations.

Access analysis must be complete over address users: for an identified
object root all memory accesses including subobjects must be
discoverable.

Access analysis must be symmetric: use-def and def-use analysis must
be consistent.

AccessPath is merely a wrapper around the existing accessed-storage
utilities and IndexTrieNode. Existing passes already very succesfully
use this approach, but in an ad-hoc way. With a general utility we
can:

- update passes to use this approach to identify memory access,
  reducing the space and time complexity of those algorithms.

- implement an inexpensive on-the-fly, debug mode address lifetime analysis

- implement a lightweight debug mode alias analysis

- ultimately improve the power, efficiency, and maintainability of
  full alias analysis

- make our type-based alias analysis sensistive to the access path
2020-10-16 15:00:10 -07:00
Andrew Trick
5ae231eaab Rename getFieldNo() to getFieldIndex().
Do I really need to justify this?
2020-09-24 22:44:13 -07:00
Andrew Trick
4ca3c232b7 Fix LICM to avoid hoisting never-executed traps
It is legal for the optimizer to consider code after a loop always
reachable, but when a loop has no exits, or when the loops exits are
dominated by a conditional statement, we should not consider
conditional statements within the loop as dominating all possible
execution paths through the loop. At least not when there is at least
one path through the loop that contains a "synchronization point",
such as a function that may contain a memory barrier, perform I/O, or
exit the program.

Sadly, we still don't model synchronization points in the optimizer,
so we need to conservatively assume all loops have a synchronization
point and avoid hoisting conditional traps that may never be executed.

Fixes rdar://66791257 (Print statement provokes "Can't unsafeBitCast
between types of different sizes" when optimizations enabled)

Originated in 2014.
2020-08-12 13:55:46 -07:00
Michael Gottesman
d064241599 [ssa-updater] Modernize style before adding support for guaranteed parameters.
Specifically:

1. I made methods, variables camelCase.
2. I expanded out variable names (e.x.: bb -> block, predBB -> predBlocks, U -> wrappedUse).
3. I changed typedef -> using.
4. I changed a few c style for loops into for each loops using llvm::enumerate.

NOTE: I left the parts needed for syncing to LLVM in the old style since LLVM
needs these to exist for CRTP to work correctly for the SILSSAUpdater.
2020-08-06 15:41:00 -07:00
Andrew Trick
5826e75b00 Generalize the MemAccessUtils API.
For use outside access enforcement passes.

Add isUniquelyIdentifiedAfterEnforcement.

Rename functions for clarity and generality.

Rename isUniquelyIdentifiedOrClass to isFormalAccessBase.

Rename findAccessedStorage to identifyFormalAccess.

Rename findAccessedStorageNonNested to findAccessedStorage.

Part of generalizing the utility for use outside the access
enforcement passes.
2020-07-17 10:13:20 -07:00
Erik Eckstein
ba4da8e0d3 LICM: enable more stores to moved out of a loop
Even if a store is not dominating the loop exits, it makes sense to move it out of the loop if the pre-header also as a store to the same memory location.
When this is done, dead-store-elimination can then most likely remove the store in the pre-header.
2020-05-18 15:31:34 +02:00
Andrew Trick
1c12de3241 Fix LICM combined load/store hoisting/sinking optimization.
This loop optimization hoists and sinks a group of loads and stores to
the same address.

Consider this SIL...

PRELOOP:
  %stackAddr = alloc_stack $Index
  %outerAddr1 = struct_element_addr %stackAddr : $*Index, #Index.value
  %innerAddr1 = struct_element_addr %outerAddr1 : $*Int, #Int._value

  %outerAddr2 = struct_element_addr %stackAddr : $*Index, #Index.value
  %innerAddr2 = struct_element_addr %outerAddr2 : $*Int, #Int._value

LOOP:
  %_ = load %innerAddr2 : $*Builtin.Int64
  store %_ to %outerAddr2 : $*Int
  %_ = load %innerAddr1 : $*Builtin.Int64

There are two bugs:

1) LICM miscompiles code during combined load/store hoisting and sinking.

When the loop contains an aliasing load from a difference projection
value, the optimization sinks the store but never replaces the
load. At runtime, the load reads a stale value.

FIX: isOnlyLoadedAndStored needs to check for other load instructions
before hoisting/sinking a seemingly unrelated set of
loads/stores. Checking side effect instructions is insufficient. The
same bug could happen with stores, which also do not produce side
effects.

Fixes <rdar://61246061> LICM miscompile:
Combined load/store hoisting/sinking with aliases

2) The LICM algorithm is not robust with respect to address projection
   because it identifies a projected address by its SILValue. This
   should never be done! It is trivial to represent a project path
   using an IndexTrieNode (there is also an abstraction called
   "ProjectionPath", but it should _never_ actually be stored by an
   analysis because of the time and space complexity of doing so).

The second bug is not necessary to fix for correctness, so will be
fixed in a follow-up commit.
2020-04-03 08:25:20 -07:00
Andrew Trick
73ee38c162 Add tracing to LICM for reloaded store/restored load optimization. 2020-04-03 08:25:20 -07:00
Erik Eckstein
3ad7d548c2 LICM: hoist calls to global_init functions
Global initializers are executed only once.
Therefore it's possible to hoist such an initializer call to a loop pre-header - in case there are no conflicting side-effects in the loop before the call.
Also, the call must post-dominate the loop pre-header. Otherwise it would be executed speculatively.
2020-03-23 16:08:56 +01:00
swift_jenkins
47af5bcec0 Merge remote-tracking branch 'origin/master' into master-next 2019-12-18 17:39:43 -08:00
Ravi Kandhadai
935686460c [SIL Optimization] Create a new utility InstructionDeleter to delete instructions
and eliminate dead code. This is meant to be a replacement for the utility:
recursivelyDeleteTriviallyDeadInstructions. The new utility performs more aggresive
dead-code elimination for ownership SIL.

This patch also migrates most non-force-delete uses of
recursivelyDeleteTriviallyDeadInstructions to the new utility.
and migrates one force-delete use of recursivelyDeleteTriviallyDeadInstructions
(in IRGenPrepare) to use the new utility.
2019-12-18 13:17:17 -08:00
swift-ci
64f712b1b4 Merge remote-tracking branch 'origin/master' into master-next 2019-11-01 11:09:36 -07:00
Erik Eckstein
c29cdd972b LICM: add an optimization to move multiple loads and stores from/to the same memory location out of a loop.
This is a combination of load hoisting and store sinking, e.g.

  preheader:
    br header_block
  header_block:
    %x = load %not_aliased_addr
    // use %x and define %y
    store %y to %not_aliased_addr
    ...
  exit_block:

is transformed to:

  preheader:
    %x = load %not_aliased_addr
    br header_block
  header_block:
    // use %x and define %y
    ...
  exit_block:
    store %y to %not_aliased_addr
2019-10-31 19:07:17 +01:00
Erik Eckstein
6c6b6849e0 LICM: rename MayWrites -> SideEffectInsts
Because the set includes all side-effect instructions, also may-reads.
NFC
2019-10-31 12:52:25 +01:00
swift-ci
90fcb675dc Merge remote-tracking branch 'origin/master' into master-next 2019-10-30 09:50:07 -07:00
eeckstein
7df5feb697 Revert "LICM: add an optimization to move multiple loads and stores from/to the same memory location out of a loop." 2019-10-30 17:26:49 +01:00
swift-ci
e981f7fa0d Merge remote-tracking branch 'origin/master' into master-next 2019-10-30 04:29:59 -07:00