Skip to content

Commit 935b5e7

Browse files
committed
Fix GenericSpecializer for addressable parameters.
Addressable parameters must remain indirect. Incidentally also fixes an obvious latent bug in which all specialization was disabled if any metatypes could not be specialized. Fixes rdar://145687827 (Crash of inline-stored Span properties with optimizations)
1 parent 501abb0 commit 935b5e7

File tree

2 files changed

+91
-72
lines changed

2 files changed

+91
-72
lines changed

include/swift/SILOptimizer/Utils/Generics.h

Lines changed: 2 additions & 1 deletion
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

lib/SILOptimizer/Utils/Generics.cpp

Lines changed: 89 additions & 71 deletions
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
}
@@ -3211,57 +3281,6 @@ static bool usePrespecialized(
32113281
return false;
32123282
}
32133283

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

0 commit comments

Comments
 (0)