Commit Graph

8 Commits

Author SHA1 Message Date
Michael Gottesman
7712639ab0 [region-isolation] Add support for project_block_storage.
rdar://119743743
2023-12-18 09:35:57 -08:00
Erik Eckstein
5bc036661c SIL optimizer: add the LetPropertyLowering pass
It lowers let property accesses of classes.
Lowering consists of two tasks:

* In class initializers, insert `end_init_let_ref` instructions at places where all let-fields are initialized.
  This strictly separates the life-range of the class into a region where let fields are still written during
  initialization and a region where let fields are truly immutable.

* Add the `[immutable]` flag to all `ref_element_addr` instructions (for let-fields) which are in the "immutable"
  region. This includes the region after an inserted `end_init_let_ref` in an class initializer, but also all
  let-field accesses in other functions than the initializer and the destructor.

This pass should run after DefiniteInitialization but before RawSILInstLowering (because it relies on `mark_uninitialized` still present in the class initializer).
Note that it's not mandatory to run this pass. If it doesn't run, SIL is still correct.

Simplified example (after lowering):

  bb0(%0 : @owned C):                           // = self of the class initializer
    %1 = mark_uninitialized %0
    %2 = ref_element_addr %1, #C.l              // a let-field
    store %init_value to %2
    %3 = end_init_let_ref %1                    // inserted by lowering
    %4 = ref_element_addr [immutable] %3, #C.l  // set to immutable by lowering
    %5 = load %4
2023-09-19 15:10:30 +02: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
Arnold Schwaighofer
9f2b6a4ebb Reuse _ContiguousArrayStorage<AnyObject> metadata for any class or objc generic type
Reduces the number of _ContiguousArrayStorage metadata.

In order to support constant time bridging we do need to set the correct
metadata when we bridge to Objective-C. This is so that the type check
succeeds when bridging back from Objective-C to reuse the storage
instance rather than bridging the elements.

To support dynamically setting the `_ContiguousArrayStorage` element
type i needed to add support for optimizing `alloc_ref_dynamic`
throughout the optimizer.

Possible future improvements:
* Use different metadata such that we can disambiguate native Swift
  classes during destruction -- allowing native release rather then unknown
  release usage.
* Optimize the newly added semantic function
  getContiguousArrayStorageType

rdar://86171143
2022-02-16 07:55:34 -08:00
Min-Yih Hsu
343d842394 [SIL][DebugInfo] PATCH 3/3: Deprecate debug_value_addr SIL instruciton
This patch removes all references to DebugValueAddrInst class and
debug_value_addr instruction in textual SIL files.
2021-08-31 12:01:04 -07:00
Michael Gottesman
79211d4869 [assembly-vision] If a nominal type is marked with @_assemblyVision emit AssemblyVisionRemarks for all of the nominal type's methods.
This is implemented by checking if a function has a self parameter and if the
self parameter is a nominal type, checking for the relevant attribute.
2021-07-08 15:08:38 -07:00
Michael Gottesman
804bcac1e6 [assembly-vision] Change release to do backwards then forwards when inferring source locs.
TLDR: I fixed a whole in the assembly-vision opt-remark pass where we were not
emitting a remark for end of scope instructions at the beginning of blocks. Now
all of these instructions (strong_release, end_access) should always reliably
have a remark emitted for them.

----

I think that this is a pragmatic first solution to the problem of
strong_release, release_value being the first instruction of a block. For those
who are unaware, this issue is that for a long time we have searched backwards
first for "end of scope" like instructions. This then allows us to identify the
"end of scope" instruction as happening at the end of the previous statement
which is where the developer thinks it should be:

```
var global: Klass
func bar() -> @owned Klass { global }
func foo() {
   // We want the remark for the
   bar()                          // expected-remark {{retain}}
}
```

This makes sense since we want to show end of scope instructions as being
applied to the earlier code whose scope it is ending. We can be clear that it is
at the end of the statement by placing the carrot on the end of statement
SourceLoc so there isn't any confusion upon whether or not

That generally has delivered nice looking results, but what if our release is
the first instruction in the block? In that case, we do not have any instruction
that we can immediately use, so traditionally we just gave up and didn't emit
anything. This is not an acceptable solution! We should be able to emit
something for every retain/release in the program if we want users to be able to
rely upon this! Thus we need to be able to get source location information from
somewhere around

First before we begin, my approach here is informed by my seeing over time that
the optimizer does a pretty good job of not breaking SourceLoc info for
terminators.

With that in mind, there are two possible approaches here: using the terminator
from the previous block and searching forward at worst taking the SourceLoc of
the current block's terminator (or earlier if we find a good SourceLoc). I
wasn't sure what the correct thing to do was at the time so I didn't fix the
issue. After some thought, I realized that the correct solution is to if we fail
a backwards search, search forwards. The reason why is that since our remarks
runs late in the optimization pipeline, there is a very high likelihood that if
we aren't folded into our previous block that there is a true need in the
program for conditional control flow here. We want to avoid placing the release
out of such pieces of code since it is misleading to the user:

```

In this example there is a release inside the case for .x but none for .y. In
that case it is possible that we get a release for .f since payload is passed in
at +1 at SILGen time. In such a case, using the terminator of the previous block
would mean that we would have the release be marked as on payload instead of
inside the case of .x. By using the terminator of the releases block, we can

switch payload {
case let .x(f):
  ...
case let .y:
  ...
}
```

So using the terminator from the previous block would be
  misleading to the user. Instead it is better to pick a location after the release that way
  we know at least the instruction we are inferring from must in some sense be

With this fix, we should not emit locations for all retains, releases. We may
not identify a good source loc for all of them, but we will identify them.

optimization pipeline, if our block was not folded into the previous block there
is a very high liklihood that there is some sort of conditional control flow
that is truly necessary in the program. If we

this
generally implies that there is a real side effect in the program that is
requiring conditional code execution (since the optimizer would have folded it).

The reason why is that we are at least going to hit a terminator or a
side-effect having instruction that generally have debug info preserved by the
optimizer.
2021-07-02 11:51:09 -07:00
Michael Gottesman
18670fc389 [assembly-vision] Rename opt remark generator to assembly vision remark generator.
TLDR: The reason why I am doing this is that often times people confuse assembly
vision remarks for normal opt remarks. I want to accentuate that this is
actually trying to do something different than a traditional opt remark. To that
end I renamed things in the compiler and added a true attribute
`@_assemblyVision` to trigger the compiler to emit these remarks to help
everyone remember what this is in their ontology. I explain below the
difference.

----

Normal opt remarks work by the optimizer telling you if it succeeded or failed
to perform an optimization. Another way of putting this is that opt remarks is
trying to give back feedback to the user from an expert system about why it did
or not do something. There is inherently an act of interpretation in the
optimizer about whether or not to report an 'action' that it perpetrated to the
user.

Assembly Vision Remarks is instead trying to be an expert tool that acts like an
xray. Instead of telling the user about what the optimizer did, it is instead a
simple visitor that visits the IR and emits SourceLocations for where specific
hazards ending up in the program. In this sense it is just telling the user
where certain instructions ended up and using heuristics to relate this to
information at the IR level. To a get a sense of this difference, consider the
following Swift Code:

```
public class Klass {
    func doSomething() {}
}

var global: Klass = Klass()

@inline(__always)
func bar() -> Klass { global }

@_assemblyVision
@inline(never)
func foo() {
  bar().doSomething()
}
```

In this case, we will emit the following remarks:

```
test.swift:16:5: remark: begin exclusive access to value of type 'Klass'
    bar().doSomething()
    ^
test.swift:7:5: note: of 'global'
var global: Klass = Klass()
    ^
test.swift:16:9: remark: end exclusive access to value of type 'Klass'
    bar().doSomething()
        ^
test.swift:7:5: note: of 'global'
var global: Klass = Klass()
    ^
test.swift:16:11: remark: retain of type 'Klass'
    bar().doSomething()
          ^
test.swift:7:5: note: of 'global'
var global: Klass = Klass()
    ^
test.swift:16:23: remark: release of type 'Klass'
    bar().doSomething()
                      ^
test.swift:7:5: note: of 'global'
var global: Klass = Klass()
    ^
```

Notice how the begin/end exclusive access are marked as actually being before
the retain, release of global. That seems weird since exclusive access to memory
seems like something that should not escape an exclusivity scope... but in fact
this corresponds directly to what we eventually see in the SIL:

```
// test.sil
sil hidden [noinline] [_semantics "optremark"] @$ss3fooyyF : $@convention(thin) () -> () {
bb0:
  %0 = global_addr @$ss6globals5KlassCvp : $*Klass
  %1 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*Klass
  %2 = load %1 : $*Klass
  end_access %1 : $*Klass
  %4 = class_method %2 : $Klass, #Klass.doSomething : (Klass) -> () -> (), $@convention(method) (@guaranteed Klass) -> ()
  strong_retain %2 : $Klass
  %6 = apply %4(%2) : $@convention(method) (@guaranteed Klass) -> ()
  strong_release %2 : $Klass
  %8 = tuple ()
  return %8 : $()
} // end sil function '$ss3fooyyF'
```

and assembly,

```
// test.S
_$ss3fooyyF:
	pushq	%rbp
	movq	%rsp, %rbp
	pushq	%r13
	pushq	%rbx
	subq	$32, %rsp
	leaq	_$ss6globals5KlassCvp(%rip), %rdi
	leaq	-40(%rbp), %rsi
	xorl	%edx, %edx
	xorl	%ecx, %ecx
	callq	_swift_beginAccess
	movq	_$ss6globals5KlassCvp(%rip), %r13
	movq	(%r13), %rax
	movq	80(%rax), %rbx
	movq	%r13, %rdi
	callq	_swift_retain
	callq	*%rbx
	movq	%r13, %rdi
	callq	_swift_release
	addq	$32, %rsp
	popq	%rbx
	popq	%r13
	popq	%rbp
	retq
```

so as one can see what we are trying to do is inform the user of hazards in the
code without trying to reason about it, automated a task that users often have
to perform by hand: inspection of assembly to determine where runtime calls and
other hazards ended up.
2021-06-12 15:09:46 -07:00