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

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

Avatar for Yuta Saito Yuta Saito
August 31, 2018

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

Avatar for Yuta Saito

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͢Δ