Skip to content

Simplify devirtualization and fix a bug #7779

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions include/swift/SILOptimizer/Utils/Devirtualize.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ DevirtualizationResult devirtualizeClassMethod(FullApplySite AI,
SILValue ClassInstance);
DevirtualizationResult tryDevirtualizeClassMethod(FullApplySite AI,
SILValue ClassInstance);
DevirtualizationResult tryDevirtualizeWitnessMethod(ApplySite AI);
/// Check if an upcast is legal.
bool isLegalUpcast(SILType FromTy, SILType ToTy);
DevirtualizationResult tryDevirtualizeWitnessMethod(ApplySite AI);
}

#endif
20 changes: 4 additions & 16 deletions include/swift/SILOptimizer/Utils/Local.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,10 @@ SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI);
/// - a type of the return value is a subclass of the expected return type.
/// - actual return type and expected return type differ in optionality.
/// - both types are tuple-types and some of the elements need to be casted.
///
/// If CheckOnly flag is set, then this function only checks if the
/// required casting is possible. If it is not possible, then None
/// is returned.
/// If CheckOnly is not set, then a casting code is generated and the final
/// casted value is returned.
Optional<SILValue> castValueToABICompatibleType(SILBuilder *B, SILLocation Loc,
SILValue Value,
SILType SrcTy,
SILType DestTy,
bool CheckOnly = false);

/// Check if the optimizer can cast a value into the expected,
/// ABI compatible type if necessary.
bool canCastValueToABICompatibleType(SILModule &M,
SILType SrcTy, SILType DestTy);
SILValue castValueToABICompatibleType(SILBuilder *B, SILLocation Loc,
SILValue Value,
SILType SrcTy,
SILType DestTy);

/// Returns a project_box if it is the next instruction after \p ABI and
/// and has \p ABI as operand. Otherwise it creates a new project_box right
Expand Down
44 changes: 3 additions & 41 deletions lib/SILOptimizer/Transforms/SimplifyCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1875,23 +1875,6 @@ static bool isTryApplyOfConvertFunction(TryApplyInst *TAI,
if (!TargetFnTy || !TargetFnTy->hasErrorResult())
return false;

// Check if the converted function type has the same number of arguments.
// Currently this is always the case, but who knows what convert_function can
// do in the future?
unsigned numParams = OrigFnTy->getParameters().size();
if (TargetFnTy->getParameters().size() != numParams)
return false;

// Check that the argument types are matching.
SILModuleConventions silConv(TAI->getModule());
for (unsigned Idx = 0; Idx < numParams; Idx++) {
if (!canCastValueToABICompatibleType(
TAI->getModule(),
silConv.getSILType(OrigFnTy->getParameters()[Idx]),
silConv.getSILType(TargetFnTy->getParameters()[Idx])))
return false;
}

// Look through the conversions and find the real callee.
Callee = getActualCallee(CFI->getConverted());
CalleeType = Callee->getType();
Expand Down Expand Up @@ -1936,12 +1919,6 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) {
auto ResultTy = calleeConv.getSILResultType();
auto OrigResultTy = TAI->getNormalBB()->getArgument(0)->getType();

// Bail if the cast between the actual and expected return types cannot
// be handled.
if (!canCastValueToABICompatibleType(TAI->getModule(),
ResultTy, OrigResultTy))
return false;

SILBuilderWithScope Builder(TAI);

auto TargetFnTy = CalleeFnTy;
Expand All @@ -1959,28 +1936,14 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) {
}
SILFunctionConventions origConv(OrigFnTy, TAI->getModule());

unsigned numArgs = TAI->getNumArguments();

// First check if it is possible to convert all arguments.
// Currently we believe that castValueToABICompatibleType can handle all
// cases, so this check should never fail. We just do it to be absolutely
// sure that we don't crash.
for (unsigned i = 0; i < numArgs; ++i) {
if (!canCastValueToABICompatibleType(TAI->getModule(),
origConv.getSILArgumentType(i),
targetConv.getSILArgumentType(i))) {
return false;
}
}

SmallVector<SILValue, 8> Args;
unsigned numArgs = TAI->getNumArguments();
for (unsigned i = 0; i < numArgs; ++i) {
auto Arg = TAI->getArgument(i);
// Cast argument if required.
Arg = castValueToABICompatibleType(&Builder, TAI->getLoc(), Arg,
origConv.getSILArgumentType(i),
targetConv.getSILArgumentType(i))
.getValue();
targetConv.getSILArgumentType(i));
Args.push_back(Arg);
}

Expand All @@ -1998,8 +1961,7 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) {
auto *NormalBB = TAI->getNormalBB();

auto CastedResult = castValueToABICompatibleType(&Builder, Loc, NewAI,
ResultTy, OrigResultTy)
.getValue();
ResultTy, OrigResultTy);

Builder.createBranch(Loc, NormalBB, { CastedResult });
TAI->eraseFromParent();
Expand Down
132 changes: 6 additions & 126 deletions lib/SILOptimizer/Utils/Devirtualize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,31 +546,6 @@ bool swift::canDevirtualizeClassMethod(FullApplySite AI,
return false;
}

// Type of the actual function to be called.
CanSILFunctionType GenCalleeType = F->getLoweredFunctionType();

// Type of the actual function to be called with substitutions applied.
CanSILFunctionType SubstCalleeType = GenCalleeType;

// For polymorphic functions, bail if the number of substitutions is
// not the same as the number of expected generic parameters.
if (GenCalleeType->isPolymorphic()) {
// First, find proper list of substitutions for the concrete
// method to be called.
SmallVector<Substitution, 4> Subs;
getSubstitutionsForCallee(Mod, GenCalleeType,
ClassOrMetatypeType.getSwiftRValueType(),
AI, Subs);
SubstCalleeType = GenCalleeType->substGenericArgs(Mod, Subs);
}

// Check if the optimizer knows how to cast the return type.
SILType ReturnType =
SILFunctionConventions(SubstCalleeType, Mod).getSILResultType();

if (!canCastValueToABICompatibleType(Mod, ReturnType, AI.getType()))
return false;

return true;
}

Expand Down Expand Up @@ -612,8 +587,7 @@ DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI,
for (auto ResultTy : substConv.getIndirectSILResultTypes()) {
NewArgs.push_back(
castValueToABICompatibleType(&B, AI.getLoc(), *IndirectResultArgIter,
IndirectResultArgIter->getType(), ResultTy)
.getValue());
IndirectResultArgIter->getType(), ResultTy));
++IndirectResultArgIter;
}

Expand All @@ -623,8 +597,7 @@ DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI,
auto paramType = substConv.getSILType(param);
NewArgs.push_back(
castValueToABICompatibleType(&B, AI.getLoc(), *ParamArgIter,
ParamArgIter->getType(), paramType)
.getValue());
ParamArgIter->getType(), paramType));
++ParamArgIter;
}

Expand All @@ -634,7 +607,7 @@ DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI,
NewArgs.push_back(castValueToABICompatibleType(&B, AI.getLoc(),
ClassOrMetatype,
ClassOrMetatypeType,
SelfParamTy).getValue());
SelfParamTy));

SILType ResultTy = substConv.getSILResultType();

Expand Down Expand Up @@ -707,7 +680,7 @@ DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI,

// Check if any casting is required for the return value.
ResultValue = castValueToABICompatibleType(&B, NewAI.getLoc(), ResultValue,
ResultTy, AI.getType()).getValue();
ResultTy, AI.getType());

DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n");
NumClassDevirt++;
Expand Down Expand Up @@ -871,75 +844,6 @@ static void getWitnessMethodSubstitutions(ApplySite AI, SILFunction *F,
origSubs, isDefaultWitness, NewSubs);
}

/// Check if an upcast is legal.
/// The logic in this function is heavily based on the checks in
/// the SILVerifier.
bool swift::isLegalUpcast(SILType FromTy, SILType ToTy) {
if (ToTy.is<MetatypeType>()) {
CanType InstTy(ToTy.castTo<MetatypeType>()->getInstanceType());
if (!FromTy.is<MetatypeType>())
return false;
CanType OpInstTy(FromTy.castTo<MetatypeType>()->getInstanceType());
auto InstClass = InstTy->getClassOrBoundGenericClass();
if (!InstClass)
return false;

bool CanBeUpcasted =
InstClass->usesObjCGenericsModel()
? InstClass->getDeclaredTypeInContext()->isBindableToSuperclassOf(
OpInstTy, nullptr)
: InstTy->isExactSuperclassOf(OpInstTy, nullptr);

return CanBeUpcasted;
}

// Upcast from Optional<B> to Optional<A> is legal as long as B is a
// subclass of A.
if (ToTy.getSwiftRValueType().getAnyOptionalObjectType() &&
FromTy.getSwiftRValueType().getAnyOptionalObjectType()) {
ToTy = SILType::getPrimitiveObjectType(
ToTy.getSwiftRValueType().getAnyOptionalObjectType());
FromTy = SILType::getPrimitiveObjectType(
FromTy.getSwiftRValueType().getAnyOptionalObjectType());
}

auto ToClass = ToTy.getClassOrBoundGenericClass();
if (!ToClass)
return false;
bool CanBeUpcasted =
ToClass->usesObjCGenericsModel()
? ToClass->getDeclaredTypeInContext()->isBindableToSuperclassOf(
FromTy.getSwiftRValueType(), nullptr)
: ToTy.isExactSuperclassOf(FromTy);

return CanBeUpcasted;
}

/// Check if we can pass/convert all arguments of the original apply
/// as required by the found devirtualized method.
/// FIXME: This method was introduced as a workaround. We need to
/// revisit it and check if it is still needed.
static bool
canPassOrConvertAllArguments(ApplySite AI,
CanSILFunctionType SubstCalleeCanType) {
SILFunctionConventions substConv(SubstCalleeCanType, AI.getModule());
unsigned substArgIdx = AI.getCalleeArgIndexOfFirstAppliedArg();
for (auto arg : AI.getArguments()) {
// Check if we can cast the provided argument into the required
// parameter type.
auto FromTy = arg->getType();
auto ToTy = substConv.getSILArgumentType(substArgIdx++);
// If types are the same, no conversion will be required.
if (FromTy == ToTy)
continue;
// Otherwise, it should be possible to upcast the arguments.
if (!isLegalUpcast(FromTy, ToTy))
return false;
}
assert(substArgIdx == substConv.getNumSILArguments());
return true;
}

/// Generate a new apply of a function_ref to replace an apply of a
/// witness_method when we've determined the actual function we'll end
/// up calling.
Expand All @@ -963,11 +867,6 @@ static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
auto CalleeCanType = F->getLoweredFunctionType();
auto SubstCalleeCanType = CalleeCanType->substGenericArgs(Module, NewSubs);

// Bail if some of the arguments cannot be converted into
// types required by the found devirtualized method.
if (!canPassOrConvertAllArguments(AI, SubstCalleeCanType))
return ApplySite();

// Collect arguments from the apply instruction.
auto Arguments = SmallVector<SILValue, 4>();

Expand All @@ -979,7 +878,8 @@ static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
for (auto arg : AI.getArguments()) {
auto paramType = substConv.getSILArgumentType(substArgIdx++);
if (arg->getType() != paramType)
arg = B.createUpcast(AI.getLoc(), arg, paramType);
arg = castValueToABICompatibleType(&B, AI.getLoc(), arg,
arg->getType(), paramType);
Arguments.push_back(arg);
}
assert(substArgIdx == substConv.getNumSILArguments());
Expand Down Expand Up @@ -1030,26 +930,6 @@ static bool canDevirtualizeWitnessMethod(ApplySite AI) {
return false;
}

// Collect all the required substitutions.
//
// The complete set of substitutions may be different, e.g. because the found
// witness thunk F may have been created by a specialization pass and have
// additional generic parameters.
SmallVector<Substitution, 4> NewSubs;

getWitnessMethodSubstitutions(AI, F, WMI->getConformance(), NewSubs);

// Figure out the exact bound type of the function to be called by
// applying all substitutions.
auto &Module = AI.getModule();
auto CalleeCanType = F->getLoweredFunctionType();
auto SubstCalleeCanType = CalleeCanType->substGenericArgs(Module, NewSubs);

// Bail if some of the arguments cannot be converted into
// types required by the found devirtualized method.
if (!canPassOrConvertAllArguments(AI, SubstCalleeCanType))
return false;

return true;
}

Expand Down
Loading