mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[SIL] Hollow out Builtin.copy, deprecate _copy.
The copy operator has been implemented and doesn't use it. Remove `Builtin.copy` and `_copy` as much as currently possible. Source compatibility requires that `_copy` remain in the stdlib. It is deprecated here and just uses the copy operator. Handling old swiftinterfaces requires that `Builtin.copy` be defined. Redefine it here as a passthrough--SILGen machinery will produce the necessary copy_addr. rdar://127502242
This commit is contained in:
@@ -165,12 +165,6 @@ extension AddressUseVisitor {
|
||||
|
||||
case let builtin as BuiltinInst:
|
||||
switch builtin.id {
|
||||
case .Copy where builtin.operands[1] == operand: // source
|
||||
return loadedAddressUse(of: operand, into: builtin.operands[0])
|
||||
|
||||
case .Copy where builtin.operands[0] == operand: // dest
|
||||
return leafAddressUse(of: operand)
|
||||
|
||||
// Builtins that cannot load a nontrivial value.
|
||||
case .TSanInoutAccess, .ResumeThrowingContinuationReturning,
|
||||
.ResumeNonThrowingContinuationReturning, .GenericAdd,
|
||||
|
||||
@@ -909,12 +909,6 @@ extension LifetimeDependenceDefUseWalker {
|
||||
case let tai as TupleAddrConstructorInst:
|
||||
return visitStoredUses(of: operand, into: tai.destinationOperand.value)
|
||||
|
||||
case let bi as BuiltinInst where bi.id == .Copy:
|
||||
// This must be a non-address-lowered form of Builtin.Copy that
|
||||
// produces an owned value.
|
||||
assert(bi.ownership == .owned)
|
||||
return walkDownUses(of: bi, using: operand)
|
||||
|
||||
default:
|
||||
return nonForwardingUse(of: operand)
|
||||
}
|
||||
|
||||
@@ -537,6 +537,14 @@ BUILTIN_SIL_OPERATION(HopToActor, "hopToActor", None)
|
||||
/// Returns the number of items in a pack.
|
||||
BUILTIN_SIL_OPERATION(PackLength, "packLength", Special)
|
||||
|
||||
/// TODO: Remove. Only exists to avoid a reverse condfail.
|
||||
/// rdar://127516085 (Complete removal of Builtin.copy)
|
||||
///
|
||||
/// copy: <T>(T) -> T
|
||||
///
|
||||
/// Creates a copy of the source at the destination.
|
||||
BUILTIN_SIL_OPERATION(Copy, "copy", Special)
|
||||
|
||||
// BUILTIN_RUNTIME_CALL - A call into a runtime function.
|
||||
// These functions accept a single argument of any type.
|
||||
#ifndef BUILTIN_RUNTIME_CALL
|
||||
@@ -804,24 +812,6 @@ BUILTIN_MISC_OPERATION(CreateTaskGroupWithFlags,
|
||||
BUILTIN_MISC_OPERATION(DestroyTaskGroup,
|
||||
"destroyTaskGroup", "", Special)
|
||||
|
||||
/// A builtin that can only be called from a transparent generic function. Takes
|
||||
/// two operands, the first operand the result address, the second operand the
|
||||
/// input address. Transforms into
|
||||
///
|
||||
/// %input = load [take] %inputAddr
|
||||
/// %result = explicit_copy_value %input
|
||||
/// store %result to [init] %resultAddr
|
||||
/// store %input to [init] %inputAddr
|
||||
///
|
||||
/// transparently inlined into a caller that has the generic of the callee
|
||||
/// specialized into a loadable type. If the transparent inlining does not
|
||||
/// specialize the type (due to being inlined into a non-generic context, the
|
||||
/// SILVerifier will abort).
|
||||
///
|
||||
/// Illegal to call except for in Swift._copy in the stdlib. This is enforced by
|
||||
/// the SILVerifier.
|
||||
BUILTIN_MISC_OPERATION(Copy, "copy", "", Special)
|
||||
|
||||
/// Unchecked pointer alignment assertion. Allows the compiler to assume
|
||||
/// alignment of the pointer to emit more efficient code.
|
||||
///
|
||||
|
||||
@@ -124,7 +124,6 @@ SEMANTICS_ATTR(FORCE_EMIT_OPT_REMARK_PREFIX, "optremark")
|
||||
SEMANTICS_ATTR(OBJC_FORBID_ASSOCIATED_OBJECTS, "objc.forbidAssociatedObjects")
|
||||
|
||||
SEMANTICS_ATTR(LIFETIMEMANAGEMENT_MOVE, "lifetimemanagement.move")
|
||||
SEMANTICS_ATTR(LIFETIMEMANAGEMENT_COPY, "lifetimemanagement.copy")
|
||||
|
||||
SEMANTICS_ATTR(NO_PERFORMANCE_ANALYSIS, "no_performance_analysis")
|
||||
|
||||
|
||||
@@ -251,7 +251,6 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) && {
|
||||
case BuiltinValueKind::TSanInoutAccess:
|
||||
case BuiltinValueKind::ResumeThrowingContinuationReturning:
|
||||
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
|
||||
case BuiltinValueKind::Copy:
|
||||
case BuiltinValueKind::GenericAdd:
|
||||
case BuiltinValueKind::GenericFAdd:
|
||||
case BuiltinValueKind::GenericAnd:
|
||||
|
||||
@@ -1437,16 +1437,6 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
|
||||
return;
|
||||
}
|
||||
|
||||
if (Builtin.ID == BuiltinValueKind::Copy) {
|
||||
auto input = args.claimNext();
|
||||
auto result = args.claimNext();
|
||||
SILType addrTy = argTypes[0];
|
||||
const TypeInfo &addrTI = IGF.getTypeInfo(addrTy);
|
||||
Address inputAttr = addrTI.getAddressForPointer(input);
|
||||
Address resultAttr = addrTI.getAddressForPointer(result);
|
||||
addrTI.initializeWithCopy(IGF, resultAttr, inputAttr, addrTy, false);
|
||||
return;
|
||||
}
|
||||
if (Builtin.ID == BuiltinValueKind::AssumeAlignment) {
|
||||
// A no-op pointer cast that passes on its first value. Common occurrences of
|
||||
// this builtin should already be removed with the alignment guarantee moved
|
||||
|
||||
@@ -899,14 +899,6 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GetEnumTag)
|
||||
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, InjectEnumTag)
|
||||
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DistributedActorAsAnyActor)
|
||||
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AddressOfRawLayout)
|
||||
OperandOwnership OperandOwnershipBuiltinClassifier::visitCopy(BuiltinInst *bi,
|
||||
StringRef) {
|
||||
if (bi->getFunction()->getConventions().useLoweredAddresses()) {
|
||||
return OperandOwnership::UnownedInstantaneousUse;
|
||||
} else {
|
||||
return OperandOwnership::DestroyingConsume;
|
||||
}
|
||||
}
|
||||
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLet)
|
||||
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, EndAsyncLet)
|
||||
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, EndAsyncLetLifetime)
|
||||
|
||||
@@ -629,7 +629,6 @@ CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroup)
|
||||
CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroupWithFlags)
|
||||
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
|
||||
CONSTANT_OWNERSHIP_BUILTIN(None, TaskRunInline)
|
||||
CONSTANT_OWNERSHIP_BUILTIN(None, Copy)
|
||||
CONSTANT_OWNERSHIP_BUILTIN(None, GetEnumTag)
|
||||
CONSTANT_OWNERSHIP_BUILTIN(None, InjectEnumTag)
|
||||
CONSTANT_OWNERSHIP_BUILTIN(Owned, DistributedActorAsAnyActor)
|
||||
|
||||
@@ -2592,11 +2592,6 @@ static void visitBuiltinAddress(BuiltinInst *builtin,
|
||||
visitor(&builtin->getAllOperands()[0]);
|
||||
return;
|
||||
|
||||
// These effect both operands.
|
||||
case BuiltinValueKind::Copy:
|
||||
visitor(&builtin->getAllOperands()[1]);
|
||||
return;
|
||||
|
||||
// These consume values out of their second operand.
|
||||
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
|
||||
case BuiltinValueKind::ResumeThrowingContinuationReturning:
|
||||
|
||||
@@ -2343,22 +2343,6 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (builtinKind == BuiltinValueKind::Copy) {
|
||||
// We expect that this builtin will be specialized during transparent
|
||||
// inlining into explicit_copy_value if we inline into a non-generic
|
||||
// context. If the builtin still remains and is not in the specific copy
|
||||
// semantic function (which is the only function marked with
|
||||
// semantics::LIFETIMEMANAGEMENT_COPY), then we know that we did
|
||||
// transparent inlining into a function that did not result in the Builtin
|
||||
// being specialized out which is user error.
|
||||
//
|
||||
// NOTE: Once we have opaque values, this restriction will go away. This
|
||||
// is just so we can call Builtin.copy outside of the stdlib.
|
||||
auto semanticName = semantics::LIFETIMEMANAGEMENT_COPY;
|
||||
require(BI->getFunction()->hasSemanticsAttr(semanticName),
|
||||
"_copy used within a generic context");
|
||||
}
|
||||
|
||||
if (builtinKind == BuiltinValueKind::CreateAsyncTask) {
|
||||
requireType(BI->getType(), _object(_tuple(_nativeObject, _rawPointer)),
|
||||
"result of createAsyncTask");
|
||||
|
||||
@@ -2027,6 +2027,23 @@ static ManagedValue emitBuiltinAddressOfRawLayout(SILGenFunction &SGF,
|
||||
return ManagedValue::forObjectRValueWithoutOwnership(bi);
|
||||
}
|
||||
|
||||
/// TODO: Remove. Only exists to avoid a reverse condfail.
|
||||
/// rdar://127516085 (Complete removal of Builtin.copy)
|
||||
static ManagedValue emitBuiltinCopy(SILGenFunction &SGF, SILLocation loc,
|
||||
SubstitutionMap subs,
|
||||
ArrayRef<ManagedValue> args, SGFContext C) {
|
||||
// Builtin.copy has no uses in current/future swiftinterfaces. It has a
|
||||
// single use in old swiftinterfaces:
|
||||
// func _copy<T>(_ t: T) -> T {
|
||||
// Builtin.copy(t)
|
||||
// }
|
||||
// It is sufficient to have the builtin pass the value through. When the
|
||||
// return is emitted, a copy will be made.
|
||||
assert(args.size() == 1 && "not two arguments!?");
|
||||
return ManagedValue::forOwnedAddressRValue(args[0].getValue(),
|
||||
CleanupHandle::invalid());
|
||||
}
|
||||
|
||||
std::optional<SpecializedEmitter>
|
||||
SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) {
|
||||
// Only consider standalone declarations in the Builtin module.
|
||||
|
||||
@@ -3389,11 +3389,6 @@ protected:
|
||||
bi->setOperand(use->getOperandNumber(), opAddr);
|
||||
break;
|
||||
}
|
||||
case BuiltinValueKind::Copy: {
|
||||
SILValue opAddr = addrMat.materializeAddress(use->get());
|
||||
bi->setOperand(0, opAddr);
|
||||
break;
|
||||
}
|
||||
case BuiltinValueKind::AddressOfBorrowOpaque:
|
||||
visitAddressOfBorrowBuiltinInst(bi, /*stackProtected=*/true);
|
||||
break;
|
||||
@@ -4084,14 +4079,6 @@ protected:
|
||||
|
||||
void visitBuiltinInst(BuiltinInst *bi) {
|
||||
switch (bi->getBuiltinKind().value_or(BuiltinValueKind::None)) {
|
||||
case BuiltinValueKind::Copy: {
|
||||
SILValue addr = addrMat.materializeAddress(bi);
|
||||
builder.createBuiltin(
|
||||
bi->getLoc(), bi->getName(),
|
||||
SILType::getEmptyTupleType(bi->getType().getASTContext()),
|
||||
bi->getSubstitutions(), {addr, bi->getOperand(0)});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
bi->dump();
|
||||
llvm::report_fatal_error("^^^ Unimplemented builtin opaque value def.");
|
||||
|
||||
@@ -183,7 +183,6 @@ static bool isBarrier(SILInstruction *inst) {
|
||||
case BuiltinValueKind::AssignCopyArrayFrontToBack:
|
||||
case BuiltinValueKind::AssignCopyArrayBackToFront:
|
||||
case BuiltinValueKind::AssignTakeArray:
|
||||
case BuiltinValueKind::Copy:
|
||||
case BuiltinValueKind::CancelAsyncTask:
|
||||
case BuiltinValueKind::StartAsyncLet:
|
||||
case BuiltinValueKind::CreateAsyncTask:
|
||||
|
||||
@@ -320,7 +320,6 @@ protected:
|
||||
void visitHopToExecutorInst(HopToExecutorInst *Inst);
|
||||
|
||||
void visitTerminator(SILBasicBlock *BB);
|
||||
void visitBuiltinInst(BuiltinInst *BI);
|
||||
|
||||
/// This hook is called after either of the top-level visitors:
|
||||
/// cloneReachableBlocks or cloneSILFunction.
|
||||
@@ -820,51 +819,6 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
|
||||
Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
|
||||
}
|
||||
|
||||
void SILInlineCloner::visitBuiltinInst(BuiltinInst *Inst) {
|
||||
if (IKind == InlineKind::MandatoryInline) {
|
||||
if (auto kind = Inst->getBuiltinKind()) {
|
||||
if (*kind == BuiltinValueKind::Copy) {
|
||||
auto otherResultAddr = getOpValue(Inst->getOperand(0));
|
||||
auto otherSrcAddr = getOpValue(Inst->getOperand(1));
|
||||
auto otherType = otherSrcAddr->getType();
|
||||
|
||||
if (!otherType.isLoadable(*Inst->getFunction())) {
|
||||
// If otherType is not loadable, emit a diagnostic since it was used
|
||||
// on a generic or existential value.
|
||||
diagnose(Inst->getModule().getASTContext(),
|
||||
getOpLocation(Inst->getLoc()).getSourceLoc(),
|
||||
diag::copy_operator_used_on_generic_or_existential_value);
|
||||
return SILCloner<SILInlineCloner>::visitBuiltinInst(Inst);
|
||||
}
|
||||
|
||||
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
|
||||
// We stash otherValue in originalOtherValue in case we need to
|
||||
// perform a writeback.
|
||||
auto opLoc = getOpLocation(Inst->getLoc());
|
||||
|
||||
assert(otherType.isAddress());
|
||||
|
||||
// Perform a load_borrow and then copy that.
|
||||
SILValue otherValue =
|
||||
getBuilder().emitLoadBorrowOperation(opLoc, otherSrcAddr);
|
||||
|
||||
auto *mvi = getBuilder().createExplicitCopyValue(opLoc, otherValue);
|
||||
|
||||
getBuilder().emitStoreValueOperation(opLoc, mvi, otherResultAddr,
|
||||
StoreOwnershipQualifier::Init);
|
||||
// End the borrowed value.
|
||||
getBuilder().emitEndBorrowOperation(opLoc, otherValue);
|
||||
|
||||
// We know that Inst returns a tuple value that isn't used by anything
|
||||
// else, so this /should/ be safe.
|
||||
return recordClonedInstruction(Inst, mvi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SILCloner<SILInlineCloner>::visitBuiltinInst(Inst);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Cost Model
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -266,31 +266,11 @@ extension String {
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Takes in a value at +0 and performs a Builtin.copy upon it.
|
||||
///
|
||||
/// IMPLEMENTATION NOTES: During transparent inlining, Builtin.copy becomes the
|
||||
/// explicit_copy_value instruction if we are inlining into a context where the
|
||||
/// specialized type is loadable. If the transparent function is called in a
|
||||
/// context where the inlined function specializes such that the specialized
|
||||
/// type is still not loadable, the compiler aborts (a). Once we have opaque
|
||||
/// values, this restriction will be lifted since after that address only types
|
||||
/// at SILGen time will be loadable objects.
|
||||
///
|
||||
/// (a). This is implemented by requiring that Builtin.copy only be called
|
||||
/// within a function marked with the semantic tag "lifetimemanagement.copy"
|
||||
/// which conveniently is only the function we are defining here: _copy.
|
||||
///
|
||||
/// NOTE: We mark this _alwaysEmitIntoClient to ensure that we are not creating
|
||||
/// new ABI that the stdlib must maintain if a user calls this ignoring the '_'
|
||||
/// implying it is stdlib SPI.
|
||||
@available(*, deprecated, message: "Use the copy operator")
|
||||
@_alwaysEmitIntoClient
|
||||
@inlinable
|
||||
@_transparent
|
||||
@_semantics("lifetimemanagement.copy")
|
||||
public func _copy<T>(_ value: T) -> T {
|
||||
#if $BuiltinCopy
|
||||
Builtin.copy(value)
|
||||
#else
|
||||
value
|
||||
#endif
|
||||
copy value
|
||||
}
|
||||
|
||||
@@ -8,93 +8,58 @@ import Swift
|
||||
|
||||
class Klass {}
|
||||
|
||||
// NOTE:
|
||||
//
|
||||
// One will notice in these tests that we are not promoting away the input
|
||||
// argument to _copy. This is because PredictableMemOpts does not know how to
|
||||
// handle store_borrow. With time, that will happen and this unfortunateness
|
||||
// will go away. At -O this problem will not exist though.
|
||||
//
|
||||
// That being said, this is actually not a big issue for the usage of this in
|
||||
// dataflow based OSSA checking since the store_borrow will always just be
|
||||
// treated as a +0 use of any owned value we are tracking, so it will not be
|
||||
// treated as a lifetime ending use (albeit it would be considered an
|
||||
// escaping use).
|
||||
// CHECK-LABEL: sil [ossa] @$s8moveonly7useCopyyAA5KlassCADF : {{.*}} {
|
||||
// CHECK: bb0([[ARG:%.*]] :
|
||||
// CHECK-NEXT: debug_value
|
||||
// CHECK-NEXT: [[COPY:%[^,]+]] = copy_value [[ARG]]
|
||||
// CHECK-NEXT: [[EXPLICIT_COPY:%[^,]+]] = explicit_copy_value [[COPY]]
|
||||
// CHECK-NEXT: destroy_value [[COPY]]
|
||||
// CHECK-NEXT: return [[EXPLICIT_COPY]]
|
||||
// CHECK-LABEL: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s8moveonly7useCopyyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass {
|
||||
// CHECK: bb0([[ARG:%.*]] :
|
||||
// CHECK-NEXT: debug_value
|
||||
// CHECK-NEXT: [[RESULT_ADDR:%.*]] = alloc_stack $Klass
|
||||
// CHECK-NEXT: [[INPUT_ADDR:%.*]] = alloc_stack $Klass
|
||||
// CHECK-NEXT: [[SB:%.*]] = store_borrow [[ARG]] to [[INPUT_ADDR]]
|
||||
// CHECK-NEXT: // function_ref _copy<A>(_:)
|
||||
// CHECK-NEXT: [[COPY:%.*]] = function_ref @$ss5_copyyxxlF :
|
||||
// CHECK-NEXT: [[APPLY_RESULT:%.*]] = apply [[COPY]]<Klass>([[RESULT_ADDR]], [[SB]])
|
||||
// CHECK-NEXT: end_borrow
|
||||
// CHECK-NEXT: dealloc_stack [[INPUT_ADDR]]
|
||||
// CHECK-NEXT: [[RELOADED_VALUE:%.*]] = load [take] [[RESULT_ADDR]]
|
||||
// CHECK-NEXT: dealloc_stack [[RESULT_ADDR]]
|
||||
// CHECK-NEXT: return [[RELOADED_VALUE]]
|
||||
// CHECK: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
|
||||
// CHECK-SIL-LABEL: sil @$s8moveonly7useCopyyAA5KlassCADF : {{.*}} {
|
||||
// CHECK-SIL: bb0([[ARG:%.*]] : $Klass):
|
||||
// CHECK-SIL-NEXT: debug_value
|
||||
// CHECK-SIL-NEXT: strong_retain [[ARG]]
|
||||
// CHECK-SIL-NEXT: strong_retain [[ARG]]
|
||||
// CHECK-SIL-NEXT: strong_release [[ARG]]
|
||||
// CHECK-SIL-NEXT: return [[ARG]] : $Klass
|
||||
// CHECK-SIL-LABEL: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
|
||||
|
||||
// CHECK-SIL-LABEL: sil @$s8moveonly7useCopyyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass {
|
||||
// CHECK-SIL: bb0([[ARG:%.*]] : $Klass):
|
||||
// CHECK-SIL-NEXT: debug_value
|
||||
// CHECK-SIL-NEXT: [[INPUT:%.*]] = alloc_stack [lexical] $Klass
|
||||
// CHECK-SIL-NEXT: store [[ARG]] to [[INPUT]]
|
||||
// CHECK-SIL-NEXT: [[VALUE:%[0-9][0-9]*]] = load [[INPUT]]{{.*}}
|
||||
// CHECK-SIL-NEXT: strong_retain [[VALUE]]
|
||||
// CHECK-SIL-NEXT: strong_retain [[VALUE]]
|
||||
// CHECK-SIL-NEXT: strong_release [[VALUE]]
|
||||
// CHECK-SIL-NEXT: dealloc_stack [[INPUT]] : $*Klass
|
||||
// CHECK-SIL-NEXT: return [[VALUE]] : $Klass
|
||||
// CHECK-SIL: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
|
||||
|
||||
// CHECK-SIL-OPT-LABEL: sil {{.*}}@$s8moveonly7useCopyyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass {
|
||||
// CHECK-SIL-OPT: bb0([[ARG:%.*]] : $Klass):
|
||||
// CHECK-SIL-OPT-NEXT: debug_value
|
||||
// CHECK-SIL-OPT-NEXT: strong_retain [[ARG]]
|
||||
// CHECK-SIL-OPT-NEXT: return [[ARG]]
|
||||
// CHECK-SIL-OPT: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
|
||||
// CHECK-SIL-OPT-LABEL: sil {{.*}}@$s8moveonly7useCopyyAA5KlassCADF : {{.*}} {
|
||||
// CHECK-SIL-OPT: bb0([[ARG:%.*]] : $Klass):
|
||||
// CHECK-SIL-OPT-NEXT: debug_value
|
||||
// CHECK-SIL-OPT-NEXT: strong_retain [[ARG]]
|
||||
// CHECK-SIL-OPT-NEXT: return [[ARG]]
|
||||
// CHECK-SIL-OPT-LABEL: } // end sil function '$s8moveonly7useCopyyAA5KlassCADF'
|
||||
public func useCopy(_ k: Klass) -> Klass {
|
||||
_copy(k)
|
||||
copy k
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s8moveonly7useCopyyxxRlzClF : $@convention(thin) <T where T : AnyObject> (@guaranteed T) -> @owned T {
|
||||
// CHECK: bb0([[ARG:%.*]] :
|
||||
// CHECK-NEXT: debug_value
|
||||
// CHECK-NEXT: [[RESULT_ADDR:%.*]] = alloc_stack $T
|
||||
// CHECK-NEXT: [[INPUT_ADDR:%.*]] = alloc_stack $T
|
||||
// CHECK-NEXT: [[SB:%.*]] = store_borrow [[ARG]] to [[INPUT_ADDR]]
|
||||
// CHECK-NEXT: // function_ref _copy<A>(_:)
|
||||
// CHECK-NEXT: [[COPY:%.*]] = function_ref @$ss5_copyyxxlF :
|
||||
// CHECK-NEXT: [[APPLY_RESULT:%.*]] = apply [[COPY]]<T>([[RESULT_ADDR]], [[SB]])
|
||||
// CHECK-NEXT: end_borrow
|
||||
// CHECK-NEXT: dealloc_stack [[INPUT_ADDR]]
|
||||
// CHECK-NEXT: [[RELOADED_VALUE:%.*]] = load [take] [[RESULT_ADDR]]
|
||||
// CHECK-NEXT: dealloc_stack [[RESULT_ADDR]]
|
||||
// CHECK-NEXT: return [[RELOADED_VALUE]]
|
||||
// CHECK: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
|
||||
// CHECK: bb0([[ARG:%.*]] :
|
||||
// CHECK-NEXT: debug_value
|
||||
// CHECK-NEXT: [[COPY:%[^,]+]] = copy_value [[ARG]]
|
||||
// CHECK-NEXT: [[EXPLICIT_COPY:%[^,]+]] = explicit_copy_value [[COPY]]
|
||||
// CHECK-NEXT: destroy_value [[COPY]]
|
||||
// CHECK-NEXT: return [[EXPLICIT_COPY]]
|
||||
// CHECK-LABEL: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
|
||||
|
||||
// CHECK-SIL-LABEL: sil @$s8moveonly7useCopyyxxRlzClF : $@convention(thin) <T where T : AnyObject> (@guaranteed T) -> @owned T {
|
||||
// CHECK-SIL: bb0([[ARG:%.*]] :
|
||||
// CHECK-SIL-NEXT: debug_value
|
||||
// CHECK-SIL-NEXT: [[INPUT_TO_BE_ELIMINATED:%.*]] = alloc_stack [lexical] $T
|
||||
// CHECK-SIL-NEXT: store [[ARG]] to [[INPUT]] : $*T
|
||||
// CHECK-SIL-NEXT: [[VALUE:%.*]] = load [[INPUT]] : $*T
|
||||
// CHECK-SIL-NEXT: strong_retain [[VALUE]]
|
||||
// CHECK-SIL-NEXT: strong_retain [[VALUE]]
|
||||
// CHECK-SIL-NEXT: strong_release [[VALUE]]
|
||||
// CHECK-SIL-NEXT: dealloc_stack [[INPUT]]
|
||||
// CHECK-SIL-NEXT: return [[VALUE]] : $T
|
||||
// CHECK-SIL: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
|
||||
// CHECK-SIL-LABEL: sil @$s8moveonly7useCopyyxxRlzClF : {{.*}} {
|
||||
// CHECK-SIL: bb0([[ARG:%.*]] :
|
||||
// CHECK-SIL-NEXT: debug_value
|
||||
// CHECK-SIL-NEXT: strong_retain [[ARG]]
|
||||
// CHECK-SIL-NEXT: strong_retain [[ARG]]
|
||||
// CHECK-SIL-NEXT: strong_release [[ARG]]
|
||||
// CHECK-SIL-NEXT: return [[ARG]]
|
||||
// CHECK-SIL-LABEL: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
|
||||
|
||||
// CHECK-SIL-OPT-LABEL: sil {{.*}}@$s8moveonly7useCopyyxxRlzClF : $@convention(thin) <T where T : AnyObject> (@guaranteed T) -> @owned T {
|
||||
// CHECK-SIL-OPT: bb0([[ARG:%.*]] :
|
||||
// CHECK-SIL-OPT-NEXT: debug_value
|
||||
// CHECK-SIL-OPT-NEXT: strong_retain [[ARG]]
|
||||
// CHECK-SIL-OPT-NEXT: return [[ARG]] : $T
|
||||
// CHECK-SIL-OPT: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
|
||||
// CHECK-SIL-OPT-LABEL: sil {{.*}}@$s8moveonly7useCopyyxxRlzClF : {{.*}} {
|
||||
// CHECK-SIL-OPT: bb0([[ARG:%.*]] :
|
||||
// CHECK-SIL-OPT-NEXT: debug_value
|
||||
// CHECK-SIL-OPT-NEXT: strong_retain [[ARG]]
|
||||
// CHECK-SIL-OPT-NEXT: return [[ARG]] : $T
|
||||
// CHECK-SIL-OPT-LABEL: } // end sil function '$s8moveonly7useCopyyxxRlzClF'
|
||||
public func useCopy<T : AnyObject>(_ k: T) -> T {
|
||||
_copy(k)
|
||||
copy k
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
import Swift
|
||||
|
||||
func addressOnlyCopy<T>(t: T) -> T {
|
||||
_copy(t) // expected-error {{copy() used on a generic or existential value}}
|
||||
copy t
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@ struct NC: ~Copyable {}
|
||||
|
||||
func checkPointerBuiltins(_ ptr: Builtin.RawPointer, _ value: consuming NC) {
|
||||
Builtin.initialize(value, ptr)
|
||||
#if ILLEGAL
|
||||
Builtin.copy(value) // expected-illegal-error {{noncopyable type 'NC' cannot be substituted for copyable generic parameter 'T' in 'copy'}}
|
||||
#endif
|
||||
}
|
||||
|
||||
func checkArrayBuiltins(_ dest: Builtin.RawPointer, src: Builtin.RawPointer, count: Builtin.Word) {
|
||||
|
||||
@@ -34,29 +34,10 @@ class X {}
|
||||
func consume(_ x : __owned X) {}
|
||||
|
||||
func foo(@_noImplicitCopy _ x: __owned X) {
|
||||
consume(_copy(x))
|
||||
consume(copy x)
|
||||
consume(x)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [transparent] [_semantics "lifetimemanagement.copy"] @_copy : {{.*}} {
|
||||
// CHECK: {{bb[0-9]+}}([[OUT_ADDR:%[^,]+]] : $*T, [[IN_ADDR:%[^,]+]] : $*T):
|
||||
// CHECK: [[TMP_ADDR:%[^,]+]] = alloc_stack $T
|
||||
// CHECK: copy_addr [[IN_ADDR]] to [init] [[TMP_ADDR]] : $*T
|
||||
// CHECK: builtin "copy"<T>([[OUT_ADDR]] : $*T, [[TMP_ADDR]] : $*T) : $()
|
||||
// CHECK: dealloc_stack [[TMP_ADDR]] : $*T
|
||||
// CHECK: return {{%[^,]+}} : $()
|
||||
// CHECK-LABEL: } // end sil function '_copy'
|
||||
@_transparent
|
||||
@_semantics("lifetimemanagement.copy")
|
||||
@_silgen_name("_copy")
|
||||
public func _copy<T>(_ value: T) -> T {
|
||||
#if $BuiltinCopy
|
||||
Builtin.copy(value)
|
||||
#else
|
||||
value
|
||||
#endif
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @getRawPointer : {{.*}} {
|
||||
// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] : $*T):
|
||||
// CHECK: [[PTR:%[^,]+]] = address_to_pointer [stack_protection] [[ADDR]]
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-move-only)
|
||||
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-move-only -enable-builtin-module)
|
||||
|
||||
// REQUIRES: executable_test
|
||||
|
||||
import StdlibUnittest
|
||||
import Builtin
|
||||
|
||||
class Klass {}
|
||||
|
||||
var suite = TestSuite("LifetimeManagement")
|
||||
|
||||
suite.test("copy") {
|
||||
suite.test("_copy") {
|
||||
let k = Klass()
|
||||
expectTrue(k === _copy(k))
|
||||
}
|
||||
|
||||
suite.test("copy") {
|
||||
let k = Klass()
|
||||
expectTrue(k === copy k)
|
||||
}
|
||||
|
||||
suite.test("move") {
|
||||
let k = Klass()
|
||||
let k2 = k
|
||||
@@ -20,3 +26,19 @@ suite.test("move") {
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
// TODO: Remove. Only exists to avoid a reverse condfail.
|
||||
// rdar://127516085 (Complete removal of Builtin.copy)
|
||||
func _oldCopy<T>(_ value: T) -> T {
|
||||
#if $BuiltinCopy
|
||||
Builtin.copy(value)
|
||||
#else
|
||||
value
|
||||
#endif
|
||||
}
|
||||
|
||||
suite.test("_oldCopy") {
|
||||
let k = Klass()
|
||||
expectTrue(k === _oldCopy(k))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user