Emit enum element arguments directly into the enum payload.

This generates significantly better code directly out of SILGen, at
the cost of having to reimplement a little bit of the argument-emission
logic to handle default arguments.  But it also neatly sidesteps the
problems we have with splitting tuple RValues when the tuple contains
a pack expansion, which will require some significant surgery to RValue
to fix.  That, in turn, fixes rdar://121489308.
This commit is contained in:
John McCall
2024-02-15 00:40:57 -05:00
parent 633051e319
commit 2d71c604ed
10 changed files with 324 additions and 127 deletions

View File

@@ -2677,6 +2677,16 @@ public:
}
}
DelayedArgument(DefaultArgumentExpr *defArg,
AbstractionPattern origParamType,
ClaimedParamsRef params,
SILFunctionTypeRepresentation functionTypeRepresentation)
: DelayedArgument(defArg, defArg->getDefaultArgsOwner(),
defArg->getParamIndex(),
defArg->getType()->getCanonicalType(),
origParamType, params, functionTypeRepresentation,
defArg->isImplicitlyAsync()) {}
DelayedArgument(SILLocation loc,
ConcreteDeclRef defaultArgsOwner,
unsigned destIndex,
@@ -3389,17 +3399,12 @@ public:
// If this is delayed default argument, prepare to emit the default argument
// generator later.
if (arg.isDelayedDefaultArg()) {
auto substParamType = arg.getSubstRValueType();
auto defArg = std::move(arg).asKnownDefaultArg();
auto numParams = getFlattenedValueCount(origParamType,
ImportAsMemberStatus());
DelayedArguments.emplace_back(defArg,
defArg->getDefaultArgsOwner(),
defArg->getParamIndex(),
substParamType, origParamType,
claimNextParameters(numParams),
Rep, defArg->isImplicitlyAsync());
DelayedArguments.emplace_back(defArg, origParamType,
claimNextParameters(numParams), Rep);
Args.push_back(ManagedValue());
maybeEmitForeignArgument();
@@ -4329,10 +4334,30 @@ private:
}
};
static ManagedValue
emitDefaultArgument(SILGenFunction &SGF, DefaultArgumentExpr *E,
AbstractionPattern origType, SILType expectedTy,
SGFContext origC) {
return SGF.emitAsOrig(E, origType, E->getType()->getCanonicalType(),
expectedTy, origC,
[&](SILGenFunction &SGF, SILLocation loc, SGFContext C) {
auto result =
SGF.emitApplyOfDefaultArgGenerator(loc, E->getDefaultArgsOwner(),
E->getParamIndex(),
E->getType()->getCanonicalType(),
E->isImplicitlyAsync(),
C);
if (result.isInContext())
return ManagedValue::forInContext();
return std::move(result).getAsSingleValue(SGF, loc);
});
}
void DelayedArgument::emitDefaultArgument(SILGenFunction &SGF,
const DefaultArgumentStorage &info,
SmallVectorImpl<ManagedValue> &args,
size_t &argIndex) {
// TODO: call emitDefaultArgument above.
auto value = SGF.emitApplyOfDefaultArgGenerator(info.loc,
info.defaultArgsOwner,
info.destIndex,
@@ -4535,7 +4560,7 @@ public:
void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override {
auto theBox = box;
if (SGF.getASTContext().SILOpts.supportsLexicalLifetimes(SGF.getModule())) {
if (auto *bbi = cast<BeginBorrowInst>(theBox)) {
if (auto *bbi = dyn_cast<BeginBorrowInst>(theBox)) {
SGF.B.createEndBorrow(l, bbi);
theBox = bbi->getOperand();
}
@@ -5132,7 +5157,7 @@ RValue CallEmission::applyEnumElementConstructor(SGFContext C) {
// pattern, to ensure that function types in payloads are re-abstracted
// correctly.
auto formalType = callee.getSubstFormalType();
auto origFormalType = callee.getOrigFormalType();
CanType formalResultType = formalType.getResult();
// We have a fully-applied enum element constructor: open-code the
// construction.
@@ -5140,9 +5165,6 @@ RValue CallEmission::applyEnumElementConstructor(SGFContext C) {
SILLocation uncurriedLoc = selfArg->Loc;
origFormalType = origFormalType.getFunctionResultType();
CanType formalResultType = formalType.getResult();
// Ignore metatype argument
SmallVector<ManagedValue, 0> metatypeVal;
emitPseudoFunctionArguments(SGF, uncurriedLoc,
@@ -5152,34 +5174,17 @@ RValue CallEmission::applyEnumElementConstructor(SGFContext C) {
assert(metatypeVal.size() == 1);
// Get the payload argument.
ArgumentSource payload;
// Get the payload argument sources, if there are any.
MutableArrayRef<ArgumentSource> payloads;
if (element->hasAssociatedValues()) {
SmallVector<ManagedValue, 4> argVals;
auto resultFnType = cast<FunctionType>(formalResultType);
emitPseudoFunctionArguments(SGF, uncurriedLoc,
AbstractionPattern(resultFnType),
resultFnType, argVals,
std::move(*callSite).forward());
// We need to implode a tuple rvalue for enum construction. This is
// essentially an implosion of the internal arguments of a pseudo case
// constructor, so we can drop the parameter flags.
auto payloadTy = AnyFunctionType::composeTuple(
SGF.getASTContext(), resultFnType->getParams(),
ParameterFlagHandling::IgnoreNonEmpty);
auto arg = RValue(SGF, argVals, payloadTy->getCanonicalType());
payload = ArgumentSource(uncurriedLoc, std::move(arg));
payloads = std::move(*callSite).forward().getSources();
formalResultType = cast<FunctionType>(formalResultType).getResult();
origFormalType = origFormalType.getFunctionResultType();
} else {
assert(!callSite.has_value());
}
ManagedValue resultMV = SGF.emitInjectEnum(
uncurriedLoc, std::move(payload),
uncurriedLoc, payloads,
SGF.getLoweredType(formalResultType),
element, uncurriedContext);
@@ -6035,16 +6040,130 @@ void SILGenFunction::emitRawYield(SILLocation loc,
B.emitBlock(resumeBB);
}
static bool isEnumElementPayloadTupled(EnumElementDecl *element,
MutableArrayRef<ArgumentSource> payloads,
CanTupleType &formalPayloadTupleType) {
auto params = element->getParameterList();
assert(params);
assert(payloads.size() == params->size());
if (payloads.size() == 1 && params->get(0)->getArgumentName().empty())
return false;
SmallVector<TupleTypeElt, 8> tupleElts;
for (auto i : indices(payloads)) {
tupleElts.push_back({payloads[i].getSubstRValueType(),
params->get(0)->getArgumentName()});
}
formalPayloadTupleType = cast<TupleType>(
CanType(TupleType::get(tupleElts, element->getASTContext())));
return true;
}
static ManagedValue
emitEnumElementPayloads(SILGenFunction &SGF, SILLocation loc,
EnumElementDecl *element,
MutableArrayRef<ArgumentSource> eltPayloads,
AbstractionPattern origPayloadType, SILType payloadTy,
Initialization *dest) {
// The payloads array is parallel to the parameters of the enum element.
// The abstraction pattern is taken from the element's argument interface
// type, so it has extra tuple structure if the argument interface type does.
CanTupleType formalPayloadTupleType;
auto treatAsTuple =
isEnumElementPayloadTupled(element, eltPayloads, formalPayloadTupleType);
// The Initialization we get passed is always one of several cases in
// emitInjectEnum, all of which are splittable.
assert(!treatAsTuple || !dest || dest->canSplitIntoTupleElements());
SmallVector<InitializationPtr, 4> eltInitBuffer;
SmallVector<ManagedValue, 4> elts;
MutableArrayRef<InitializationPtr> eltInits;
if (!treatAsTuple) {
// nothing required
} else if (dest) {
eltInits = dest->splitIntoTupleElements(SGF, loc, formalPayloadTupleType,
eltInitBuffer);
} else {
elts.reserve(eltPayloads.size());
}
// Do an initial pass, emitting non-default arguments.
SmallVector<unsigned, 4> delayedArgIndices;
for (auto i : indices(eltPayloads)) {
auto &eltPayload = eltPayloads[i];
if (eltPayload.isDelayedDefaultArg()) {
delayedArgIndices.push_back(i);
if (!dest) elts.push_back(ManagedValue());
continue;
}
AbstractionPattern origEltType =
(treatAsTuple ? origPayloadType.getTupleElementType(i) : origPayloadType);
SILType eltTy =
(treatAsTuple ? payloadTy.getTupleElementType(i) : payloadTy);
Initialization *eltInit =
(dest ? (treatAsTuple ? eltInits[i].get() : dest) : nullptr);
if (dest) {
std::move(eltPayload).forwardInto(SGF, origEltType, eltInit,
SGF.getTypeLowering(eltTy));
} else {
auto elt = std::move(eltPayload).getAsSingleValue(SGF, origEltType, eltTy);
elts.push_back(elt);
}
}
// Emit all of the default arguments in a separate pass.
for (auto i : delayedArgIndices) {
auto &eltPayload = eltPayloads[i];
AbstractionPattern origEltType =
(treatAsTuple ? origPayloadType.getTupleElementType(i) : origPayloadType);
SILType eltTy =
(treatAsTuple ? payloadTy.getTupleElementType(i) : payloadTy);
Initialization *eltInit =
(dest ? (treatAsTuple ? eltInits[i].get() : dest) : nullptr);
auto result = emitDefaultArgument(SGF,
std::move(eltPayload).asKnownDefaultArg(),
origEltType, eltTy, SGFContext(eltInit));
if (dest) {
assert(result.isInContext());
} else {
elts[i] = result;
}
}
// If we're not breaking down a tuple, we can wrap up immediately.
if (!treatAsTuple) {
if (dest) return ManagedValue::forInContext();
assert(elts.size() == 1);
return elts[0];
}
// If we've been emitting into split element contexts, finish the
// overall tuple initialization.
if (dest) {
dest->finishInitialization(SGF);
return ManagedValue::forInContext();
}
// Otherwise, create a tuple value.
return SGF.B.createTuple(loc, payloadTy.getObjectType(), elts);
}
/// Emits SIL instructions to create an enum value. Attempts to avoid
/// unnecessary copies by emitting the payload directly into the enum
/// payload, or into the box in the case of an indirect payload.
ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc,
ArgumentSource &&payload,
MutableArrayRef<ArgumentSource> payloads,
SILType enumTy,
EnumElementDecl *element,
SGFContext C) {
// Easy case -- no payload
if (!payload) {
if (!element->hasAssociatedValues()) {
assert(payloads.empty());
if (enumTy.isLoadable(F) || !silConv.useLoweredAddresses()) {
return emitManagedRValueWithCleanup(
B.createEnum(loc, SILValue(), element, enumTy.getObjectType()));
@@ -6057,25 +6176,29 @@ ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc,
});
}
ManagedValue payloadMV;
AbstractionPattern origFormalType =
(element == getASTContext().getOptionalSomeDecl()
? AbstractionPattern(payload.getSubstRValueType())
: SGM.M.Types.getAbstractionPattern(element));
auto &payloadTL = getTypeLowering(origFormalType,
payload.getSubstRValueType());
AbstractionPattern origPayloadType = [&] {
if (element == getASTContext().getOptionalSomeDecl()) {
assert(payloads.size() == 1);
auto substPayloadType = payloads[0].getSubstRValueType();
return AbstractionPattern(substPayloadType);
} else {
return SGM.M.Types.getAbstractionPattern(element);
}
}();
SILType loweredPayloadType = payloadTL.getLoweredType();
SILType payloadTy = enumTy.getEnumElementType(element, &F);
// If the payload is indirect, emit it into a heap allocated box.
//
// To avoid copies, evaluate it directly into the box, being
// careful to stage the cleanups so that if the expression
// throws, we know to deallocate the uninitialized box.
ManagedValue boxMV;
if (element->isIndirect() || element->getParentEnum()->isIndirect()) {
auto boxTy = SGM.M.Types.getBoxTypeForEnumElement(getTypeExpansionContext(),
enumTy, element);
auto *box = B.createAllocBox(loc, boxTy);
CanSILBoxType boxType = payloadTy.castTo<SILBoxType>();
assert(boxType->getLayout()->getFields().size() == 1);
SILType boxPayloadTy = payloadTy.getSILBoxFieldType(&F, 0);
auto *box = B.createAllocBox(loc, boxType);
auto *addr = B.createProjectBox(loc, box, 0);
CleanupHandle initCleanup = enterDestroyCleanup(box);
@@ -6083,20 +6206,26 @@ ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc,
CleanupHandle uninitCleanup = enterDeallocBoxCleanup(box);
BoxInitialization dest(box, addr, uninitCleanup, initCleanup);
std::move(payload).forwardInto(*this, origFormalType, &dest,
payloadTL);
auto result =
emitEnumElementPayloads(*this, loc, element, payloads, origPayloadType,
boxPayloadTy, &dest);
assert(result.isInContext()); (void) result;
payloadMV = dest.getManagedBox();
loweredPayloadType = payloadMV.getType();
boxMV = dest.getManagedBox();
payloadTy = boxMV.getType();
}
// Loadable with payload
if (enumTy.isLoadable(F) || !silConv.useLoweredAddresses()) {
if (!payloadMV) {
ManagedValue payloadMV;
if (boxMV) {
payloadMV = boxMV;
} else {
// If the payload was indirect, we already evaluated it and
// have a single value. Otherwise, evaluate the payload.
payloadMV = std::move(payload).getAsSingleValue(*this, origFormalType,
loweredPayloadType);
payloadMV = emitEnumElementPayloads(*this, loc, element, payloads,
origPayloadType, payloadTy,
/*emit into*/ nullptr);
}
SILValue argValue = payloadMV.forward(*this);
@@ -6107,34 +6236,36 @@ ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc,
// Address-only with payload
return B.bufferForExpr(
loc, enumTy, getTypeLowering(enumTy), C, [&](SILValue bufferAddr) {
SILValue resultData = B.createInitEnumDataAddr(
loc, bufferAddr, element, loweredPayloadType.getAddressType());
loc, enumTy, getTypeLowering(enumTy), C, [&](SILValue enumAddr) {
SILValue payloadAddr = B.createInitEnumDataAddr(
loc, enumAddr, element, payloadTy.getAddressType());
if (payloadMV) {
if (boxMV) {
// If the payload was indirect, we already evaluated it and
// have a single value. Store it into the result.
B.emitStoreValueOperation(loc, payloadMV.forward(*this), resultData,
B.emitStoreValueOperation(loc, boxMV.forward(*this), payloadAddr,
StoreOwnershipQualifier::Init);
} else if (payloadTL.isLoadable()) {
} else if (payloadTy.isLoadable(F)) {
// The payload of this specific enum case might be loadable
// even if the overall enum is address-only.
payloadMV =
std::move(payload).getAsSingleValue(*this, origFormalType,
loweredPayloadType);
B.emitStoreValueOperation(loc, payloadMV.forward(*this), resultData,
auto payloadMV =
emitEnumElementPayloads(*this, loc, element, payloads,
origPayloadType, payloadTy,
/*emit into*/ nullptr);
B.emitStoreValueOperation(loc, payloadMV.forward(*this), payloadAddr,
StoreOwnershipQualifier::Init);
} else {
// The payload is address-only. Evaluate it directly into
// the enum.
TemporaryInitialization dest(resultData, CleanupHandle::invalid());
std::move(payload).forwardInto(*this, origFormalType, &dest,
payloadTL);
TemporaryInitialization dest(payloadAddr, CleanupHandle::invalid());
auto result =
emitEnumElementPayloads(*this, loc, element, payloads,
origPayloadType, payloadTy, &dest);
assert(result.isInContext()); (void) result;
}
// The payload is initialized, now apply the tag.
B.createInjectEnumAddr(loc, bufferAddr, element);
B.createInjectEnumAddr(loc, enumAddr, element);
});
}

View File

@@ -898,12 +898,21 @@ void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) {
LoweredParamsInContextGenerator loweredParams(*this);
// Emit the exploded constructor argument.
ArgumentSource payload;
SmallVector<ArgumentSource, 2> payloads;
if (element->hasAssociatedValues()) {
auto eltArgTy = element->getArgumentInterfaceType()->getCanonicalType();
RValue arg = emitImplicitValueConstructorArg(*this, Loc, eltArgTy, element,
loweredParams);
payload = ArgumentSource(Loc, std::move(arg));
auto elementFnTy =
cast<AnyFunctionType>(
cast<AnyFunctionType>(element->getInterfaceType()->getCanonicalType())
.getResult());
auto elementParams = elementFnTy.getParams();
payloads.reserve(elementParams.size());
for (auto param: elementParams) {
auto paramType = param.getParameterType();
RValue arg = emitImplicitValueConstructorArg(*this, Loc, paramType,
element, loweredParams);
payloads.emplace_back(Loc, std::move(arg));
}
}
// Emit the metatype argument.
@@ -913,7 +922,7 @@ void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) {
// If possible, emit the enum directly into the indirect return.
SGFContext C = (dest ? SGFContext(dest.get()) : SGFContext());
ManagedValue mv = emitInjectEnum(Loc, std::move(payload),
ManagedValue mv = emitInjectEnum(Loc, payloads,
enumTI.getLoweredType(),
element, C);

View File

@@ -1026,6 +1026,34 @@ ManagedValue SILGenFunction::manageOpaqueValue(ManagedValue value,
return value.copyUnmanaged(*this, loc);
}
ManagedValue SILGenFunction::emitAsOrig(SILLocation loc,
AbstractionPattern origType,
CanType substType,
SILType expectedTy,
SGFContext C,
ValueProducerRef produceValue) {
// If the lowered substituted type already matches the substitution,
// we can just emit directly.
if (getLoweredType(substType).getASTType() == expectedTy.getASTType()) {
auto result = produceValue(*this, loc, C);
// For convenience, force the result into the destination.
if (auto init = C.getEmitInto(); init && !result.isInContext()) {
result.forwardInto(*this, loc, init);
return ManagedValue::forInContext();
}
return result;
}
auto conversion =
Conversion::getSubstToOrig(origType, substType, expectedTy);
auto result = emitConvertedRValue(loc, conversion, C, produceValue);
// emitConvertedRValue always forces results into the context.
assert((C.getEmitInto() != nullptr) == result.isInContext());
return result;
}
ManagedValue SILGenFunction::emitConvertedRValue(Expr *E,
const Conversion &conversion,
SGFContext C) {

View File

@@ -1300,7 +1300,7 @@ public:
//===--------------------------------------------------------------------===//
ManagedValue emitInjectEnum(SILLocation loc,
ArgumentSource &&payload,
MutableArrayRef<ArgumentSource> payload,
SILType enumTy,
EnumElementDecl *element,
SGFContext C);
@@ -1562,6 +1562,22 @@ public:
SGFContext C,
ValueProducerRef produceValue);
/// Call the produceValue function and convert the result to the given
/// original abstraction pattern.
///
/// The SGFContext provided to the produceValue function includes the
/// conversion, if it's non-trivial, and thus permits it to be peepholed
/// and combined with other conversions. This can result in substantially
/// more efficient code than just emitting the value and reabstracting
/// it afterwards.
///
/// If the provided SGFContext includes an initialization, the result
/// will always be ManagedValue::forInContext().
ManagedValue emitAsOrig(SILLocation loc, AbstractionPattern origType,
CanType substType, SILType expectedTy,
SGFContext C,
ValueProducerRef produceValue);
/// Emit the given expression as an r-value that follows the
/// abstraction patterns of the original type.
ManagedValue emitRValueAsOrig(Expr *E, AbstractionPattern origPattern,

View File

@@ -135,22 +135,11 @@ public func constructResilientEnumPayload(_ s: Size) -> Medium {
// CHECK-NEXT: [[VWT_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[METADATA]], [[INT]] -1
// CHECK-NEXT: [[VWT:%.*]] = load ptr, ptr [[VWT_ADDR]]
// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds %swift.vwtable, ptr [[VWT]], i32 0, i32 8
// CHECK-NEXT: [[WITNESS_FOR_SIZE:%size]] = load [[INT]], ptr [[WITNESS_ADDR]]
// CHECK-NEXT: [[ALLOCA:%.*]] = alloca i8, {{.*}} [[WITNESS_FOR_SIZE]], align 16
// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i32|i64)}} -1, ptr [[ALLOCA]])
// CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32 2
// CHECK-NEXT: [[WITNESS:%.*]] = load ptr, ptr [[WITNESS_ADDR]]
// CHECK-arm64e-NEXT: ptrtoint ptr [[WITNESS_ADDR]] to i64
// CHECK-arm64e-NEXT: call i64 @llvm.ptrauth.blend
// CHECK-NEXT: call ptr [[WITNESS]](ptr noalias [[ALLOCA]], ptr noalias %1, ptr [[METADATA]])
// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32 4
// CHECK-NEXT: [[WITNESS:%.*]] = load ptr, ptr [[WITNESS_ADDR]]
// CHECK-arm64e-NEXT: ptrtoint ptr [[WITNESS_ADDR]] to i64
// CHECK-arm64e-NEXT: call i64 @llvm.ptrauth.blend
// CHECK-NEXT: call ptr [[WITNESS]](ptr noalias %0, ptr noalias [[ALLOCA]], ptr [[METADATA]])
// CHECK-NEXT: call ptr [[WITNESS]](ptr noalias %0, ptr noalias %1, ptr [[METADATA]])
// CHECK-NEXT: [[TAG:%.*]] = load i32, ptr @"$s14resilient_enum6MediumO8PostcardyAC0A7_struct4SizeVcACmFWC"
// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s14resilient_enum6MediumOMa"([[INT]] 0)
@@ -165,7 +154,6 @@ public func constructResilientEnumPayload(_ s: Size) -> Medium {
// CHECK-arm64e-NEXT: ptrtoint ptr [[WITNESS_ADDR]] to i64
// CHECK-arm64e-NEXT: call i64 @llvm.ptrauth.blend
// CHECK-NEXT: call void [[WITNESS_FN]](ptr noalias %0, i32 [[TAG]], ptr [[METADATA2]])
// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i32|i64)}} -1, ptr [[ALLOCA]])
// CHECK-NEXT: ret void

View File

@@ -63,16 +63,13 @@ func AddressOnly_cases(_ s: S) {
_ = AddressOnly.nought
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin AddressOnly.Type
// CHECK-NEXT: [[P_BUF:%.*]] = alloc_stack $any P
// CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = init_existential_addr [[P_BUF]]
// CHECK-NEXT: store %0 to [trivial] [[PAYLOAD_ADDR]]
// CHECK-NEXT: [[MERE:%.*]] = alloc_stack $AddressOnly
// CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[MERE]]
// CHECK-NEXT: copy_addr [take] [[P_BUF]] to [init] [[PAYLOAD]] : $*any P
// CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = init_existential_addr [[PAYLOAD]]
// CHECK-NEXT: store %0 to [trivial] [[PAYLOAD_ADDR]]
// CHECK-NEXT: inject_enum_addr [[MERE]]
// CHECK-NEXT: destroy_addr [[MERE]]
// CHECK-NEXT: dealloc_stack [[MERE]]
// CHECK-NEXT: dealloc_stack [[P_BUF]] : $*any P
_ = AddressOnly.mere(s)
// Address-only enum vs loadable payload
@@ -105,15 +102,12 @@ func PolyOptionable_cases<T>(_ t: T) {
_ = PolyOptionable<T>.nought
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin PolyOptionable<T>.Type
// CHECK-NEXT: [[T_BUF:%.*]] = alloc_stack $T
// CHECK-NEXT: copy_addr %0 to [init] [[T_BUF]]
// CHECK-NEXT: [[MERE:%.*]] = alloc_stack $PolyOptionable<T>
// CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[MERE]]
// CHECK-NEXT: copy_addr [take] [[T_BUF]] to [init] [[PAYLOAD]] : $*T
// CHECK-NEXT: copy_addr %0 to [init] [[PAYLOAD]] : $*T
// CHECK-NEXT: inject_enum_addr [[MERE]]
// CHECK-NEXT: destroy_addr [[MERE]]
// CHECK-NEXT: dealloc_stack [[MERE]]
// CHECK-NEXT: dealloc_stack [[T_BUF]] : $*T
_ = PolyOptionable<T>.mere(t)
// CHECK-NOT: destroy_addr %0
@@ -265,3 +259,27 @@ enum rdar81817725 {
}
}
}
enum Indirected {
case a
case b
indirect case c(Int)
}
func throwingFunction() throws -> Int { return 0 }
// CHECK-LABEL: sil hidden [ossa] @$s4enum29throwInIndirectConstructorArgAA10IndirectedOyKF
// CHECK: [[BOX:%.*]] = alloc_box ${ var Int }
// CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = project_box [[BOX]]
// CHECK-NEXT: // function_ref
// CHECK-NEXT: [[FN:%.*]] = function_ref
// CHECK-NEXT: try_apply [[FN]]()
// CHECK: bb1([[RESULT:%.*]] : $Int):
// CHECK-NEXT: store [[RESULT]] to [trivial] [[PAYLOAD_ADDR]]
// CHECK-NEXT: [[ENUM:%.*]] = enum $Indirected, #Indirected.c!enumelt, [[BOX]]
// CHECK-NEXT: return [[ENUM]] : $Indirected
// CHECK: bb2([[ERROR:%.*]] : @owned $any Error):
// CHECK-NEXT: dealloc_box [[BOX]]
// CHECK-NEXT: throw [[ERROR]]
func throwInIndirectConstructorArg() throws -> Indirected {
return .c(try throwingFunction())
}

View File

@@ -132,8 +132,8 @@ class NestedGeneric<U> {
// CHECK-LABEL: sil hidden [ossa] @$s16generic_closures13NestedGenericC20nested_reabstraction{{[_0-9a-zA-Z]*}}F
// CHECK: [[REABSTRACT:%.*]] = function_ref @$sIeg_ytIegr_TR
// CHECK: partial_apply [callee_guaranteed] [[REABSTRACT]]
func nested_reabstraction<T>(_ x: T) -> Optionable<() -> ()> {
return .some({})
func nested_reabstraction<T>(_ x: T, fn: @escaping () -> ()) -> Optionable<() -> ()> {
return .some(fn)
}
}

View File

@@ -16,24 +16,21 @@ func TreeA_cases<T>(_ t: T, l: TreeA<T>, r: TreeA<T>) {
let _ = TreeA<T>.Nil
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeA<T>.Type
// CHECK-NEXT: [[T_BUF:%.*]] = alloc_stack $T
// CHECK-NEXT: copy_addr [[ARG1]] to [init] [[T_BUF]] : $*T
// CHECK-NEXT: [[BOX:%.*]] = alloc_box $<τ_0_0> { var τ_0_0 } <T>
// CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]]
// CHECK-NEXT: copy_addr [take] [[T_BUF]] to [init] [[PB]]
// CHECK-NEXT: copy_addr [[ARG1]] to [init] [[PB]]
// CHECK-NEXT: [[LEAF:%.*]] = enum $TreeA<T>, #TreeA.Leaf!enumelt, [[BOX]]
// CHECK-NEXT: destroy_value [[LEAF]]
// CHECK-NEXT: dealloc_stack [[T_BUF]] : $*T
let _ = TreeA<T>.Leaf(t)
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeA<T>.Type
// CHECK-NEXT: [[ARG2_COPY:%.*]] = copy_value [[ARG2]]
// CHECK-NEXT: [[ARG3_COPY:%.*]] = copy_value [[ARG3]]
// CHECK-NEXT: [[BOX:%.*]] = alloc_box $<τ_0_0> { var (left: TreeA<τ_0_0>, right: TreeA<τ_0_0>) } <T>
// CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]]
// CHECK-NEXT: [[LEFT:%.*]] = tuple_element_addr [[PB]] : $*(left: TreeA<T>, right: TreeA<T>), 0
// CHECK-NEXT: [[RIGHT:%.*]] = tuple_element_addr [[PB]] : $*(left: TreeA<T>, right: TreeA<T>), 1
// CHECK-NEXT: [[ARG2_COPY:%.*]] = copy_value [[ARG2]]
// CHECK-NEXT: store [[ARG2_COPY]] to [init] [[LEFT]]
// CHECK-NEXT: [[ARG3_COPY:%.*]] = copy_value [[ARG3]]
// CHECK-NEXT: store [[ARG3_COPY]] to [init] [[RIGHT]]
// CHECK-NEXT: [[BRANCH:%.*]] = enum $TreeA<T>, #TreeA.Branch!enumelt, [[BOX]]
// CHECK-NEXT: destroy_value [[BRANCH]]
@@ -46,9 +43,9 @@ func TreeA_cases<T>(_ t: T, l: TreeA<T>, r: TreeA<T>) {
func TreeA_reabstract(_ f: @escaping (Int) -> Int) {
// CHECK: bb0([[ARG:%.*]] : @guaranteed $@callee_guaranteed (Int) -> Int):
// CHECK: [[METATYPE:%.*]] = metatype $@thin TreeA<(Int) -> Int>.Type
// CHECK-NEXT: [[ARG_COPY:%.*]] = copy_value [[ARG]]
// CHECK-NEXT: [[BOX:%.*]] = alloc_box $<τ_0_0> { var τ_0_0 } <(Int) -> Int>
// CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]]
// CHECK-NEXT: [[ARG_COPY:%.*]] = copy_value [[ARG]]
// CHECK: [[THUNK:%.*]] = function_ref @$sS2iIegyd_S2iIegnr_TR
// CHECK-NEXT: [[FN:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[ARG_COPY]])
// CHECK-NEXT: [[FNC:%.*]] = convert_function [[FN]]
@@ -77,36 +74,27 @@ func TreeB_cases<T>(_ t: T, l: TreeB<T>, r: TreeB<T>) {
let _ = TreeB<T>.Nil
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeB<T>.Type
// CHECK-NEXT: [[T_BUF:%.*]] = alloc_stack $T
// CHECK-NEXT: copy_addr %0 to [init] [[T_BUF]]
// CHECK-NEXT: [[LEAF:%.*]] = alloc_stack $TreeB<T>
// CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[LEAF]] : $*TreeB<T>, #TreeB.Leaf!enumelt
// CHECK-NEXT: copy_addr [take] [[T_BUF]] to [init] [[PAYLOAD]]
// CHECK-NEXT: copy_addr %0 to [init] [[PAYLOAD]]
// CHECK-NEXT: inject_enum_addr [[LEAF]] : $*TreeB<T>, #TreeB.Leaf!enumelt
// CHECK-NEXT: destroy_addr [[LEAF]]
// CHECK-NEXT: dealloc_stack [[LEAF]]
// CHECK-NEXT: dealloc_stack [[T_BUF]]
let _ = TreeB<T>.Leaf(t)
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeB<T>.Type
// CHECK-NEXT: [[ARG1_COPY:%.*]] = alloc_stack $TreeB<T>
// CHECK-NEXT: copy_addr %1 to [init] [[ARG1_COPY]] : $*TreeB<T>
// CHECK-NEXT: [[ARG2_COPY:%.*]] = alloc_stack $TreeB<T>
// CHECK-NEXT: copy_addr %2 to [init] [[ARG2_COPY]] : $*TreeB<T>
// CHECK-NEXT: [[BOX:%.*]] = alloc_box $<τ_0_0> { var (left: TreeB<τ_0_0>, right: TreeB<τ_0_0>) } <T>
// CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]]
// CHECK-NEXT: [[LEFT:%.*]] = tuple_element_addr [[PB]]
// CHECK-NEXT: [[RIGHT:%.*]] = tuple_element_addr [[PB]]
// CHECK-NEXT: copy_addr [take] [[ARG1_COPY]] to [init] [[LEFT]] : $*TreeB<T>
// CHECK-NEXT: copy_addr [take] [[ARG2_COPY]] to [init] [[RIGHT]] : $*TreeB<T>
// CHECK-NEXT: copy_addr %1 to [init] [[LEFT]] : $*TreeB<T>
// CHECK-NEXT: copy_addr %2 to [init] [[RIGHT]] : $*TreeB<T>
// CHECK-NEXT: [[BRANCH:%.*]] = alloc_stack $TreeB<T>
// CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[BRANCH]]
// CHECK-NEXT: store [[BOX]] to [init] [[PAYLOAD]]
// CHECK-NEXT: inject_enum_addr [[BRANCH]] : $*TreeB<T>, #TreeB.Branch!enumelt
// CHECK-NEXT: destroy_addr [[BRANCH]]
// CHECK-NEXT: dealloc_stack [[BRANCH]]
// CHECK-NEXT: dealloc_stack [[ARG2_COPY]]
// CHECK-NEXT: dealloc_stack [[ARG1_COPY]]
let _ = TreeB<T>.Branch(left: l, right: r)
// CHECK: return
@@ -126,13 +114,13 @@ func TreeInt_cases(_ t: Int, l: TreeInt, r: TreeInt) {
let _ = TreeInt.Leaf(t)
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeInt.Type
// CHECK-NEXT: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] : $TreeInt
// CHECK-NEXT: [[ARG3_COPY:%.*]] = copy_value [[ARG3]] : $TreeInt
// CHECK-NEXT: [[BOX:%.*]] = alloc_box ${ var (left: TreeInt, right: TreeInt) }
// CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]]
// CHECK-NEXT: [[LEFT:%.*]] = tuple_element_addr [[PB]]
// CHECK-NEXT: [[RIGHT:%.*]] = tuple_element_addr [[PB]]
// CHECK-NEXT: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] : $TreeInt
// CHECK-NEXT: store [[ARG2_COPY]] to [init] [[LEFT]]
// CHECK-NEXT: [[ARG3_COPY:%.*]] = copy_value [[ARG3]] : $TreeInt
// CHECK-NEXT: store [[ARG3_COPY]] to [init] [[RIGHT]]
// CHECK-NEXT: [[BRANCH:%.*]] = enum $TreeInt, #TreeInt.Branch!enumelt, [[BOX]]
// CHECK-NEXT: destroy_value [[BRANCH]]

View File

@@ -399,3 +399,27 @@ func test() {
let tuple = identityOnVariadicTuples((1, 2, 3))
takesVariadicTuple(tuple: tuple)
}
func createTuple<each T>(including: repeat Stored<each T>, from: Int) -> (repeat each T) {
fatalError()
}
// rdar://121489308
func testTupleExpansionInEnumConstructor<each T>(
from: repeat Stored<each T>,
to: @escaping (Result<(repeat each T), Error>) -> ()
) {
_ = {
let tuple = createTuple(including: repeat each from,
from: 42)
to(.success(tuple))
}
}
// CHECK-LABEL: sil {{.*}}@$s4main35testTupleExpansionInEnumConstructor4from2toyAA6StoredVyxGxQp_ys6ResultOyxxQp_ts5Error_pGctRvzlFyycfU_ :
// CHECK: [[VAR:%.*]] = alloc_stack [lexical] $(repeat each T), let, name "tuple"
// (a few moments later)
// CHECK: metatype $@thin Result<(repeat each T), any Error>.Type
// CHECK: [[RESULT_TEMP:%.*]] = alloc_stack $Result<(repeat each T), any Error>
// CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = init_enum_data_addr [[RESULT_TEMP]] : $*Result<(repeat each T), any Error>, #Result.success
// CHECK-NEXT: copy_addr [[VAR]] to [init] [[PAYLOAD_ADDR]] : $*(repeat each T)
// CHECK-NEXT: inject_enum_addr [[RESULT_TEMP]] : $*Result<(repeat each T), any Error>, #Result.success

View File

@@ -334,14 +334,9 @@ func testInitGenericEnum<T>(t: T) -> GenericEnum<T>? {
// CHECK: alloc_box $<τ_0_0> { var GenericEnum<τ_0_0> } <T>, var, name "self"
// CHECK: mark_uninitialized [delegatingself] %3 : $<τ_0_0> { var GenericEnum<τ_0_0> } <T>
// CHECK: [[PROJ:%.*]] = project_box
// CHECK: [[ADR1:%.*]] = alloc_stack $T
// CHECK-NOT: begin_access
// CHECK: copy_addr %1 to [init] [[ADR1]] : $*T
// CHECK: [[STK:%.*]] = alloc_stack $GenericEnum<T>
// CHECK: [[ENUMDATAADDR:%.*]] = init_enum_data_addr [[STK]]
// CHECK: [[ACCESSENUM:%.*]] = begin_access [modify] [unsafe] [[ENUMDATAADDR]] : $*T
// CHECK: copy_addr [take] [[ADR1]] to [init] [[ACCESSENUM]] : $*T
// CHECK: end_access [[ACCESSENUM]] : $*T
// CHECK: copy_addr %1 to [init] [[ENUMDATAADDR]] : $*T
// CHECK: inject_enum_addr
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] [[PROJ]]
// CHECK: copy_addr [take] %{{.*}} to [[ACCESS]] : $*GenericEnum<T>
@@ -362,9 +357,9 @@ func testIndirectEnum() -> IndirectEnum {
}
// CHECK-LABEL: sil hidden [ossa] @$s20access_marker_verify16testIndirectEnumAA0eF0OyF : $@convention(thin) () -> @owned IndirectEnum {
// CHECK: bb0:
// CHECK: apply
// CHECK: alloc_box ${ var Int }
// CHECK: [[PROJ:%.*]] = project_box
// CHECK: apply
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unsafe] [[PROJ]]
// CHECK: store %{{.*}} to [trivial] [[ACCESS]] : $*Int
// CHECK: end_access