MandatoryPerformanceOptimizations: don't de-virtualize a generic class method call to specialized method

This results in wrong argument/return calling conventions.
First, the method call must be specialized. Only then the call can be de-virtualized.
Usually, it's done in this order anyway, because the `class_method` instruction is located before the `apply`.
But when inlining functions, the order (in the worklist) can be the other way round.

Fixes a compiler crash.
rdar://154631438
This commit is contained in:
Erik Eckstein
2025-07-01 09:44:47 +02:00
parent 7651db7902
commit 606f7693d4
4 changed files with 32 additions and 5 deletions

View File

@@ -76,7 +76,7 @@ bool canDevirtualizeClassMethod(FullApplySite AI, ClassDecl *CD,
CanType ClassType,
OptRemark::Emitter *ORE = nullptr,
bool isEffectivelyFinalMethod = false);
SILFunction *getTargetClassMethod(SILModule &M, ClassDecl *CD,
SILFunction *getTargetClassMethod(SILModule &M, FullApplySite as, ClassDecl *CD,
CanType ClassType, MethodInst *MI);
CanType getSelfInstanceType(CanType ClassOrMetatypeType);

View File

@@ -446,7 +446,7 @@ static bool tryToSpeculateTarget(SILPassManager *pm, FullApplySite AI, ClassHier
// Try to devirtualize the static class of instance
// if it is possible.
if (auto F = getTargetClassMethod(M, CD, ClassType, CMI)) {
if (auto F = getTargetClassMethod(M, AI, CD, ClassType, CMI)) {
// Do not devirtualize if a method in the base class is marked
// as non-optimizable. This way it is easy to disable the
// devirtualization of this method in the base class and

View File

@@ -712,7 +712,7 @@ void swift::deleteDevirtualizedApply(ApplySite old) {
recursivelyDeleteTriviallyDeadInstructions(oldApply, true);
}
SILFunction *swift::getTargetClassMethod(SILModule &module, ClassDecl *cd,
SILFunction *swift::getTargetClassMethod(SILModule &module, FullApplySite as, ClassDecl *cd,
CanType classType, MethodInst *mi) {
assert((isa<ClassMethodInst>(mi) || isa<SuperMethodInst>(mi)) &&
"Only class_method and super_method instructions are supported");
@@ -721,6 +721,11 @@ SILFunction *swift::getTargetClassMethod(SILModule &module, ClassDecl *cd,
SILType silType = SILType::getPrimitiveObjectType(classType);
if (auto *vtable = module.lookUpSpecializedVTable(silType)) {
// We cannot de-virtualize a generic method call to a specialized method.
// This would result in wrong argument/return calling conventions.
if (as.getSubstitutionMap().hasAnySubstitutableParams())
return nullptr;
return vtable->getEntry(module, member)->getImplementation();
}
@@ -757,7 +762,7 @@ bool swift::canDevirtualizeClassMethod(FullApplySite applySite, ClassDecl *cd,
auto *mi = cast<MethodInst>(applySite.getCallee());
// Find the implementation of the member which should be invoked.
auto *f = getTargetClassMethod(module, cd, classType, mi);
auto *f = getTargetClassMethod(module, applySite, cd, classType, mi);
// If we do not find any such function, we have no function to devirtualize
// to... so bail.
@@ -843,7 +848,7 @@ swift::devirtualizeClassMethod(SILPassManager *pm, FullApplySite applySite,
SILModule &module = applySite.getModule();
auto *mi = cast<MethodInst>(applySite.getCallee());
auto *f = getTargetClassMethod(module, cd, classType, mi);
auto *f = getTargetClassMethod(module, applySite, cd, classType, mi);
CanSILFunctionType genCalleeType = f->getLoweredFunctionTypeInContext(
TypeExpansionContext(*applySite.getFunction()));

View File

@@ -0,0 +1,22 @@
// RUN: %target-swift-frontend %s -parse-as-library -enable-experimental-feature Embedded -Xllvm -sil-disable-pass=mandatory-inlining -emit-ir -o /dev/null
// REQUIRES: swift_feature_Embedded
// Check that the compiler doesn't crash
public class Base<T> {
func foo(_ t: T) -> T {
return t
}
}
@_transparent
func callee(_ i: Int, _ c: Base<Int>) -> Int {
return c.foo(i)
}
public func testit(_ i : Int) -> Int {
return callee(i, Base<Int>())
}