Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
case noRuntime
case noExistentials
case noObjCRuntime
case manualOwnership
}

public var performanceConstraints: PerformanceConstraints {
Expand All @@ -303,6 +304,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
case .NoRuntime: return .noRuntime
case .NoExistentials: return .noExistentials
case .NoObjCBridging: return .noObjCRuntime
case .ManualOwnership: return .manualOwnership
default: fatalError("unknown performance constraint")
}
}
Expand Down
5 changes: 4 additions & 1 deletion include/swift/AST/DeclAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,10 @@ SIMPLE_DECL_ATTR(_noObjCBridging, NoObjCBridging,
UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
155)

// Unused '156': Used to be `_distributedThunkTarget` but completed implementation in Swift 6.0 does not need it after all
SIMPLE_DECL_ATTR(_manualOwnership, ManualOwnership,
OnAbstractFunction | OnSubscript,
UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
156)

DECL_ATTR(_allowFeatureSuppression, AllowFeatureSuppression,
OnAnyDecl,
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ ERROR(wrong_linkage_for_serialized_function,none,
"function has wrong linkage to be called from %0", (StringRef))
NOTE(performance_called_from,none,
"called from here", ())
ERROR(manualownership_copy,none,
"explicit 'copy' required here", ())

// 'transparent' diagnostics
ERROR(circular_transparent,none,
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2145,6 +2145,10 @@ ERROR(attr_static_exclusive_only_mutating,none,
ERROR(attr_static_exclusive_no_setters,none,
"variable of type %0 must not have a setter", (Type))

// @_manualOwnership
ERROR(attr_manual_ownership_experimental,none,
"'@_manualOwnership' requires '-enable-experimental-feature ManualOwnership'", ())

// @extractConstantsFromMembers
ERROR(attr_extractConstantsFromMembers_experimental,none,
"'@extractConstantsFromMembers' requires "
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ EXPERIMENTAL_FEATURE(LifetimeDependence, true)
/// Enable the `@_staticExclusiveOnly` attribute.
EXPERIMENTAL_FEATURE(StaticExclusiveOnly, true)

/// Enable the `@_manualOwnership` attribute.
EXPERIMENTAL_FEATURE(ManualOwnership, false)

/// Enable the @extractConstantsFromMembers attribute.
EXPERIMENTAL_FEATURE(ExtractConstantsFromMembers, false)

Expand Down
3 changes: 2 additions & 1 deletion include/swift/SIL/SILBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,8 @@ struct BridgedFunction {
NoLocks = 2,
NoRuntime = 3,
NoExistentials = 4,
NoObjCBridging = 5
NoObjCBridging = 5,
ManualOwnership = 6,
};

enum class InlineStrategy {
Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ class SILBuilder {
return false;
}

/// If we have a SILFunction, return true if it has a ManualOwnership
/// PerformanceConstraint, which corresponds to an attribute on the FuncDecl.
bool hasManualOwnershipAttr() const {
if (F)
return F->getPerfConstraints() == PerformanceConstraints::ManualOwnership;
return false;
}

//===--------------------------------------------------------------------===//
// Insertion Point Management
//===--------------------------------------------------------------------===//
Expand Down
19 changes: 19 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,16 @@ template<typename ImplClass>
void
SILCloner<ImplClass>::visitCopyAddrInst(CopyAddrInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
// When cloning into a function using manual ownership, convert to explicit
// copies, in order to preserve the local nature of the perf constraint.
if (getBuilder().hasManualOwnershipAttr() && getBuilder().hasOwnership()) {
recordClonedInstruction(
Inst, getBuilder().createExplicitCopyAddr(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()),
getOpValue(Inst->getDest()), Inst->isTakeOfSrc(),
Inst->isInitializationOfDest()));
return;
}
recordClonedInstruction(
Inst, getBuilder().createCopyAddr(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()),
Expand Down Expand Up @@ -2092,6 +2102,15 @@ void SILCloner<ImplClass>::visitCopyValueInst(CopyValueInst *Inst) {
return recordFoldedValue(Inst, newValue);
}

// When cloning into a function using manual ownership, convert to explicit
// copies, in order to preserve the local nature of the perf constraint.
if (getBuilder().hasManualOwnershipAttr() && getBuilder().hasOwnership()) {
recordClonedInstruction(
Inst, getBuilder().createExplicitCopyValue(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand())));
return;
}

recordClonedInstruction(
Inst, getBuilder().createCopyValue(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand())));
Expand Down
3 changes: 2 additions & 1 deletion include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ enum class PerformanceConstraints : uint8_t {
NoLocks = 2,
NoRuntime = 3,
NoExistentials = 4,
NoObjCBridging = 5
NoObjCBridging = 5,
ManualOwnership = 6,
};

class SILSpecializeAttr final {
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5030,6 +5030,7 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, Label>,
TRIVIAL_ATTR_PRINTER(NoLocks, no_locks)
TRIVIAL_ATTR_PRINTER(NoMetadata, no_metadata)
TRIVIAL_ATTR_PRINTER(NoObjCBridging, no_objc_bridging)
TRIVIAL_ATTR_PRINTER(ManualOwnership, manual_ownership)
TRIVIAL_ATTR_PRINTER(NoRuntime, no_runtime)
TRIVIAL_ATTR_PRINTER(NonEphemeral, non_ephemeral)
TRIVIAL_ATTR_PRINTER(NonEscapable, non_escapable)
Expand Down
1 change: 1 addition & 0 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ static bool usesFeatureInlineArrayTypeSugar(Decl *D) {
}

UNINTERESTING_FEATURE(StaticExclusiveOnly)
UNINTERESTING_FEATURE(ManualOwnership)
UNINTERESTING_FEATURE(ExtractConstantsFromMembers)
UNINTERESTING_FEATURE(GroupActorErrors)
UNINTERESTING_FEATURE(SameElementRequirements)
Expand Down
1 change: 1 addition & 0 deletions lib/ASTGen/Sources/ASTGen/DeclAttrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ extension ASTGenVisitor {
.LexicalLifetimes,
.LLDBDebuggerFunction,
.MainType,
.ManualOwnership,
.Marker,
.MoveOnly,
.NeverEmitIntoClient,
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/IR/SILFunctionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ void SILFunctionBuilder::addFunctionAttributes(
F->setPerfConstraints(PerformanceConstraints::NoExistentials);
} else if (Attrs.hasAttribute<NoObjCBridgingAttr>()) {
F->setPerfConstraints(PerformanceConstraints::NoObjCBridging);
} else if (Attrs.hasAttribute<ManualOwnershipAttr>()) {
F->setPerfConstraints(PerformanceConstraints::ManualOwnership);
}

if (Attrs.hasAttribute<LexicalLifetimesAttr>()) {
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3658,6 +3658,7 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
case PerformanceConstraints::NoRuntime: OS << "[no_runtime] "; break;
case PerformanceConstraints::NoExistentials: OS << "[no_existentials] "; break;
case PerformanceConstraints::NoObjCBridging: OS << "[no_objc_bridging] "; break;
case PerformanceConstraints::ManualOwnership: OS << "[manual_ownership] "; break;
}

if (isPerformanceConstraint())
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,8 @@ static bool parseDeclSILOptional(
*perfConstraints = PerformanceConstraints::NoExistentials;
else if (perfConstraints && SP.P.Tok.getText() == "no_objc_bridging")
*perfConstraints = PerformanceConstraints::NoObjCBridging;
else if (perfConstraints && SP.P.Tok.getText() == "manual_ownership")
*perfConstraints = PerformanceConstraints::ManualOwnership;
else if (isPerformanceConstraint && SP.P.Tok.getText() == "perf_constraint")
*isPerformanceConstraint = true;
else if (markedAsUsed && SP.P.Tok.getText() == "used")
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/Utils/SILBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ static_assert((int)BridgedFunction::PerformanceConstraints::NoLocks == (int)swif
static_assert((int)BridgedFunction::PerformanceConstraints::NoRuntime == (int)swift::PerformanceConstraints::NoRuntime);
static_assert((int)BridgedFunction::PerformanceConstraints::NoExistentials == (int)swift::PerformanceConstraints::NoExistentials);
static_assert((int)BridgedFunction::PerformanceConstraints::NoObjCBridging == (int)swift::PerformanceConstraints::NoObjCBridging);
static_assert((int)BridgedFunction::PerformanceConstraints::ManualOwnership == (int)swift::PerformanceConstraints::ManualOwnership);

static_assert((int)BridgedFunction::InlineStrategy::InlineDefault == (int)swift::InlineDefault);
static_assert((int)BridgedFunction::InlineStrategy::NoInline == (int)swift::NoInline);
Expand Down
65 changes: 65 additions & 0 deletions lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ bool PerformanceDiagnostics::visitCallee(SILInstruction *callInst,
CalleeList callees,
PerformanceConstraints perfConstr,
LocWithParent *parentLoc) {
// Manual Ownership is not checked recursively within callees of the function,
// it's a local, non-viral performance annotation.
if (perfConstr == PerformanceConstraints::ManualOwnership)
return false;

LocWithParent asLoc(callInst->getLoc().getSourceLoc(), parentLoc);
LocWithParent *loc = &asLoc;
if (parentLoc && asLoc.loc == callInst->getFunction()->getLocation().getSourceLoc())
Expand Down Expand Up @@ -370,6 +375,66 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
RuntimeEffect impact = getRuntimeEffect(inst, impactType);
LocWithParent loc(inst->getLoc().getSourceLoc(), parentLoc);

if (perfConstr == PerformanceConstraints::ManualOwnership) {
if (impact == RuntimeEffect::RefCounting) {
bool shouldDiagnose = false;
switch (inst->getKind()) {
case SILInstructionKind::ExplicitCopyAddrInst:
case SILInstructionKind::ExplicitCopyValueInst:
break;
case SILInstructionKind::LoadInst: {
// FIXME: we don't have an `explicit_load` and transparent functions can
// end up bringing in a `load [copy]`. A better approach is needed to
// handle transparent functions, in general, as rewriting them during
// inlining of transparent functions is also not so great, as it
// may hide a copy that semantically is there, but we happened to
// tuck away in a transparent synthesized function, like those
// synthesized getters!
//
// For now look to see if the load copy's only non-destroying users are
// explicit_copy's, as that would indicate the user has acknowledged it:
//
// %y = load [copy] %x
// %z = explicit_copy_value %y
// destroy_value %y
//
// In all other cases, it's safer to diagnose.
//
auto load = cast<LoadInst>(inst);
if (load->getOwnershipQualifier() != LoadOwnershipQualifier::Copy)
break;

for (auto *use : load->getUsers()) {
if (!isa<ExplicitCopyAddrInst, ExplicitCopyValueInst,
DestroyAddrInst, DestroyValueInst>(use)) {
shouldDiagnose = true;
break;
}
}
break;
}
default:
shouldDiagnose = true;
break;
}
if (shouldDiagnose) {
diagnose(loc, diag::manualownership_copy);
llvm::dbgs() << "function " << inst->getFunction()->getName();
llvm::dbgs() << "\n has unexpected copying instruction: ";
inst->dump();
return false; // Don't bail-out early; diagnose more issues in the func.
}
} else if (impact == RuntimeEffect::ExclusivityChecking) {
// TODO: diagnose only the nested exclusivity; perhaps as a warning
// since we don't yet have reference bindings?

// diagnose(loc, diag::performance_arc);
// inst->dump();
// return true;
}
return false;
}

if (perfConstr == PerformanceConstraints::NoExistentials &&
((impact & RuntimeEffect::Existential) ||
(impact & RuntimeEffect::ExistentialClassBound))) {
Expand Down
5 changes: 5 additions & 0 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,11 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
diag::copy_expression_cannot_be_used_with_noncopyable_types);
}

/// FIXME: there really is no reason for the restriction on copy not being
/// permitted on fields, other than needing tests to ensure it works.
if (Ctx.LangOpts.hasFeature(ManualOwnership))
return;

// We only allow for copy_expr to be applied directly to lvalues. We do
// not allow currently for it to be applied to fields.
auto *subExpr = copyExpr->getSubExpr();
Expand Down
8 changes: 8 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
void visitUnsafeNonEscapableResultAttr(UnsafeNonEscapableResultAttr *attr);

void visitStaticExclusiveOnlyAttr(StaticExclusiveOnlyAttr *attr);
void visitManualOwnershipAttr(ManualOwnershipAttr *attr);
void visitWeakLinkedAttr(WeakLinkedAttr *attr);
void visitSILGenNameAttr(SILGenNameAttr *attr);
void visitLifetimeAttr(LifetimeAttr *attr);
Expand Down Expand Up @@ -8358,6 +8359,13 @@ void AttributeChecker::visitStaticExclusiveOnlyAttr(
}
}

void AttributeChecker::visitManualOwnershipAttr(ManualOwnershipAttr *attr) {
if (Ctx.LangOpts.hasFeature(Feature::ManualOwnership))
return;

diagnoseAndRemoveAttr(attr, diag::attr_manual_ownership_experimental);
}

void AttributeChecker::visitWeakLinkedAttr(WeakLinkedAttr *attr) {
if (!Ctx.LangOpts.Target.isOSBinFormatCOFF())
return;
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,7 @@ namespace {
UNINTERESTING_ATTR(NoRuntime)
UNINTERESTING_ATTR(NoExistentials)
UNINTERESTING_ATTR(NoObjCBridging)
UNINTERESTING_ATTR(ManualOwnership)
UNINTERESTING_ATTR(Inlinable)
UNINTERESTING_ATTR(Effects)
UNINTERESTING_ATTR(Expose)
Expand Down
Loading