Commit Graph

410 Commits

Author SHA1 Message Date
Erik Eckstein
14422cdb50 libswift: Add the StackList data structure
StackList is a very efficient data structure for worklist type things.
This is a port of the C++ utility with the same name.

Compared to Array, it does not require any memory allocations.
2021-06-09 11:33:41 +02:00
Erik Eckstein
81f5e2f467 libswift: Infrastructure to call libswift function passes from the SILOptimizer's PassManager
With the macro SWIFT_FUNCTION_PASS a new libswift function pass can be defined in Passes.def.
The SWIFT_FUNCTION_PASS_WITH_LEGACY is similar, but it allows to keep an original C++ “legacy” implementation of the pass, which is used if the compiler is not built with libswift.
2021-06-09 11:30:59 +02:00
Erik Eckstein
4977850092 SIL: remove the notifyDeleteHandlers mechanism
It's not needed anymore with delayed instruction deletion.
It was used for two purposes:
1. For analysis, which cache instructions, to avoid dangling instruction pointers
2. For passes, which maintain worklists of instructions, to remove a deleted instructions from the worklist. This is now done by checking SILInstruction::isDeleted().
2021-05-26 21:57:54 +02: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
Adrian Prantl
38b2660043 Expose the LowerHopToActorPass as API so LLDB can schedule it
rdar://75905336
2021-04-28 18:16:22 -07:00
Nate Chandler
d765434e2b [IRGen] Restored non-constant async function calls in PAFs.
Previously, because partial apply forwarders for async functions were
not themselves fully-fledged async functions, they were not able to
handle dynamic functions.  Specifically, the reason was that it was not
possible to produce an async function pointer for the partial apply
forwarder because the size to be used was not knowable.

Thanks to https://github.com/apple/swift/pull/36700, that cause has been
eliminated.  With it, partial apply forwarders are fully-fledged async
functions and in particular have their own async function pointers.
Consequently, it is again possible for these partial apply forwarders to
handle non-constant function pointers.

Here, that behavior is restored, by way of reverting part of
ee63777332 while preserving the ABI it
introduced.

rdar://76122027
2021-04-06 15:51:32 -07:00
John McCall
4f6f8b3377 Rewrite hop_to_executor so that it takes a Builtin.Executor in IRGen
The comment in LowerHopToActor explains the design here.
We want SILGen to emit hops to actors, ignoring executors,
because it's easier to fully optimize in a world where deriving
an executor is a non-trivial operation.  But we also want something
prior to IRGen to lower the executor derivation because there are
useful static optimizations we can do, such as doing the derivation
exactly once on a dominance path and strength-reducing the derivation
(e.g. exploiting static knowledge that an actor is a default actor).

There are probably phase-ordering problems with doing this so late,
but hopefully they're restricted to situations like actors that
share an executor.  We'll want to optimize that eventually, but
in the meantime, this unblocks the executor work.
2021-03-30 20:08:41 -04:00
Nate Chandler
ee63777332 [IRGen] Fix ABI for thick async functions.
Previously, thick async functions were represented sometimes as a pair
of (AsyncFunctionPointer, nullptr)--when the thick function was produced
via a thin_to_thick_function, e.g.--and sometimes as a pair of
(FunctionPointer, ThickContext)--when the thick function was produced by
a partial_apply--with the size stored in the slot of the ThickContext.

That optimized for the wrong case: partial applies of dynamic async
functions; in that case, there is no appropriate AsyncFunctionPointer to
form when lowering the partial_apply instruction.  The far more common
case is to know exactly which function is being partially applied.  In
that case, we can form the appropriate AsyncFunctionPointer.

Furthermore, the previous representation made calling a thick function
more complex: it was always necessary to check whether the context was
in fact null and then proceed along two different paths depending.

Here, that behavior is corrected by creating a thunk in a mandatory
IRGen SIL pass in the case that the function that is being partially
applied is dynamic.  That new thunk is then partially applied in place
of the original partial_apply of the dynamic function.
2021-03-15 13:37:40 -07:00
Andrew Trick
b689b1dabe Rename GuaranteedARCOpts to MandatoryARCOpts.
This bleeds into the implementation where "guaranteed" is used
everywhere to talk about optimization of guaranteed values. We need to
use mandatory to indicate we're talking about the pass pipeline.
2021-03-02 22:20:13 -08:00
Erik Eckstein
a17f8c2f3f SILOptimizer: add a diagnostics pass to warn about lifetime issues with weak references.
The DiagnoseLifetimeIssuesPass pass prints a warning if an object is stored to a weak property (or is weakly captured) and destroyed before the property (or captured reference) is ever used again.
This can happen if the programmer relies on the lexical scope to keep an object alive, but copy-propagation can shrink the object's lifetime to its last use.
For example:

  func test() {
    let k = Klass()
      // k is deallocated immediately after the closure capture (a store_weak).
      functionWithClosure({ [weak k] in
                            // crash!
                            k!.foo()
                          })
    }

Unfortunately this pass can only catch simple cases, but it's better than nothing.

rdar://73910632
2021-02-15 11:11:35 +01:00
Erik Eckstein
9f616e5e37 DeadFunctionElimination: rename the pass and some other refactoring
The main change is to rename DeadFunctionElimination -> DeadFunctionAndGlobalElimination, because the pass is now also doing dead-global elimination.
A second change is to remove the FunctionLivenessComputation base class. It’s not used anywhere else.
2021-02-09 19:56:43 +01:00
Andrew Trick
02784a2dc2 Add a variation on CopyPropagation to handle debug_value correctly.
MandatoryCopyPropagation must be a separate pass in order to preserve
all debug_value instructions. CopyPropagation cannot preserve
debug_value because, as a rule, debug information cannot affect -O
behavior.
2021-01-05 20:38:31 -08:00
Andrew Trick
cead6a5122 Add an OptimizedMandatoryCombine pass variant.
It's against the principles of pass design to check the driver mode
within the pass. A pass always needs to do the same thing regardless
of where it runs in the pass pipeline. It also needs to be possible to
test passes in isolation.
2021-01-01 19:22:19 -08:00
eeckstein
56928ba851 Merge pull request #34593 from eeckstein/optimize_hte
[concurrency] SILOptimizer: optimize hop_to_executor instructions.
2020-11-09 09:23:46 +01:00
Ben Barham
7cee600bcd [SILGen] Add flag to skip typechecking and SIL gen for function bodies
Adds a new flag "-experimental-skip-all-function-bodies" that skips
typechecking and SIL generation for all function bodies (where
possible).

`didSet` functions are still typechecked and have SIL generated as their
body is checked for the `oldValue` parameter, but are not serialized.
Parsing will generally be skipped as well, but this isn't necessarily
the case since other flags (eg. "-verify-syntax-tree") may force delayed
parsing off.
2020-11-06 12:08:19 +10:00
Erik Eckstein
a47ebabe54 [concurrency] SILOptimizer: optimize hop_to_executor instructions.
* Redundant hop_to_executor elimination: if a hop_to_executor is dominated by another hop_to_executor with the same operand, it is eliminated:

      hop_to_executor %a
      ... // no suspension points
      hop_to_executor %a // can be eliminated

* Dead hop_to_executor elimination: if a hop_to_executor is not followed by any code which requires to run on its actor's executor, it is eliminated:

      hop_to_executor %a
      ... // no instruction which require to run on %a
      return

rdar://problem/70304809
2020-11-05 18:48:22 +01:00
Andrew Trick
3128eae3f0 Add NestedSemanticFunctionCheck diagnostic
to check for improperly nested '@_semantic' functions.

Add a missing @_semantics("array.init") in ArraySlice found by the
diagnostic.

Distinguish between array.init and array.init.empty.

Categorize the types of semantic functions by how they affect the
inliner and pass pipeline, and centralize this logic in
PerformanceInlinerUtils. The ultimate goal is to prevent inlining of
"Fundamental" @_semantics calls and @_effects calls until the late
pipeline where we can safely discard semantics. However, that requires
significant pipeline changes.

In the meantime, this change prevents the situation from getting worse
and makes the intention clear. However, it has no significant effect
on the pass pipeline and inliner.
2020-10-26 17:02:33 -07:00
Andrew Trick
b2d1ac1631 Add AccessPathVerification pass and run it in the pipeline. 2020-10-16 15:00:10 -07:00
Arnold Schwaighofer
b994bf3191 Add support for _specialize(exported: true, ...)
This attribute allows to define a pre-specialized entry point of a
generic function in a library.

The following definition provides a pre-specialized entry point for
`genericFunc(_:)` for the parameter type `Int` that clients of the
library can call.

```
@_specialize(exported: true, where T == Int)
public func genericFunc<T>(_ t: T) { ... }
```

Pre-specializations of internal `@inlinable` functions are allowed.

```
@usableFromInline
internal struct GenericThing<T> {
  @_specialize(exported: true, where T == Int)
  @inlinable
  internal func genericMethod(_ t: T) {
  }
}
```

There is syntax to pre-specialize a method from a different module.

```
import ModuleDefiningGenericFunc

@_specialize(exported: true, target: genericFunc(_:), where T == Double)
func prespecialize_genericFunc(_ t: T) { fatalError("dont call") }

```

Specially marked extensions allow for pre-specialization of internal
methods accross module boundries (respecting `@inlinable` and
`@usableFromInline`).

```
import ModuleDefiningGenericThing
public struct Something {}

@_specializeExtension
extension GenericThing {
  @_specialize(exported: true, target: genericMethod(_:), where T == Something)
  func prespecialize_genericMethod(_ t: T) { fatalError("dont call") }
}
```

rdar://64993425
2020-10-12 09:19:29 -07:00
Meghana Gupta
163d47ec90 Revert "Revert #33106 and #33205" (#34106) 2020-09-28 23:08:14 -07:00
Meghana Gupta
77a76a8422 Revert "Merge pull request #33205 from meg-gupta/ometofunctionpass"
This reverts commit 8dbac48c18, reversing
changes made to c22ba90700.
2020-09-25 11:49:52 -07:00
Meghana Gupta
9c9a8ef224 Allow OME to run mandatorily 2020-09-22 18:02:04 -07:00
David Zarzycki
1e940c2c7e [NFC] Fix -Wsuggest-override warnings
LLVM, as of 77e0e9e17daf0865620abcd41f692ab0642367c4, now builds with
-Wsuggest-override. Let's clean up the swift sources rather than disable
the warning locally.
2020-08-13 16:17:46 -04:00
Erik Eckstein
2a035432e7 SILOptimizer: make a separate SROA pass for high-level SIL, which doesn't split String types.
The StringOptimization relies on seeing String values a a whole and not being split.
2020-08-03 12:01:29 +02:00
Erik Eckstein
7f684b62e2 SIL optimizer: Add a new string optimization.
Optimizes String operations with constant operands.

Specifically:
  * Replaces x.append(y) with x = y if x is empty.
  * Removes x.append("")
  * Replaces x.append(y) with x = x + y if x and y are constant strings.
  * Replaces _typeName(T.self) with a constant string if T is statically known.

With this optimization it's possible to constant fold string interpolations, like "the \(Int.self) type" -> "the Int type"

This new pass runs on high-level SIL, where semantic calls are still in place.

rdar://problem/65642843
2020-07-27 21:32:56 +02:00
Andrew Trick
5091aee6f2 Add an AccessedStorageDumper pass to verify findAccessedStorage.
Rename the existing pass to AccessedStorageAnalysisDumper.

AccessedStorage is useful on its own as a utility without the
analysis. We need a way to test the utility itself.

Add test cases for the previous commit that introduced
FindPhiStorageVisitor.
2020-07-20 16:42:28 -07:00
Michael Gottesman
4559db01cb [opt-remark] Add a pass called Opt Remark Generator that implements small opt remarks that do not result from large dataflow checks.
We do allow for limited def-use traversal to do things like find debug_value.
But nothing like a full data flow pass. As a proof of concept I just emit
opt-remarks when we see retains, releases. Eventually I would like to also print
out which lvalue the retain is from but that is more than I want to do with this
proof of concept.
2020-07-14 19:32:46 -07:00
Erik Eckstein
9e92389fa5 SILOptimizer: a new "TempLValueOpt" optimization pass for copy_addr
Optimizes copies from a temporary (an "l-value") to a destination.

    %temp = alloc_stack $Ty
    instructions_which_store_to %temp
    copy_addr [take] %temp to %destination
    dealloc_stack %temp

is optimized to

    destroy_addr %destination
    instructions_which_store_to %destination

The name TempLValueOpt refers to the TempRValueOpt pass, which performs a related transformation, just with the temporary on the "right" side.
The TempLValueOpt is similar to CopyForwarding::backwardPropagateCopy.
It's more restricted (e.g. the copy-source must be an alloc_stack).
That enables other patterns to be optimized, which backwardPropagateCopy cannot handle.

This pass also performs a small peephole optimization which simplifies copy_addr - destroy sequences.

    copy_addr %source to %destination
    destroy_addr %source

is replace with

    copy_addr [take] %source to %destination
2020-06-22 13:47:31 +02:00
Michael Gottesman
c749f37e80 [ownership] Add new OwnershipEliminatorPass that does not run when the current module is the stdlib.
As part of bringing up ossa on the rest of the optimizer, I am going to be first
moving ossa back for the stdlib module since the stdlib module does not have any
sil based dependencies. To do so, I am adding this pass that I can place at the
beginning of the pipeline (where NonTransparentFunctionOwnershipModelEliminator
runs today) and then move NonTransparentFunctionOwnershipModelEliminator down as
I update passes. If we are processing the stdlib, the ome doesn't run early and
instead runs late. If we are not processing the stdlib, we perform first an OME
run early and then perform an additional OME run that does nothing since
lowering ownership is an idempotent operation.
2020-06-09 13:55:02 -07:00
Joe Groff
e3ce94d97d Introduce a PruneVTables pass to mark non-overridden entries.
Start with some high-level checks of whether a declaration is formally final, or has no visible overrides
in the domain of the program visible to the SIL module.

We can eventually adopt more of the logic from the Devirtualizer pass to tell whether call sites are
"effectively final", and maybe make the Devirtualizer consume that information applied by this pass.
2020-06-05 11:03:04 -07:00
Varun Gandhi
82e87f6399 [NFC] Remove self-include in PassPipeline.h. 2020-05-31 13:07:45 -07:00
Varun Gandhi
044189471b [NFC] Remove redundant includes for llvm/ADT/ArrayRef.h. 2020-05-31 13:06:57 -07:00
Erik Eckstein
9722578df6 SILOptimizer: a new optimization for copy-on-write
Constant folds the uniqueness result of begin_cow_mutation instructions, if it can be proved that the buffer argument is uniquely referenced.
For example:

     %buffer = end_cow_mutation %mutable_buffer
     // ...
     // %buffer does not escape here
     // ...
     (%is_unique, %mutable_buffer2) = begin_cow_mutation %buffer
     cond_br %is_unique, ...

is replaced with

     %buffer = end_cow_mutation [keep_unique] %mutable_buffer
     // ...
     (%not_used, %mutable_buffer2) = begin_cow_mutation %buffer
     %true = integer_literal 1
     cond_br %true, ...

Note that the keep_unique flag is set on the end_cow_mutation because the code now relies on that the buffer is really uniquely referenced.

The optimization can also handle def-use chains between end_cow_mutation and begin_cow_mutation which involve phi-arguments.

An additional peephole optimization is performed: if the begin_cow_mutation is the only use of the end_cow_mutation, the whole pair of instructions is eliminated.
2020-05-26 18:01:17 +02:00
Erik Eckstein
ad99b9d4f8 SILOptimizer: a new phi-argument expansion optimization.
If only a single field of a struct phi-argument is used, replace the argument by the field value.

     br bb(%str)
   bb(%phi):
     %f = struct_extract %phi, #Field // the only use of %phi
     use %f

is replaced with

     %f = struct_extract %str, #Field
     br bb(%f)
   bb(%phi):
     use %phi

This also works if the phi-argument is in a def-use cycle.

The new PhiExpansionPass is in the same file as the RedundantPhiEliminationPass. Therefore I renamed the source file to PhiArgumentOptimizations.cpp
2020-05-25 09:36:09 +02:00
Michael Gottesman
0640f33f12 Merge pull request #31539 from gottesmm/pr-88ca78b6938c98c9da3c1f7a88d81fac297d0ee2
[ownership] Add new pass OwnershipVerifierTextualErrorDumper and use it in ownership verifier FileCheck tests instead of SILVerifier.
2020-05-04 18:16:36 -07:00
Michael Gottesman
82b08c5722 [ownership] Add new pass OwnershipVerifierTextualErrorDumper and use it in ownership verifier FileCheck tests instead of SILVerifier.
This will make it easier for me with a few further refactors to make the
ownership verifier testing mode emit per function error numbers instead of the
global error number that it is emitting now.

The reason why this is necessary is that today, the verification by
-sil-verify-all causes the errors to be emitted. That verification is done on a
per value level, rather than a per function level, so it is hard to get per
function error numbers without doing unprincipled things like propagating around
state saying what the current function being verified is.

This pass instead will let me make the error counter be per ErrorBuilder which
are created per function.

One thing to be aware of is that this /will/ cause SILValue::verifyOwnership to
not emit any output when the testing flag is enabled. This is to ensure I only
do not get duplicate textual error messages from the SILVerifier.
2020-05-04 13:58:56 -07:00
Meghana Gupta
47fe49a2a9 Fix the mid-level function-pass pipeline (#31424)
* Fix the mid-level pass pipeline.

Module passes need to be in a separate pipeline, otherwise the
pipeline restart mechanism will be broken.

This makes GlobalOpt and serialization run earlier in the
pipeline. There's no explicit reason for them to be run later, in the
middle of a function pass pipeline.

Also, pipeline boundaries, like serialization and module passes should
be explicit at the the top level function that creates the pass
pipelines.

* SILOptimizer: Add enforcement of function-pass pipelines.

Don't allow module passes to be inserted within a function pass
pipeline. This silently breaks the function pipeline both interfering
with analysis and the normal pipeline restart mechanism.

* Add misssing pass in addFunctionPasses

Co-authored-by: Andrew Trick <atrick@apple.com>
2020-05-03 18:23:40 -07:00
Meghana Gupta
6c3857b6d6 Add forced precomputation and verification of analysis (#31251)
-sil-verify-all flag will verify analyses before and after a pass to
confirm correct invalidations. But if an analysis was never
constructed or invalidated as per current pass order,
it may never detect insufficient invalidations.

-sil-verify-force-analysis will force construct an analysis so that we
can better check for insufficient invalidations.
It is also terribly slow compared to -sil-verify-all.
2020-04-24 10:55:45 -07:00
Erik Eckstein
53f6fdadc6 SILOptimizer: reorganize the optimization-prepare passpipeline
Don't create a separate pass manager for those passes, just let them run at the beginning of the performance pipeline.
Regarding generated code this is a NFC.

This change fixes a problem with pass-bisecting (for debugging). Having two instances of the pass manager can cause troubles with bisecting, because -sil-opt-pass-count affects both pass managers at the same time.
2020-04-24 15:48:48 +02:00
ematejska
4cd68edf8c [Autodiff upstream] Add DifferentiabilityWitnessDevirtualizer SILOptimizer pass (#30984)
Add DifferentiabilityWitnessDevirtualizer: an optimization pass that
devirtualizes `differentiability_witness_function` instructions into
`function_ref` instructions.

Co-authored-by: Dan Zheng <danielzheng@google.com>
2020-04-23 02:13:05 -07:00
Dan Zheng
aa66cce808 [AutoDiff upstream] Add differentiation transform.
The differentiation transform does the following:
- Canonicalizes differentiability witnesses by filling in missing derivative
  function entries.
- Canonicalizes `differentiable_function` instructions by filling in missing
  derivative function operands.
- If necessary, performs automatic differentiation: generating derivative
  functions for original functions.
  - When encountering non-differentiability code, produces a diagnostic and
    errors out.

Partially resolves TF-1211: add the main canonicalization loop.

To incrementally stage changes, derivative functions are currently created
with empty bodies that fatal error with a nice message.

Derivative emitters will be upstreamed separately.
2020-04-02 15:43:57 -07:00
Erik Eckstein
93a0dfc578 SILOptimizer: a new small optimization pass to remove redundant basic block arguments.
RedundantPhiElimination eliminates block phi-arguments which have the same value as other arguments of the same block.
This also works with cycles, like two equivalent loop induction variables. Such patterns are generated e.g. when using stdlib's enumerated() on Array.

   preheader:
     br bb1(%initval, %initval)
   header(%phi1, %phi2):
     %next1 = builtin "add" (%phi1, %one)
     %next2 = builtin "add" (%phi2, %one)
     cond_br %loopcond, header(%next1, %next2), exit
   exit:

is replaced with

   preheader:
     br bb1(%initval)
   header(%phi1):
     %next1 = builtin "add" (%phi1, %one)
     %next2 = builtin "add" (%phi1, %one) // dead: will be cleaned-up later
     cond_br %loopcond, header(%next1), exit
   exit:

Any remaining dead or "trivially" equivalent instructions will then be cleaned-up by DCE and CSE, respectively.

rdar://problem/33438123
2020-03-26 19:30:01 +01:00
Michael Gottesman
e3f2bb74d2 [inliner] Add a new Inliner that only inlines AlwaysInline functions (but do not put it in the pass pipeline).
We need this anyways for -Onone and I want to do some experiments with running
this very early so I can expose more of the stdlib (modulo inlining) to the new
ownership optimizing passes.

I also changed how the inliner handles inlining around OSSA by changing it to
check early that if the caller is in ossa, then we only inline if all of the
callees that the caller calls are in ossa. The intention is to hopefully avoid
weird swings in code-size/perf due to the inliner heuristic's calculation being
artificially manipulated due to some callees not being available to inline (due
to this difference) when others are already available.
2020-03-10 19:53:18 -07:00
Hamish Knight
13217b600c [SILOptimizer] Add pipeline execution request
Add ExecuteSILPipelineRequest which executes a
pipeline plan on a given SIL (and possibly IRGen)
module. This serves as a top-level request for
the SILOptimizer that we'll be able to hang
dependencies off.
2020-02-14 09:58:32 -08:00
Hamish Knight
54629e1538 [SILOptimizer] Remove Stage param from SILPassManager
The value of this parameter doesn't appear to be
used for anything, as it gets immediately
overwritten by the pipeline stage name.
2020-02-14 09:57:27 -08:00
Hamish Knight
13bfac1820 Register IRGen SIL passes with the ASTContext
Rather than registering individual IRGen passes
when we want to execute them, store function
pointers to all the pass constructors on the
ASTContext. This will make it easier to requestify
the execution of pass pipelines.
2020-02-14 09:57:27 -08:00
Ravi Kandhadai
ec9844b2d9 [SIL Optimization] Add a new mandatory pass for unrolling forEach
calls over arrays created from array literals. This enables optimizing
further the output of the OSLogOptimization pass, and results in
highly-compact and optimized IR for calls to the new os log API.

<rdar://58928427>
2020-02-07 20:06:29 -08:00
Erik Eckstein
03b0a6c148 DeadFunctionElimination: remove externally available witness tables at the end of the pipeline
... including all SIL functions with are transitively referenced from such witness tables.

After the last devirtualizer run witness tables are not needed in the optimizer anymore.
We can delete witness tables with an available-externally linkage. IRGen does not emit such witness tables anyway.
This can save a little bit of compile time, because it reduces the amount of SIL at the end of the optimizer pipeline.
It also reduces the size of the SIL output after the optimizer, which makes debugging the SIL output easier.
2020-01-27 14:45:10 +01:00
Michael Gottesman
5b557afbbe [sil] Now that we aren't using mark-uninitialized-fixup anywhere, remove it. 2020-01-02 12:10:36 -08:00
Erik Eckstein
f03956b30c Cross-module-optimization: Serialize immediately after CrossModuleSerializationSetup
Otherwise it can happen that e.g. specialization runs between CrossModuleSerializationSetup  and serialization, resulting that an inlinable function references a shared function (which doesn't have a public linkage).
The solution is to move serialization right after CrossModuleSerializationSetup. But only do that if cross-module-optimization is enabled (it would be a disruptive change to move serialization in general).
2019-12-11 18:14:41 +01:00