Skip to content

Commit 94ad140

Browse files
committed
[Exclusivity] Check that accesses are well-formed during diagnostics.
This whitelists all the operations that produce an address that may be accessed and adds SIL test cases.
1 parent 7c34295 commit 94ad140

File tree

2 files changed

+128
-28
lines changed

2 files changed

+128
-28
lines changed

lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -435,30 +435,15 @@ static SILValue findUnderlyingObject(SILValue Value) {
435435
static AccessedStorage findAccessedStorage(SILValue Source) {
436436
SILValue Iter = Source;
437437
while (true) {
438-
// Inductive cases: look through operand to find ultimate source.
439-
if (auto *PBI = dyn_cast<ProjectBoxInst>(Iter)) {
440-
Iter = PBI->getOperand();
441-
continue;
442-
}
443-
444-
if (auto *CVI = dyn_cast<CopyValueInst>(Iter)) {
445-
Iter = CVI->getOperand();
446-
continue;
447-
}
448-
449-
if (auto *MII = dyn_cast<MarkUninitializedInst>(Iter)) {
450-
Iter = MII->getOperand();
451-
continue;
452-
}
453-
454-
// Base cases: make sure ultimate source is recognized.
438+
// Base case for globals: make sure ultimate source is recognized.
455439
if (auto *GAI = dyn_cast<GlobalAddrInst>(Iter)) {
456440
return AccessedStorage(GAI->getReferencedGlobal());
457441
}
458442

443+
// Base case for class objects.
459444
if (auto *REA = dyn_cast<RefElementAddrInst>(Iter)) {
460445
// Do a best-effort to find the identity of the object being projected
461-
// from. It is OK to unsound here (i.e., miss when two ref_element_addrs
446+
// from. It is OK to be unsound here (i.e. miss when two ref_element_addrs
462447
// actually refer the same address) because these will be dynamically
463448
// checked.
464449
SILValue Object = findUnderlyingObject(REA->getOperand());
@@ -467,18 +452,50 @@ static AccessedStorage findAccessedStorage(SILValue Source) {
467452
return AccessedStorage(AccessedStorageKind::ClassProperty, OP);
468453
}
469454

470-
if (isa<AllocBoxInst>(Iter) || isa<BeginAccessInst>(Iter) ||
471-
isa<SILFunctionArgument>(Iter)) {
472-
// Treat the instruction itself as the identity of the storage being
473-
// being accessed.
455+
switch (Iter->getKind()) {
456+
// Inductive cases: look through operand to find ultimate source.
457+
case ValueKind::ProjectBoxInst:
458+
case ValueKind::CopyValueInst:
459+
case ValueKind::MarkUninitializedInst:
460+
case ValueKind::UncheckedAddrCastInst:
461+
// Inlined access to subobjects.
462+
case ValueKind::StructElementAddrInst:
463+
case ValueKind::TupleElementAddrInst:
464+
case ValueKind::UncheckedTakeEnumDataAddrInst:
465+
case ValueKind::RefTailAddrInst:
466+
case ValueKind::TailAddrInst:
467+
case ValueKind::IndexAddrInst:
468+
Iter = cast<SILInstruction>(Iter)->getOperand(0);
469+
continue;
470+
471+
// Base address producers.
472+
case ValueKind::AllocBoxInst:
473+
// An AllocBox is a fully identified memory location.
474+
case ValueKind::AllocStackInst:
475+
// An AllocStack is a fully identified memory location, which may occur
476+
// after inlining code already subjected to stack promotion.
477+
case ValueKind::BeginAccessInst:
478+
// The current access is nested within another access.
479+
// View the outer access as a separate location because nested accesses do
480+
// not conflict with each other.
481+
case ValueKind::SILFunctionArgument:
482+
// A function argument is effectively a nested access, enforced
483+
// independently in the caller and callee.
484+
case ValueKind::PointerToAddressInst:
485+
// An addressor provides access to a global or class property via a
486+
// RawPointer. Calling the addressor casts that raw pointer to an address.
474487
return AccessedStorage(Iter);
475-
}
476488

477-
// For now we're still allowing arbitrary addresses here. Once
478-
// we start doing a best-effort static check for dynamically-enforced
479-
// accesses we should lock this down to only recognized sources.
480-
assert(Iter->getType().isAddress() || Iter->getType().is<SILBoxType>());
481-
return AccessedStorage(Iter);
489+
// Unsupported address producers.
490+
// Initialization is always local.
491+
case ValueKind::InitEnumDataAddrInst:
492+
case ValueKind::InitExistentialAddrInst:
493+
// Accessing an existential value requires a cast.
494+
case ValueKind::OpenExistentialAddrInst:
495+
default:
496+
DEBUG(llvm::dbgs() << "Bad memory access source: " << Iter);
497+
llvm_unreachable("Unexpected access source.");
498+
}
482499
}
483500
}
484501

test/SILOptimizer/exclusivity_static_diagnostics.sil

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,86 @@ bb23:
462462
bbUnreachable:
463463
br bb22
464464
}
465+
466+
// Check that a pointer_to_address access passes diagnostics.
467+
//
468+
// CHECK-LABEL: sil hidden @pointerToAddr
469+
sil hidden @pointerToAddr : $@convention(thin) (Builtin.RawPointer) -> Int {
470+
bb0(%0: $Builtin.RawPointer):
471+
%adr = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Int
472+
%access = begin_access [read] [dynamic] %adr : $*Int
473+
%val = load [trivial] %access : $*Int
474+
end_access %access : $*Int
475+
return %val : $Int
476+
}
477+
478+
// Helper.
479+
struct S {
480+
var x: Int
481+
}
482+
483+
// Check inlined struct element access.
484+
// This only happens when mandatory passes are applied repeatedly.
485+
// e.g. importing from a debug standard library.
486+
//
487+
// CHECK-LABEL: sil hidden @inlinedStructElement
488+
sil hidden @inlinedStructElement : $@convention(thin) (@inout S) -> Int {
489+
bb0(%0 : $*S):
490+
%2 = begin_access [modify] [static] %0 : $*S
491+
%3 = struct_element_addr %2 : $*S, #S.x
492+
%4 = begin_access [read] [static] %3 : $*Int
493+
%5 = load [trivial] %4 : $*Int
494+
end_access %4 : $*Int
495+
end_access %2 : $*S
496+
return %5 : $Int
497+
}
498+
499+
// Check inlined tuple element access.
500+
// This only happens when mandatory passes are applied repeatedly.
501+
//
502+
// CHECK-LABEL: sil hidden @inlinedTupleElement
503+
sil hidden @inlinedTupleElement : $@convention(thin) (@inout (Int, Int)) -> Int {
504+
bb0(%0 : $*(Int, Int)):
505+
%2 = begin_access [modify] [static] %0 : $*(Int, Int)
506+
%3 = tuple_element_addr %2 : $*(Int, Int), 0
507+
%4 = begin_access [read] [static] %3 : $*Int
508+
%5 = load [trivial] %4 : $*Int
509+
end_access %4 : $*Int
510+
end_access %2 : $*(Int, Int)
511+
return %5 : $Int
512+
}
513+
514+
// Check inlined enum access.
515+
// This only happens when mandatory passes are applied repeatedly.
516+
//
517+
// CHECK-LABEL: sil hidden @inlinedEnumValue
518+
sil hidden @inlinedEnumValue : $@convention(thin) (Int) -> (@out Optional<Int>, Int) {
519+
bb0(%0 : $*Optional<Int>, %1 : $Int):
520+
%6 = unchecked_take_enum_data_addr %0 : $*Optional<Int>, #Optional.some!enumelt.1
521+
%7 = begin_access [read] [static] %6 : $*Int
522+
%8 = load [trivial] %7 : $*Int
523+
end_access %7 : $*Int
524+
return %8 : $Int
525+
}
526+
527+
// Helper.
528+
class Storage {}
529+
530+
// Check inlined array access.
531+
// This only happens when mandatory passes are applied repeatedly.
532+
//
533+
// CHECK-LABEL: sil hidden @inlinedArrayProp
534+
sil hidden @inlinedArrayProp : $@convention(thin) (@guaranteed Storage, Builtin.Word) -> Int {
535+
bb0(%0 : $Storage, %1 : $Builtin.Word):
536+
%2 = ref_tail_addr %0 : $Storage, $UInt
537+
%3 = begin_access [read] [static] %2 : $*UInt
538+
%4 = tail_addr %3 : $*UInt, %1 : $Builtin.Word, $Int
539+
%5 = begin_access [read] [static] %4 : $*Int
540+
%6 = index_addr %5 : $*Int, %1 : $Builtin.Word
541+
%7 = begin_access [read] [static] %6 : $*Int
542+
%8 = load [trivial] %7 : $*Int
543+
end_access %7 : $*Int
544+
end_access %5 : $*Int
545+
end_access %3 : $*UInt
546+
return %8 : $Int
547+
}

0 commit comments

Comments
 (0)