Skip to content
Merged
20 changes: 18 additions & 2 deletions include/swift/AST/SILLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ class SILField final {
/// destroying the old value and emplacing a new value with the modified
/// field in the same place.
bool isMutable() const { return LoweredTypeAndFlags.getInt() & IsMutable; }

/// Change the current SILField's mutability field.
void setIsMutable(bool newValue) {
LoweredTypeAndFlags = {getLoweredType(), getFlagsValue(newValue)};
}
};

/// A layout.
Expand Down Expand Up @@ -133,14 +138,25 @@ class SILLayout final : public llvm::FoldingSetNode,
bool isMutable() const {
return GenericSigAndFlags.getInt() & IsMutable;
}


/// Returns a SILLayout that is the same as the current layout but with the
/// mutability of each field specified via index in \p
/// fieldIndexMutabilityUpdatePairs to have its mutability be the index's
/// associated bool pair.
SILLayout *withMutable(ASTContext &ctx,
std::initializer_list<std::pair<unsigned, bool>>
fieldIndexMutabilityUpdatePairs) const;

/// True if the layout captures the generic arguments it is substituted with
/// and can provide generic bindings when passed as a closure argument.
bool capturesGenericEnvironment() const {
return GenericSigAndFlags.getInt() & CapturesGenericEnvironment;
}

/// Get the fields inside the layout.
///
/// NOTE: The types inside the fields have not been specialized for the given
/// environment.
ArrayRef<SILField> getFields() const { return getTrailingObjects(NumFields); }

/// Produce a profile of this layout, for use in a folding set.
Expand Down
47 changes: 47 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -6167,6 +6167,7 @@ DEFINE_EMPTY_CAN_TYPE_WRAPPER(SILFunctionType, Type)
class SILBoxType;
class SILLayout; // From SIL
class SILModule; // From SIL
class SILField; // From SIL
typedef CanTypeWrapper<SILBoxType> CanSILBoxType;

/// The SIL-only type for boxes, which represent a reference to a (non-class)
Expand All @@ -6187,6 +6188,52 @@ class SILBoxType final : public TypeBase, public llvm::FoldingSetNode
SILLayout *getLayout() const { return Layout; }
SubstitutionMap getSubstitutions() const { return Substitutions; }

/// Return the fields of this type from its layout.
///
/// NOTE: These types have not been appropriately specialized either for a
/// SILFunction where it occurs or any substitutions that are stored within
/// the substitution map of the function. In order to get appropriate SILTypes
/// for fields to work with directly in SIL, please call getFieldType below
/// which handles the relevant specializations correctly for you.
ArrayRef<SILField> getFields() const;

/// Return the SILType of the field of the layout of the SILBoxType.
///
/// NOTE: This ensures that the type is probably specialized both for the
/// substitutions of this type and the relevant SILFunction.
///
/// Defined in SILType.cpp.
SILType getFieldType(SILFunction &fn, unsigned index);

/// Returns the number of fields in the box type's layout.
unsigned getNumFields() const { return getFields().size(); }

/// Returns true if the given field in the box is mutable. Returns false
/// otherwise.
bool isFieldMutable(unsigned index) const;

/// Returns a SILBoxType that is the same as the current box type but with the
/// mutability of each field specified via index in \p
/// fieldIndexMutabilityUpdatePairs to have its mutability be the index's
/// associated bool pair.
CanSILBoxType withMutable(ASTContext &ctx,
std::initializer_list<std::pair<unsigned, bool>>
fieldIndexMutabilityUpdatePairs) const;

using SILFieldIndexToSILTypeTransform = std::function<SILType(unsigned)>;
using SILFieldToSILTypeRange =
iterator_range<llvm::mapped_iterator<IntRange<unsigned>::iterator,
SILFieldIndexToSILTypeTransform>>;

/// Returns a range of SILTypes that have been specialized correctly for use
/// in the passed in SILFunction.
///
/// DISCUSSION: The inner range is an IntRange since the inner API that we use
/// to transform fields is defined in terms of indices.
///
/// Defined in SILType.cpp.
SILFieldToSILTypeRange getSILFieldTypes(SILFunction &fn);

// TODO: SILBoxTypes should be explicitly constructed in terms of specific
// layouts. As a staging mechanism, we expose the old single-boxed-type
// interface.
Expand Down
17 changes: 14 additions & 3 deletions include/swift/SIL/OperandDatastructures.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,19 @@ class OperandWorklist {
return false;
}

/// Pushes the operands of all uses of \p value onto the worklist if the
/// operands have never been pushed before. Returns \p true if we inserted
/// /any/ values.
///
/// This is a bulk convenience API.
bool pushResultOperandsIfNotVisited(SILValue value) {
bool insertedOperand = false;
for (auto *use : value->getUses()) {
insertedOperand |= pushIfNotVisited(use);
}
return insertedOperand;
}

/// Pushes the operands of all uses of \p instruction onto the worklist if the
/// operands have never been pushed before. Returns \p true if we inserted
/// /any/ values.
Expand All @@ -110,9 +123,7 @@ class OperandWorklist {
bool pushResultOperandsIfNotVisited(SILInstruction *inst) {
bool insertedOperand = false;
for (auto result : inst->getResults()) {
for (auto *use : result->getUses()) {
insertedOperand |= pushIfNotVisited(use);
}
insertedOperand |= pushResultOperandsIfNotVisited(result);
}
return insertedOperand;
}
Expand Down
15 changes: 14 additions & 1 deletion include/swift/SIL/SILArgument.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,15 @@ class SILFunctionArgument : public SILArgument {
ValueOwnershipKind ownershipKind, ValueDecl *decl = nullptr,
bool isNoImplicitCopy = false,
LifetimeAnnotation lifetimeAnnotation = LifetimeAnnotation::None,
bool isCapture = false, bool isParameterPack = false)
bool isCapture = false, bool isParameterPack = false,
bool isInferredImmutable = false)
: SILArgument(ValueKind::SILFunctionArgument, parentBlock, type,
ownershipKind, decl) {
sharedUInt32().SILFunctionArgument.noImplicitCopy = isNoImplicitCopy;
sharedUInt32().SILFunctionArgument.lifetimeAnnotation = lifetimeAnnotation;
sharedUInt32().SILFunctionArgument.closureCapture = isCapture;
sharedUInt32().SILFunctionArgument.parameterPack = isParameterPack;
sharedUInt32().SILFunctionArgument.inferredImmutable = isInferredImmutable;
}

// A special constructor, only intended for use in
Expand Down Expand Up @@ -415,6 +417,16 @@ class SILFunctionArgument : public SILArgument {
sharedUInt32().SILFunctionArgument.parameterPack = isPack;
}

/// Returns true if this argument is inferred to be immutable.
bool isInferredImmutable() const {
return sharedUInt32().SILFunctionArgument.inferredImmutable;
}

/// Set whether this argument is inferred to be immutable.
void setInferredImmutable(bool newValue) {
sharedUInt32().SILFunctionArgument.inferredImmutable = newValue;
}

LifetimeAnnotation getLifetimeAnnotation() const {
return LifetimeAnnotation::Case(
sharedUInt32().SILFunctionArgument.lifetimeAnnotation);
Expand Down Expand Up @@ -472,6 +484,7 @@ class SILFunctionArgument : public SILArgument {
setLifetimeAnnotation(arg->getLifetimeAnnotation());
setClosureCapture(arg->isClosureCapture());
setFormalParameterPack(arg->isFormalParameterPack());
setInferredImmutable(arg->isInferredImmutable());
}

static bool classof(const SILInstruction *) = delete;
Expand Down
17 changes: 9 additions & 8 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,12 +490,12 @@ class SILBuilder {
bool reflection = false,
UsesMoveableValueDebugInfo_t usesMoveableValueDebugInfo =
DoesNotUseMoveableValueDebugInfo,
HasPointerEscape_t hasPointerEscape = DoesNotHavePointerEscape) {
return createAllocBox(loc, SILBoxType::get(fieldType.getASTType()), Var,
hasDynamicLifetime, reflection,
usesMoveableValueDebugInfo,
/*skipVarDeclAssert*/ false,
hasPointerEscape);
HasPointerEscape_t hasPointerEscape = DoesNotHavePointerEscape,
bool inferredImmutable = false) {
return createAllocBox(
loc, SILBoxType::get(fieldType.getASTType()), Var, hasDynamicLifetime,
reflection, usesMoveableValueDebugInfo,
/*skipVarDeclAssert*/ false, hasPointerEscape, inferredImmutable);
}

AllocBoxInst *createAllocBox(
Expand All @@ -506,7 +506,8 @@ class SILBuilder {
UsesMoveableValueDebugInfo_t usesMoveableValueDebugInfo =
DoesNotUseMoveableValueDebugInfo,
bool skipVarDeclAssert = false,
HasPointerEscape_t hasPointerEscape = DoesNotHavePointerEscape) {
HasPointerEscape_t hasPointerEscape = DoesNotHavePointerEscape,
bool inferredImmutable = false) {
#if NDEBUG
(void)skipVarDeclAssert;
#endif
Expand All @@ -521,7 +522,7 @@ class SILBuilder {
return insert(AllocBoxInst::create(
getSILDebugLocation(Loc, true), BoxType, *F,
substituteAnonymousArgs(Name, Var, Loc), hasDynamicLifetime, reflection,
usesMoveableValueDebugInfo, hasPointerEscape));
usesMoveableValueDebugInfo, hasPointerEscape, inferredImmutable));
}

AllocExistentialBoxInst *
Expand Down
3 changes: 2 additions & 1 deletion include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,8 @@ SILCloner<ImplClass>::visitAllocBoxInst(AllocBoxInst *Inst) {
Loc, this->getOpType(Inst->getType()).template castTo<SILBoxType>(),
VarInfo, Inst->hasDynamicLifetime(), Inst->emitReflectionMetadata(),
Inst->usesMoveableValueDebugInfo(),
/*skipVarDeclAssert*/ true, Inst->hasPointerEscape()));
/*skipVarDeclAssert*/ true, Inst->hasPointerEscape(),
Inst->isInferredImmutable()));
}

template<typename ImplClass>
Expand Down
14 changes: 12 additions & 2 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2527,14 +2527,16 @@ class AllocBoxInst final
HasDynamicLifetime_t hasDynamicLifetime, bool reflection = false,
UsesMoveableValueDebugInfo_t usesMoveableValueDebugInfo =
DoesNotUseMoveableValueDebugInfo,
HasPointerEscape_t hasPointerEscape = DoesNotHavePointerEscape);
HasPointerEscape_t hasPointerEscape = DoesNotHavePointerEscape,
bool inferredImmutable = false);

static AllocBoxInst *create(
SILDebugLocation Loc, CanSILBoxType boxType, SILFunction &F,
std::optional<SILDebugVariable> Var,
HasDynamicLifetime_t hasDynamicLifetime, bool reflection = false,
UsesMoveableValueDebugInfo_t wasMoved = DoesNotUseMoveableValueDebugInfo,
HasPointerEscape_t hasPointerEscape = DoesNotHavePointerEscape);
HasPointerEscape_t hasPointerEscape = DoesNotHavePointerEscape,
bool inferredImmutable = false);

public:
CanSILBoxType getBoxType() const {
Expand All @@ -2557,6 +2559,14 @@ class AllocBoxInst final
return HasPointerEscape_t(sharedUInt8().AllocBoxInst.pointerEscape);
}

void setInferredImmutable(bool value) {
sharedUInt8().AllocBoxInst.inferredImmutable = value;
}

bool isInferredImmutable() const {
return sharedUInt8().AllocBoxInst.inferredImmutable;
}

/// True if the box should be emitted with reflection metadata for its
/// contents.
bool emitReflectionMetadata() const {
Expand Down
5 changes: 3 additions & 2 deletions include/swift/SIL/SILNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ class alignas(8) SILNode :
dynamicLifetime : 1,
reflection : 1,
usesMoveableValueDebugInfo : 1,
pointerEscape : 1);
pointerEscape : 1,
inferredImmutable : 1);

SHARED_FIELD(AllocRefInstBase, uint8_t
objC : 1,
Expand Down Expand Up @@ -318,7 +319,7 @@ class alignas(8) SILNode :
SHARED_FIELD(PointerToAddressInst, uint32_t alignment);
SHARED_FIELD(SILFunctionArgument, uint32_t noImplicitCopy : 1,
lifetimeAnnotation : 2, closureCapture : 1,
parameterPack : 1);
parameterPack : 1, inferredImmutable : 1);
SHARED_FIELD(MergeRegionIsolationInst, uint32_t numOperands);

// Do not use `_sharedUInt32_private` outside of SILNode.
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,8 @@ LEGACY_PASS(DiagnoseUnnecessaryPreconcurrencyImports, "sil-diagnose-unnecessary-
"Diagnose any preconcurrency imports that Sema and TransferNonSendable did not use")
LEGACY_PASS(ThunkLowering, "sil-thunk-lowering",
"Lower thunk instructions to actual thunks")
LEGACY_PASS(MarkNeverWrittenMutableClosureBoxesAsImmutable, "mark-never-written-mutable-closure-boxes-as-immutable",
"Mark never written mutable closure boxes as immutable")
LEGACY_PASS(PruneVTables, "prune-vtables",
"Mark class methods that do not require vtable dispatch")

Expand Down
17 changes: 13 additions & 4 deletions include/swift/SILOptimizer/Utils/SILIsolationInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ class SILIsolationInfo {
/// SILIsolationInfo.
static SILIsolationInfo getFunctionIsolation(SILFunction *fn);

private:
/// A helper that is used to ensure that we treat certain builtin values as
/// non-Sendable that the AST level otherwise thinks are non-Sendable.
///
Expand All @@ -580,12 +581,20 @@ class SILIsolationInfo {
return !isNonSendableType(type, fn);
}

static bool isNonSendableType(SILValue value) {
return isNonSendableType(value->getType(), value->getFunction());
public:
static bool isSendable(SILValue value);

static bool isNonSendable(SILValue value) { return !isSendable(value); }

static bool boxContainsOnlySendableFields(AllocBoxInst *abi) {
return boxTypeContainsOnlySendableFields(abi->getBoxType(),
abi->getFunction());
}

static bool isSendableType(SILValue value) {
return !isNonSendableType(value);
static bool boxTypeContainsOnlySendableFields(CanSILBoxType boxType,
SILFunction *fn) {
return llvm::all_of(boxType->getSILFieldTypes(*fn),
[&](SILType type) { return isSendableType(type, fn); });
}

bool hasSameIsolation(ActorIsolation actorIsolation) const;
Expand Down
32 changes: 32 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6984,6 +6984,21 @@ SILLayout *SILLayout::get(ASTContext &C,
return newLayout;
}

SILLayout *
SILLayout::withMutable(ASTContext &ctx,
std::initializer_list<std::pair<unsigned, bool>>
fieldIndexMutabilityUpdatePairs) const {
// Copy the fields, setting the mutable field to newMutable.
SmallVector<SILField, 8> newFields;
llvm::copy(getFields(), std::back_inserter(newFields));
for (auto p : fieldIndexMutabilityUpdatePairs) {
newFields[p.first].setIsMutable(p.second);
}

return SILLayout::get(ctx, getGenericSignature(), newFields,
capturesGenericEnvironment());
}

CanSILBoxType SILBoxType::get(ASTContext &C,
SILLayout *Layout,
SubstitutionMap Substitutions) {
Expand Down Expand Up @@ -7020,6 +7035,23 @@ CanSILBoxType SILBoxType::get(CanType boxedType) {
return get(boxedType->getASTContext(), layout, subMap);
}

CanSILBoxType
SILBoxType::withMutable(ASTContext &ctx,
std::initializer_list<std::pair<unsigned, bool>>
fieldIndexMutabilityUpdatePairs) const {
return SILBoxType::get(
ctx, getLayout()->withMutable(ctx, fieldIndexMutabilityUpdatePairs),
getSubstitutions());
}

ArrayRef<SILField> SILBoxType::getFields() const {
return getLayout()->getFields();
}

bool SILBoxType::isFieldMutable(unsigned index) const {
return getFields()[index].isMutable();
}

LayoutConstraint
LayoutConstraint::getLayoutConstraint(LayoutConstraintKind Kind,
ASTContext &C) {
Expand Down
Loading