diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index b7b527532448f..dddd61e6e3905 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -1447,6 +1447,8 @@ final public class CopyableToMoveOnlyWrapperAddrInst final public class MoveOnlyWrapperToCopyableAddrInst : SingleValueInstruction, UnaryInstruction {} +final public class UncheckedOwnershipInst: SingleValueInstruction, UnaryInstruction {} + final public class ObjectInst : SingleValueInstruction { public var baseOperands: OperandArray { operands[0...Elements, Value> { + enclosingOperands.values + } + public override var isFunctionExiting: Bool { true } +} + final public class ThrowInst : TermInst, UnaryInstruction { public var thrownValue: Value { operand.value } public override var isFunctionExiting: Bool { true } diff --git a/SwiftCompilerSources/Sources/SIL/Registration.swift b/SwiftCompilerSources/Sources/SIL/Registration.swift index 868fac0033694..5b1b54393bf86 100644 --- a/SwiftCompilerSources/Sources/SIL/Registration.swift +++ b/SwiftCompilerSources/Sources/SIL/Registration.swift @@ -120,6 +120,7 @@ private func registerSILClasses() { register(MoveOnlyWrapperToCopyableBoxInst.self) register(CopyableToMoveOnlyWrapperAddrInst.self) register(MoveOnlyWrapperToCopyableAddrInst.self) + register(UncheckedOwnershipInst.self) register(ObjectInst.self) register(VectorInst.self) register(VectorBaseAddrInst.self) @@ -247,6 +248,7 @@ private func registerSILClasses() { register(UnreachableInst.self) register(ReturnInst.self) + register(ReturnBorrowInst.self) register(ThrowInst.self) register(ThrowAddrInst.self) register(YieldInst.self) diff --git a/docs/SIL/Instructions.md b/docs/SIL/Instructions.md index f70c4eb704bdc..dde6385d4d93a 100644 --- a/docs/SIL/Instructions.md +++ b/docs/SIL/Instructions.md @@ -1199,6 +1199,17 @@ the `set` case is passed `self`. This is only valid in Raw SIL. +### unchecked_ownership + +``` +sil-instruction ::= 'unchecked_ownership' sil-operand + +unchecked_ownership %1 : $T +``` + +unchecked_ownership disables the ownership verification of it's operand. This used in cases +we cannot resolve ownership until a mandatory pass runs. This is only valid in Raw SIL. + ### copy_addr ``` @@ -5003,6 +5014,20 @@ does not apply in the `raw` SIL stage. A function must not contain more than one `return` instruction. +### return_borrow + +``` +sil-terminator ::= 'return_borrow' sil-operand 'from_scopes' '(' (sil-operand (',' sil-operand)*)? ')' + +return_borrow %0 : $T from_scopes (%1, %2 ...) +// %0 must be a @guaranteed value +// %1, %2, ... must be borrow introducers for %0, like `load_borrow` +// $T must be the return type of the current function +``` + +return_borrow instruction is valid only for functions @guaranteed results. +It is used to a return a @guaranteed value that maybe produced within borrow scopes local to the function. + ### throw ``` diff --git a/include/swift/SIL/AddressWalker.h b/include/swift/SIL/AddressWalker.h index 2b680b69c7038..db2b564f96ed6 100644 --- a/include/swift/SIL/AddressWalker.h +++ b/include/swift/SIL/AddressWalker.h @@ -185,6 +185,7 @@ TransitiveAddressWalker::walk(SILValue projectedAddress) { llvm_unreachable("Never takes an address"); // Point uses. case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::YieldInst: case TermKind::TryApplyInst: diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 2b45e1d852fac..9413c2873a5f9 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -1555,6 +1555,12 @@ class SILBuilder { MoveOnlyWrapperToCopyableValueInst::Guaranteed)); } + UncheckedOwnershipInst *createUncheckedOwnership(SILLocation Loc, + SILValue Operand) { + return insert(new (getModule()) UncheckedOwnershipInst( + getSILDebugLocation(Loc), Operand, Operand->getOwnershipKind())); + } + UnconditionalCheckedCastInst * createUnconditionalCheckedCast(SILLocation Loc, CheckedCastInstOptions options, @@ -2658,6 +2664,12 @@ class SILBuilder { getFunction(), getSILDebugLocation(Loc), ReturnValue)); } + ReturnBorrowInst *createReturnBorrow(SILLocation Loc, SILValue returnValue, + ArrayRef enclosingValues) { + return insertTerminator(ReturnBorrowInst::create( + getSILDebugLocation(Loc), returnValue, enclosingValues, getModule())); + } + ThrowInst *createThrow(SILLocation Loc, SILValue errorValue) { return insertTerminator( new (getModule()) ThrowInst(getSILDebugLocation(Loc), errorValue)); diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 0ac7bcea8a3f8..621ac4cdefff8 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -2247,6 +2247,19 @@ void SILCloner::visitCopyableToMoveOnlyWrapperValueInst( recordClonedInstruction(inst, cvt); } +template +void SILCloner::visitUncheckedOwnershipInst( + UncheckedOwnershipInst *uoi) { + getBuilder().setCurrentDebugScope(getOpScope(uoi->getDebugScope())); + if (!getBuilder().hasOwnership()) { + return recordFoldedValue(uoi, getOpValue(uoi->getOperand())); + } + + recordClonedInstruction( + uoi, getBuilder().createUncheckedOwnership( + getOpLocation(uoi->getLoc()), getOpValue(uoi->getOperand()))); +} + template void SILCloner::visitReleaseValueInst(ReleaseValueInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); @@ -3417,6 +3430,22 @@ SILCloner::visitReturnInst(ReturnInst *Inst) { getOpValue(Inst->getOperand()))); } +template +void SILCloner::visitReturnBorrowInst(ReturnBorrowInst *rbi) { + getBuilder().setCurrentDebugScope(getOpScope(rbi->getDebugScope())); + if (!getBuilder().hasOwnership()) { + return recordClonedInstruction( + rbi, getBuilder().createReturn(getOpLocation(rbi->getLoc()), + getOpValue(rbi->getReturnValue()))); + } + + auto enclosingValues = getOpValueArray<8>(rbi->getEnclosingValues()); + recordClonedInstruction( + rbi, getBuilder().createReturnBorrow(getOpLocation(rbi->getLoc()), + getOpValue(rbi->getReturnValue()), + enclosingValues)); +} + template void SILCloner::visitThrowInst(ThrowInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 29111df02451d..c2ec1085671a3 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -9553,6 +9553,17 @@ class MoveOnlyWrapperToCopyableAddrInst } }; +class UncheckedOwnershipInst final + : public UnaryInstructionBase { + friend class SILBuilder; + + UncheckedOwnershipInst(SILDebugLocation DebugLoc, SILValue operand, + ValueOwnershipKind forwardingOwnershipKind) + : UnaryInstructionBase(DebugLoc, operand, operand->getType(), + forwardingOwnershipKind) {} +}; + /// Given an object reference, return true iff it is non-nil and refers /// to a native swift object with strong reference count of 1. class IsUniqueInst @@ -10123,6 +10134,7 @@ class TermInst : public NonValueInstruction { case TermKind::UnwindInst: case TermKind::UnreachableInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::YieldInst: @@ -10252,6 +10264,34 @@ class ReturnInst } }; +class ReturnBorrowInst final + : public InstructionBaseWithTrailingOperands< + SILInstructionKind::ReturnBorrowInst, ReturnBorrowInst, TermInst> { + friend SILBuilder; + + ReturnBorrowInst(SILDebugLocation DebugLoc, ArrayRef operands); + + static ReturnBorrowInst *create(SILDebugLocation DebugLoc, SILValue value, + ArrayRef enclosingValues, + SILModule &M); + +public: + SILValue getReturnValue() const { return getAllOperands()[0].get(); } + + ArrayRef getEnclosingValueOperands() const { + return getAllOperands().drop_front(); + } + + OperandValueArrayRef getEnclosingValues() const { + return OperandValueArrayRef(getEnclosingValueOperands()); + } + + SuccessorListTy getSuccessors() { + // No Successors. + return SuccessorListTy(); + } +}; + /// ThrowInst - Throw a typed error, returning it via the direct error result. class ThrowInst : public UnaryInstructionBase @@ -11667,6 +11707,7 @@ OwnershipForwardingSingleValueInstruction::classof(SILInstructionKind kind) { case SILInstructionKind::DropDeinitInst: case SILInstructionKind::BorrowedFromInst: case SILInstructionKind::ImplicitActorToOpaqueIsolationCastInst: + case SILInstructionKind::UncheckedOwnershipInst: return true; default: return false; diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 23c4279c00cd9..748c59565457c 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -469,7 +469,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) #include "swift/AST/ReferenceStorage.def" SINGLE_VALUE_INST(UncheckedOwnershipConversionInst, unchecked_ownership_conversion, - SingleValueInstruction, None, MayRelease) + SingleValueInstruction, None, DoesNotRelease) + // A move_value is an OSSA only instruction. Its result does not have any side // effects relative to other OSSA values like copy_value. SINGLE_VALUE_INST(MoveValueInst, move_value, SingleValueInstruction, None, @@ -520,6 +521,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SINGLE_VALUE_INST(CopyableToMoveOnlyWrapperAddrInst, copyable_to_moveonlywrapper_addr, SingleValueInstruction, None, DoesNotRelease) + SINGLE_VALUE_INST(UncheckedOwnershipInst, unchecked_ownership, + SingleValueInstruction, None, DoesNotRelease) // IsUnique does not actually write to memory but should be modeled // as such. Its operand is a pointer to an object reference. The @@ -731,6 +734,8 @@ ABSTRACT_INST(TermInst, SILInstruction) TermInst, None, DoesNotRelease) TERMINATOR(ReturnInst, return, TermInst, None, DoesNotRelease) + TERMINATOR(ReturnBorrowInst, return_borrow, + TermInst, None, DoesNotRelease) TERMINATOR(ThrowInst, throw, TermInst, None, DoesNotRelease) TERMINATOR(ThrowAddrInst, throw_addr, diff --git a/include/swift/SILOptimizer/Utils/SCCVisitor.h b/include/swift/SILOptimizer/Utils/SCCVisitor.h index 0d8c03c584e43..143a8696a6865 100644 --- a/include/swift/SILOptimizer/Utils/SCCVisitor.h +++ b/include/swift/SILOptimizer/Utils/SCCVisitor.h @@ -130,6 +130,7 @@ class SCCVisitor { case TermKind::UnreachableInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::SwitchValueInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 2dff0464d5bf8..0f8e100984a13 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1302,7 +1302,9 @@ class IRGenSILFunction : auto e = getLoweredExplosion(i->getOperand()); setLoweredExplosion(i, e); } - + void visitUncheckedOwnershipInst(UncheckedOwnershipInst *i) { + llvm_unreachable("unimplemented"); + } void visitMergeIsolationRegionInst(MergeIsolationRegionInst *i) { llvm_unreachable("Valid only when ownership is enabled"); } @@ -1504,6 +1506,9 @@ class IRGenSILFunction : void visitBranchInst(BranchInst *i); void visitCondBranchInst(CondBranchInst *i); void visitReturnInst(ReturnInst *i); + void visitReturnBorrowInst(ReturnBorrowInst *i) { + llvm_unreachable("unimplemented"); + } void visitThrowInst(ThrowInst *i); void visitThrowAddrInst(ThrowAddrInst *i); void visitUnwindInst(UnwindInst *i); diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 39f6d163720e6..24ddd68b21a1d 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -421,6 +421,7 @@ AGGREGATE_OWNERSHIP(DestructureTuple) AGGREGATE_OWNERSHIP(Enum) AGGREGATE_OWNERSHIP(UncheckedEnumData) AGGREGATE_OWNERSHIP(SwitchEnum) +AGGREGATE_OWNERSHIP(UncheckedOwnership) #undef AGGREGATE_OWNERSHIP // A begin_borrow is conditionally nested. @@ -636,6 +637,12 @@ OperandOwnership OperandOwnershipClassifier::visitReturnInst(ReturnInst *i) { llvm_unreachable("covered switch"); } +OperandOwnership +OperandOwnershipClassifier::visitReturnBorrowInst(ReturnBorrowInst *rbi) { + return getOperandIndex() == 0 ? OperandOwnership::GuaranteedForwarding + : OperandOwnership::EndBorrow; +} + OperandOwnership OperandOwnershipClassifier::visitAssignInst(AssignInst *i) { if (getValue() != i->getSrc()) { return OperandOwnership::TrivialUse; diff --git a/lib/SIL/IR/SILArgument.cpp b/lib/SIL/IR/SILArgument.cpp index 3cbfc18e350bb..4e0bd8496ce16 100644 --- a/lib/SIL/IR/SILArgument.cpp +++ b/lib/SIL/IR/SILArgument.cpp @@ -315,6 +315,7 @@ getSingleTerminatorOperandForPred(const SILBasicBlock *parentBlock, switch (predTermInst->getTermKind()) { case TermKind::UnreachableInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::UnwindInst: diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index e88d48ee68277..3c959a16d6d80 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1814,6 +1814,7 @@ bool TermInst::isFunctionExiting() const { case TermKind::YieldInst: return false; case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::UnwindInst: @@ -1835,6 +1836,7 @@ bool TermInst::isProgramTerminating() const { case TermKind::CheckedCastBranchInst: case TermKind::CheckedCastAddrBranchInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::UnwindInst: @@ -1861,6 +1863,7 @@ const Operand *TermInst::forwardedOperand() const { case TermKind::UnwindInst: case TermKind::UnreachableInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::YieldInst: @@ -3428,6 +3431,26 @@ ReturnInst::ReturnInst(SILFunction &func, SILDebugLocation debugLoc, "result info?!"); } +ReturnBorrowInst *ReturnBorrowInst::create(SILDebugLocation DebugLoc, + SILValue returnValue, + ArrayRef enclosingValues, + SILModule &M) { + auto Size = totalSizeToAlloc(enclosingValues.size() + 1); + auto Buffer = M.allocateInst(Size, alignof(ReturnBorrowInst)); + SmallVector operands; + operands.push_back(returnValue); + for (SILValue ev : enclosingValues) { + operands.push_back(ev); + } + return ::new (Buffer) ReturnBorrowInst(DebugLoc, operands); +} + +ReturnBorrowInst::ReturnBorrowInst(SILDebugLocation DebugLoc, + ArrayRef operands) + : InstructionBaseWithTrailingOperands(operands, DebugLoc) { + assert(operands[0]->getOwnershipKind() == OwnershipKind::Guaranteed); +} + // This may be called in an invalid SIL state. SILCombine creates new // terminators in non-terminator position and defers deleting the original // terminator until after all modification. diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index d286e37a38e7c..0d6b1f985b7a3 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2367,6 +2367,10 @@ class SILPrinter : public SILInstructionVisitor { *this << getIDAndType(I->getOperand()); } + void visitUncheckedOwnershipInst(UncheckedOwnershipInst *I) { + *this << getIDAndType(I->getOperand()); + } + void visitUnownedCopyValueInst(UnownedCopyValueInst *I) { *this << getIDAndType(I->getOperand()); } @@ -2907,6 +2911,21 @@ class SILPrinter : public SILInstructionVisitor { *this << getIDAndType(RI->getOperand()); } + void visitReturnBorrowInst(ReturnBorrowInst *rbi) { + *this << getIDAndType(rbi->getReturnValue()); + + *this << " from_scopes ("; + bool first = true; + for (SILValue ev : rbi->getEnclosingValues()) { + if (!first) { + *this << ", "; + } + first = false; + *this << getIDAndType(ev); + } + *this << ")"; + } + void visitSpecifyTestInst(SpecifyTestInst *TSI) { *this << QuotedString(TSI->getArgumentsSpecification()); } diff --git a/lib/SIL/IR/ValueOwnership.cpp b/lib/SIL/IR/ValueOwnership.cpp index d6f8f710c84a6..762dd1ae579d4 100644 --- a/lib/SIL/IR/ValueOwnership.cpp +++ b/lib/SIL/IR/ValueOwnership.cpp @@ -323,6 +323,7 @@ FORWARDING_OWNERSHIP_INST(MarkUnresolvedReferenceBinding) FORWARDING_OWNERSHIP_INST(MoveOnlyWrapperToCopyableValue) FORWARDING_OWNERSHIP_INST(CopyableToMoveOnlyWrapperValue) FORWARDING_OWNERSHIP_INST(MoveOnlyWrapperToCopyableBox) +FORWARDING_OWNERSHIP_INST(UncheckedOwnership) #undef FORWARDING_OWNERSHIP_INST ValueOwnershipKind diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 7a542149e69ba..b699b15a333f0 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -3852,6 +3852,15 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, break; } + case SILInstructionKind::UncheckedOwnershipInst: { + if (parseTypedValueRef(Val, B)) + return true; + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createUncheckedOwnership(InstLoc, Val); + break; + } + case SILInstructionKind::LoadInst: { std::optional Qualifier; SourceLoc AddrLoc; @@ -5449,6 +5458,28 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ResultVal = B.createReturn(InstLoc, Val); break; } + case SILInstructionKind::ReturnBorrowInst: { + SILValue returnValue; + if (parseTypedValueRef(returnValue, B)) + return true; + + if (parseVerbatim("from_scopes") || + P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) + return true; + + if (P.Tok.isNot(tok::r_paren)) { + do { + if (parseTypedValueRef(Val, B)) + return true; + OpList.push_back(Val); + } while (P.consumeIf(tok::comma)); + } + if (P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")")) + return true; + + ResultVal = B.createReturnBorrow(InstLoc, returnValue, OpList); + break; + } case SILInstructionKind::ThrowInst: { if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; diff --git a/lib/SIL/Utils/BasicBlockUtils.cpp b/lib/SIL/Utils/BasicBlockUtils.cpp index 2154252012698..e234b3c8550a7 100644 --- a/lib/SIL/Utils/BasicBlockUtils.cpp +++ b/lib/SIL/Utils/BasicBlockUtils.cpp @@ -235,6 +235,7 @@ void swift::getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB, return; case SILInstructionKind::ReturnInst: + case SILInstructionKind::ReturnBorrowInst: case SILInstructionKind::ThrowInst: case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::UnwindInst: diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 9aea7ad4d2d32..429a1e660381f 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -592,6 +592,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) case SILInstructionKind::ProjectBlockStorageInst: case SILInstructionKind::UnreachableInst: case SILInstructionKind::ReturnInst: + case SILInstructionKind::ReturnBorrowInst: case SILInstructionKind::ThrowInst: case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::YieldInst: @@ -641,6 +642,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) case SILInstructionKind::TypeValueInst: case SILInstructionKind::IgnoredUseInst: case SILInstructionKind::ImplicitActorToOpaqueIsolationCastInst: + case SILInstructionKind::UncheckedOwnershipInst: return RuntimeEffect::NoEffect; case SILInstructionKind::LoadInst: { diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 76a881f9bd7fa..da0b8ad94c25a 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -924,6 +924,17 @@ void swift::findGuaranteedReferenceRoots(SILValue referenceValue, // regardless of whether they were already visited. continue; } + + // Special handling until borrowing apply is represented as a + // ForwardingOperation. + if (auto *apply = + dyn_cast_or_null(value->getDefiningInstruction())) { + if (apply->hasGuaranteedResult()) { + worklist.pushIfNotVisited(apply->getSelfArgument()); + continue; + } + } + // Found a potential root. if (lookThroughNestedBorrows) { if (auto *bbi = dyn_cast(value)) { diff --git a/lib/SIL/Utils/PrunedLiveness.cpp b/lib/SIL/Utils/PrunedLiveness.cpp index a0eb8871dbccc..0fb623082d49b 100644 --- a/lib/SIL/Utils/PrunedLiveness.cpp +++ b/lib/SIL/Utils/PrunedLiveness.cpp @@ -249,7 +249,7 @@ void PrunedLiveRange::updateForUse( // argument must be copied. auto iterAndSuccess = users.insert({user, lifetimeEnding}); if (!iterAndSuccess.second) { - if (isa(user)) { + if (isa(user) || isa(user)) { branchMeetInPlace(iterAndSuccess.first->second, lifetimeEnding); } else { iterAndSuccess.first->second.meetInPlace(lifetimeEnding); diff --git a/lib/SIL/Verifier/DebugInfoVerifier.cpp b/lib/SIL/Verifier/DebugInfoVerifier.cpp index 146e4e93fbb72..0564403db6a1c 100644 --- a/lib/SIL/Verifier/DebugInfoVerifier.cpp +++ b/lib/SIL/Verifier/DebugInfoVerifier.cpp @@ -48,6 +48,7 @@ void SILInstruction::verifyDebugInfo() const { require( instKind == SILInstructionKind::BranchInst || instKind == SILInstructionKind::ReturnInst || + instKind == SILInstructionKind::ReturnBorrowInst || instKind == SILInstructionKind::UnreachableInst, "return locations are only allowed on branch and return instructions"); diff --git a/lib/SIL/Verifier/SILOwnershipVerifier.cpp b/lib/SIL/Verifier/SILOwnershipVerifier.cpp index 9ada227b9b268..fbb243c6bbf3a 100644 --- a/lib/SIL/Verifier/SILOwnershipVerifier.cpp +++ b/lib/SIL/Verifier/SILOwnershipVerifier.cpp @@ -346,6 +346,10 @@ bool SILValueOwnershipChecker::gatherUsers( Operand *op = uses.pop_back_val(); SILInstruction *user = op->getUser(); + if (isa(user)) { + continue; + } + // If this op is a type dependent operand, skip it. It is not interesting // from an ownership perspective. if (user->isTypeDependentOperand(*op)) diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 10bfceda86076..a76a817ef5d5d 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -2728,6 +2728,8 @@ class SILVerifier : public SILVerifierBase { requireSameType(LBI->getOperand()->getType().getObjectType(), LBI->getType(), "Load operand type and result type mismatch"); + require(F.getModule().getStage() == SILStage::Raw || !LBI->isUnchecked(), + "load_borrow's unchecked bit is on"); } void checkBeginBorrowInst(BeginBorrowInst *bbi) { @@ -6709,6 +6711,11 @@ class SILVerifier : public SILVerifierBase { "Result and operand must have the same type, today."); } + void checkUncheckedOwnershipInst(UncheckedOwnershipInst *uoi) { + require(F.getModule().getStage() == SILStage::Raw, + "unchecked_ownership is valid only in raw SIL"); + } + void checkAllocPackMetadataInst(AllocPackMetadataInst *apmi) { require(apmi->getIntroducer()->mayRequirePackMetadata(*apmi->getFunction()), "Introduces instruction of kind which cannot emit on-stack pack " @@ -6752,9 +6759,9 @@ class SILVerifier : public SILVerifierBase { bool FoundThrowBlock = false; bool FoundUnwindBlock = false; for (auto &BB : *F) { - if (isa(BB.getTerminator())) { - require(!FoundReturnBlock, - "more than one return block in function"); + if (isa(BB.getTerminator()) || + isa(BB.getTerminator())) { + require(!FoundReturnBlock, "more than one return block in function"); FoundReturnBlock = true; } else if (isa(BB.getTerminator()) || isa(BB.getTerminator())) { diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 8ffef12f8b3b4..91fea1b09949d 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -5482,9 +5482,10 @@ ManagedValue SILGenFunction::applyBorrowMutateAccessor( if (fn.getFunction()->getConventions().hasGuaranteedResult()) { auto selfArg = args.back().getValue(); if (isa(selfArg)) { - rawResult = emitUncheckedGuaranteedConversion(rawResult); + rawResult = B.createUncheckedOwnership(loc, rawResult); } } + if (rawResult->getType().isMoveOnly()) { if (rawResult->getType().isAddress()) { auto result = B.createMarkUnresolvedNonCopyableValueInst( diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index db567b68fc624..9ceea09ad01be 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -807,10 +807,16 @@ void SILGenFunction::emitCaptures(SILLocation loc, // If we have a mutable binding for a 'let', such as 'self' in an // 'init' method, load it. if (val->getType().isMoveOnly()) { - val = B.createMarkUnresolvedNonCopyableValueInst( - loc, val, - MarkUnresolvedNonCopyableValueInst::CheckKind:: - NoConsumeOrAssign); + auto *moveOnlyIntroducer = + dyn_cast_or_null(val); + if (!moveOnlyIntroducer || moveOnlyIntroducer->getCheckKind() != + MarkUnresolvedNonCopyableValueInst:: + CheckKind::NoConsumeOrAssign) { + val = B.createMarkUnresolvedNonCopyableValueInst( + loc, val, + MarkUnresolvedNonCopyableValueInst::CheckKind:: + NoConsumeOrAssign); + } } val = emitLoad(loc, val, tl, SGFContext(), IsNotTake).forward(*this); } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 0643672cd3f9e..4b0e20290b782 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -2276,8 +2276,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction emitBorrowOrMutateAccessorResult(SILLocation loc, Expr *ret, SmallVectorImpl &directResults); - SILValue emitUncheckedGuaranteedConversion(SILValue value); - void emitYield(SILLocation loc, MutableArrayRef yieldValues, ArrayRef origTypes, JumpDest unwindDest); diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 7092b9ac91844..095cc11e48bda 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -708,21 +708,6 @@ static Expr *lookThroughProjections(Expr *expr) { return lookThroughProjections(lookupExpr->getBase()); } -SILValue SILGenFunction::emitUncheckedGuaranteedConversion(SILValue value) { - assert(value->getType().isObject()); - assert(F.getConventions().hasGuaranteedResult()); - auto regularLoc = RegularLocation::getAutoGeneratedLocation(); - // Introduce a pair of unchecked_ownership_conversion instructions to - // avoid ownership errors from returning a load borrowed value. - // TODO: Introduce new SIL semantics to allow returning borrowed - // values from within a local borrow scope. - auto result = B.createUncheckedOwnershipConversion(regularLoc, value, - OwnershipKind::Unowned); - result = B.createUncheckedOwnershipConversion(regularLoc, result, - OwnershipKind::Guaranteed); - return result; -} - bool SILGenFunction::emitBorrowOrMutateAccessorResult( SILLocation loc, Expr *ret, SmallVectorImpl &directResults) { auto *afd = cast(FunctionDC->getAsDecl()); @@ -734,9 +719,8 @@ bool SILGenFunction::emitBorrowOrMutateAccessorResult( assert(guaranteedAddress.getValue()->getType().isAddress()); assert(F.getConventions().hasGuaranteedResult()); auto regularLoc = RegularLocation::getAutoGeneratedLocation(); - auto load = - B.createLoadBorrow(regularLoc, guaranteedAddress).getValue(); - return emitUncheckedGuaranteedConversion(load); + auto load = B.createLoadBorrow(regularLoc, guaranteedAddress).getValue(); + return B.createUncheckedOwnership(regularLoc, load); }; // If the return expression is a literal, emit as a regular return @@ -853,7 +837,7 @@ bool SILGenFunction::emitBorrowOrMutateAccessorResult( // unnecessary copy_value + mark_unresolved_non_copyable_value // instructions. if (selfType.isMoveOnly()) { - result = lookThroughMoveOnlyCheckerPattern(result); + result = lookThroughMoveOnlyCheckerPattern(result); } // If the SIL convention is @guaranteed and the generated result is an // address, emit a load_borrow. diff --git a/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp b/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp index 064a915c5b72b..4915dbb163820 100644 --- a/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp +++ b/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp @@ -30,6 +30,7 @@ bool isARCSignificantTerminator(TermInst *TI) { case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::UnwindInst: case TermKind::YieldInst: case TermKind::AwaitAsyncContinuationInst: diff --git a/lib/SILOptimizer/Analysis/ColdBlockInfo.cpp b/lib/SILOptimizer/Analysis/ColdBlockInfo.cpp index 9fde5f1ea0431..d92e4f07ac9d1 100644 --- a/lib/SILOptimizer/Analysis/ColdBlockInfo.cpp +++ b/lib/SILOptimizer/Analysis/ColdBlockInfo.cpp @@ -102,6 +102,7 @@ static bool isColdTerminator(const TermInst *term) { case TermKind::TryApplyInst: case TermKind::YieldInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: return false; case TermKind::ThrowInst: case TermKind::ThrowAddrInst: diff --git a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp index da7f3c7053e97..8f432796d32a7 100644 --- a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp @@ -378,6 +378,7 @@ struct TermArgSources { switch (cast(inst)->getTermKind()) { case TermKind::UnreachableInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::YieldInst: @@ -3482,6 +3483,7 @@ CONSTANT_TRANSLATION(MoveOnlyWrapperToCopyableBoxInst, LookThrough) CONSTANT_TRANSLATION(MoveOnlyWrapperToCopyableAddrInst, LookThrough) CONSTANT_TRANSLATION(CopyableToMoveOnlyWrapperAddrInst, LookThrough) CONSTANT_TRANSLATION(MarkUninitializedInst, LookThrough) +CONSTANT_TRANSLATION(UncheckedOwnershipInst, LookThrough) // We identify destructured results with their operand's region. CONSTANT_TRANSLATION(DestructureTupleInst, LookThrough) CONSTANT_TRANSLATION(DestructureStructInst, LookThrough) @@ -3892,6 +3894,15 @@ TranslationSemantics PartitionOpTranslator::visitReturnInst(ReturnInst *ri) { return TranslationSemantics::Require; } +TranslationSemantics +PartitionOpTranslator::visitReturnBorrowInst(ReturnBorrowInst *ri) { + addEndOfFunctionChecksForInOutSendingParameters(ri); + if (ri->getFunction()->getLoweredFunctionType()->hasSendingResult()) { + return TranslationSemantics::SendingNoResult; + } + return TranslationSemantics::Require; +} + TranslationSemantics PartitionOpTranslator::visitRefToBridgeObjectInst(RefToBridgeObjectInst *r) { translateSILLookThrough( diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index 7174e21a0203e..62927383e9bd9 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -192,6 +192,15 @@ struct OwnershipModelEliminatorVisitor eraseInstruction(eli); return true; } + + bool visitReturnBorrowInst(ReturnBorrowInst *rbi) { + return withBuilder(rbi, [&](SILBuilder &b, SILLocation loc) { + b.createReturn(loc, rbi->getReturnValue()); + eraseInstruction(rbi); + return true; + }); + } + bool visitUncheckedOwnershipConversionInst( UncheckedOwnershipConversionInst *uoci) { eraseInstructionAndRAUW(uoci, uoci->getOperand()); diff --git a/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp b/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp index 40ee8c02f635f..f216f94be07ef 100644 --- a/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp +++ b/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp @@ -31,6 +31,7 @@ #include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" #include "swift/SILOptimizer/Utils/CanonicalizeInstruction.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/InstructionDeleter.h" #include "llvm/ADT/PostOrderIterator.h" using namespace swift; @@ -107,6 +108,7 @@ namespace { struct SILGenCleanup : SILModuleTransform { void run() override; + bool fixupBorrowAccessors(SILFunction *function); bool completeOSSALifetimes(SILFunction *function); template bool completeLifetimesInRange(Range const &range, @@ -229,6 +231,87 @@ void collectReachableRoots(SILFunction *function, BasicBlockWorklist &backward, } while (changed); } +/* SILGen may produce a borrow accessor result from within a local borrow + * scope. Such as: + * + * ``` + * %ld = load_borrow %self + * %fwd = unchecked_ownership %ld + * %ex = struct_extract %fwd, #Struct.storedProperty + * end_borrow %ld + * return %ex + * ``` + * This is illegal OSSA, since the return uses a value outside it's borrow + * scope. + * + * Transform this into valid OSSA: + * + * ``` + * %ld = load_borrow %self + * %ex = struct_extract %ld, #Struct.storedProperty + * return_borrow %ex from_scopes %ld + * ``` + */ +bool SILGenCleanup::fixupBorrowAccessors(SILFunction *function) { + if (!function->getConventions().hasGuaranteedResult()) { + return false; + } + auto returnBB = function->findReturnBB(); + if (returnBB == function->end()) { + return false; + } + + auto *returnInst = cast(returnBB->getTerminator()); + if (returnInst->getOperand()->getOwnershipKind() != + OwnershipKind::Guaranteed) { + return false; + } + + SmallVector enclosingValues; + findGuaranteedReferenceRoots(returnInst->getOperand(), + /*lookThroughNestedBorrows=*/false, + enclosingValues); + SmallVector scopeEnds; + SmallVector operands; + SmallVector toDelete; + + // For all the local borrow scopes that enclose the return value, delete + // their end_borrow instructions and use them as an encolsing value in + // return_borrow instruction. + for (auto enclosingValue : enclosingValues) { + BorrowedValue borrow(enclosingValue); + if (!borrow.isLocalScope()) { + continue; + } + borrow.getLocalScopeEndingInstructions(scopeEnds); + for (auto *scopeEnd : scopeEnds) { + if (auto *endBorrow = dyn_cast(scopeEnd)) { + operands.push_back(endBorrow->getOperand()); + endBorrow->eraseFromParent(); + } + } + for (auto *uncheckedOwnership : + borrow->getUsersOfType()) { + uncheckedOwnership->replaceAllUsesWith(*borrow); + toDelete.push_back(uncheckedOwnership); + } + } + + for (auto *inst : toDelete) { + inst->eraseFromParent(); + } + + if (operands.empty()) { + return false; + } + + SILBuilderWithScope(returnInst) + .createReturnBorrow(returnInst->getLoc(), returnInst->getOperand(), + operands); + returnInst->eraseFromParent(); + return true; +} + bool SILGenCleanup::completeOSSALifetimes(SILFunction *function) { if (!getModule()->getOptions().OSSACompleteLifetimes) return false; @@ -339,7 +422,8 @@ void SILGenCleanup::run() { LLVM_DEBUG(llvm::dbgs() << "\nRunning SILGenCleanup on " << function.getName() << "\n"); - bool changed = completeOSSALifetimes(&function); + bool changed = fixupBorrowAccessors(&function); + changed |= completeOSSALifetimes(&function); DeadEndBlocks deadEndBlocks(&function); SILGenCanonicalize sgCanonicalize(deadEndBlocks); diff --git a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp index f79aab4be0a02..9b880b7986878 100644 --- a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp @@ -465,6 +465,7 @@ void DCE::markTerminatorArgsLive(SILBasicBlock *Pred, switch (Term->getTermKind()) { case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::UnwindInst: @@ -576,6 +577,7 @@ void DCE::propagateLiveness(SILInstruction *I) { return; case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::CondBranchInst: case TermKind::SwitchEnumInst: diff --git a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp index 782ae108c4ba0..a726870950a65 100644 --- a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp @@ -112,7 +112,9 @@ bool swift::specializeAppliesInFunction(SILFunction &F, // temporary stack location and returns a projection from the // store_borrow. This does not work for borrow accessors that return the // projection from within the store_borrow scope. - if (F.hasOwnership() && Callee->getConventions().hasAddressResult()) { + if (F.hasOwnership() && + (Callee->getConventions().hasAddressResult() || + Callee->getConventions().hasGuaranteedResult())) { continue; } diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index cf3cc99864b55..98b5c0d87849b 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -2848,6 +2848,7 @@ bool SimplifyCFG::simplifyBlocks() { case TermKind::ThrowAddrInst: case TermKind::DynamicMethodBranchInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::UnwindInst: case TermKind::YieldInst: break; diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 3cee22cc13a20..1d215d7fac0cc 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -241,6 +241,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::KeyPathInst: case SILInstructionKind::UnreachableInst: case SILInstructionKind::ReturnInst: + case SILInstructionKind::ReturnBorrowInst: case SILInstructionKind::ThrowInst: case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::YieldInst: @@ -336,6 +337,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::TypeValueInst: case SILInstructionKind::IgnoredUseInst: case SILInstructionKind::ImplicitActorToOpaqueIsolationCastInst: + case SILInstructionKind::UncheckedOwnershipInst: // Handle by operand and result check. break; diff --git a/lib/SILOptimizer/Utils/CFGOptUtils.cpp b/lib/SILOptimizer/Utils/CFGOptUtils.cpp index 1b4d7599f0c0c..54563ae998c0b 100644 --- a/lib/SILOptimizer/Utils/CFGOptUtils.cpp +++ b/lib/SILOptimizer/Utils/CFGOptUtils.cpp @@ -519,6 +519,7 @@ bool isSafeNonExitTerminator(TermInst *ti) { return true; case TermKind::UnreachableInst: case TermKind::ReturnInst: + case TermKind::ReturnBorrowInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::UnwindInst: diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index d0e52f495672e..75be95ec1e62f 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -77,6 +77,15 @@ bool SILInliner::canInlineApplySite(FullApplySite apply) { if (auto BA = dyn_cast(apply)) return canInlineBeginApply(BA); + if (apply.hasGuaranteedResult()) { + if (auto *callee = apply.getReferencedFunctionOrNull()) { + auto returnBB = callee->findReturnBB(); + if (returnBB != callee->end() && + isa(returnBB->getTerminator())) { + return false; + } + } + } return true; } @@ -941,6 +950,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::MoveOnlyWrapperToCopyableBoxInst: case SILInstructionKind::IgnoredUseInst: case SILInstructionKind::ImplicitActorToOpaqueIsolationCastInst: + case SILInstructionKind::UncheckedOwnershipInst: return InlineCost::Free; // Typed GEPs are free. @@ -1057,6 +1067,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { // Return and unreachable are free. case SILInstructionKind::UnreachableInst: case SILInstructionKind::ReturnInst: + case SILInstructionKind::ReturnBorrowInst: case SILInstructionKind::ThrowInst: case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::UnwindInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 214f2b6aa4c59..db4874b8d16a8 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -2917,6 +2917,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, CKind, Strict); break; } + case SILInstructionKind::UncheckedOwnershipInst: { + llvm_unreachable("Invalid unchecked_ownership in deserialization"); + } case SILInstructionKind::StoreInst: { auto Ty = MF->getType(TyID); SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); @@ -3116,6 +3119,18 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, } break; } + case SILInstructionKind::ReturnBorrowInst: { + SmallVector OpList; + for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) { + auto EltTy = MF->getType(ListOfValues[I]); + OpList.push_back(getLocalValue( + Builder.maybeGetFunction(), ListOfValues[I + 2], + getSILType(EltTy, (SILValueCategory)ListOfValues[I + 1], Fn))); + } + ResultInst = Builder.createReturnBorrow(Loc, OpList[0], + ArrayRef(OpList).drop_front()); + break; + } case SILInstructionKind::TupleElementAddrInst: case SILInstructionKind::TupleExtractInst: { // Use OneTypeOneOperand layout where the field number is stored in TypeID. diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 8a431abdcc62c..a7a57755a1dda 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 969; // Module import paths +const uint16_t SWIFTMODULE_VERSION_MINOR = 970; // return_borrow /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 894a679dca044..f728cfb934481 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1806,6 +1806,9 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { writeOneOperandExtraAttributeLayout(SI.getKind(), Attr, SI.getOperand(0)); break; } + case SILInstructionKind::UncheckedOwnershipInst: { + llvm_unreachable("Invalid unchecked_ownership during serialzation"); + } case SILInstructionKind::YieldInst: { auto YI = cast(&SI); SmallVector args; @@ -2588,6 +2591,24 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { (unsigned)svi->getType().getCategory(), ListOfValues); break; } + + case SILInstructionKind::ReturnBorrowInst: { + // Format: a type followed by a list of typed values. A typed value is + // expressed by 4 IDs: TypeID, TypeCategory, ValueID, ValueResultNumber. + const auto *rbi = cast(&SI); + SmallVector ListOfValues; + for (auto Elt : rbi->getOperandValues()) { + ListOfValues.push_back(S.addTypeRef(Elt->getType().getRawASTType())); + ListOfValues.push_back((unsigned)Elt->getType().getCategory()); + ListOfValues.push_back(addValueRef(Elt)); + } + + SILValuesLayout::emitRecord(Out, ScratchRecord, + SILAbbrCodes[SILValuesLayout::Code], + (unsigned)SI.getKind(), ListOfValues); + break; + } + case SILInstructionKind::TupleElementAddrInst: case SILInstructionKind::TupleExtractInst: { SILValue operand; diff --git a/test/SIL/Parser/borrow.sil b/test/SIL/Parser/borrow.sil index a3488e533deff..12998f39e91e1 100644 --- a/test/SIL/Parser/borrow.sil +++ b/test/SIL/Parser/borrow.sil @@ -53,6 +53,24 @@ sil [ossa] @foo : $@convention(thin) () -> () { return %res : $() } +struct Wrapper { + var _k: C +} + +// CHECK-LABEL: sil [ossa] @return_borrow_test : $@convention(method) (@in_guaranteed Wrapper) -> @guaranteed C { +// CHECK: bb0(%0 : $*Wrapper): +// CHECK: %1 = struct_element_addr %0 : $*Wrapper, #Wrapper._k +// CHECK: %2 = load_borrow %1 : $*C +// CHECK: return_borrow %2 : $C from_scopes (%2 : $C) +// CHECK: } +sil [ossa] @return_borrow_test : $@convention(method) (@in_guaranteed Wrapper) -> @guaranteed C { +bb0(%0 : $*Wrapper): + %1 = struct_element_addr %0, #Wrapper._k + %2 = load_borrow %1 + return_borrow %2 from_scopes (%2) +} + + // CHECK-LABEL: sil [ossa] @reborrowTest : $@convention(thin) (@owned C) -> () { // CHECK: bb3([[ARG:%.*]] : @reborrow $C): // CHECK-NEXT: {{%.*}} = borrowed [[ARG]] : $C from (%0 : $C) @@ -91,3 +109,4 @@ bb1(%2 : @guaranteed $FakeOptional): %4 = tuple () return %4 : $() } + diff --git a/test/SILGen/addressable_read.swift b/test/SILGen/addressable_read.swift new file mode 100644 index 0000000000000..6fe4a4352cdc9 --- /dev/null +++ b/test/SILGen/addressable_read.swift @@ -0,0 +1,23 @@ +// RUN: %target-swift-frontend -emit-sil -verify -enable-experimental-feature AddressableParameters %s + +// REQUIRES: swift_feature_AddressableParameters + +public struct Container: ~Copyable { + var _storage: UnsafeMutableBufferPointer + var _count: Int + + public subscript(index: Int) -> Element { + @_addressableSelf + _read { + precondition(index >= 0 && index < _count, "Index out of bounds") + yield _storage.baseAddress.unsafelyUnwrapped.advanced(by: index).pointee + } + _modify { + precondition(index >= 0 && index < _count, "Index out of bounds") + yield &_storage.baseAddress.unsafelyUnwrapped.advanced(by: index).pointee + } + } +} + +extension Container: Copyable where Element: Copyable {} + diff --git a/test/SILGen/borrow_accessor.swift b/test/SILGen/borrow_accessor.swift index fa1bcbef98f7f..94cc4ca1bd1c4 100644 --- a/test/SILGen/borrow_accessor.swift +++ b/test/SILGen/borrow_accessor.swift @@ -491,10 +491,9 @@ public struct GenNCWrapper : ~Copyable { // CHECK: debug_value [[REG0]], let, name "self", argno 1, expr op_deref // CHECK: [[REG2:%.*]] = struct_element_addr [[REG0]], #GenWrapper._s // CHECK: [[REG3:%.*]] = load_borrow [[REG2]] -// CHECK: [[REG4:%.*]] = unchecked_ownership_conversion [[REG3]], @guaranteed to @unowned -// CHECK: [[REG5:%.*]] = unchecked_ownership_conversion [[REG4]], @unowned to @guaranteed +// CHECK: [[REG4:%.*]] = unchecked_ownership [[REG3]] // CHECK: end_borrow [[REG3]] -// CHECK: return [[REG5]] +// CHECK: return [[REG4]] // CHECK: } // CHECK: sil hidden [ossa] @$s15borrow_accessor10GenWrapperV1sAA1SVvz : $@convention(method) (@inout GenWrapper) -> @inout S { @@ -509,10 +508,9 @@ public struct GenNCWrapper : ~Copyable { // CHECK: debug_value [[REG0]], let, name "self", argno 1, expr op_deref // CHECK: [[REG2:%.*]] = struct_element_addr [[REG0]], #GenWrapper._k // CHECK: [[REG3:%.*]] = load_borrow [[REG2]] -// CHECK: [[REG4:%.*]] = unchecked_ownership_conversion [[REG3]], @guaranteed to @unowned -// CHECK: [[REG5:%.*]] = unchecked_ownership_conversion [[REG4]], @unowned to @guaranteed +// CHECK: [[REG4:%.*]] = unchecked_ownership [[REG3]] // CHECK: end_borrow [[REG3]] -// CHECK: return [[REG5]] +// CHECK: return [[REG4]] // CHECK: } // CHECK: sil hidden [ossa] @$s15borrow_accessor10GenWrapperV1kAA5KlassCvz : $@convention(method) (@inout GenWrapper) -> @inout Klass { @@ -555,10 +553,9 @@ public struct GenNCWrapper : ~Copyable { // CHECK: [[REG3:%.*]] = load_borrow [[REG2]] // CHECK: [[REG4:%.*]] = function_ref @$s15borrow_accessor1SV1kAA5KlassCvb : $@convention(method) (@guaranteed S) -> @guaranteed Klass // CHECK: [[REG5:%.*]] = apply [[REG4]]([[REG3]]) : $@convention(method) (@guaranteed S) -> @guaranteed Klass -// CHECK: [[REG6:%.*]] = unchecked_ownership_conversion [[REG5]], @guaranteed to @unowned -// CHECK: [[REG7:%.*]] = unchecked_ownership_conversion [[REG6]], @unowned to @guaranteed +// CHECK: [[REG6:%.*]] = unchecked_ownership [[REG5]] // CHECK: end_borrow [[REG3]] -// CHECK: return [[REG7]] +// CHECK: return [[REG6]] // CHECK: } // CHECK: sil hidden [ossa] @$s15borrow_accessor10GenWrapperV9nested_k1AA5KlassCvz : $@convention(method) (@inout GenWrapper) -> @inout Klass { @@ -652,10 +649,9 @@ public struct GenNCWrapper : ~Copyable { // CHECK: [[REG2:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[REG0]] // CHECK: [[REG3:%.*]] = struct_element_addr [[REG2]], #GenNCWrapper._nc // CHECK: [[REG4:%.*]] = load_borrow [[REG3]] -// CHECK: [[REG5:%.*]] = unchecked_ownership_conversion [[REG4]], @guaranteed to @unowned -// CHECK: [[REG6:%.*]] = unchecked_ownership_conversion [[REG5]], @unowned to @guaranteed +// CHECK: [[REG5:%.*]] = unchecked_ownership [[REG4]] // CHECK: end_borrow [[REG4]] -// CHECK: return [[REG6]] +// CHECK: return [[REG5]] // CHECK: } // CHECK: sil hidden [ossa] @$s15borrow_accessor12GenNCWrapperVAARi_zrlE2ncAA2NCVvz : $@convention(method) (@inout GenNCWrapper) -> @inout NC { @@ -672,10 +668,9 @@ public struct GenNCWrapper : ~Copyable { // CHECK: [[REG2:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[REG0]] // CHECK: [[REG3:%.*]] = struct_element_addr [[REG2]], #GenNCWrapper._ncw // CHECK: [[REG4:%.*]] = load_borrow [[REG3]] -// CHECK: [[REG5:%.*]] = unchecked_ownership_conversion [[REG4]], @guaranteed to @unowned -// CHECK: [[REG6:%.*]] = unchecked_ownership_conversion [[REG5]], @unowned to @guaranteed +// CHECK: [[REG5:%.*]] = unchecked_ownership [[REG4]] // CHECK: end_borrow [[REG4]] -// CHECK: return [[REG6]] +// CHECK: return [[REG5]] // CHECK: } // CHECK: sil hidden [ossa] @$s15borrow_accessor12GenNCWrapperVAARi_zrlE3ncwAA0D0Vvz : $@convention(method) (@inout GenNCWrapper) -> @inout NCWrapper { @@ -736,15 +731,14 @@ public struct GenNCWrapper : ~Copyable { // CHECK: [[REG4:%.*]] = load_borrow [[REG3]] // CHECK: [[REG5:%.*]] = function_ref @$s15borrow_accessor9NCWrapperV2ncAA2NCVvb : $@convention(method) (@guaranteed NCWrapper) -> @guaranteed NC // CHECK: [[REG6:%.*]] = apply [[REG5]]([[REG4]]) : $@convention(method) (@guaranteed NCWrapper) -> @guaranteed NC -// CHECK: [[REG7:%.*]] = unchecked_ownership_conversion [[REG6]], @guaranteed to @unowned -// CHECK: [[REG8:%.*]] = unchecked_ownership_conversion [[REG7]], @unowned to @guaranteed -// CHECK: [[REG9:%.*]] = copy_value [[REG8]] +// CHECK: [[REG7:%.*]] = unchecked_ownership [[REG6]] +// CHECK: [[REG9:%.*]] = copy_value [[REG7]] // CHECK: [[REG10:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[REG9]] // CHECK: [[REG11:%.*]] = begin_borrow [[REG10]] // CHECK: end_borrow [[REG4]] // CHECK: end_borrow [[REG11]] // CHECK: destroy_value [[REG10]] -// CHECK: return [[REG8]] +// CHECK: return [[REG7]] // CHECK: } // CHECK: sil hidden [ossa] @$s15borrow_accessor12GenNCWrapperVAARi_zrlE10nested_nc1AA2NCVvz : $@convention(method) (@inout GenNCWrapper) -> @inout NC { diff --git a/test/SILGen/borrow_accessor_container.swift b/test/SILGen/borrow_accessor_container.swift index 5dd82f1f2a5bd..a5ebe0cbb7856 100644 --- a/test/SILGen/borrow_accessor_container.swift +++ b/test/SILGen/borrow_accessor_container.swift @@ -224,12 +224,11 @@ public struct CopyableContainer { // CHECK: [[REG27:%.*]] = begin_access [read] [unsafe] [[REG26]] // CHECK: [[REG28:%.*]] = struct_element_addr [[REG27]], #S._k // CHECK: [[REG29:%.*]] = load_borrow [[REG28]] -// CHECK: [[REG31:%.*]] = unchecked_ownership_conversion [[REG29]], @guaranteed to @unowned -// CHECK: [[REG32:%.*]] = unchecked_ownership_conversion [[REG31]], @unowned to @guaranteed +// CHECK: [[REG31:%.*]] = unchecked_ownership [[REG29]] // CHECK: end_access [[REG27]] // CHECK: end_borrow [[REG29]] // CHECK: dealloc_stack [[REG4]] -// CHECK: return [[REG32]] +// CHECK: return [[REG31]] // CHECK: } // CHECK: sil [ossa] @$s25borrow_accessor_container17CopyableContainerVyAA5KlassCSiciz : $@convention(method) (Int, @inout CopyableContainer) -> @inout Klass { @@ -329,13 +328,12 @@ public struct NonCopyableContainer : ~Copyable { // CHECK: [[REG31:%.*]] = begin_access [read] [unsafe] [[REG30]] // CHECK: [[REG32:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[REG31]] // CHECK: [[REG33:%.*]] = load_borrow [[REG32]] -// CHECK: [[REG35:%.*]] = unchecked_ownership_conversion [[REG33]], @guaranteed to @unowned -// CHECK: [[REG36:%.*]] = unchecked_ownership_conversion [[REG35]], @unowned to @guaranteed +// CHECK: [[REG35:%.*]] = unchecked_ownership [[REG33]] // CHECK: end_access [[REG31]] // CHECK: end_borrow [[REG33]] // CHECK: dealloc_stack [[REG6]] // CHECK: destroy_value [[REG4]] -// CHECK: return [[REG36]] +// CHECK: return [[REG35]] // CHECK: } // CHECK: sil [ossa] @$s25borrow_accessor_container20NonCopyableContainerVyAA2NCVSiciz : $@convention(method) (Int, @inout NonCopyableContainer) -> @inout NC { diff --git a/test/SILGen/borrow_accessor_evolution.swift b/test/SILGen/borrow_accessor_evolution.swift index 9bd9a26030e89..4bc30f6976a81 100644 --- a/test/SILGen/borrow_accessor_evolution.swift +++ b/test/SILGen/borrow_accessor_evolution.swift @@ -1,4 +1,5 @@ // RUN:%target-swift-frontend -emit-silgen %s -verify -enable-experimental-feature BorrowAndMutateAccessors -enable-library-evolution | %FileCheck %s +// RUN:%target-swift-frontend -c -Xllvm -sil-print-after=SILGenCleanup %s -verify -enable-experimental-feature BorrowAndMutateAccessors -enable-library-evolution 2>&1 | %FileCheck --check-prefixes=CHECK-SILGEN-CLEANUP %s // REQUIRES: swift_feature_BorrowAndMutateAccessors @@ -131,11 +132,18 @@ func test() { // CHECK: debug_value [[REG0]], let, name "self", argno 1, expr op_deref // CHECK: [[REG2:%.*]] = struct_element_addr [[REG0]], #Wrapper._k // CHECK: [[REG3:%.*]] = load_borrow [[REG2]] -// CHECK: [[REG4:%.*]] = unchecked_ownership_conversion [[REG3]], @guaranteed to @unowned -// CHECK: [[REG5:%.*]] = unchecked_ownership_conversion [[REG4]], @unowned to @guaranteed -// CHECK: return [[REG5]] +// CHECK: [[REG4:%.*]] = unchecked_ownership [[REG3]] +// CHECK: end_borrow [[REG3]] +// CHECK: return [[REG4]] // CHECK: } +// CHECK-SILGEN-CLEANUP: sil hidden [ossa] @$s25borrow_accessor_evolution7WrapperV1kAA5KlassCvb : $@convention(method) (@in_guaranteed Wrapper) -> @guaranteed Klass { +// CHECK-SILGEN-CLEANUP: bb0([[REG0:%.*]] : $*Wrapper): +// CHECK-SILGEN-CLEANUP: [[REG2:%.*]] = struct_element_addr [[REG0]], #Wrapper._k +// CHECK-SILGEN-CLEANUP: [[REG3:%.*]] = load_borrow [[REG2]] +// CHECK-SILGEN-CLEANUP: return_borrow [[REG3]] from_scopes ([[REG3]]) +// CHECK-SILGEN-CLEANUP: } + // CHECK-LABEL: sil hidden [ossa] @$s25borrow_accessor_evolution7WrapperV7nested1AA5KlassCvb : $@convention(method) (@in_guaranteed Wrapper) -> @guaranteed Klass { // CHECK: bb0([[REG0:%.*]] : $*Wrapper): // CHECK: debug_value [[REG0]], let, name "self", argno 1, expr op_deref @@ -159,12 +167,18 @@ func test() { // CHECK: debug_value [[REG1]], let, name "self", argno 2, expr op_deref // CHECK: [[REG4:%.*]] = struct_element_addr [[REG1]], #Wrapper._k // CHECK: [[REG5:%.*]] = load_borrow [[REG4]] -// CHECK: [[REG6:%.*]] = unchecked_ownership_conversion [[REG5]], @guaranteed to @unowned -// CHECK: [[REG7:%.*]] = unchecked_ownership_conversion [[REG6]], @unowned to @guaranteed +// CHECK: [[REG6:%.*]] = unchecked_ownership [[REG5]] // CHECK: end_borrow [[REG5]] -// CHECK: return [[REG7]] +// CHECK: return [[REG6]] // CHECK: } +// CHECK-SILGEN-CLEANUP: sil hidden [ossa] @$s25borrow_accessor_evolution7WrapperVyAA5KlassCSicib : $@convention(method) (Int, @in_guaranteed Wrapper) -> @guaranteed Klass { +// CHECK-SILGEN-CLEANUP: bb0([[REG0:%.*]] : $Int, [[REG1:%.*]] : $*Wrapper): +// CHECK-SILGEN-CLEANUP: [[REG4:%.*]] = struct_element_addr [[REG1]], #Wrapper._k +// CHECK-SILGEN-CLEANUP: [[REG5:%.*]] = load_borrow [[REG4]] +// CHECK-SILGEN-CLEANUP: return_borrow [[REG5]] from_scopes ([[REG5]]) +// CHECK-SILGEN-CLEANUP: } + // CHECK-LABEL: sil hidden [ossa] @$s25borrow_accessor_evolution7WrapperV16nested_subscriptAA5KlassCvb : $@convention(method) (@in_guaranteed Wrapper) -> @guaranteed Klass { // CHECK: bb0([[REG0:%.*]] : $*Wrapper): // CHECK: debug_value [[REG0]], let, name "self", argno 1, expr op_deref diff --git a/test/Serialization/ossa_sil.sil b/test/Serialization/ossa_sil.sil index fd34923cf2764..02617d6e8d5f6 100644 --- a/test/Serialization/ossa_sil.sil +++ b/test/Serialization/ossa_sil.sil @@ -19,6 +19,24 @@ bb0(%0 : @guaranteed $AnyObject): return %2 : $X } +struct Wrapper { + var _k: X +} + +// CHECK-LABEL: sil [serialized] [canonical] [ossa] @return_borrow_test : $@convention(method) (@in_guaranteed Wrapper) -> @guaranteed X { +// CHECK: bb0(%0 : $*Wrapper): +// CHECK: %1 = struct_element_addr %0 : $*Wrapper, #Wrapper._k +// CHECK: %2 = load_borrow %1 : $*X +// CHECK: return_borrow %2 : $X from_scopes (%2 : $X) +// CHECK: } + +sil [serialized] [ossa] @return_borrow_test : $@convention(method) (@in_guaranteed Wrapper) -> @guaranteed X { +bb0(%0 : $*Wrapper): + %1 = struct_element_addr %0, #Wrapper._k + %2 = load_borrow %1 + return_borrow %2 from_scopes (%2) +} + // CHECK-LABEL: sil [serialized] [canonical] [ossa] @reborrowTest : // CHECK: bb1: // CHECK-NEXT: {{%.*}} = begin_borrow %0 : $X