Skip to content

Commit 00b5a9d

Browse files
committed
Emit unenforced access markers in SILGen for accesses to local temporaries.
These accesses can't be recognized as obviously local temporaries in the verification pass, so the only way to exhaustively verify exclusivity is by added unenforced markers. SILGen currently only emits unenforced markers under -verify-exlcusivity. Once opaque values is the only supported SILGen mode, then we should turn the markers on by default (SILGen should not have different modes of operation).
1 parent 34a968c commit 00b5a9d

File tree

6 files changed

+232
-21
lines changed

6 files changed

+232
-21
lines changed

lib/SILGen/FormalEvaluation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class LogicalPathComponent;
2626

2727
class FormalAccess {
2828
public:
29-
enum Kind { Shared, Exclusive, Owned };
29+
enum Kind { Shared, Exclusive, Owned, Unenforced };
3030

3131
private:
3232
unsigned allocatedSize;

lib/SILGen/LValue.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,62 @@ struct LLVM_LIBRARY_VISIBILITY ExclusiveBorrowFormalAccess : FormalAccess {
535535
}
536536
};
537537

538+
struct LLVM_LIBRARY_VISIBILITY UnenforcedAccess {
539+
// Make sure someone called `endAccess` before destroying this.
540+
struct DeleterCheck {
541+
void operator()(BeginAccessInst *) {
542+
llvm_unreachable("access scope must be ended");
543+
}
544+
};
545+
typedef std::unique_ptr<BeginAccessInst, DeleterCheck> BeginAccessPtr;
546+
BeginAccessPtr beginAccessPtr;
547+
548+
UnenforcedAccess() = default;
549+
UnenforcedAccess(const UnenforcedAccess &other) = delete;
550+
UnenforcedAccess(UnenforcedAccess &&other) = default;
551+
552+
UnenforcedAccess &operator=(const UnenforcedAccess &) = delete;
553+
UnenforcedAccess &operator=(UnenforcedAccess &&other) = default;
554+
555+
// Return the a new begin_access if it was required, otherwise return the
556+
// given `address`.
557+
SILValue beginAccess(SILGenFunction &SGF, SILLocation loc, SILValue address,
558+
SILAccessKind kind);
559+
560+
// End the access and release beginAccessPtr.
561+
void endAccess(SILGenFunction &SGF);
562+
563+
// Emit the end_access (on a branch) without marking this access as ended.
564+
void emitEndAccess(SILGenFunction &SGF);
565+
};
566+
567+
/// Pseudo-formal access that emits access markers but does not actually
568+
/// require enforcement. It may be used for access to formal memory that is
569+
/// exempt from exclusivity checking, such as initialization, or it may be used
570+
/// for accesses to local memory that are indistinguishable from formal access
571+
/// at the SIL level. Adding the access markers in these cases gives SIL address
572+
/// users a structural property that allows for exhaustive verification.
573+
struct LLVM_LIBRARY_VISIBILITY UnenforcedFormalAccess : FormalAccess {
574+
575+
static SILValue enter(SILGenFunction &SGF, SILLocation loc, SILValue address,
576+
SILAccessKind kind);
577+
578+
// access.beginAccessPtr is either the begin_access or null if no access was
579+
// required.
580+
UnenforcedAccess access;
581+
582+
UnenforcedFormalAccess(SILLocation loc, UnenforcedAccess &&access,
583+
CleanupHandle cleanup)
584+
: FormalAccess(sizeof(*this), FormalAccess::Unenforced, loc, cleanup),
585+
access(std::move(access)) {}
586+
587+
// Emit the end_access (on a branch) without marking this access as ended.
588+
void emitEndAccess(SILGenFunction &SGF);
589+
590+
// Only called at the end formal evaluation scope. End this access.
591+
void finishImpl(SILGenFunction &SGF) override;
592+
};
593+
538594
} // namespace Lowering
539595
} // namespace swift
540596

lib/SILGen/SILGenDecl.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "Initialization.h"
14+
#include "LValue.h"
1415
#include "RValue.h"
1516
#include "SILGen.h"
1617
#include "SILGenDynamicCast.h"
@@ -192,15 +193,27 @@ void SingleBufferInitialization::
192193
copyOrInitValueIntoSingleBuffer(SILGenFunction &SGF, SILLocation loc,
193194
ManagedValue value, bool isInit,
194195
SILValue destAddr) {
196+
// Emit an unchecked access around initialization of the local buffer to
197+
// silence access marker verification.
198+
//
199+
// FIXME: This is not a good place for FormalEvaluationScope +
200+
// UnenforcedFormalAccess. However, there's no way to identify the buffer
201+
// initialization sequence after SILGen, and no easy way to wrap the
202+
// Initialization in an access during top-level expression evaluation.
203+
FormalEvaluationScope scope(SGF);
195204
if (!isInit) {
196205
assert(value.getValue() != destAddr && "copying in place?!");
197-
value.copyInto(SGF, destAddr, loc);
206+
SILValue accessAddr =
207+
UnenforcedFormalAccess::enter(SGF, loc, destAddr, SILAccessKind::Modify);
208+
value.copyInto(SGF, accessAddr, loc);
198209
return;
199210
}
200211

201212
// If we didn't evaluate into the initialization buffer, do so now.
202213
if (value.getValue() != destAddr) {
203-
value.forwardInto(SGF, loc, destAddr);
214+
SILValue accessAddr =
215+
UnenforcedFormalAccess::enter(SGF, loc, destAddr, SILAccessKind::Modify);
216+
value.forwardInto(SGF, loc, accessAddr);
204217
} else {
205218
// If we did evaluate into the initialization buffer, disable the
206219
// cleanup.
@@ -828,10 +841,14 @@ void EnumElementPatternInitialization::emitEnumMatch(
828841
SILValue boxedValue = SGF.B.createProjectBox(loc, mv.getValue(), 0);
829842
auto &boxedTL = SGF.getTypeLowering(boxedValue->getType());
830843
// SEMANTIC ARC TODO: Revisit this when the verifier is enabled.
831-
if (boxedTL.isLoadable() || !SGF.silConv.useLoweredAddresses())
832-
boxedValue = boxedTL.emitLoad(SGF.B, loc, boxedValue,
844+
if (boxedTL.isLoadable() || !SGF.silConv.useLoweredAddresses()) {
845+
UnenforcedAccess access;
846+
SILValue accessAddress =
847+
access.beginAccess(SGF, loc, boxedValue, SILAccessKind::Read);
848+
boxedValue = boxedTL.emitLoad(SGF.B, loc, accessAddress,
833849
LoadOwnershipQualifier::Take);
834-
850+
access.endAccess(SGF);
851+
}
835852
// We must treat the boxed value as +0 since it may be shared. Copy it
836853
// if nontrivial.
837854
//

lib/SILGen/SILGenExpr.cpp

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -966,33 +966,56 @@ emitRValueForDecl(SILLocation loc, ConcreteDeclRef declRef, Type ncRefType,
966966
if (var->isLet())
967967
guaranteedValid = true;
968968

969+
// Protect the lvalue read with access markers. The !is<LValueType> assert
970+
// above ensures that the "LValue" is actually immutable, so we use an
971+
// unenforced access marker.
972+
SILValue destAddr = result.getLValueAddress();
973+
SILValue accessAddr = UnenforcedFormalAccess::enter(*this, loc, destAddr,
974+
SILAccessKind::Read);
975+
auto propagateRValuePastAccess = [&](RValue &&rvalue) {
976+
// Check if a new begin_access was emitted and returned as the
977+
// RValue. This means that the load did not actually load. If so, then
978+
// fix the rvalue to begin_access operand. The end_access cleanup
979+
// doesn't change. FIXME: this can't happen with sil-opaque-values.
980+
if (accessAddr != destAddr && rvalue.isComplete()
981+
&& rvalue.isPlusZero(*this) && !isa<TupleType>(rvalue.getType())) {
982+
auto mv = std::move(rvalue).getScalarValue();
983+
if (mv.getValue() == accessAddr)
984+
mv = std::move(mv).transform(
985+
cast<BeginAccessInst>(accessAddr)->getOperand());
986+
return RValue(*this, loc, refType, mv);
987+
}
988+
return std::move(rvalue);
989+
};
969990
// If we have self, see if we are in an 'init' delegation sequence. If so,
970991
// call out to the special delegation init routine. Otherwise, use the
971992
// normal RValue emission logic.
972993
if (var->getName() == getASTContext().Id_self &&
973994
SelfInitDelegationState != NormalSelf) {
974-
return emitRValueForSelfInDelegationInit(loc, refType,
975-
result.getLValueAddress(), C);
995+
auto rvalue =
996+
emitRValueForSelfInDelegationInit(loc, refType, accessAddr, C);
997+
return propagateRValuePastAccess(std::move(rvalue));
976998
}
977999

9781000
// Avoid computing an abstraction pattern for local variables.
9791001
// This is a slight compile-time optimization, but more importantly
9801002
// it avoids problems where locals don't always have interface types.
9811003
if (var->getDeclContext()->isLocalContext()) {
982-
return RValue(*this, loc, refType,
983-
emitLoad(loc, result.getLValueAddress(),
984-
getTypeLowering(refType), C, shouldTake,
985-
guaranteedValid));
1004+
auto rvalue = RValue(*this, loc, refType,
1005+
emitLoad(loc, accessAddr, getTypeLowering(refType),
1006+
C, shouldTake, guaranteedValid));
1007+
1008+
return propagateRValuePastAccess(std::move(rvalue));
9861009
}
9871010

9881011
// Otherwise, do the full thing where we potentially bridge and
9891012
// reabstract the declaration.
9901013
auto origFormalType = SGM.Types.getAbstractionPattern(var);
991-
return RValue(*this, loc, refType,
992-
emitLoad(loc, result.getLValueAddress(),
993-
origFormalType, refType,
994-
getTypeLowering(refType), C, shouldTake,
995-
guaranteedValid));
1014+
auto rvalue = RValue(*this, loc, refType,
1015+
emitLoad(loc, accessAddr, origFormalType, refType,
1016+
getTypeLowering(refType), C, shouldTake,
1017+
guaranteedValid));
1018+
return propagateRValuePastAccess(std::move(rvalue));
9961019
}
9971020

9981021
// For local decls, use the address we allocated or the value if we have it.
@@ -1276,14 +1299,21 @@ RValue SILGenFunction::emitRValueForStorageLoad(
12761299
result = result.copyUnmanaged(*this, loc);
12771300
}
12781301
} else {
1302+
// Create a tiny unenforced access scope around a load from local memory. No
1303+
// cleanup is necessary since we directly emit the load here. This will
1304+
// probably go away with opaque values.
1305+
UnenforcedAccess access;
1306+
SILValue accessAddress =
1307+
access.beginAccess(*this, loc, base.getValue(), SILAccessKind::Read);
1308+
12791309
// For address-only sequences, the base is in memory. Emit a
12801310
// struct_element_addr to get to the field, and then load the element as an
12811311
// rvalue.
1282-
SILValue ElementPtr =
1283-
B.createStructElementAddr(loc, base.getValue(), field);
1312+
SILValue ElementPtr = B.createStructElementAddr(loc, accessAddress, field);
12841313

12851314
result = emitLoad(loc, ElementPtr, abstractedTL,
12861315
hasAbstractionChange ? SGFContext() : C, IsNotTake);
1316+
access.endAccess(*this);
12871317
}
12881318

12891319
// If we're accessing this member with an abstraction change, perform that

lib/SILGen/SILGenLValue.cpp

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/DiagnosticsSIL.h"
2525
#include "swift/AST/DiagnosticsCommon.h"
2626
#include "swift/AST/GenericEnvironment.h"
27+
#include "swift/SIL/InstructionUtils.h"
2728
#include "swift/SIL/PrettyStackTrace.h"
2829
#include "swift/SIL/SILArgument.h"
2930
#include "swift/SIL/SILUndef.h"
@@ -52,7 +53,7 @@ struct LValueWritebackCleanup : Cleanup {
5253
void dump(SILGenFunction &) const override {
5354
#ifndef NDEBUG
5455
llvm::errs() << "LValueWritebackCleanup\n"
55-
<< "State: " << getState() << "Depth: " << Depth.getDepth()
56+
<< "State: " << getState() << " Depth: " << Depth.getDepth()
5657
<< "\n";
5758
#endif
5859
}
@@ -266,6 +267,13 @@ ManagedValue LogicalPathComponent::getMaterialized(SILGenFunction &SGF,
266267

267268
ManagedValue temp = std::move(*this).materializeIntoTemporary(SGF, loc, base);
268269

270+
if (SGF.getOptions().VerifyExclusivity) {
271+
// Begin an access of the temporary. It is unenforced because enforcement
272+
// isn't required for RValues.
273+
SILValue accessAddress = UnenforcedFormalAccess::enter(
274+
SGF, loc, temp.getValue(), SILAccessKind::Modify);
275+
temp = std::move(temp).transform(accessAddress);
276+
}
269277
// Push a writeback for the temporary.
270278
pushWriteback(SGF, loc, std::move(clonedComponent), base,
271279
MaterializedLValue(temp));
@@ -475,6 +483,93 @@ static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc,
475483
return addr;
476484
}
477485

486+
// Find the base of the formal access at `address`. If the base requires an
487+
// access marker, then create a begin_access on `address`. Return the
488+
// address to be used for the access.
489+
//
490+
// FIXME: In order to generate more consistent and verifiable SIL patterns, or
491+
// subobject projections, create the access on the base address and recreate the
492+
// projection.
493+
SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc,
494+
SILValue address, SILAccessKind kind) {
495+
if (!SGF.getOptions().VerifyExclusivity)
496+
return address;
497+
498+
SILValue base = findAccessedAddressBase(address);
499+
if (!base || !isPossibleFormalAccessBase(base))
500+
return address;
501+
502+
auto BAI =
503+
SGF.B.createBeginAccess(loc, address, kind, SILAccessEnforcement::Unsafe);
504+
beginAccessPtr = BeginAccessPtr(BAI, DeleterCheck());
505+
506+
return BAI;
507+
}
508+
509+
void UnenforcedAccess::endAccess(SILGenFunction &SGF) {
510+
emitEndAccess(SGF);
511+
beginAccessPtr.release();
512+
}
513+
514+
void UnenforcedAccess::emitEndAccess(SILGenFunction &SGF) {
515+
if (!beginAccessPtr)
516+
return;
517+
518+
SGF.B.createEndAccess(beginAccessPtr->getLoc(), beginAccessPtr.get(),
519+
/*abort*/ false);
520+
}
521+
522+
// Emit an end_access marker when executing a cleanup (on a side branch).
523+
void UnenforcedFormalAccess::emitEndAccess(SILGenFunction &SGF) {
524+
access.emitEndAccess(SGF);
525+
}
526+
527+
// End the access when existing the FormalEvaluationScope.
528+
void UnenforcedFormalAccess::finishImpl(SILGenFunction &SGF) {
529+
access.endAccess(SGF);
530+
}
531+
532+
namespace {
533+
struct UnenforcedAccessCleanup : Cleanup {
534+
FormalEvaluationContext::stable_iterator Depth;
535+
536+
UnenforcedAccessCleanup() : Depth() {}
537+
538+
void emit(SILGenFunction &SGF, CleanupLocation loc) override {
539+
auto &evaluation = *SGF.FormalEvalContext.find(Depth);
540+
assert(evaluation.getKind() == FormalAccess::Unenforced);
541+
auto &formalAccess = static_cast<UnenforcedFormalAccess &>(evaluation);
542+
formalAccess.emitEndAccess(SGF);
543+
}
544+
545+
void dump(SILGenFunction &) const override {
546+
#ifndef NDEBUG
547+
llvm::errs() << "UnenforcedAccessCleanup\n"
548+
<< "State: " << getState() << " Depth: " << Depth.getDepth()
549+
<< "\n";
550+
#endif
551+
}
552+
};
553+
} // end anonymous namespace
554+
555+
SILValue UnenforcedFormalAccess::enter(SILGenFunction &SGF, SILLocation loc,
556+
SILValue address, SILAccessKind kind) {
557+
assert(SGF.InFormalEvaluationScope);
558+
559+
UnenforcedAccess access;
560+
SILValue accessAddress = access.beginAccess(SGF, loc, address, kind);
561+
if (!access.beginAccessPtr)
562+
return address;
563+
564+
auto &cleanup = SGF.Cleanups.pushCleanup<UnenforcedAccessCleanup>();
565+
CleanupHandle handle = SGF.Cleanups.getTopCleanup();
566+
auto &context = SGF.FormalEvalContext;
567+
context.push<UnenforcedFormalAccess>(loc, std::move(access), handle);
568+
cleanup.Depth = context.stable_begin();
569+
570+
return accessAddress;
571+
}
572+
478573
namespace {
479574
class RefElementComponent : public PhysicalPathComponent {
480575
VarDecl *Field;
@@ -1259,6 +1354,7 @@ namespace {
12591354
// indirectly.
12601355
SILValue baseAddress;
12611356
SILValue baseMetatype;
1357+
UnenforcedAccess baseAccess;
12621358
if (base) {
12631359
if (base.getType().isAddress()) {
12641360
baseAddress = base.getValue();
@@ -1269,6 +1365,10 @@ namespace {
12691365
baseFormalType);
12701366

12711367
baseAddress = SGF.emitTemporaryAllocation(loc, base.getType());
1368+
// Create an unenforced formal access for the temporary base, which
1369+
// is passed @inout to the callback.
1370+
baseAddress = baseAccess.beginAccess(SGF, loc, baseAddress,
1371+
SILAccessKind::Modify);
12721372
if (base.getOwnershipKind() == ValueOwnershipKind::Guaranteed) {
12731373
SGF.B.createStoreBorrow(loc, base.getValue(), baseAddress);
12741374
} else {
@@ -1298,6 +1398,9 @@ namespace {
12981398
baseAddress,
12991399
baseMetatype
13001400
}, false);
1401+
1402+
if (baseAccess.beginAccessPtr)
1403+
baseAccess.endAccess(SGF);
13011404
}
13021405

13031406
// Continue.

lib/SILGen/SILGenPattern.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "Cleanup.h"
1515
#include "ExitableFullExpr.h"
1616
#include "Initialization.h"
17+
#include "LValue.h"
1718
#include "RValue.h"
1819
#include "SILGen.h"
1920
#include "Scope.h"
@@ -2156,9 +2157,13 @@ void PatternMatchEmission::emitEnumElementDispatch(
21562157
SILValue boxedValue = SGF.B.createProjectBox(loc, origCMV.getValue(), 0);
21572158
eltTL = &SGF.getTypeLowering(boxedValue->getType());
21582159
if (eltTL->isLoadable()) {
2160+
UnenforcedAccess access;
2161+
SILValue accessAddress =
2162+
access.beginAccess(SGF, loc, boxedValue, SILAccessKind::Read);
21592163
ManagedValue newLoadedBoxValue = SGF.B.createLoadBorrow(
2160-
loc, ManagedValue::forUnmanaged(boxedValue));
2164+
loc, ManagedValue::forUnmanaged(accessAddress));
21612165
boxedValue = newLoadedBoxValue.getUnmanagedValue();
2166+
access.endAccess(SGF);
21622167
}
21632168

21642169
// The boxed value may be shared, so we always have to copy it.

0 commit comments

Comments
 (0)