Skip to content

Commit 8802395

Browse files
authored
Merge pull request #80652 from atrick/62-address-specialize
[6.2] Fix GenericSpecializer for addressable parameters.
2 parents 29db1ec + 0714624 commit 8802395

File tree

12 files changed

+221
-83
lines changed

12 files changed

+221
-83
lines changed

include/swift/AST/LifetimeDependence.h

-5
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,6 @@ class LifetimeDependenceInfo {
316316
&& scopeLifetimeParamIndices->contains(index);
317317
}
318318

319-
bool checkAddressable(int index) const {
320-
return hasAddressableParamIndices()
321-
&& getAddressableIndices()->contains(index);
322-
}
323-
324319
std::string getString() const;
325320
void Profile(llvm::FoldingSetNodeID &ID) const;
326321
void getConcatenatedData(SmallVectorImpl<bool> &concatenatedData) const;

include/swift/AST/Types.h

+7
Original file line numberDiff line numberDiff line change
@@ -5649,6 +5649,13 @@ class SILFunctionType final
56495649
return getLifetimeDependenceFor(getNumParameters());
56505650
}
56515651

5652+
/// Return true of the specified parameter is addressable based on its type
5653+
/// lowering in 'caller's context. This includes @_addressableForDependencies
5654+
/// parameter types.
5655+
///
5656+
/// Defined in SILType.cpp.
5657+
bool isAddressable(unsigned paramIdx, SILFunction *caller);
5658+
56525659
/// Returns true if the function type stores a Clang type that cannot
56535660
/// be derived from its Swift type. Returns false otherwise, including if
56545661
/// the function type is not @convention(c) or @convention(block).

include/swift/SIL/ApplySite.h

+4
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,10 @@ class ApplySite {
641641
return getArgumentParameterInfo(oper).hasOption(SILParameterInfo::Sending);
642642
}
643643

644+
/// Return true if 'operand' is addressable after type substitution in the
645+
/// caller's context.
646+
bool isAddressable(const Operand &operand) const;
647+
644648
static ApplySite getFromOpaqueValue(void *p) { return ApplySite(p); }
645649

646650
friend bool operator==(ApplySite lhs, ApplySite rhs) {

include/swift/SILOptimizer/Utils/Generics.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ class ReabstractionInfo {
8484
/// specializer.
8585
bool ConvertIndirectToDirect = true;
8686

87-
/// If true, drop unused arguments.
87+
/// If true, drop unused arguments. Dropping unused arguments is a
88+
/// prerequisite before promoting an indirect argument to a direct argument.
8889
/// See `droppedArguments`.
8990
bool dropUnusedArguments = false;
9091

@@ -204,7 +205,7 @@ class ReabstractionInfo {
204205
ReabstractionInfo(ModuleDecl *targetModule, bool isModuleWholeModule,
205206
ApplySite Apply, SILFunction *Callee,
206207
SubstitutionMap ParamSubs, SerializedKind_t Serialized,
207-
bool ConvertIndirectToDirect, bool dropMetatypeArgs,
208+
bool ConvertIndirectToDirect, bool dropUnusedArguments,
208209
OptRemark::Emitter *ORE = nullptr);
209210

210211
/// Constructs the ReabstractionInfo for generic function \p Callee with

lib/SIL/IR/ApplySite.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,12 @@ void ApplySite::insertAfterApplication(
4545
llvm_unreachable("covered switch isn't covered");
4646
}
4747

48+
bool ApplySite::isAddressable(const Operand &operand) const {
49+
unsigned calleeArgIndex = getCalleeArgIndex(operand);
50+
assert(calleeArgIndex >= getSubstCalleeConv().getSILArgIndexOfFirstParam());
51+
unsigned paramIdx =
52+
calleeArgIndex - getSubstCalleeConv().getSILArgIndexOfFirstParam();
53+
54+
CanSILFunctionType calleeType = getSubstCalleeType();
55+
return calleeType->isAddressable(paramIdx, getFunction());
56+
}

lib/SIL/IR/SILType.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,29 @@ bool SILFunctionType::isNoReturnFunction(SILModule &M,
706706
return false;
707707
}
708708

709+
bool SILFunctionType::isAddressable(unsigned paramIdx, SILFunction *caller) {
710+
SILParameterInfo paramInfo = getParameters()[paramIdx];
711+
for (auto &depInfo : getLifetimeDependencies()) {
712+
auto *addressableIndices = depInfo.getAddressableIndices();
713+
if (addressableIndices && addressableIndices->contains(paramIdx)) {
714+
return true;
715+
}
716+
auto *condAddressableIndices = depInfo.getConditionallyAddressableIndices();
717+
if (condAddressableIndices && condAddressableIndices->contains(paramIdx)) {
718+
CanType argType = paramInfo.getArgumentType(
719+
caller->getModule(), this, caller->getTypeExpansionContext());
720+
CanType contextType =
721+
argType->hasTypeParameter()
722+
? caller->mapTypeIntoContext(argType)->getCanonicalType()
723+
: argType;
724+
auto &tl = caller->getTypeLowering(contextType);
725+
if (tl.getRecursiveProperties().isAddressableForDependencies())
726+
return true;
727+
}
728+
}
729+
return false;
730+
}
731+
709732
#ifndef NDEBUG
710733
static bool areOnlyAbstractionDifferent(CanType type1, CanType type2) {
711734
assert(type1->isLegalSILType());

lib/SILOptimizer/IPO/CapturePropagation.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ static SILFunction *getSpecializedWithDeadParams(
496496
FuncBuilder.getModule().getSwiftModule(),
497497
FuncBuilder.getModule().isWholeModule(), ApplySite(), Specialized,
498498
PAI->getSubstitutionMap(), Specialized->getSerializedKind(),
499-
/* ConvertIndirectToDirect */ false, /*dropMetatypeArgs=*/false);
499+
/* ConvertIndirectToDirect */ false, /*dropUnusedArguments=*/false);
500500
GenericFuncSpecializer FuncSpecializer(FuncBuilder,
501501
Specialized,
502502
ReInfo.getClonerParamSubstitutionMap(),

lib/SILOptimizer/IPO/UsePrespecialized.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) {
9696
ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), AI,
9797
ReferencedF, Subs, IsNotSerialized,
9898
/*ConvertIndirectToDirect=*/ true,
99-
/*dropMetatypeArgs=*/ false);
99+
/*dropUnusedArguments=*/ false);
100100

101101
if (!ReInfo.canBeSpecialized())
102102
continue;

lib/SILOptimizer/Utils/Generics.cpp

+94-73
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,82 @@ static bool shouldNotSpecialize(SILFunction *Callee, SILFunction *Caller,
455455
return false;
456456
}
457457

458+
// Addressable parameters cannot be dropped because the address may
459+
// escape. They also can't be promoted to direct convention, so there
460+
// is no danger in preserving them.
461+
static bool canConvertArg(CanSILFunctionType substType, unsigned paramIdx,
462+
SILFunction *caller) {
463+
return !substType->isAddressable(paramIdx, caller);
464+
}
465+
466+
// If there is no read from an indirect argument, this argument has to be
467+
// dropped. At the call site the store to the argument's memory location could
468+
// have been removed (based on the callee's memory effects). Therefore,
469+
// converting such an unused indirect argument to a direct argument, would load
470+
// an uninitialized value at the call site. This would lead to verifier errors
471+
// and in worst case to a miscompile because IRGen can implicitly use dead
472+
// arguments, e.g. for getting the type of a class reference.
473+
static bool canDropUnusedArg(ApplySite apply, SILFunction *callee,
474+
CanSILFunctionType substType,
475+
unsigned paramIdx) {
476+
FullApplySite fas = apply.asFullApplySite();
477+
if (!fas) {
478+
return false;
479+
}
480+
Operand &op = fas.getOperandsWithoutIndirectResults()[paramIdx];
481+
return !callee->argumentMayRead(&op, op.get());
482+
}
483+
484+
static bool isUsedAsDynamicSelf(SILArgument *arg) {
485+
for (Operand *use : arg->getUses()) {
486+
if (use->isTypeDependent())
487+
return true;
488+
}
489+
return false;
490+
}
491+
492+
static bool canDropMetatypeArg(ApplySite apply, SILFunction *callee,
493+
unsigned paramIdx) {
494+
if (!callee->isDefinition())
495+
return false;
496+
497+
unsigned calleeArgIdx =
498+
apply.getSubstCalleeConv().getSILArgIndexOfFirstParam() + paramIdx;
499+
SILArgument *calleeArg = callee->getArguments()[calleeArgIdx];
500+
501+
if (isUsedAsDynamicSelf(calleeArg))
502+
return false;
503+
504+
if (calleeArg->getType().getASTType()->hasDynamicSelfType())
505+
return false;
506+
507+
// We don't drop metatype arguments of not applied arguments (in case of
508+
// `partial_apply`).
509+
unsigned firstAppliedArgIdx = apply.getCalleeArgIndexOfFirstAppliedArg();
510+
if (firstAppliedArgIdx > calleeArgIdx)
511+
return false;
512+
513+
auto mt = calleeArg->getType().castTo<MetatypeType>();
514+
if (mt->hasRepresentation()
515+
&& mt->getRepresentation() == MetatypeRepresentation::Thin) {
516+
return true;
517+
}
518+
// If the passed thick metatype value is not a `metatype` instruction
519+
// we don't know the real metatype at runtime. It's not necessarily the
520+
// same as the declared metatype. It could e.g. be an upcast of a class
521+
// metatype.
522+
SILValue callerArg = apply.getArguments()[calleeArgIdx - firstAppliedArgIdx];
523+
if (isa<MetatypeInst>(callerArg))
524+
return true;
525+
526+
// But: if the metatype is not used in the callee we don't have to care
527+
// what metatype value is passed. We can just remove it.
528+
if (onlyHaveDebugUses(calleeArg))
529+
return true;
530+
531+
return false;
532+
}
533+
458534
/// Prepares the ReabstractionInfo object for further processing and checks
459535
/// if the current function can be specialized at all.
460536
/// Returns false, if the current function cannot be specialized.
@@ -771,7 +847,7 @@ void ReabstractionInfo::createSubstitutedAndSpecializedTypes() {
771847
for (SILParameterInfo PI : SubstitutedType->getParameters()) {
772848
auto IdxToInsert = IdxForParam;
773849
++IdxForParam;
774-
unsigned argIdx = i++;
850+
unsigned paramIdx = i++;
775851

776852
SILFunctionConventions substConv(SubstitutedType, getModule());
777853
TypeCategory tc = getParamTypeCategory(PI, substConv, getResilienceExpansion());
@@ -782,22 +858,14 @@ void ReabstractionInfo::createSubstitutedAndSpecializedTypes() {
782858
case ParameterConvention::Indirect_In_CXX:
783859
case ParameterConvention::Indirect_In:
784860
case ParameterConvention::Indirect_In_Guaranteed: {
785-
if (Callee && Apply && dropUnusedArguments) {
786-
// If there is no read from an indirect argument, this argument has to
787-
// be dropped. At the call site the store to the argument's memory location
788-
// could have been removed (based on the callee's memory effects).
789-
// Therefore, converting such an unused indirect argument to a direct
790-
// argument, would load an uninitialized value at the call site.
791-
// This would lead to verifier errors and in worst case to a miscompile
792-
// because IRGen can implicitly use dead arguments, e.g. for getting the
793-
// type of a class reference.
794-
if (FullApplySite fas = Apply.asFullApplySite()) {
795-
Operand &op = fas.getOperandsWithoutIndirectResults()[argIdx];
796-
if (!Callee->argumentMayRead(&op, op.get())) {
797-
droppedArguments.set(IdxToInsert);
798-
break;
799-
}
800-
}
861+
if (Apply && !canConvertArg(SubstitutedType, paramIdx,
862+
Apply.getFunction())) {
863+
continue;
864+
}
865+
if (Callee && Apply && dropUnusedArguments
866+
&& canDropUnusedArg(Apply, Callee, SubstitutedType, paramIdx)) {
867+
droppedArguments.set(IdxToInsert);
868+
break;
801869
}
802870
Conversions.set(IdxToInsert);
803871
if (tc == LoadableAndTrivial)
@@ -822,8 +890,10 @@ void ReabstractionInfo::createSubstitutedAndSpecializedTypes() {
822890
case ParameterConvention::Direct_Unowned:
823891
case ParameterConvention::Direct_Guaranteed: {
824892
CanType ty = PI.getInterfaceType();
825-
if (dropUnusedArguments && isa<MetatypeType>(ty) && !ty->hasArchetype())
893+
if (dropUnusedArguments && isa<MetatypeType>(ty) && !ty->hasArchetype()
894+
&& Apply && Callee && canDropMetatypeArg(Apply, Callee, paramIdx)) {
826895
droppedArguments.set(IdxToInsert);
896+
}
827897
break;
828898
}
829899
}
@@ -2916,7 +2986,8 @@ static bool createPrespecialized(StringRef UnspecializedName,
29162986
ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), ApplySite(),
29172987
UnspecFunc, Apply.getSubstitutionMap(),
29182988
IsNotSerialized,
2919-
/*ConvertIndirectToDirect= */true, /*dropMetatypeArgs=*/ false);
2989+
/*ConvertIndirectToDirect= */true,
2990+
/*dropUnusedArguments=*/ false);
29202991

29212992
if (!ReInfo.canBeSpecialized())
29222993
return false;
@@ -3005,7 +3076,7 @@ static bool usePrespecialized(
30053076
funcBuilder.getModule().isWholeModule(), apply, refF,
30063077
apply.getSubstitutionMap(), IsNotSerialized,
30073078
/*ConvertIndirectToDirect=*/ true,
3008-
/*dropMetatypeArgs=*/ false);
3079+
/*dropUnusedArguments=*/ false);
30093080

30103081
for (auto *SA : refF->getSpecializeAttrs()) {
30113082
if (!SA->isExported())
@@ -3149,7 +3220,8 @@ static bool usePrespecialized(
31493220
funcBuilder.getModule().getSwiftModule(),
31503221
funcBuilder.getModule().isWholeModule(), apply, refF, newSubstMap,
31513222
apply.getFunction()->getSerializedKind(),
3152-
/*ConvertIndirectToDirect=*/ true, /*dropMetatypeArgs=*/ false, nullptr);
3223+
/*ConvertIndirectToDirect=*/ true,
3224+
/*dropUnusedArguments=*/ false, nullptr);
31533225

31543226
if (layoutReInfo.getSpecializedType() == reInfo.getSpecializedType()) {
31553227
layoutMatches.push_back(
@@ -3209,57 +3281,6 @@ static bool usePrespecialized(
32093281
return false;
32103282
}
32113283

3212-
static bool isUsedAsDynamicSelf(SILArgument *arg) {
3213-
for (Operand *use : arg->getUses()) {
3214-
if (use->isTypeDependent())
3215-
return true;
3216-
}
3217-
return false;
3218-
}
3219-
3220-
static bool canDropMetatypeArgs(ApplySite apply, SILFunction *callee) {
3221-
if (!callee->isDefinition())
3222-
return false;
3223-
3224-
auto calleeArgs = callee->getArguments();
3225-
unsigned firstAppliedArgIdx = apply.getCalleeArgIndexOfFirstAppliedArg();
3226-
for (unsigned calleeArgIdx = 0; calleeArgIdx < calleeArgs.size(); ++calleeArgIdx) {
3227-
SILArgument *calleeArg = calleeArgs[calleeArgIdx];
3228-
auto mt = calleeArg->getType().getAs<MetatypeType>();
3229-
if (!mt)
3230-
continue;
3231-
3232-
if (isUsedAsDynamicSelf(calleeArg))
3233-
return false;
3234-
3235-
if (calleeArg->getType().getASTType()->hasDynamicSelfType())
3236-
return false;
3237-
3238-
// We don't drop metatype arguments of not applied arguments (in case of `partial_apply`).
3239-
if (firstAppliedArgIdx > calleeArgIdx)
3240-
return false;
3241-
3242-
if (mt->hasRepresentation() && mt->getRepresentation() == MetatypeRepresentation::Thin)
3243-
continue;
3244-
3245-
// If the passed thick metatype value is not a `metatype` instruction
3246-
// we don't know the real metatype at runtime. It's not necessarily the
3247-
// same as the declared metatype. It could e.g. be an upcast of a class
3248-
// metatype.
3249-
SILValue callerArg = apply.getArguments()[calleeArgIdx - firstAppliedArgIdx];
3250-
if (isa<MetatypeInst>(callerArg))
3251-
continue;
3252-
3253-
// But: if the metatype is not used in the callee we don't have to care
3254-
// what metatype value is passed. We can just remove it.
3255-
if (callee->isDefinition() && onlyHaveDebugUses(calleeArg))
3256-
continue;
3257-
3258-
return false;
3259-
}
3260-
return true;
3261-
}
3262-
32633284
void swift::trySpecializeApplyOfGeneric(
32643285
SILOptFunctionBuilder &FuncBuilder,
32653286
ApplySite Apply, DeadInstructionSet &DeadApplies,
@@ -3312,7 +3333,7 @@ void swift::trySpecializeApplyOfGeneric(
33123333
FuncBuilder.getModule().isWholeModule(), Apply, RefF,
33133334
Apply.getSubstitutionMap(), serializedKind,
33143335
/*ConvertIndirectToDirect=*/ true,
3315-
/*dropMetatypeArgs=*/ canDropMetatypeArgs(Apply, RefF),
3336+
/*dropUnusedArguments=*/ true,
33163337
&ORE);
33173338
if (!ReInfo.canBeSpecialized())
33183339
return;

lib/SILOptimizer/Utils/OptimizerBridging.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ OptionalBridgedFunction BridgedPassContext::specializeFunction(BridgedFunction f
206206
ReabstractionInfo ReInfo(mod->getSwiftModule(), mod->isWholeModule(),
207207
ApplySite(), origFunc, subs, IsNotSerialized,
208208
/*ConvertIndirectToDirect=*/true,
209-
/*dropMetatypeArgs=*/false);
209+
/*dropUnusedArguments=*/false);
210210

211211
if (!ReInfo.canBeSpecialized()) {
212212
return {nullptr};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-frontend -emit-sil -parse-as-library -O -module-name=test \
2+
// RUN: -enable-experimental-feature LifetimeDependence \
3+
// RUN: -enable-experimental-feature AddressableTypes \
4+
// RUN: %s | %FileCheck %s
5+
6+
// REQUIRES: swift_feature_AddressableTypes
7+
// REQUIRES: swift_feature_LifetimeDependence
8+
9+
// Enable this test as soon as CollectionOfOne is marked @_addressableForDependencies.
10+
// REQUIRES: rdar145687827
11+
12+
// CHECK-LABEL: sil {{.*}}@$s4test0A10OneIntSpan1cs0D0VySiGs012CollectionOfB0VySiG_tF : $@convention(thin) (@in_guaranteed CollectionOfOne<Int>) -> @lifetime(borrow address_for_deps 0) @owned Span<Int> {
13+
// CHECK: bb0(%0 : $*CollectionOfOne<Int>):
14+
// CHECK: [[RP:%.*]] = address_to_pointer {{.*}}%0 to $Builtin.RawPointer
15+
// CHECK: [[UP:%.*]] = struct $UnsafeRawPointer ([[RP]])
16+
// CHECK: [[OP:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt, [[UP]]
17+
// CHECK: [[SPAN:%.*]] = struct $Span<Int> ([[OP]]
18+
// CHECK: return [[SPAN]]
19+
// CHECK-LABEL: } // end sil function '$s4test0A10OneIntSpan1cs0D0VySiGs012CollectionOfB0VySiG_tF'
20+
@available(SwiftStdlib 6.2, *)
21+
@lifetime(borrow c)
22+
public func testOneIntSpan(c: CollectionOfOne<Int>) -> Span<Int> {
23+
c.span
24+
}

0 commit comments

Comments
 (0)