This is necessary since other passes rely on DIMemoryUseCollector.h and I want
to update each one of them individually to minimize disruption.
rdar://31521023
(This re-applies #7736 with an update to the
tsan-inout.swift execution test to handle configurations where
TSan's ignore_interceptors_accesses is enabled by default.)
Add SILGen instrumentation to treat inout accesses as Thread Sanitizer writes.
The goal is to catch races on inout accesses even when there is a not an
llvm-level read/write to a particular address. Ultimately
this will enable TSan to, for example, report racy writes to distinct
stored properties of a common struct as a data race.
This instrumentation is off by default. It can be enabled with the
'enable-experimental-tsan-inout-instrumentation' frontend flag.
The high-level approach is to add a SIL-level builtin that represents a call
to a TSan routine in compiler-rt. Then, when emitting an address for an LValue
as part of an inout expression, we call this builtin for each path component
that represents an LValue. I've added an 'isRValue()' method to PathComponent
that tracks whether a component represents an RValue or an LValue. Right the
only PathComponent that sometimes returns 'true' is ValueComponent().
For now, we're instrumenting only InoutExprs, but in the future it probably
makes sense to instrument all LValue accesses. In this patch I've
added a 'TSanKind' parameter to SILGenFunction::emitAddressOfLValue() and
its helpers to limit instrumentation to inout accesses. I envision that this
parameter will eventually go away.
Add SILGen instrumentation to treat inout accesses as Thread Sanitizer writes.
The goal is to catch races on inout accesses even when there is a not an
llvm-level read/write to a particular address. Ultimately
this will enable TSan to, for example, report racy writes to distinct
stored properties of a common struct as a data race.
This instrumentation is off by default. It can be enabled with the
'enable-experimental-tsan-inout-instrumentation' frontend flag.
The high-level approach is to add a SIL-level builtin that represents a call
to a TSan routine in compiler-rt. Then, when emitting an address for an LValue
as part of an inout expression, we call this builtin for each path component
that represents an LValue. I've added an 'isRValue()' method to PathComponent
that tracks whether a component represents an RValue or an LValue. Right the
only PathComponent that sometimes returns 'true' is ValueComponent().
For now, we're instrumenting only InoutExprs, but in the future it probably
makes sense to instrument all LValue accesses. In this patch I've
added a 'TSanKind' parameter to SILGenFunction::emitAddressOfLValue() and
its helpers to limit instrumentation to inout accesses. I envision that this
parameter will eventually go away.
A lot of files transitively include Expr.h, because it was
included from SILInstruction.h, SILLocation.h and SILDeclRef.h.
However in reality most of these files don't do anything
with Exprs, especially not anything in IRGen or the SILOptimizer.
Now we're down to 171 files in the frontend which depend on
Expr.h, which is still a lot but much better than before.
If a convenience initializer in a subclass delegated to an inherited initializer from its base, we would end up type-checking the reference to the base class constructor as returning the base type, leading to type mismatches in the result AST and downstream crashes. We can wrap up the synthesized OtherConstructorRefExpr in a CovariantFunctionConversionExpr, which will trick the type checker into propagating the covariant result that gets rebound to `self` on return, avoiding this problem. (For now, I'm avoiding making the constructor decl formally have a Self return type, since that exposes a bunch of downstream breakage in code paths that only expect FuncDecls to be covariant, and also affects the mangling of constructors, causing a bunch of test case thrash we really don't want to inflict on the 3.1 branch.)
Separate formal lowered types from SIL types.
The SIL type of an argument will depend on the SIL module's conventions.
The module conventions are determined by the SIL stage and LangOpts.
Almost NFC, but specialized manglings are broken incidentally as a result of
fixes to the way passes handle book-keeping of aruments. The mangler is fixed in
the subsequent commit.
Otherwise, NFC is intended, but quite possible do to rewriting the logic in many
places.
This is dead code and can be re-added if it is needed. Right now though there
really isnt a ValueOwnershipKind that corresponds to deallocating and I do not
want to add a new ValueOwnershipKind for dead code.
Handling address_to_pointer as a plain inout missed some mutations and lead to miscompiles.
We now treat address_to_pointer as escaping address.
Fixes SR-3554
When adding a designated initializer to a nominal type in another
module, we would call getType() on deserialized VarDecls, which
is not allowed.
Instead, it is more correct to use SILTypes throughout and call
SILType::getFieldType() to get a substituted field type.
Fixes <https://bugs.swift.org/browse/SR-3545>.
Applying nontrivial generic arguments to a nontrivial SIL layout requires lowered SILType substitution, which requires a SILModule. NFC yet, just an API change.
There's no longer a single element type to speak of. Update uses to either iterate all box fields or to assert that they're working with a single-field box.
This was already done for getSuccessorBlocks() to distinguish getting successor
blocks from getting the full list of SILSuccessors via getSuccessors(). This
commit just makes all of the successor/predecessor code follow that naming
convention.
Some examples:
getSingleSuccessor() => getSingleSuccessorBlock().
isSuccessor() => isSuccessorBlock().
getPreds() => getPredecessorBlocks().
Really, IMO, we should consider renaming SILSuccessor to a more verbose name so
that it is clear that it is more of an internal detail of SILBasicBlock's
implementation rather than something that one should consider as apart of one's
mental model of the IR when one really wants to be thinking about predecessor
and successor blocks. But that is not what this commit is trying to change, it
is just trying to eliminate a bit of technical debt by making the naming
conventions here consistent.
This is working around an additional use-list DI ordering issue that I exposed
when implementing High Level Memory Operations. Specifically, DI started to
error on:
(class_method x)
instead of on:
(apply (class_method x) x)
We would also try to emit an error on the apply, but we would squelch the apply
error (which is more accuracte) since we had already emitted the class_method
error.
This commit conservatively checks for this condition and skips the class method
so we can emit the more descriptive error on the apply.
Today, loads and stores are treated as having @unowned(unsafe) ownership
semantics. This leaves the user to specify ownership changes on the loaded or
stored value independently of the load/store by inserting ARC operations. With
the change to Semantic SIL, this will no longer be true. Instead loads, stores
have ownership semantics that one must reason about such as copy, take, and
trivial.
This change moves us closer to that world by eliminating the default
OwnershipQualification argument from create{Load,Store}. This means that the
compiler developer cannot ignore reasoning about the ownership semantics of the
memory operation that they are creating.
Operationally, this is a NFC change since I have just gone through the compiler
and updated all places where we create loads, stores to pass in the former
default argument ({Load,Store}OwnershipQualifier::Unqualified), to
SILBuilder::create{Load,Store}(...). For now, one can just do that in situations
where one needs to create loads/stores, but over time, I am going to tighten the
semantics up via the verifier.
rdar://28685236
This specific issue came up due to my SILGen changes for copy_value,
destroy_value. Specifically, we used to emit the diagnostic:
super.init isn't called on all paths before returning from initializer.
After my change, we began to emit:
'self' used before super.init call
Since the code was:
init() {}
there is clearly no use, so the new error is incorrect or at minimum
misleading. When I investigated why the change in diagnostic happened, it was
because as a result of my SILGen change, the order of the strong_retain and
return in the use-list swapped in the following code:
%1 = load %0 : $*MultipleInitDerived
strong_retain %1 : $MultipleInitDerived
return %1 : $MultipleInitDerived
Before my change, return was visited first, causing the correct diagnostic to be
emitted. After my change, the strong_retain is visited first causing the second
diagnostic to be emitted. Since we only emit one source error for each
SILLocation, in the 2nd case, the error associated with the return is not
emitted.
We should not be considering strong_retain to be a use that propagates
liveness. The reason why is that a strong_retain should always be paired with
some local use that consumes it, whether that is an apply, closure, or a store
into memory. Since we will always have a consuming operation for the retain and
the consuming operation actually is able to be something visible at the source
level to the user (versus the retain), it makes more sense to just ignore
strong_retain.
In general though, we should try to be more careful about how we swallow errors
in DI, especially in the light of use-list twiddling. I attempted to improve our
testing situation by added a flag '-definite-init-visit-debuginfo-locs' that
would cause DI to consider SIL Location debug info locs when deciding whether or
not to swallow errors. Sadly, our diagnostics (from what I can tell) do not
support emitting diagnositics for such locations, so I failed.
Thus I could not actually test this, but it will fail once I get in the
copy_value, destroy_value change, so that /should/ act as good enough of a test.
rdar://28851920
Previously, we were only able to detect factory initializers
dispatched through class_method. This didn't work for
factory initializers defined in protocol extensions.
The end result would be that we would strong_release an
uninitialized class instance, which could cause crashes.
Fix DI to correctly release the old instance using
dealloc_partial_ref instead.
Fixes <rdar://problem/27713221>.
Add yet one more flavor of hack to DI to recognize where we are
delegating the initialization of 'self'. The existing hack in this
area (for Objective-C factory initializers) is based on recognizing
the value_metatype instruction that feeds into the application of the
factory initializer. C functions imported as initializers don't have a
metatype argument, so instead tag the assignment into the self box as
the initialization of self.
As a minor cleanup in this area, don't emit the dead value_metatype
instruction when invoking a C-imported factory initializer.
We were giving special handling to ApplyInst when we were attempting to use
getMemoryBehavior(). This commit changes the special handling to work on all
full apply sites instead of just AI. Additionally, we look through partial
applies and thin to thick functions.
I also added a dumper called BasicInstructionPropertyDumper that just dumps the
results of SILInstruction::get{Memory,Releasing}Behavior() for all instructions
in order to verify this behavior.
remove the mixed concept that was SILFileLocation.
Also add support for a third type of underlying storage that will be used
for deserialized debug lcoations from textual SIL.
NFC
<rdar://problem/22706994>
Similarly to how we've always handled parameter types, we
now recursively expand tuples in result types and separately
determine a result convention for each result.
The most important code-generation change here is that
indirect results are now returned separately from each
other and from any direct results. It is generally far
better, when receiving an indirect result, to receive it
as an independent result; the caller is much more likely
to be able to directly receive the result in the address
they want to initialize, rather than having to receive it
in temporary memory and then copy parts of it into the
target.
The most important conceptual change here that clients and
producers of SIL must be aware of is the new distinction
between a SILFunctionType's *parameters* and its *argument
list*. The former is just the formal parameters, derived
purely from the parameter types of the original function;
indirect results are no longer in this list. The latter
includes the indirect result arguments; as always, all
the indirect results strictly precede the parameters.
Apply instructions and entry block arguments follow the
argument list, not the parameter list.
A relatively minor change is that there can now be multiple
direct results, each with its own result convention.
This is a minor change because I've chosen to leave
return instructions as taking a single operand and
apply instructions as producing a single result; when
the type describes multiple results, they are implicitly
bound up in a tuple. It might make sense to split these
up and allow e.g. return instructions to take a list
of operands; however, it's not clear what to do on the
caller side, and this would be a major change that can
be separated out from this already over-large patch.
Unsurprisingly, the most invasive changes here are in
SILGen; this requires substantial reworking of both call
emission and reabstraction. It also proved important
to switch several SILGen operations over to work with
RValue instead of ManagedValue, since otherwise they
would be forced to spuriously "implode" buffers.
As there are no instructions left which produce multiple result values, this is a NFC regarding the generated SIL and generated code.
Although this commit is large, most changes are straightforward adoptions to the changes in the ValueBase and SILValue classes.
And use project_box to get to the address value.
SILGen now generates a project_box for each alloc_box.
And IRGen re-uses the address value from the alloc_box if the operand of project_box is an alloc_box.
This lets the generated code be the same as before.
Other than that most changes of this (quite large) commit are straightforward.
Partial applications of a root self value are an escape point, not a load. This
improves the diagnostic in this case from:
t.swift:18:24: error: variable 'self.B' used before being initialized
self.A.withCString { cString -> () in
^
to:
t.swift:18:24: error: variable 'self.B' captured by a closure before being initialized
self.A.withCString { cString -> () in
^
Having a separate address and container value returned from alloc_stack is not really needed in SIL.
Even if they differ we have both addresses available during IRGen, because a dealloc_stack is always dominated by the corresponding alloc_stack in the same function.
Although this commit quite large, most changes are trivial. The largest non-trivial change is in IRGenSIL.
This commit is a NFC regarding the generated code. Even the generated SIL is the same (except removed #0, #1 and @local_storage).
(libraries now)
It has been generally agreed that we need to do this reorg, and now
seems like the perfect time. Some major pass reorganization is in the
works.
This does not have to be the final word on the matter. The consensus
among those working on the code is that it's much better than what we
had and a better starting point for future bike shedding.
Note that the previous organization was designed to allow separate
analysis and optimization libraries. It turns out this is an
artificial distinction and not an important goal.