Commit Graph

138 Commits

Author SHA1 Message Date
Erik Eckstein
82734b6ac2 Swift Optimizer: simplification for apply, try_apply, begin_apply and partial_apply
* move the apply of partial_apply transformation from simplify-apply to simplify-partial_apply
* delete dead partial_apply instructions
* devirtualize apply, try_apply and begin_apply
2023-05-11 08:11:44 +02:00
Anton Korobeynikov
9ee8c732f5 [AutoDiff] Do not propagate same adjoint buffer multiple times (#64963)
Adjoint buffers of projections (e.g. obtained via begin_access) are same as adjoint buffer of underlying struct value. As a result, when propagating adjoint values to pullback successor blocks we tend to produce lots of identical copies (essentially for every struct access and in every basic block) of adjoint buffers.

These copy_addrs instructions are then lowered down to plain loads and stores and while the redundant copies are usually optimized away by subsequent optimization passes, presence of such copies leads to elevated memory consumption and compilation time as one needs to track liveness of these values being copied.

Track the values being propagated and simply do not generate extra copies if the same value was already propagated.

One step towards #61773
2023-04-06 16:49:01 -07:00
Nate Chandler
cda365ca8d [stdlib] Collection types are eagerMove.
Types that have "value semantics" should not have lexical lifetimes.
Value types are not expected to have custom deinits. Are not expected to
expose unsafe interior pointers. And cannot have weak references because
they are structs. Therefore, deinitialization barriers are irrelevant.

rdar://107076869
2023-03-30 11:04:47 -07:00
Anthony Latsis
14b70f306b DiagnosticVerifier: Default expected fix-it start line to the diagnostic's 2023-03-08 12:10:27 +03:00
Anton Korobeynikov
9c227626b8 Enable propagation of @differentiable attribute from storage declarations to setters. (#63988)
Fixes #63169 and TF-129
2023-03-06 07:57:19 -08:00
Anton Korobeynikov
11de73639c [AutoDiff] Unwrap the top level of linear map tuple when it is possible (#63770)
This essentially passes the members of a linear map tuple as individual arguments. It yields few nice simplifications:

 * No linear map tuples at all for getters / setters
 * No tuple formation / deconstruction around pullbacks
 * Pullbacks with loops still use heap-allocated tuples
2023-02-19 18:46:49 -08:00
Erik Eckstein
d25b1ed834 Optimizer: Replace the MandatoryCombine pass with a Simplification pass, which is implemented in Swift
The Swift Simplification pass can do more than the old MandatoryCombine pass: simplification of more instruction types and dead code elimination.
The result is a better -Onone performance while still keeping debug info consistent.

Currently following code patterns are simplified:
* `struct` -> `struct_extract`
* `enum` -> `unchecked_enum_data`
* `partial_apply` -> `apply`
* `br` to a 1:1 related block
* `cond_br` with a constant condition
* `isConcrete` and `is_same_metadata` builtins

More simplifications can be added in the future.

rdar://96708429
rdar://104562580
2023-02-09 06:50:05 +01:00
Anton Korobeynikov
d2e022d5b4 Remove linear map structs and use plain tuples instead. (#63444)
The changes are intentionally were made close to the original implementation w/o possible simplifications to ease the review

Fixes #63207, supersedes #63379 (and fixes #63234)
2023-02-08 07:42:54 -08:00
Erik Eckstein
70981cf95f tests: fix misspelled check prefixes
Fix the common error of using underscores instead of dashes.
In the rebranch this is an error (lit got more picky), but it also makes sense to fix the tests in the main branch
2022-11-08 17:27:48 +01:00
Nate Chandler
ed623d7b64 [NFC] Shortened SIL [init] flag.
Instead of writing out [initalization] for some instructions, use [init]
everywhere.
2022-10-27 10:38:54 -07:00
Anthony Latsis
549a6a9d7f Gardening: Migrate test suite to GH issues: AutoDiff/SILOptimizer 2022-09-19 02:44:03 +03:00
Nate Chandler
3c78a0bb90 [SILGen] Only lexical types get lexical lifetimes.
Only emit `begin_borrow [lexical]` and only mark `alloc_stack`s
`[lexical]` when the variable in question's lifetime is lexical, not
eager move.
2022-08-22 15:28:00 -07:00
Holly Borla
8713d78704 [PrintOptions] Print explicit 'any' in SIL. 2022-08-18 01:15:12 -04:00
Pavel Yaskevich
86165291aa [TypeChecker] Change the way for-in statement in type-checked
Instead of asking SILGen to build calls to `makeIterator` and
`$generator.next()`, let's synthesize and type-check them
together with the rest of for-in preamble. This greatly simplifies
interaction between Sema and SILGen for for-in statements.
2022-05-30 23:17:41 -07:00
Anton Korobeynikov
a1e138b2af [AutoDiff] Implement cross-file lookup of derivatives (#58644)
Look-up for functions with @derivative attributes defined in non-primary source files

Fixes #55170
2022-05-11 08:18:51 -07:00
Slava Pestov
dac8d666ee Stop passing -requirement-machine-{abstract,inferred,protocol}-signatures flags in tests
These flags are now no-ops.
2022-05-10 12:56:17 -04:00
Slava Pestov
f39372b33d RequirementMachine: Turn off redundant requirement warnings by default and add -warn-redundant-requirements frontend flag 2022-05-10 01:49:56 -04:00
Joe Groff
482cc8ae24 SILGen: Emit a closure literal in a function conversion as the converted type.
Closure literals are sometimes type-checked as one type then immediately converted to another
type in the AST. One particular case of this is when a closure body never throws, but the closure
is used as an argument to a function that takes a parameter that `throws`. Emitting this naively,
by emitting the closure as its original type, then converting to throws, can be expensive for
async closures, since that takes a reabstraction thunk. Even for non-async functions, we still want
to get the benefit of reabstraction optimization for the closure literal through the conversion too.
So if the function conversion just add `throws`, emit the closure as throwing, and pass down the
context abstraction pattern when emitting the closure as well.
2022-05-04 10:24:11 -07:00
Anton Korobeynikov
121e28051a Force escaping all thin functions. Ensure types are always unique. 2022-04-20 10:34:45 +02:00
Saleem Abdulrasool
36c951d31e Revert "[DebugInfo] Ignore noescape bit for all @convention(c) pointers" 2022-04-19 18:09:23 -07:00
Anton Korobeynikov
0258ed3209 Force escaping all thin functions. Ensure types are always unique. 2022-04-18 22:43:57 +02:00
Slava Pestov
b4b873332f Update -requirement-machine-* flags in various tests
- Don't pass 'verify' since it's now the default
- Update tests where diagnostics changed in a correct way to pass 'on' instead
- Delete compiler_scale/explicit_requirements_perf.swift since it's not testing anything with the requirement machine
2022-03-31 15:57:36 -04:00
Erik Eckstein
6a020f8f15 Stabilize and simplify SIL linkage and serialization
The main point of this change is to make sure that a shared function always has a body: both, in the optimizer pipeline and in the swiftmodule file.
This is important because the compiler always needs to emit code for a shared function. Shared functions cannot be referenced from outside the module.
In several corner cases we missed to maintain this invariant which resulted in unresolved-symbol linker errors.

As side-effect of this change we can drop the shared_external SIL linkage and the IsSerializable flag, which simplifies the serialization and linkage concept.
2022-03-09 15:28:05 +01:00
Slava Pestov
527f31ff64 AutoDiff: Another ugly hack to work around generics invariant violations 2022-03-02 17:42:40 -05:00
Richard Wei
a1cb9bbcc9 [AutoDiff] Move differentiable curry thunk bug reproducer to an XFAIL test.
Due to rdar://87429620, test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift is still using `-requirement-machine=off`. This patch moves the reproducer to a standalone XFAIL test, and removes `-requirement-machine=off` from differentiation_diagnostics.swift.
2022-01-11 18:01:43 -08:00
Michael Gottesman
5d5b0daf9c [silgen] Add the lexical flag on more alloc_stack that are used to represent variables.
And update all of the tests.
2021-12-03 14:33:00 -08:00
Michael Gottesman
785153045b [move-operator] Start having SILGen emit lexical lifetimes and teach the optimizer how to maintain lexical lifetimes until the lexical lifetime elimination.
I am doing this so that I can use lexical lifetimes to emit diagnostics such as
the move operator diagnostics.
2021-11-29 18:02:13 -08:00
Richard Wei
9bcba98213 Revert "Revert "[AutoDiff] Fix two derivative type calculation bugs caught by RequirementMachine""
This reverts commit 262965418f.
2021-11-05 10:29:08 -07:00
Doug Gregor
262965418f Revert "[AutoDiff] Fix two derivative type calculation bugs caught by RequirementMachine" 2021-11-04 15:56:48 -07:00
Richard Wei
90ec31a068 [AutoDiff] Compute derivative types using requirements from archetypes.
Resolves rdar://84213107 and partially resolves rdar://82549134.
2021-10-13 21:23:17 -07:00
Richard Wei
808c7ec783 [AutoDiff] Fix two derivative type calculation bugs caught by RequirementMachine.
1. When calculating the differential type of an original function with an inout parameter and when the inout parameter has a type parameter, the inout parameter should get a generic parameter in the subst generic signature of the differential but it currently doesn't. This causes SILGen to attempt to reabstract the differential value in the JVP protocol witness thunk, whilst the generic signature is lacking requirements, leading to a requirement machine error. This patch fixes the calculation so that the JVP's result type (the differential type) always matches the witness thunk's result type.

    Wrong type:
    ```swift
             sil private [transparent] [thunk] [ossa] @... <τ_0_0 where τ_0_0 : Differentiable> (...) -> @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <τ_0_0.TangentVector, τ_0_0.TangentVector> {
               %6 = differentiable_function_extract [jvp] %5 : $@differentiable(reverse) @convention(method) <τ_0_0 where τ_0_0 : Differentiable> (@in_guaranteed τ_0_0, @noDerivative @inout τ_0_0, @noDerivative SR_13305_Struct) -> () // user: %7
    HERE ====> %7 = apply %6<τ_0_0>(%0, %1, %3) : $@convention(method) <τ_0_0 where τ_0_0 : Differentiable> (@in_guaranteed τ_0_0, @inout τ_0_0, SR_13305_Struct) -> @owned @callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 for <τ_0_0.TangentVector>
    ```

    Should be:
    ```swift
      %7 = apply %6<τ_0_0>(%0, %1, %3) : $@convention(method) <τ_0_0 where τ_0_0 : Differentiable> (@in_guaranteed τ_0_0, @inout τ_0_0, SR_13305_Struct) -> @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <τ_0_0.TangentVector, τ_0_0.TangentVector>
    ```

2. `TypeConverter::makeConstantInterfaceType` is not passing down the derivative generic signature to `SILFunctionType::getAutoDiffDerivativeFunctionType` for class methods, and this was caught by RequirementMachine during vtable emission. This patch fixes that.

Partially resolves rdar://82549134. The only remaining tests that require `-requirement-machine=off` are SILOptimizer/semantic_member_accessors_sil.swift and SILOptimizer/differentiation_diagnostics.swift which I will fix next. Then I'll do a proper fix for workaround #39416.
2021-09-30 20:51:36 -07:00
Slava Pestov
bbb71962f0 AutoDiff: Workaround for performing generic signature queries on the wrong signature 2021-09-23 13:24:40 -04:00
Slava Pestov
b57be6dbf1 Revert "AutoDiff: Disable requirement machine when building or testing Differentiation library"
This reverts commit 4f6ba29715.
2021-09-23 13:24:40 -04:00
Slava Pestov
4f6ba29715 AutoDiff: Disable requirement machine when building or testing Differentiation library
The SIL type lowering logic for AutoDiff gets the substituted generic signature
mixed up with the invocation generic signature, so it tries to ask questions
about DependentMemberTypes in a signature with no requirements. This triggers
assertions when the requirement machine is enabled.

Disable the requirement machine until this is fixed.
2021-07-30 19:42:31 -04:00
Luciano Almeida
ddeb1929c4 [DiagnosticQol][SR-14505] Use DeclDescriptive kind in missing return data flow diagnostics (#36952)
* [Diagnostics] Use DeclDescriptiveKind on data flow diagnostics to improve diagnostic message

* [tests] Add regression tests to SILOptimizer/return.swift

* [tests] Adapt other tests to changes of SR-14505

* [Diagnostics] Adapt message for missing return diagnostics, remove article

* [Diagnostics] Adapt message for missing return diagnostics to have a note with fix

* [tests] Adjust tests in validation suit
2021-04-20 08:16:32 -03:00
Slava Pestov
1e8ce52736 SIL: Strip [serialized] flag from functions even at -Onone
While the comment is correct to state that this won't enable any
new optimizations with -Onone, it does enable IRGen's lazy
function emission, which is important for 'reasync' functions,
which we don't want to emit at all even at -Onone.

This fixes debug stdlib builds with the new reasync versions
of the &&, || and ?? operators.
2021-04-08 01:47:27 -04:00
Richard Wei
0b53a02544 [AutoDiff] Rename 'in:' to 'of:' in differential operators.
Rename the argument label `in:` in `gradient(at:in:)`, `pullback(at:in:)`, etc to `of:`, as suggested in the [pitch thread](https://forums.swift.org/t/differentiable-programming-for-gradient-based-machine-learning/42147).
2021-02-24 01:33:42 -05:00
Richard Wei
8bc6143a4c [AutoDiff] Rename 'move(along:)' to 'move(by:)'.
Rename `move(along:)` to `move(by:)` based on the proposal feedback. The main argument for the change is that tangent vectors specify both a direction and a magnitude, whereas `along:` does not indicate that `self` is being moved by the specified magnitude.
2021-02-23 21:45:01 -05:00
Richard Wei
e3b480b0c9 [AutoDiff] Enable variable debugging support for pullback functions.
- Properly clone and use debug scopes for all instructions in pullback functions.
- Emit `debug_value` instructions for adjoint values.
- Add debug locations and variable info to adjoint buffer allocations.
- Add `TangentBuilder` (a `SILBuilder` subclass) to unify and simplify special emitter utilities for tangent vector code generation. More simplifications to come.

Pullback variable inspection example:
```console
(lldb) n
Process 50984 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100003497 main`pullback of foo(x=0) at main.swift:12:11
   9   	import _Differentiation
   10
   11  	func foo(_ x: Float) -> Float {
-> 12  	  let y = sin(x)
   13  	  let z = cos(y)
   14  	  let k = tanh(z) + cos(y)
   15  	  return k
Target 0: (main) stopped.
(lldb) fr v
(Float) x = 0
(Float) k = 1
(Float) z = 0.495846391
(Float) y = -0.689988375
```

Resolves rdar://68616528 / SR-13535.
2021-02-21 19:00:07 -05:00
Richard Wei
e494df2ee6 [AutoDiff] Add differentiability kind to differentiability witnesses and mangle them.
Differentiability witnesses are now keyed by the original function name, the differentiability kind, and the autodiff config.

Updated SIL syntax:
```
differentiability-kind ::= 'forward' | 'reverse' | 'normal' | 'linear'
sil-differentiability-witness ::=
    'sil_differentiability_witness'
    sil-linkage?
    '[' differentiability-kind ']'
    '[' 'parameters' sil-differentiability-witness-function-index-list ']'
    '[' 'results' sil-differentiability-witness-function-index-list ']'
    generic-parameter-clause?
    sil-function-name ':' sil-type
    sil-differentiability-witness-body?
sil-instruction ::=
    'differentiability_witness_function'
    '[' sil-differentiability-witness-function-kind ']'
    '[' differentiability-kind ']'
    '[' 'parameters' sil-differentiability-witness-function-index-list ']'
    '[' 'results' sil-differentiability-witness-function-index-list ']'
    generic-parameter-clause?
    sil-function-name ':' sil-type
```
```console
sil_differentiability_witness [reverse] [parameters 0 1] [results 0] <T where T: Differentiable> @foo : <T> $(T) -> T
differentiability_witness_function [vjp] [reverse] [parameters 0] [results 0] <T where T: Differentiable> @foo : $(T) -> T
```

New mangling:
```swift
  global ::= global generic-signature? 'WJ' DIFFERENTIABILITY-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' // differentiability witness
```
```console
$s13test_mangling3fooyS2f_S2ftFWJrSpSr ---> reverse differentiability witness for test_mangling.foo(Swift.Float, Swift.Float, Swift.Float) -> Swift.Float with respect to parameters {0} and results {0}
```

Resolves rdar://74380324.
2021-02-17 18:27:42 -05:00
Richard Wei
f9ddecf459 [AutoDiff] Mangle linear map self-reordering thunks and subset parameters thunks.
Add the following new mangling rules.
```
global ::= from-type to-type 'TJO' AUTODIFF-FUNCTION-KIND // autodiff self-reordering reabstraction thunk
global ::= from-type 'TJS' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' INDEX-SUBSET 'P' // autodiff linear map subset parameters thunk
global ::= global to-type 'TJS' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' INDEX-SUBSET 'P' // autodiff derivative function subset parameters thunk
```

Example:
```console
$s13TangentVector16_Differentiation14DifferentiablePQzAaDQy_SdAFIegnnnr_TJSdSSSpSrSUSP ---> autodiff subset parameters thunk for differential from @escaping @callee_guaranteed (@in_guaranteed A._Differentiation.Differentiable.TangentVector, @in_guaranteed B._Differentiation.Differentiable.TangentVector, @in_guaranteed Swift.Double) -> (@out B._Differentiation.Differentiable.TangentVector) with respect to parameters {0, 1, 2} and results {0} to parameters {0, 2}
$sS2f8mangling3FooV13TangentVectorVIegydd_SfAESfIegydd_TJOp ---> autodiff self-reordering reabstraction thunk for pullback from @escaping @callee_guaranteed (@unowned Swift.Float) -> (@unowned Swift.Float, @unowned mangling.Foo.TangentVector) to @escaping @callee_guaranteed (@unowned Swift.Float) -> (@unowned mangling.Foo.TangentVector, @unowned Swift.Float)
```

Resolves rdar://72666310 / SR-13508.

Also fix a bug in `AutoDiffFunction` mangling where the original may be a global that contains more than 1 node (rdar://74151229 / SR-14106).
2021-02-13 01:38:35 -08:00
Richard Wei
af8942d940 [AutoDiff] Rename '@differentiable' to '@differentiable(reverse)'.
Compiler:
- Add `Forward` and `Reverse` to `DifferentiabilityKind`.
- Expand `DifferentiabilityMask` in `ExtInfo` to 3 bits so that it now holds all 4 cases of `DifferentiabilityKind`.
- Parse `@differentiable(reverse)` and `@differentiable(_forward)` declaration attributes and type attributes.
- Emit a warning for `@differentiable` without `reverse`.
- Emit an error for `@differentiable(_forward)`.
- Rename `@differentiable(linear)` to `@differentiable(_linear)`.
- Make `@differentiable(reverse)` type lowering go through today's `@differentiable` code path. We will specialize it to reverse-mode in a follow-up patch.

ABI:
- Add `Forward` and `Reverse` to `FunctionMetadataDifferentiabilityKind`.
- Extend `TargetFunctionTypeFlags` by 1 bit to store the highest bit of differentiability kind (linear). Note that there is a 2-bit gap in `DifferentiabilityMask` which is reserved for `AsyncMask` and `ConcurrentMask`; `AsyncMask` is ABI-stable so we cannot change that.

_Differentiation module:
- Replace all occurrences of `@differentiable` with `@differentiable(reverse)`.
- Delete `_transpose(of:)`.

Resolves rdar://69980056.
2021-02-07 14:09:46 -08:00
Richard Wei
eadb4bc87c [AutoDiff] Differentials should have private linkage
Like pullbacks, differentials should have private linkage, not hidden linkage. This patch fixes that.
2021-01-08 19:42:44 -08:00
Richard Wei
ffe6064101 Mangle derivative functions and linear maps.
- `Mangle::ASTMangler::mangleAutoDiffDerivativeFunction()` and `Mangle::ASTMangler::mangleAutoDiffLinearMap()` accept original function declarations and return a mangled name for a derivative function or linear map. This is called during SILGen and TBDGen.
- `Mangle::DifferentiationMangler` handles differentiation function mangling in the differentiation transform. This part is necessary because we need to perform demangling on the original function and remangle it as part of a differentiation function mangling tree in order to get the correct substitutions in the mangled derivative generic signature.

A mangled differentiation function name includes:
- The original function.
- The differentiation function kind.
- The parameter indices for differentiation.
- The result indices for differentiation.
- The derivative generic signature.
2021-01-07 02:21:10 -08:00
Marc Rasi
0142e524e1 [AutoDiff] generated linear maps should be "convention(thin)" 2020-12-28 16:50:34 -08:00
Richard Wei
8d8614058b [AudoDiff] NFC: Replace 'SILAutoDiffIndices' with 'AutoDiffConfig'. (#35079)
Resolve rdar://71678394 / SR-13889.
2020-12-14 14:32:40 -08:00
Dan Zheng
2cf7d63a5e [AutoDiff] Fix ownership error in VJPCloner::visitApplyInst. (#35003)
Change the following code pattern:

```
%x1 = convert_function %x0 : $@differentiable (...) -> ...
%x2 = begin_borrow %x1
... // use %x2
%x3 = end_borrow %x2
destroy_value %x0
```

To the following:

```
%x1 = begin_borrow %x0 : $@differentiable (...) -> ...
%x2 = convert_function %x0
... // use %x2
%x3 = end_borrow %x1
destroy_value %x0
```

Resolves SR-13933: "multiple consuming users" ownership error caused by
`VJPCloner::visitApply` related to `@differentiable`-function-typed callees.

Also upstream test/AutoDiff/SILOptimizer/generics.swift from tensorflow branch.
2020-12-08 19:48:31 -05:00
Michael Gottesman
ec71713fb0 [ownership] Teach CanonicalizeInstruction how to eliminate trivial copies/borrows.
I am going to be using in inst-simplify/sil-combine/canonicalize instruction a
RAUW everything against everything API (*). This creates some extra ARC
traffic/borrows. It is going to be useful to have some simple peepholes that
gets rid of some of the extraneous traffic.

(*) Noting that we are not going to support replacing non-trivial
OwnershipKind::None values with non-trivial OwnershipKind::* values. This is a
corner case that only comes up with non-trivial enums that have a non-payloaded
or trivial enum case. It is much more complex to implement that transform, but
it is an edge case, so we are just not going to support those for now.

----

I also eliminated the dependence of SILGenCleanup on Swift/SwiftShims. This
speeds up iterating on the test case with a debug compiler since we don't need
those modules.
2020-12-02 17:25:13 -08:00
Michael Gottesman
affeba98f9 [auto-diff] Fix a bunch of places in the *Cloners where we were not closing borrow scopes.
These were all just trying to open a borrow scope, so I changed them to use the
API SILBuilder::emitScopedBorrowOperation(SILLocation, SIL).
2020-11-30 16:05:37 -08:00
Michael Gottesman
58d4191470 [ownership] Try harder to make sure we do not propagate ownership info when ownership is disabled.
Specifically, I made it so that assuming our instruction is inserted into a
block already that we:

1. Return a constraint of {OwnershipKind::Any, UseLifetimeConstraint::NonLifetimeEnding}.
2. Return OwnershipKind::None for all values.

Noticed above I said that if the instruction is already inserted into a block
then we do this. The reason why is that if this is called before an instruction
is inserted into a block, we can't get access to the SILFunction that has the
information on whether or not we are in OSSA form. The only time this can happen
is if one is using these APIs from within SILBuilder since SILBuilder is the
only place where we allow this to happen. In SILBuilder, we already know whether
or not our function is in ossa or not and already does different things as
appropriate (namely in non-ossa does not call getOwnershipKind()). So we know
that if these APIs are called in such a situation, we will only be calling it if
we are in OSSA already. Given that, we just assume we are in OSSA if we do not
have a function.

To make sure that no mistakes are made as a result of that assumption, I put in
a verifier check that all values when ownership is disabled return a
OwnershipKind::None from getOwnershipKind().

The main upside to this is this means that we can write code for both
OSSA/non-OSSA and write code for non-None ownership without needing to check if
ownership is enabled.
2020-11-11 18:56:59 -08:00