Upgrade to Pro — share decks privately, control downloads, hide ads and more …

コンパイラから紐解くSwift method dispatch

Yuta Saito
August 31, 2018

コンパイラから紐解くSwift method dispatch

Yuta Saito

August 31, 2018
Tweet

More Decks by Yuta Saito

Other Decks in Programming

Transcript

  1. class Cat { func bark() -> String { return "ʹΌΜ"

    } } class Dog { func bark() -> String { return "ΘΜ" } } let cat = Cat() let dog = unsafeBitCast(cat, to: Dog.self) print(dog.bark()) 2Կ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ ʹΌΜPSΘΜ
  2. class Cat { func bark() -> String { return "ʹΌΜ"

    } } class Dog { func bark() -> String { return "ΘΜ" } } let cat = Cat() let dog = unsafeBitCast(cat, to: Dog.self) print(dog.bark()) 2Կ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ "ΘΜ
  3. class Animal { func bark() { print("ΞχϚϧͰ͢") } } class

    Cat: Animal { override func bark() { print("ʹΌΜ") } } func callAnimalBark(_ animal: Animal) { animal.bark() }
  4. class Animal { func bark() { print("ΞχϚϧͰ͢") } } class

    Cat: Animal { override func bark() { print("ʹΌΜ") } } func callAnimalBark(_ animal: Animal) { animal.bark() // ΞχϚϧͰ͢ or ʹΌΜ }
  5. class Animal { func bark() { print("ΞχϚϧͰ͢") } } class

    Cat: Animal { override func bark() { print("ʹΌΜ") } } func callAnimalBark(_ animal: Animal) { animal.bark() // ΞχϚϧͰ͢ or ʹΌΜ } ࣮ߦ͞ΕΔϝιου͸Ͳͬͪʁʁ
  6. 4UBUJD%JTQBUDI struct S { func foo() {} } let s

    = S() s.foo() ಉ͡γάωνϟͷϝιου͕ଘࡏ͠ͳ͍ w ؔ਺ w ஋ܕͷϝιου w FYUFOTJPOʹॻ͍ͨϝιου w pOBMͳϝιου
  7. %ZOBNJD%JTQBUDI w $MBTTͷϝιου w 1SPUPDPMͷϝιου protocol Animal { func foo()

    } struct Cat: Animal { func foo() {} } func callFoo<T: Animal>(_ animal: T) { animal.foo() }
  8. w $MBTTͷϝιουΦʔόʔϥΠυ w 1SPUPDPMͷϝιουෳ਺ͷܕ͕४ڌ %ZOBNJD%JTQBUDI protocol Animal { func foo()

    } struct Cat: Animal { func foo() {} } func callFoo<T: Animal>(_ animal: T) { animal.foo() } ಉ͡γάωνϟͷϝιου͕ෳ਺ଘࡏ͢Δ
  9. class Animal { func bark() {} } class Cat: Animal

    { override func bark() {} func foo() {} } 75BCMFGPS$BU func Animal.bark() 75BCMFGPS"OJNBM *Animal.bark *Animal.bark *Cat.foo func Cat.bark() func Cat.foo()
  10. protocol Animal { func bark() } struct Dog: Animal {

    func bark() {} } 8JUOFTT5BCMFGPS"OJNBM JODPOGPSNBODF%PH *Animal.bark func Animal.bark(dog) { Dog.bark(dog) } 8JUOFTTNFUIPEGPS"OJNBM JODPOGPSNBODF%PH
  11. class Cat { func bark() -> String { return "ʹΌΜ"

    } } class Dog { func bark() -> String { return “ΘΜ" } } let cat = Cat() let dog = unsafeBitCast(cat, to: Dog.self) print(dog.bark()) ϝϞϦϨΠΞ΢τ͸มߋ͞Εͳ͍ ࢀর͢ΔVTable΋มΘΒͳ͍ Catͷςʔϒϧ͕ࢀর͞ΕΔ ʮʹΌΜʯ͕ग़ྗ͞ΕΔ͸ͣʂ
  12. class Cat { func bark() -> String { return "ʹΌΜ"

    } } class Dog { func bark() -> String { return "ΘΜ" } } let cat = Cat() let dog = unsafeBitCast(cat, to: Dog.self) print(dog.bark()) // ΘΜ ʮΘΜʯ
  13. USZ%FWJSUVBMJ[F"QQMZ auto &M = FAS.getModule(); auto Instance = stripUpCasts(CMI->getOperand()); auto

    ClassType = Instance->getType(); ... if (isEffectivelyFinalMethod(FAS, ClassType, CD, CHA)) return tryDevirtualizeClassMethod(FAS, Instance, ORE, true /*isEffectivelyFinalMethod*/); if (auto Instance = getInstanceWithExactDynamicType(CMI->getOperand(), CMI->getModule(), CHA)) return tryDevirtualizeClassMethod(FAS, Instance, ORE); if (auto ExactTy = getExactDynamicType(CMI->getOperand(), CMI->getModule(), CHA)) { if (ExactTy == CMI->getOperand()->getType()) return tryDevirtualizeClassMethod(FAS, CMI->getOperand(), ORE); } MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  14. USZ%FWJSUVBMJ[F"QQMZ auto &M = FAS.getModule(); auto Instance = stripUpCasts(CMI->getOperand()); auto

    ClassType = Instance->getType(); ... if (isEffectivelyFinalMethod(FAS, ClassType, CD, CHA)) return tryDevirtualizeClassMethod(FAS, Instance, ORE, true /*isEffectivelyFinalMethod*/); if (auto Instance = getInstanceWithExactDynamicType(CMI->getOperand(), CMI->getModule(), CHA)) return tryDevirtualizeClassMethod(FAS, Instance, ORE); if (auto ExactTy = getExactDynamicType(CMI->getOperand(), CMI->getModule(), CHA)) { if (ExactTy == CMI->getOperand()->getType()) return tryDevirtualizeClassMethod(FAS, CMI->getOperand(), ORE); } Πϯελϯεͷ࣮ߦ࣌ͷܕ͕੩తʹܾఆͰ͖Δύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  15. %FWJSUVBMJ[FGPS75BCMF class Animal { func bark() {} } class Cat:

    Animal { override func bark() {} } let animal: Animal = Cat() // ΠχγϟϥΠβ͔Β࡞ͬͯΔͷͰCatܕ animal.bark() Πϯελϯεͷ࣮ߦ࣌ͷܕ͕੩తʹܾఆͰ͖Δύλʔϯ
  16. USZ%FWJSUVBMJ[F"QQMZ auto &M = FAS.getModule(); auto Instance = stripUpCasts(CMI->getOperand()); auto

    ClassType = Instance->getType(); ... if (isEffectivelyFinalMethod(FAS, ClassType, CD, CHA)) return tryDevirtualizeClassMethod(FAS, Instance, ORE, true /*isEffectivelyFinalMethod*/); if (auto Instance = getInstanceWithExactDynamicType(CMI->getOperand(), CMI->getModule(), CHA)) return tryDevirtualizeClassMethod(FAS, Instance, ORE); if (auto ExactTy = getExactDynamicType(CMI->getOperand(), CMI->getModule(), CHA)) { if (ExactTy == CMI->getOperand()->getType()) return tryDevirtualizeClassMethod(FAS, CMI->getOperand(), ORE); } ϝιου͕z࣮࣭pOBMzͳύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  17. JT&⒎FDUJWFMZ'JOBM.FUIPE static bool isEffectivelyFinalMethod(FullApplySite AI, SILType ClassType, ClassDecl *CD, ClassHierarchyAnalysis

    *CHA) { if (CD && CD->isFinal()) return true; const DeclContext *DC = AI.getModule().getAssociatedContext(); auto *CMI = cast<MethodInst>(AI.getCallee()); if (!calleesAreStaticallyKnowable(AI.getModule(), CMI->getMember())) return false; auto *Method = CMI->getMember().getAbstractFunctionDecl(); if (!Method->isOverridden()) return true; ... return true; } ϝιου͕z࣮࣭pOBMzͳύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  18. JT&⒎FDUJWFMZ'JOBM.FUIPE static bool isEffectivelyFinalMethod(FullApplySite AI, SILType ClassType, ClassDecl *CD, ClassHierarchyAnalysis

    *CHA) { if (CD && CD->isFinal()) return true; const DeclContext *DC = AI.getModule().getAssociatedContext(); auto *CMI = cast<MethodInst>(AI.getCallee()); if (!calleesAreStaticallyKnowable(AI.getModule(), CMI->getMember())) return false; auto *Method = CMI->getMember().getAbstractFunctionDecl(); if (!Method->isOverridden()) return true; ... return true; } ϝιου͕z࣮࣭pOBMzͳύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  19. JT&⒎FDUJWFMZ'JOBM.FUIPE static bool isEffectivelyFinalMethod(FullApplySite AI, SILType ClassType, ClassDecl *CD, ClassHierarchyAnalysis

    *CHA) { if (CD && CD->isFinal()) return true; const DeclContext *DC = AI.getModule().getAssociatedContext(); auto *CMI = cast<MethodInst>(AI.getCallee()); if (!calleesAreStaticallyKnowable(AI.getModule(), CMI->getMember())) return false; auto *Method = CMI->getMember().getAbstractFunctionDecl(); if (!Method->isOverridden()) return true; ... return true; } ϝιου͕z࣮࣭pOBMzͳύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  20. DBMMFFT"SF4UBUJDBMMZ,OPXBCMF bool swift::calleesAreStaticallyKnowable(SILModule &M, SILDeclRef Decl) { ... switch (AFD->getEffectiveAccess())

    { case AccessLevel::Open: return false; case AccessLevel::Public: if (isa<ConstructorDecl>(AFD)) { auto *ND = AFD->getDeclContext()->getSelfNominalTypeDecl(); if (ND->getEffectiveAccess() == AccessLevel::Open) return false; } case AccessLevel::Internal: return M.isWholeModule(); case AccessLevel::FilePrivate: case AccessLevel::Private: return true; } } ϝιουΛ੩తʹܾఆͰ͖Δύλʔϯ MJC4*-0QUJNJ[FS6UJMT-PDBMDQQ
  21. JT&⒎FDUJWFMZ'JOBM.FUIPE static bool isEffectivelyFinalMethod(FullApplySite AI, SILType ClassType, ClassDecl *CD, ClassHierarchyAnalysis

    *CHA) { if (CD && CD->isFinal()) return true; const DeclContext *DC = AI.getModule().getAssociatedContext(); auto *CMI = cast<MethodInst>(AI.getCallee()); if (!calleesAreStaticallyKnowable(AI.getModule(), CMI->getMember())) return false; auto *Method = CMI->getMember().getAbstractFunctionDecl(); if (!Method->isOverridden()) return true; ... return true; } ϝιου͕z࣮࣭pOBMzͳύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  22. JT&⒎FDUJWFMZ'JOBM.FUIPE static bool isEffectivelyFinalMethod(FullApplySite AI, SILType ClassType, ClassDecl *CD, ClassHierarchyAnalysis

    *CHA) { ... if (!Method->isOverridden()) return true; ClassHierarchyAnalysis::ClassList Subs; getAllSubclasses(CHA, CD, ClassType, AI.getModule(), Subs); auto *ImplMethod = CD->findImplementingMethod(Method); for (auto S : Subs) { auto *ImplFD = S->findImplementingMethod(Method); if (ImplFD != ImplMethod) return false; } return true; } ϝιου͕z࣮࣭pOBMzͳύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  23. JT&⒎FDUJWFMZ'JOBM.FUIPE static bool isEffectivelyFinalMethod(FullApplySite AI, SILType ClassType, ClassDecl *CD, ClassHierarchyAnalysis

    *CHA) { ... if (!Method->isOverridden()) return true; ClassHierarchyAnalysis::ClassList Subs; getAllSubclasses(CHA, CD, ClassType, AI.getModule(), Subs); auto *ImplMethod = CD->findImplementingMethod(Method); for (auto S : Subs) { auto *ImplFD = S->findImplementingMethod(Method); if (ImplFD != ImplMethod) return false; } return true; } ϝιου͕z࣮࣭pOBMzͳύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  24. JT&⒎FDUJWFMZ'JOBM.FUIPE static bool isEffectivelyFinalMethod(FullApplySite AI, SILType ClassType, ClassDecl *CD, ClassHierarchyAnalysis

    *CHA) { ... if (!Method->isOverridden()) return true; ClassHierarchyAnalysis::ClassList Subs; getAllSubclasses(CHA, CD, ClassType, AI.getModule(), Subs); auto *ImplMethod = CD->findImplementingMethod(Method); for (auto S : Subs) { auto *ImplFD = S->findImplementingMethod(Method); if (ImplFD != ImplMethod) return false; } return true; } ϝιου͕z࣮࣭pOBMzͳύλʔϯ MJC4*-0QUJNJ[FS6UJMT%FWJSUVBMJ[FDQQ
  25. 0WFSSJEF͞ΕͭͭΠϯελϯε͕ ͦͷαϒΫϥεʹͳΓಘͳ͍৔߹ class Cat<Name> { func bark() {} } enum

    Tama {} enum Kuro {} class TamaCat: Cat<Tama> { override func bark() {} } func callBark(cat: Cat<Kuro>) { cat.bark() }
  26. class Cat<Name> { func bark() {} } enum Tama {}

    enum Kuro {} // Cat<Kuro>ͷαϒλΠϓͰ͸ͳ͍ class TamaCat: Cat<Tama> { override func bark() {} } func callBark(cat: Cat<Kuro>) { cat.bark() } 0WFSSJEF͞ΕͭͭΠϯελϯε͕ ͦͷαϒΫϥεʹͳΓಘͳ͍৔߹
  27. class Cat { func bark() -> String { return “ʹΌΜ"

    } } class Dog { func bark() -> String { return "ΘΜ" } } let cat = Cat() let dog = unsafeBitCast(cat, to: Dog.self) print(dog.bark()) // ΘΜ ࠷దԽ͋Γͷ৔߹ Dog.barkϝιου͸non-final ΦʔόʔϥΠυ͞Ε͍ͯͳ͍ͷͰ”࣮࣭final” dog.bark()͕Static Dispatchʹ࠷దԽ͞ΕΔ
  28. ࠷దԽͳ͠ͷ৔߹ class Cat { func bark() -> String { return

    “ʹΌΜ" } } class Dog { func bark() -> String { return "ΘΜ" } } let cat = Cat() let dog = unsafeBitCast(cat, to: Dog.self) print(dog.bark()) // ʹΌΜ Whole Module࠷దԽ͕Φϑ Ϟδϡʔϧ಺ͰΦʔόʔϥΠυ͞ΕΔՄೳੑ͕͋Δ ΠϯελϯεͷϝλσʔλͷVTable͔Β Dynamic Dispatch͢Δ