diff --git a/lib/SILPasses/Devirtualizer.cpp b/lib/SILPasses/Devirtualizer.cpp index 5907e7dcacc..d13cb96c452 100644 --- a/lib/SILPasses/Devirtualizer.cpp +++ b/lib/SILPasses/Devirtualizer.cpp @@ -1216,10 +1216,11 @@ void SILDevirtualizer::optimizeApplyInst(ApplyInst *AI) { // %9 = apply %8(%6#1) : ... // WitnessMethodInst *AMI = dyn_cast(AI->getCallee()); - if (AMI && AMI->getConformance() ) { + if (AMI && AMI->getConformance()) { + ProtocolConformance *C = AMI->getConformance(); // Lookup the function reference in the witness tables. std::pair> Ret = - AI->getModule().lookUpWitnessTable(AMI->getConformance()); + AI->getModule().lookUpWitnessTable(C); if (!Ret.first) return; @@ -1229,19 +1230,37 @@ void SILDevirtualizer::optimizeApplyInst(ApplyInst *AI) { if (Entry.getKind() != SILWitnessTable::WitnessKind::Method) continue; - SILWitnessTable::MethodWitness MethodEntry = Entry.getMethodWitness(); // Check if this is the member we were looking for. + SILWitnessTable::MethodWitness MethodEntry = Entry.getMethodWitness(); if (MethodEntry.Requirement != AMI->getMember()) continue; + // Ok, we found the member we are looking for. Devirtualize away! SILBuilder Builder(AI); SILLocation Loc = AI->getLoc(); SILFunction *F = MethodEntry.Witness; FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); - // Collect the function arguments. + // Collect args from the apply inst. + ArrayRef ApplyArgs = AI->getArgumentOperands(); SmallVector Args; - for (auto &A : AI->getArgumentOperands()) + + // Begin by upcasting self to the appropriate type if we have an inherited + // conformance... + SILValue Self = ApplyArgs[0].get(); + if (C->getKind() == ProtocolConformanceKind::Inherited) { + CanType Ty = Ret.first->getConformance()->getType()->getCanonicalType(); + SILType SILTy = SILType::getPrimitiveType(Ty, + Self.getType().getCategory()); + assert(SILTy.isSuperclassOf(Self.getType()) && + "Should only create upcasts for sub class devirtualization."); + Self = Builder.createUpcast(Loc, Self, SILTy); + } + // and then adding in either the original or newly upcasted value. + Args.push_back(Self); + + // Then add the rest of the arguments. + for (auto &A : ApplyArgs.slice(1)) Args.push_back(A.get()); SmallVector NewSubstList(Ret.second.begin(), diff --git a/test/SILPasses/devirt_inherited_conformance.swift b/test/SILPasses/devirt_inherited_conformance.swift new file mode 100644 index 00000000000..3826df8b391 --- /dev/null +++ b/test/SILPasses/devirt_inherited_conformance.swift @@ -0,0 +1,37 @@ +// RUN: %swift -O3 %s -emit-sil -sil-inline-threshold 0 | FileCheck %s + +// CHECK-LABEL: sil shared @_TTSC28devirt_inherited_conformance2B2S0_S_1P___TF28devirt_inherited_conformance11doSomethingUS_1P__FT1tQ__T_ : $@thin (@in B2) -> () { +// CHECK: bb0([[INPUT_PTR:%[0-9]+]] : $*B2): +// CHECK-NEXT: function_ref protocol witness for devirt_inherited_conformance.P.doSomething (@inout devirt_inherited_conformance.P.Self)() -> () in conformance devirt_inherited_conformance.B : devirt_inherited_conformance.P +// CHECK-NEXT: [[FUNCTION_REF:%[0-9]+]] = function_ref @_TTWC28devirt_inherited_conformance1BS_1PFS1_11doSomethingUS1___fRQPS1_FT_T_ : $@cc(witness_method) @thin (@inout B) -> () +// CHECK-NEXT: [[CAST_PTR:%[0-9]+]] = upcast [[INPUT_PTR]] : $*B2 to $*B +// CHECK-NEXT: apply [[FUNCTION_REF]]([[CAST_PTR]]) : $@cc(witness_method) @thin (@inout B) -> () +// CHECK-NEXT: load +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple +// CHECK-NEXT: return + +@asmname("unknown1") +func unknown1() -> () + +protocol P { + func doSomething() +} + +class B : P { + func doSomething() { + unknown1() + } +} + +class B2 : B { + +} + +func doSomething(t : T) { + t.doSomething() +} + +var b2 = B2() + +doSomething(b2)