From d9b95a026f115c2981a80dee547e8cd4d871d3d9 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 3 Nov 2019 11:56:11 -0800 Subject: [PATCH 001/381] Implement devirtualization calls to composition type protocols --- lib/AST/SubstitutionMap.cpp | 2 ++ .../SILCombiner/SILCombinerApplyVisitors.cpp | 16 ++++++++++++++++ .../SILCombiner/SILCombinerCastVisitors.cpp | 3 ++- .../SILCombiner/SILCombinerMiscVisitors.cpp | 5 ++++- lib/SILOptimizer/Utils/Existential.cpp | 9 +++++++++ lib/SILOptimizer/Utils/InstOptUtils.cpp | 6 ++++-- 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index 682ea0cc29e1d..52a9e5f472340 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -33,6 +33,8 @@ #include "swift/AST/Types.h" #include "llvm/Support/Debug.h" +#include + using namespace swift; SubstitutionMap::Storage::Storage( diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 42bd42c43c75d..68e4e9d248383 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -33,6 +33,8 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" +#include + using namespace swift; using namespace swift::PatternMatch; @@ -985,6 +987,7 @@ void SILCombiner::buildConcreteOpenedExistentialInfos( SILOpenedArchetypesTracker &OpenedArchetypesTracker) { for (unsigned ArgIdx = 0; ArgIdx < Apply.getNumArguments(); ArgIdx++) { auto ArgASTType = Apply.getArgument(ArgIdx)->getType().getASTType(); + ArgASTType->dump(); if (!ArgASTType->hasArchetype()) continue; @@ -1332,10 +1335,13 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( SILInstruction * SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply, WitnessMethodInst *WMI) { + WMI->getConformance().dump(); + // Check if it is legal to perform the propagation. if (WMI->getConformance().isConcrete()) return nullptr; + WMI->getLookupType()->dump(); // If the lookup type is not an opened existential type, // it cannot be made more concrete. if (!WMI->getLookupType()->isOpenedExistential()) @@ -1358,6 +1364,8 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply, // Bail, if no argument has a concrete existential to propagate. if (COEIs.empty()) return nullptr; + + std::cout << "here" << std::endl; auto SelfCOEIIt = COEIs.find(Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand())); @@ -1396,6 +1404,12 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply, replaceWitnessMethodInst(WMI, BuilderCtx, SelfCEI.ConcreteType, SelfConformance); } + + std::cout << "done" << std::endl; + + std::cout << std::endl << "FULL FN:" << std::endl; + Apply.getFunction()->dump(); + std::cout << std::endl; /// Create the new apply instruction using concrete types for arguments. return createApplyWithConcreteType(Apply, COEIs, BuilderCtx); @@ -1671,6 +1685,8 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) { // (apply (witness_method)) -> propagate information about // a concrete type from init_existential_addr or init_existential_ref. if (auto *WMI = dyn_cast(AI->getCallee())) { + AI->dump(); + WMI->dump(); if (propagateConcreteTypeOfInitExistential(AI, WMI)) { return nullptr; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index eee7bf3a22fe2..be18eb13a6f85 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -49,9 +49,10 @@ SILCombiner::visitRefToRawPointerInst(RefToRawPointerInst *RRPI) { // (ref_to_raw_pointer (open_existential_ref (init_existential_ref x))) -> // (ref_to_raw_pointer x) if (auto *OER = dyn_cast(RRPI->getOperand())) - if (auto *IER = dyn_cast(OER->getOperand())) + if (auto *IER = dyn_cast(OER->getOperand())) { return Builder.createRefToRawPointer(RRPI->getLoc(), IER->getOperand(), RRPI->getType()); + } return nullptr; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index aa41f2c8d55b2..fd464ea927e6c 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -1255,12 +1255,15 @@ SILInstruction *SILCombiner::visitStrongReleaseInst(StrongReleaseInst *SRI) { // Release of a classbound existential converted from a class is just a // release of the class, squish the conversion. - if (auto ier = dyn_cast(SRI->getOperand())) + if (auto ier = dyn_cast(SRI->getOperand())) { +// ier->dump(); +// ier->getOperand()->dump(); if (ier->hasOneUse()) { SRI->setOperand(ier->getOperand()); eraseInstFromFunction(*ier); return SRI; } + } return nullptr; } diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 012f8aa3f9e32..8c6004eb7501a 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -19,6 +19,8 @@ #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "llvm/ADT/SmallPtrSet.h" +#include + using namespace swift; /// Determine InitExistential from global_addr. @@ -203,6 +205,8 @@ static SILValue getAddressOfStackInit(SILValue allocStackAddr, OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { SILValue openedVal = use.get(); SILInstruction *user = use.getUser(); + openedVal->dump(); + user->dump(); if (auto *instance = dyn_cast(openedVal)) { // Handle: // %opened = open_existential_addr @@ -213,6 +217,11 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { getAddressOfStackInit(instance, user, isOpenedValueCopied)) { openedVal = stackInitVal; } + + auto firstUse = *instance->getUses().begin(); + if (auto *store = dyn_cast(firstUse->getUser())) { + openedVal = store->getSrc(); + } } if (auto *Open = dyn_cast(openedVal)) { OpenedArchetype = Open->getType().castTo(); diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index e952b85f57dbc..6877fc26a9308 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -472,8 +472,10 @@ SILValue swift::castValueToABICompatibleType(SILBuilder *builder, if (srcTy == destTy) return value; - assert(srcTy.isAddress() == destTy.isAddress() - && "Addresses aren't compatible with values"); +// assert(srcTy.isAddress() == destTy.isAddress() +// && "Addresses aren't compatible with values"); + + if (srcTy.isAddress() == destTy.isAddress()) return nullptr; if (srcTy.isAddress() && destTy.isAddress()) { // Cast between two addresses and that's it. From bfb3dd72d779a13afd49a28b365c66e1b90f49af Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 3 Nov 2019 12:50:26 -0800 Subject: [PATCH 002/381] Clean up implementation and remove debug --- lib/AST/SubstitutionMap.cpp | 2 -- .../SILCombiner/SILCombinerApplyVisitors.cpp | 17 ----------------- .../SILCombiner/SILCombinerCastVisitors.cpp | 3 +-- .../SILCombiner/SILCombinerMiscVisitors.cpp | 5 +---- lib/SILOptimizer/Utils/Existential.cpp | 8 ++++---- lib/SILOptimizer/Utils/InstOptUtils.cpp | 3 --- 6 files changed, 6 insertions(+), 32 deletions(-) diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index 52a9e5f472340..682ea0cc29e1d 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -33,8 +33,6 @@ #include "swift/AST/Types.h" #include "llvm/Support/Debug.h" -#include - using namespace swift; SubstitutionMap::Storage::Storage( diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 68e4e9d248383..bae584a21071c 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -33,8 +33,6 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" -#include - using namespace swift; using namespace swift::PatternMatch; @@ -987,7 +985,6 @@ void SILCombiner::buildConcreteOpenedExistentialInfos( SILOpenedArchetypesTracker &OpenedArchetypesTracker) { for (unsigned ArgIdx = 0; ArgIdx < Apply.getNumArguments(); ArgIdx++) { auto ArgASTType = Apply.getArgument(ArgIdx)->getType().getASTType(); - ArgASTType->dump(); if (!ArgASTType->hasArchetype()) continue; @@ -1335,13 +1332,9 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( SILInstruction * SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply, WitnessMethodInst *WMI) { - WMI->getConformance().dump(); - // Check if it is legal to perform the propagation. if (WMI->getConformance().isConcrete()) return nullptr; - - WMI->getLookupType()->dump(); // If the lookup type is not an opened existential type, // it cannot be made more concrete. if (!WMI->getLookupType()->isOpenedExistential()) @@ -1364,8 +1357,6 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply, // Bail, if no argument has a concrete existential to propagate. if (COEIs.empty()) return nullptr; - - std::cout << "here" << std::endl; auto SelfCOEIIt = COEIs.find(Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand())); @@ -1404,12 +1395,6 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply, replaceWitnessMethodInst(WMI, BuilderCtx, SelfCEI.ConcreteType, SelfConformance); } - - std::cout << "done" << std::endl; - - std::cout << std::endl << "FULL FN:" << std::endl; - Apply.getFunction()->dump(); - std::cout << std::endl; /// Create the new apply instruction using concrete types for arguments. return createApplyWithConcreteType(Apply, COEIs, BuilderCtx); @@ -1685,8 +1670,6 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) { // (apply (witness_method)) -> propagate information about // a concrete type from init_existential_addr or init_existential_ref. if (auto *WMI = dyn_cast(AI->getCallee())) { - AI->dump(); - WMI->dump(); if (propagateConcreteTypeOfInitExistential(AI, WMI)) { return nullptr; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index be18eb13a6f85..eee7bf3a22fe2 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -49,10 +49,9 @@ SILCombiner::visitRefToRawPointerInst(RefToRawPointerInst *RRPI) { // (ref_to_raw_pointer (open_existential_ref (init_existential_ref x))) -> // (ref_to_raw_pointer x) if (auto *OER = dyn_cast(RRPI->getOperand())) - if (auto *IER = dyn_cast(OER->getOperand())) { + if (auto *IER = dyn_cast(OER->getOperand())) return Builder.createRefToRawPointer(RRPI->getLoc(), IER->getOperand(), RRPI->getType()); - } return nullptr; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index fd464ea927e6c..aa41f2c8d55b2 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -1255,15 +1255,12 @@ SILInstruction *SILCombiner::visitStrongReleaseInst(StrongReleaseInst *SRI) { // Release of a classbound existential converted from a class is just a // release of the class, squish the conversion. - if (auto ier = dyn_cast(SRI->getOperand())) { -// ier->dump(); -// ier->getOperand()->dump(); + if (auto ier = dyn_cast(SRI->getOperand())) if (ier->hasOneUse()) { SRI->setOperand(ier->getOperand()); eraseInstFromFunction(*ier); return SRI; } - } return nullptr; } diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 8c6004eb7501a..77fd94461208c 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -19,8 +19,6 @@ #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "llvm/ADT/SmallPtrSet.h" -#include - using namespace swift; /// Determine InitExistential from global_addr. @@ -205,8 +203,6 @@ static SILValue getAddressOfStackInit(SILValue allocStackAddr, OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { SILValue openedVal = use.get(); SILInstruction *user = use.getUser(); - openedVal->dump(); - user->dump(); if (auto *instance = dyn_cast(openedVal)) { // Handle: // %opened = open_existential_addr @@ -218,6 +214,10 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { openedVal = stackInitVal; } + // Handle: + // %4 = open_existential_ref + // %5 = alloc_stack + // store %4 to %5 auto firstUse = *instance->getUses().begin(); if (auto *store = dyn_cast(firstUse->getUser())) { openedVal = store->getSrc(); diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 6877fc26a9308..a841fff5b5207 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -471,9 +471,6 @@ SILValue swift::castValueToABICompatibleType(SILBuilder *builder, // No cast is required if types are the same. if (srcTy == destTy) return value; - -// assert(srcTy.isAddress() == destTy.isAddress() -// && "Addresses aren't compatible with values"); if (srcTy.isAddress() == destTy.isAddress()) return nullptr; From 287bbd4df40cdf96549894b4c378a55ac9c5e26e Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 3 Nov 2019 13:01:52 -0800 Subject: [PATCH 003/381] Add a test --- .../devirtualize_protocol_composition.swift | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/SILOptimizer/devirtualize_protocol_composition.swift diff --git a/test/SILOptimizer/devirtualize_protocol_composition.swift b/test/SILOptimizer/devirtualize_protocol_composition.swift new file mode 100644 index 0000000000000..b4f939ec44264 --- /dev/null +++ b/test/SILOptimizer/devirtualize_protocol_composition.swift @@ -0,0 +1,31 @@ +// RUN: %target-swift-frontend -parse-as-library -O -wmo -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -parse-as-library -Osize -wmo -emit-sil %s | %FileCheck %s + +// This is an end-to-end test to ensure that the optimizer devertualizes +// calls to a protocol composition type. + +public class ClassA { } + +protocol ProtocolA { + func foo() -> Int +} + +func shouldOptimizeWitness(_ x: ClassA & ProtocolA) -> Int { + return x.foo() +} + +public class ClassB: ClassA { + func foo() -> Int { + return 10 + } +} +extension ClassB: ProtocolA { } + +//CHECK: witnessEntryPoint +//CHECK-NOT: init_existential_ref +//CHECK-NOT: open_existential_ref +//CHECK-NOT: witness_method +//CHECK: return +public func witnessEntryPoint(c: ClassB) -> Int { + return shouldOptimizeWitness(c) +} From f3ac3bd08b6bbece20fd20567963590ef63b5432 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 3 Nov 2019 13:03:05 -0800 Subject: [PATCH 004/381] Cleanup and fix spacing (again) --- lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp | 1 + lib/SILOptimizer/Utils/Existential.cpp | 2 +- lib/SILOptimizer/Utils/InstOptUtils.cpp | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index bae584a21071c..42bd42c43c75d 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -1335,6 +1335,7 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply, // Check if it is legal to perform the propagation. if (WMI->getConformance().isConcrete()) return nullptr; + // If the lookup type is not an opened existential type, // it cannot be made more concrete. if (!WMI->getLookupType()->isOpenedExistential()) diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 77fd94461208c..44ecbafbac618 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -213,7 +213,7 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { getAddressOfStackInit(instance, user, isOpenedValueCopied)) { openedVal = stackInitVal; } - + // Handle: // %4 = open_existential_ref // %5 = alloc_stack diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index a841fff5b5207..7803a27d60fd6 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -471,8 +471,9 @@ SILValue swift::castValueToABICompatibleType(SILBuilder *builder, // No cast is required if types are the same. if (srcTy == destTy) return value; - - if (srcTy.isAddress() == destTy.isAddress()) return nullptr; + + if (srcTy.isAddress() == destTy.isAddress()) + return nullptr; if (srcTy.isAddress() && destTy.isAddress()) { // Cast between two addresses and that's it. From f1c75f6d628fa47996cf1bafba7ed51162bd626f Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 4 Nov 2019 11:01:39 -0800 Subject: [PATCH 005/381] Remove assertion all together and use getAddressOfStackInit instead of dyn_cast to StoreInst --- lib/SILOptimizer/Utils/Existential.cpp | 5 +++-- lib/SILOptimizer/Utils/InstOptUtils.cpp | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 44ecbafbac618..10f03cbe368f1 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -219,8 +219,9 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { // %5 = alloc_stack // store %4 to %5 auto firstUse = *instance->getUses().begin(); - if (auto *store = dyn_cast(firstUse->getUser())) { - openedVal = store->getSrc(); + if (auto stackInitVal = + getAddressOfStackInit(instance, firstUse->getUser(), isOpenedValueCopied)) { + openedVal = stackInitVal; } } if (auto *Open = dyn_cast(openedVal)) { diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 7803a27d60fd6..ab7a1d013c025 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -472,9 +472,6 @@ SILValue swift::castValueToABICompatibleType(SILBuilder *builder, if (srcTy == destTy) return value; - if (srcTy.isAddress() == destTy.isAddress()) - return nullptr; - if (srcTy.isAddress() && destTy.isAddress()) { // Cast between two addresses and that's it. return builder->createUncheckedAddrCast(loc, value, destTy); From 4b36f3fd69d10fcc2e1ba405847f0da8747359c6 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 4 Nov 2019 12:27:35 -0800 Subject: [PATCH 006/381] Fix based on review and add more tests --- .../SILCombiner/SILCombinerApplyVisitors.cpp | 2 +- .../devirtualize_protocol_composition.swift | 113 +++++++++++++++++- 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 42bd42c43c75d..6d68082542653 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -907,7 +907,7 @@ SILCombiner::buildConcreteOpenedExistentialInfoFromSoleConformingType( (archetypeTy->getConformsTo().size() == 1)) { PD = archetypeTy->getConformsTo()[0]; } else if (ArgType.isExistentialType() && !ArgType.isAnyObject() && - !SwiftArgType->isAny()) { + !SwiftArgType->isAny() && SwiftArgType->getAnyNominal()) { PD = dyn_cast(SwiftArgType->getAnyNominal()); } } diff --git a/test/SILOptimizer/devirtualize_protocol_composition.swift b/test/SILOptimizer/devirtualize_protocol_composition.swift index b4f939ec44264..f4b444b758a6d 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition.swift +++ b/test/SILOptimizer/devirtualize_protocol_composition.swift @@ -10,8 +10,8 @@ protocol ProtocolA { func foo() -> Int } -func shouldOptimizeWitness(_ x: ClassA & ProtocolA) -> Int { - return x.foo() +protocol ProtocolB { + func bar() -> Int } public class ClassB: ClassA { @@ -19,13 +19,116 @@ public class ClassB: ClassA { return 10 } } + extension ClassB: ProtocolA { } -//CHECK: witnessEntryPoint +public class ClassC: ClassA { + func foo() -> Int { + return 10 + } +} + +extension ClassC: ProtocolA { } + +public class ClassD { } +public class ClassE : ClassD { + func foo() -> Int { + return 10 + } +} + +extension ClassE: ProtocolA { } + +public class ClassF { + func foo() -> Int { + return 10 + } + + func bar() -> Int { + return 10 + } +} + +extension ClassF: ProtocolA, ProtocolB { } + +public class ClassG { + func foo() -> Int { + return 10 + } + + func bar() -> Int { + return 10 + } +} + +extension ClassG: ProtocolA, ProtocolB { } + +public class ClassH { + typealias type = ClassD +} + +func shouldOptimize1(_ x: ClassA & ProtocolA) -> Int { + return x.foo() +} + +func shouldOptimize2(_ x: ClassD & ProtocolA) -> Int { + return x.foo() +} + +func shouldOptimize3(_ x: ProtocolA & ProtocolB) -> Int { + return x.foo() + x.bar() +} + +func shouldOptimize4(_ x: ClassH.type & ProtocolA) -> Int { + return x.foo() +} + +//CHECK: entryPoint1 +//CHECK-NOT: init_existential_ref +//CHECK-NOT: open_existential_ref +//CHECK-NOT: witness_method +//CHECK: return +public func entryPoint1(c: ClassB) -> Int { + return shouldOptimize1(c) +} + +// TODO: is this broken on master too? +//public func entryPoint2(c: ClassC) -> Int { +// return shouldOptimize1(c) +//} + +//CHECK: entryPoint3 +//CHECK-NOT: init_existential_ref +//CHECK-NOT: open_existential_ref +//CHECK-NOT: witness_method +//CHECK: return +public func entryPoint3(c: ClassE) -> Int { + return shouldOptimize2(c) +} + +//CHECK: entryPoint4 +//CHECK-NOT: init_existential_ref +//CHECK-NOT: open_existential_ref +//CHECK-NOT: witness_method +//CHECK: return +public func entryPoint4(c: ClassF) -> Int { + return shouldOptimize3(c) +} + +//CHECK: entryPoint5 +//CHECK-NOT: init_existential_ref +//CHECK-NOT: open_existential_ref +//CHECK-NOT: witness_method +//CHECK: return +public func entryPoint5(c: ClassG) -> Int { + return shouldOptimize3(c) +} + +//CHECK: entryPoint6 //CHECK-NOT: init_existential_ref //CHECK-NOT: open_existential_ref //CHECK-NOT: witness_method //CHECK: return -public func witnessEntryPoint(c: ClassB) -> Int { - return shouldOptimizeWitness(c) +public func entryPoint6(c: ClassE) -> Int { + return shouldOptimize4(c) } From f1c15d26f30ca5abfffe633daf0f97ab0bda2d8c Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 4 Nov 2019 13:06:38 -0800 Subject: [PATCH 007/381] Fix tests --- lib/SILOptimizer/Utils/Existential.cpp | 9 ++-- .../devirtualize_protocol_composition.swift | 46 +++++++++++++++---- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 10f03cbe368f1..75310c4fc4d74 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -219,10 +219,13 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { // %5 = alloc_stack // store %4 to %5 auto firstUse = *instance->getUses().begin(); - if (auto stackInitVal = - getAddressOfStackInit(instance, firstUse->getUser(), isOpenedValueCopied)) { - openedVal = stackInitVal; + if (auto *store = dyn_cast(firstUse->getUser())) { + openedVal = store->getSrc(); } +// if (auto stackInitVal = +// getAddressOfStackInit(instance, firstUse->getUser(), isOpenedValueCopied)) { +// openedVal = stackInitVal; +// } } if (auto *Open = dyn_cast(openedVal)) { OpenedArchetype = Open->getType().castTo(); diff --git a/test/SILOptimizer/devirtualize_protocol_composition.swift b/test/SILOptimizer/devirtualize_protocol_composition.swift index f4b444b758a6d..73c4bcc8be0f7 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition.swift +++ b/test/SILOptimizer/devirtualize_protocol_composition.swift @@ -14,6 +14,30 @@ protocol ProtocolB { func bar() -> Int } +protocol ProtocolC { + func foo() -> Int +} + +protocol ProtocolD { + func foo() -> Int +} + +protocol ProtocolE { + func foo() -> Int +} + +protocol ProtocolF { + func foo() -> Int +} + +protocol ProtocolG { + func bar() -> Int +} + +protocol ProtocolH { + func bar() -> Int +} + public class ClassB: ClassA { func foo() -> Int { return 10 @@ -28,7 +52,7 @@ public class ClassC: ClassA { } } -extension ClassC: ProtocolA { } +extension ClassC: ProtocolC { } public class ClassD { } public class ClassE : ClassD { @@ -37,7 +61,7 @@ public class ClassE : ClassD { } } -extension ClassE: ProtocolA { } +extension ClassE: ProtocolD { } public class ClassF { func foo() -> Int { @@ -49,7 +73,7 @@ public class ClassF { } } -extension ClassF: ProtocolA, ProtocolB { } +extension ClassF: ProtocolE, ProtocolG { } public class ClassG { func foo() -> Int { @@ -61,7 +85,7 @@ public class ClassG { } } -extension ClassG: ProtocolA, ProtocolB { } +extension ClassG: ProtocolF, ProtocolH { } public class ClassH { typealias type = ClassD @@ -71,15 +95,19 @@ func shouldOptimize1(_ x: ClassA & ProtocolA) -> Int { return x.foo() } -func shouldOptimize2(_ x: ClassD & ProtocolA) -> Int { +func shouldOptimize2(_ x: ClassD & ProtocolD) -> Int { return x.foo() } -func shouldOptimize3(_ x: ProtocolA & ProtocolB) -> Int { +func shouldOptimize3(_ x: ProtocolE & ProtocolG) -> Int { return x.foo() + x.bar() } -func shouldOptimize4(_ x: ClassH.type & ProtocolA) -> Int { +func shouldOptimize4(_ x: ProtocolF & ProtocolH) -> Int { + return x.foo() + x.bar() +} + +func shouldOptimize5(_ x: ClassH.type & ProtocolD) -> Int { return x.foo() } @@ -121,7 +149,7 @@ public func entryPoint4(c: ClassF) -> Int { //CHECK-NOT: witness_method //CHECK: return public func entryPoint5(c: ClassG) -> Int { - return shouldOptimize3(c) + return shouldOptimize4(c) } //CHECK: entryPoint6 @@ -130,5 +158,5 @@ public func entryPoint5(c: ClassG) -> Int { //CHECK-NOT: witness_method //CHECK: return public func entryPoint6(c: ClassE) -> Int { - return shouldOptimize4(c) + return shouldOptimize5(c) } From 6c48a8d35fd5a821408ce29475db38b1fe72267a Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 4 Nov 2019 20:01:27 -0800 Subject: [PATCH 008/381] Ensure that only correct patterns are optimized --- lib/SILOptimizer/Utils/Existential.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 75310c4fc4d74..e29966d5507db 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -218,14 +218,27 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { // %4 = open_existential_ref // %5 = alloc_stack // store %4 to %5 - auto firstUse = *instance->getUses().begin(); - if (auto *store = dyn_cast(firstUse->getUser())) { + // apply (%5) + // It's important that the only uses of %5 (instance) are in + // a store and an apply. + StoreInst *store; + ApplyInst *apply; + bool shouldOptimize = true; + + for (auto *use : instance->getUses()) { + if (auto *foundStore = dyn_cast(use->getUser())) { store = foundStore; } + else if (auto *foundApply = dyn_cast(use->getUser())) { apply = foundApply; } + else if (auto *dealloc = dyn_cast(use->getUser())) { + shouldOptimize = store && apply; + } else { + // TODO: this may be too harsh + shouldOptimize = false; + } + } + + if (shouldOptimize) { openedVal = store->getSrc(); } -// if (auto stackInitVal = -// getAddressOfStackInit(instance, firstUse->getUser(), isOpenedValueCopied)) { -// openedVal = stackInitVal; -// } } if (auto *Open = dyn_cast(openedVal)) { OpenedArchetype = Open->getType().castTo(); From 01a452f0cb72a42a84f2af25c92e9d4cc0b4130c Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 4 Nov 2019 20:14:33 -0800 Subject: [PATCH 009/381] Update comment --- test/SILOptimizer/devirtualize_protocol_composition.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SILOptimizer/devirtualize_protocol_composition.swift b/test/SILOptimizer/devirtualize_protocol_composition.swift index 73c4bcc8be0f7..14f9d51d458a6 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition.swift +++ b/test/SILOptimizer/devirtualize_protocol_composition.swift @@ -120,7 +120,7 @@ public func entryPoint1(c: ClassB) -> Int { return shouldOptimize1(c) } -// TODO: is this broken on master too? +// TODO: create SR -- this causes a crash on master too //public func entryPoint2(c: ClassC) -> Int { // return shouldOptimize1(c) //} From 17894bb6eb8f85161bd3d6369693b3718ca70035 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 4 Nov 2019 20:23:00 -0800 Subject: [PATCH 010/381] Remove redundant protocols --- .../devirtualize_protocol_composition.swift | 40 ++++--------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/test/SILOptimizer/devirtualize_protocol_composition.swift b/test/SILOptimizer/devirtualize_protocol_composition.swift index 14f9d51d458a6..75d6d7bf5dbb0 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition.swift +++ b/test/SILOptimizer/devirtualize_protocol_composition.swift @@ -14,30 +14,6 @@ protocol ProtocolB { func bar() -> Int } -protocol ProtocolC { - func foo() -> Int -} - -protocol ProtocolD { - func foo() -> Int -} - -protocol ProtocolE { - func foo() -> Int -} - -protocol ProtocolF { - func foo() -> Int -} - -protocol ProtocolG { - func bar() -> Int -} - -protocol ProtocolH { - func bar() -> Int -} - public class ClassB: ClassA { func foo() -> Int { return 10 @@ -52,7 +28,7 @@ public class ClassC: ClassA { } } -extension ClassC: ProtocolC { } +extension ClassC: ProtocolA { } public class ClassD { } public class ClassE : ClassD { @@ -61,7 +37,7 @@ public class ClassE : ClassD { } } -extension ClassE: ProtocolD { } +extension ClassE: ProtocolA { } public class ClassF { func foo() -> Int { @@ -73,7 +49,7 @@ public class ClassF { } } -extension ClassF: ProtocolE, ProtocolG { } +extension ClassF: ProtocolA, ProtocolB { } public class ClassG { func foo() -> Int { @@ -85,7 +61,7 @@ public class ClassG { } } -extension ClassG: ProtocolF, ProtocolH { } +extension ClassG: ProtocolA, ProtocolB { } public class ClassH { typealias type = ClassD @@ -95,19 +71,19 @@ func shouldOptimize1(_ x: ClassA & ProtocolA) -> Int { return x.foo() } -func shouldOptimize2(_ x: ClassD & ProtocolD) -> Int { +func shouldOptimize2(_ x: ClassD & ProtocolA) -> Int { return x.foo() } -func shouldOptimize3(_ x: ProtocolE & ProtocolG) -> Int { +func shouldOptimize3(_ x: ProtocolA & ProtocolB) -> Int { return x.foo() + x.bar() } -func shouldOptimize4(_ x: ProtocolF & ProtocolH) -> Int { +func shouldOptimize4(_ x: ProtocolA & ProtocolB) -> Int { return x.foo() + x.bar() } -func shouldOptimize5(_ x: ClassH.type & ProtocolD) -> Int { +func shouldOptimize5(_ x: ClassH.type & ProtocolA) -> Int { return x.foo() } From 2913dbc430579b9fba575204473f660d847b2604 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 1 Dec 2019 10:50:18 -0800 Subject: [PATCH 011/381] Check dominance order and add .sil test --- .../SILCombiner/SILCombinerApplyVisitors.cpp | 1 + lib/SILOptimizer/Utils/Existential.cpp | 23 +- ...alize_protocol_composition_two_applies.sil | 301 ++++++++++++++++++ 3 files changed, 317 insertions(+), 8 deletions(-) create mode 100644 test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 444e005e340a2..b36a4883d8616 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -1673,6 +1673,7 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) { // a concrete type from init_existential_addr or init_existential_ref. if (auto *WMI = dyn_cast(AI->getCallee())) { if (propagateConcreteTypeOfInitExistential(AI, WMI)) { + Builder.getFunction().dump(); return nullptr; } } diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 2a1dd48a110af..d270189f2da57 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -221,23 +221,30 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { // apply (%5) // It's important that the only uses of %5 (instance) are in // a store and an apply. - StoreInst *store; - ApplyInst *apply; + StoreInst *store = nullptr; + ApplyInst *apply = nullptr; + DeallocStackInst *dealloc = nullptr; bool shouldOptimize = true; for (auto *use : instance->getUses()) { - if (auto *foundStore = dyn_cast(use->getUser())) { store = foundStore; } - else if (auto *foundApply = dyn_cast(use->getUser())) { apply = foundApply; } - else if (auto *dealloc = dyn_cast(use->getUser())) { - shouldOptimize = store && apply; + if (auto *foundStore = dyn_cast(use->getUser())) { + store = foundStore; + } else if (auto *foundApply = dyn_cast(use->getUser())) { + apply = foundApply; + } else if (auto *foundDealloc = dyn_cast(use->getUser())) { + dealloc = foundDealloc; } else { // TODO: this may be too harsh shouldOptimize = false; } } - if (shouldOptimize) { - openedVal = store->getSrc(); + if (shouldOptimize && store && apply && dealloc) { + DominanceInfo domInfo(store->getFunction()); + bool correctOrder = domInfo.dominates(store, apply) && + domInfo.dominates(apply, dealloc); + if (correctOrder) + openedVal = store->getSrc(); } } if (auto *Open = dyn_cast(openedVal)) { diff --git a/test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil b/test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil new file mode 100644 index 0000000000000..f6a579c2c1c83 --- /dev/null +++ b/test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil @@ -0,0 +1,301 @@ +// RUN: %target-build-swift %s -O -wmo -emit-sil | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +public class A { + @objc deinit + init() +} + +public protocol P { + func foo() -> Int +} + +public class B : A, P { + public func foo() -> Int + @objc deinit + override init() +} + +public class C : A, P { + public func foo() -> Int + @objc deinit + override init() +} + +func imp(x: A & P, y: A & P) -> Int + +public func test(x: B, y: C) -> Int + +// main +sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): + %2 = integer_literal $Builtin.Int32, 0 // user: %3 + %3 = struct $Int32 (%2 : $Builtin.Int32) // user: %4 + return %3 : $Int32 // id: %4 +} // end sil function 'main' + +// A.deinit +sil @$s3run1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject { +// %0 // users: %2, %1 +bb0(%0 : $A): + debug_value %0 : $A, let, name "self", argno 1 // id: %1 + %2 = unchecked_ref_cast %0 : $A to $Builtin.NativeObject // user: %3 + return %2 : $Builtin.NativeObject // id: %3 +} // end sil function '$s3run1ACfd' + +// A.__deallocating_deinit +sil @$s3run1ACfD : $@convention(method) (@owned A) -> () { +// %0 // users: %3, %1 +bb0(%0 : $A): + debug_value %0 : $A, let, name "self", argno 1 // id: %1 + // function_ref A.deinit + %2 = function_ref @$s3run1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %3 + %3 = apply %2(%0) : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %4 + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $A // user: %5 + dealloc_ref %4 : $A // id: %5 + %6 = tuple () // user: %7 + return %6 : $() // id: %7 +} // end sil function '$s3run1ACfD' + +// A.__allocating_init() +sil hidden [exact_self_class] @$s3run1ACACycfC : $@convention(method) (@thick A.Type) -> @owned A { +bb0(%0 : $@thick A.Type): + %1 = alloc_ref $A // user: %3 + // function_ref A.init() + %2 = function_ref @$s3run1ACACycfc : $@convention(method) (@owned A) -> @owned A // user: %3 + %3 = apply %2(%1) : $@convention(method) (@owned A) -> @owned A // user: %4 + return %3 : $A // id: %4 +} // end sil function '$s3run1ACACycfC' + +// A.init() +sil hidden @$s3run1ACACycfc : $@convention(method) (@owned A) -> @owned A { +// %0 // users: %2, %1 +bb0(%0 : $A): + debug_value %0 : $A, let, name "self", argno 1 // id: %1 + return %0 : $A // id: %2 +} // end sil function '$s3run1ACACycfc' + +// B.foo() +sil [noinline] @foo : $@convention(method) (@guaranteed B) -> Int { +// %0 // user: %1 +bb0(%0 : $B): + debug_value %0 : $B, let, name "self", argno 1 // id: %1 + %2 = integer_literal $Builtin.Int64, 1 // user: %3 + %3 = struct $Int (%2 : $Builtin.Int64) // user: %4 + return %3 : $Int // id: %4 +} // end sil function '$s3run1BC3fooSiyF' + +// Int.init(_builtinIntegerLiteral:) +sil public_external [transparent] [serialized] @$sSi22_builtinIntegerLiteralSiBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int { +// %0 // user: %2 +bb0(%0 : $Builtin.IntLiteral, %1 : $@thin Int.Type): + %2 = builtin "s_to_s_checked_trunc_IntLiteral_Int64"(%0 : $Builtin.IntLiteral) : $(Builtin.Int64, Builtin.Int1) // user: %3 + %3 = tuple_extract %2 : $(Builtin.Int64, Builtin.Int1), 0 // user: %4 + %4 = struct $Int (%3 : $Builtin.Int64) // user: %5 + return %4 : $Int // id: %5 +} // end sil function '$sSi22_builtinIntegerLiteralSiBI_tcfC' + +// B.deinit +sil @$s3run1BCfd : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject { +// %0 // users: %2, %1 +bb0(%0 : $B): + debug_value %0 : $B, let, name "self", argno 1 // id: %1 + %2 = upcast %0 : $B to $A // user: %4 + // function_ref A.deinit + %3 = function_ref @$s3run1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %4 + %4 = apply %3(%2) : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // users: %5, %6 + %5 = unchecked_ref_cast %4 : $Builtin.NativeObject to $B + return %4 : $Builtin.NativeObject // id: %6 +} // end sil function '$s3run1BCfd' + +// B.__deallocating_deinit +sil @$s3run1BCfD : $@convention(method) (@owned B) -> () { +// %0 // users: %3, %1 +bb0(%0 : $B): + debug_value %0 : $B, let, name "self", argno 1 // id: %1 + // function_ref B.deinit + %2 = function_ref @$s3run1BCfd : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject // user: %3 + %3 = apply %2(%0) : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject // user: %4 + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $B // user: %5 + dealloc_ref %4 : $B // id: %5 + %6 = tuple () // user: %7 + return %6 : $() // id: %7 +} // end sil function '$s3run1BCfD' + +// B.__allocating_init() +sil hidden [exact_self_class] @$s3run1BCACycfC : $@convention(method) (@thick B.Type) -> @owned B { +bb0(%0 : $@thick B.Type): + %1 = alloc_ref $B // user: %3 + // function_ref B.init() + %2 = function_ref @$s3run1BCACycfc : $@convention(method) (@owned B) -> @owned B // user: %3 + %3 = apply %2(%1) : $@convention(method) (@owned B) -> @owned B // user: %4 + return %3 : $B // id: %4 +} // end sil function '$s3run1BCACycfC' + +// B.init() +sil hidden @$s3run1BCACycfc : $@convention(method) (@owned B) -> @owned B { +// %0 // user: %2 +bb0(%0 : $B): + %1 = alloc_stack $B, let, name "self" // users: %9, %3, %2, %10, %11 + store %0 to %1 : $*B // id: %2 + %3 = load %1 : $*B // user: %4 + %4 = upcast %3 : $B to $A // user: %6 + // function_ref A.init() + %5 = function_ref @$s3run1ACACycfc : $@convention(method) (@owned A) -> @owned A // user: %6 + %6 = apply %5(%4) : $@convention(method) (@owned A) -> @owned A // user: %7 + %7 = unchecked_ref_cast %6 : $A to $B // users: %12, %9, %8 + strong_retain %7 : $B // id: %8 + store %7 to %1 : $*B // id: %9 + destroy_addr %1 : $*B // id: %10 + dealloc_stack %1 : $*B // id: %11 + return %7 : $B // id: %12 +} // end sil function '$s3run1BCACycfc' + +// C.foo() +sil @bar : $@convention(method) (@guaranteed C) -> Int { +// %0 // user: %1 +bb0(%0 : $C): + debug_value %0 : $C, let, name "self", argno 1 // id: %1 + %2 = integer_literal $Builtin.Int64, 0 // user: %3 + %3 = struct $Int (%2 : $Builtin.Int64) // user: %4 + return %3 : $Int // id: %4 +} // end sil function 'bar' + +// C.deinit +sil @$s3run1CCfd : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject { +// %0 // users: %2, %1 +bb0(%0 : $C): + debug_value %0 : $C, let, name "self", argno 1 // id: %1 + %2 = upcast %0 : $C to $A // user: %4 + // function_ref A.deinit + %3 = function_ref @$s3run1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %4 + %4 = apply %3(%2) : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // users: %5, %6 + %5 = unchecked_ref_cast %4 : $Builtin.NativeObject to $C + return %4 : $Builtin.NativeObject // id: %6 +} // end sil function '$s3run1CCfd' + +// C.__deallocating_deinit +sil @$s3run1CCfD : $@convention(method) (@owned C) -> () { +// %0 // users: %3, %1 +bb0(%0 : $C): + debug_value %0 : $C, let, name "self", argno 1 // id: %1 + // function_ref C.deinit + %2 = function_ref @$s3run1CCfd : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject // user: %3 + %3 = apply %2(%0) : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject // user: %4 + %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $C // user: %5 + dealloc_ref %4 : $C // id: %5 + %6 = tuple () // user: %7 + return %6 : $() // id: %7 +} // end sil function '$s3run1CCfD' + +// C.__allocating_init() +sil hidden [exact_self_class] @$s3run1CCACycfC : $@convention(method) (@thick C.Type) -> @owned C { +bb0(%0 : $@thick C.Type): + %1 = alloc_ref $C // user: %3 + // function_ref C.init() + %2 = function_ref @$s3run1CCACycfc : $@convention(method) (@owned C) -> @owned C // user: %3 + %3 = apply %2(%1) : $@convention(method) (@owned C) -> @owned C // user: %4 + return %3 : $C // id: %4 +} // end sil function '$s3run1CCACycfC' + +// C.init() +sil hidden @$s3run1CCACycfc : $@convention(method) (@owned C) -> @owned C { +// %0 // user: %2 +bb0(%0 : $C): + %1 = alloc_stack $C, let, name "self" // users: %9, %3, %2, %10, %11 + store %0 to %1 : $*C // id: %2 + %3 = load %1 : $*C // user: %4 + %4 = upcast %3 : $C to $A // user: %6 + // function_ref A.init() + %5 = function_ref @$s3run1ACACycfc : $@convention(method) (@owned A) -> @owned A // user: %6 + %6 = apply %5(%4) : $@convention(method) (@owned A) -> @owned A // user: %7 + %7 = unchecked_ref_cast %6 : $A to $C // users: %12, %9, %8 + strong_retain %7 : $C // id: %8 + store %7 to %1 : $*C // id: %9 + destroy_addr %1 : $*C // id: %10 + dealloc_stack %1 : $*C // id: %11 + return %7 : $C // id: %12 +} // end sil function '$s3run1CCACycfc' + +// protocol witness for P.foo() in conformance B +sil shared [transparent] [serialized] [thunk] @$s3run1BCAA1PA2aDP3fooSiyFTW : $@convention(witness_method: P) (@in_guaranteed B) -> Int { +// %0 // user: %1 +bb0(%0 : $*B): + %1 = load %0 : $*B // users: %2, %3 + %2 = class_method %1 : $B, #B.foo!1 : (B) -> () -> Int, $@convention(method) (@guaranteed B) -> Int // user: %3 + %3 = apply %2(%1) : $@convention(method) (@guaranteed B) -> Int // user: %4 + return %3 : $Int // id: %4 +} // end sil function '$s3run1BCAA1PA2aDP3fooSiyFTW' + +// CHECK-LABEL: sil shared [noinline] @${{.*}}impl{{.*}} +// In the optimization pass we look for uses of alloc_stack (aka %5). +// This test makes sure that we look at the correct uses with the +// correct dominance order (store before apply before dealloc). +// This function will be passed arguments of type B and C for arguments +// %0 and %1 respectively. We want to make sure that we call B's foo method +// and not C's foo method. +sil hidden [noinline] @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int { +bb0(%0 : $A & P, %1 : $A & P): + %2 = open_existential_ref %0 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + %3 = unchecked_ref_cast %1 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + + %5 = alloc_stack $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + store %2 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + + // We want to make sure that we picked up on the FIRST store and not the second one. + // class C's foo method is named "bar" whereas class B's foo method is named "foo". + // We want to make sure that we call a function named "foo" not "bar". + // CHECK: [[FN:%[0-9]+]] = function_ref @${{.*}}foo{{.*}} : $@convention(thin) () -> Int + %7 = witness_method $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P, #P.foo!1 : (Self) -> () -> Int, %2 : $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int + // CHECK: apply [[FN]] + %8 = apply %7<@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P>(%5) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int + + store %3 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + dealloc_stack %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + return %8 : $Int +} // end sil function 'impl' + +// test(x:y:) +sil @test :$@convention(thin) (@guaranteed B, @guaranteed C) -> Int { +// %0 // users: %5, %4, %2 +// %1 // users: %7, %6, %3 +bb0(%0 : $B, %1 : $C): + debug_value %0 : $B, let, name "x", argno 1 // id: %2 + debug_value %1 : $C, let, name "y", argno 2 // id: %3 + strong_retain %0 : $B // id: %4 + %5 = init_existential_ref %0 : $B : $B, $A & P // users: %11, %9 + strong_retain %1 : $C // id: %6 + %7 = init_existential_ref %1 : $C : $C, $A & P // users: %10, %9 + // function_ref imp(x:y:) + %8 = function_ref @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int // user: %9 + %9 = apply %8(%5, %7) : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int // user: %12 + strong_release %7 : $A & P // id: %10 + strong_release %5 : $A & P // id: %11 + return %9 : $Int // id: %12 +} // end sil function 'test' + +sil_vtable [serialized] A { + #A.init!allocator.1: (A.Type) -> () -> A : @$s3run1ACACycfC // A.__allocating_init() + #A.deinit!deallocator.1: @$s3run1ACfD // A.__deallocating_deinit +} + +sil_vtable [serialized] B { + #A.init!allocator.1: (A.Type) -> () -> A : @$s3run1BCACycfC [override] // B.__allocating_init() + #B.foo!1: (B) -> () -> Int : @foo // B.foo() + #B.deinit!deallocator.1: @$s3run1BCfD // B.__deallocating_deinit +} + +sil_vtable [serialized] C { + #A.init!allocator.1: (A.Type) -> () -> A : @$s3run1CCACycfC [override] // C.__allocating_init() + #C.foo!1: (C) -> () -> Int : @bar // C.foo() + #C.deinit!deallocator.1: @$s3run1CCfD // C.__deallocating_deinit +} + +sil_witness_table [serialized] B: P module run { + method #P.foo!1: (Self) -> () -> Int : @$s3run1BCAA1PA2aDP3fooSiyFTW // protocol witness for P.foo() in conformance B +} From 331a6af2a422e376a733f3800e06b9f355dfeeec Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 1 Dec 2019 10:51:55 -0800 Subject: [PATCH 012/381] Remove .dump in unrelated file --- lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index b36a4883d8616..444e005e340a2 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -1673,7 +1673,6 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) { // a concrete type from init_existential_addr or init_existential_ref. if (auto *WMI = dyn_cast(AI->getCallee())) { if (propagateConcreteTypeOfInitExistential(AI, WMI)) { - Builder.getFunction().dump(); return nullptr; } } From d032de63a7db6d558d85914b5e6a2b2fd0d290d6 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 1 Dec 2019 10:55:59 -0800 Subject: [PATCH 013/381] Move relevant part of sil test to the top of the file --- ...alize_protocol_composition_two_applies.sil | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil b/test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil index f6a579c2c1c83..e3639ed2f1224 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil +++ b/test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil @@ -31,6 +31,34 @@ func imp(x: A & P, y: A & P) -> Int public func test(x: B, y: C) -> Int +// CHECK-LABEL: sil shared [noinline] @${{.*}}impl{{.*}} +// In the optimization pass we look for uses of alloc_stack (aka %5). +// This test makes sure that we look at the correct uses with the +// correct dominance order (store before apply before dealloc). +// This function will be passed arguments of type B and C for arguments +// %0 and %1 respectively. We want to make sure that we call B's foo method +// and not C's foo method. +sil hidden [noinline] @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int { +bb0(%0 : $A & P, %1 : $A & P): + %2 = open_existential_ref %0 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + %3 = unchecked_ref_cast %1 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + + %5 = alloc_stack $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + store %2 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + + // We want to make sure that we picked up on the FIRST store and not the second one. + // class C's foo method is named "bar" whereas class B's foo method is named "foo". + // We want to make sure that we call a function named "foo" not "bar". + // CHECK: [[FN:%[0-9]+]] = function_ref @${{.*}}foo{{.*}} : $@convention(thin) () -> Int + %7 = witness_method $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P, #P.foo!1 : (Self) -> () -> Int, %2 : $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int + // CHECK: apply [[FN]] + %8 = apply %7<@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P>(%5) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int + + store %3 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + dealloc_stack %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + return %8 : $Int +} // end sil function 'impl' + // main sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): @@ -232,34 +260,6 @@ bb0(%0 : $*B): return %3 : $Int // id: %4 } // end sil function '$s3run1BCAA1PA2aDP3fooSiyFTW' -// CHECK-LABEL: sil shared [noinline] @${{.*}}impl{{.*}} -// In the optimization pass we look for uses of alloc_stack (aka %5). -// This test makes sure that we look at the correct uses with the -// correct dominance order (store before apply before dealloc). -// This function will be passed arguments of type B and C for arguments -// %0 and %1 respectively. We want to make sure that we call B's foo method -// and not C's foo method. -sil hidden [noinline] @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int { -bb0(%0 : $A & P, %1 : $A & P): - %2 = open_existential_ref %0 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - %3 = unchecked_ref_cast %1 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - - %5 = alloc_stack $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - store %2 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - - // We want to make sure that we picked up on the FIRST store and not the second one. - // class C's foo method is named "bar" whereas class B's foo method is named "foo". - // We want to make sure that we call a function named "foo" not "bar". - // CHECK: [[FN:%[0-9]+]] = function_ref @${{.*}}foo{{.*}} : $@convention(thin) () -> Int - %7 = witness_method $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P, #P.foo!1 : (Self) -> () -> Int, %2 : $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int - // CHECK: apply [[FN]] - %8 = apply %7<@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P>(%5) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int - - store %3 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - dealloc_stack %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - return %8 : $Int -} // end sil function 'impl' - // test(x:y:) sil @test :$@convention(thin) (@guaranteed B, @guaranteed C) -> Int { // %0 // users: %5, %4, %2 From 644689dc12082942800327074c76d97851b07af8 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 1 Dec 2019 13:57:12 -0800 Subject: [PATCH 014/381] Rename test --- ...plies.sil => devirtualize_protocol_composition_two_stores.sil} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/SILOptimizer/{devirtualize_protocol_composition_two_applies.sil => devirtualize_protocol_composition_two_stores.sil} (100%) diff --git a/test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil similarity index 100% rename from test/SILOptimizer/devirtualize_protocol_composition_two_applies.sil rename to test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil From e1ceb4f437471ca836f89dde72b3329b468d5f70 Mon Sep 17 00:00:00 2001 From: Jonathan Keller <19418817+NobodyNada@users.noreply.github.com> Date: Sun, 15 Dec 2019 13:57:42 -0800 Subject: [PATCH 015/381] [SILOptimizer] Generalize optimization of static keypaths We have an optimization in SILCombiner that "inlines" the use of compile-time constant key paths by performing the property access directly instead of calling a runtime function (leading to huge performance gains e.g. for heavy use of @dynamicMemberLookup). However, this optimization previously only supported key paths which solely access stored properties, so computed properties, optional chaining, etc. still had to call a runtime function. This commit generalizes the optimization to support all types of key paths. --- .../SILOptimizer/Utils/KeyPathProjector.h | 81 ++ .../SILCombiner/SILCombinerApplyVisitors.cpp | 149 +--- lib/SILOptimizer/Utils/CMakeLists.txt | 1 + lib/SILOptimizer/Utils/KeyPathProjector.cpp | 723 ++++++++++++++++++ test/SILOptimizer/optimize_keypath.swift | 191 +++++ 5 files changed, 1033 insertions(+), 112 deletions(-) create mode 100644 include/swift/SILOptimizer/Utils/KeyPathProjector.h create mode 100644 lib/SILOptimizer/Utils/KeyPathProjector.cpp diff --git a/include/swift/SILOptimizer/Utils/KeyPathProjector.h b/include/swift/SILOptimizer/Utils/KeyPathProjector.h new file mode 100644 index 0000000000000..eb9d5a6607805 --- /dev/null +++ b/include/swift/SILOptimizer/Utils/KeyPathProjector.h @@ -0,0 +1,81 @@ +//===-- KeyPathProjector.h - Project a static key path ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Utility class to project a statically known key path +/// expression to a direct property access sequence. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_UTILS_KEYPATHPROJECTOR_H +#define SWIFT_SILOPTIMIZER_UTILS_KEYPATHPROJECTOR_H + +#include "swift/SIL/SILBuilder.h" +#include + +namespace swift { + +/// Projects a statically known key path expression to +/// a direct property access. +class KeyPathProjector { +public: + /// The type of a key path access. + enum class AccessType { + /// A get-only access (i.e. swift_getAtKeyPath). + Get, + + /// A set-only access (i.e. swift_setAtWritableKeyPath). + Set, + + /// A modification (i.e. swift_modifyAtWritableKeyPath). + Modify + }; + + /// Creates a key path projector for a key path. + /// + /// Returns nullptr if \p keyPath is not a keypath instruction or if there is + /// any other reason why the optimization cannot be done. + /// + /// \param keyPath The key path to project. Must be the result of either + /// a keypath instruction or an upcast of a key path instruction. + /// \param root The address of the object the key path is applied to. + /// \param loc The location of the key path application. + /// \param builder The SILBuilder to use. + static std::unique_ptr + create(SILValue keyPath, SILValue root, SILLocation loc, SILBuilder &builder); + + /// Projects the key path to an address. Sets up the projection, + /// invokes the callback, then tears down the projection. + /// \param accessType The access type of the projected address. + /// \param callback A callback to invoke with the projected adddress. + /// The projected address is only valid from within \p callback. + virtual void project(AccessType accessType, + std::function callback) = 0; + + virtual ~KeyPathProjector() {}; + + /// Whether this projection returns a struct. + virtual bool isStruct() = 0; +protected: + KeyPathProjector(SILLocation loc, SILBuilder &builder) + : loc(loc), builder(builder) {} + + /// The location of the key path application. + SILLocation loc; + + /// The SILBuilder to use. + SILBuilder &builder; +}; + +} // end namespace swift + +#endif /* KeyPathProjector_h */ diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 90607e637f7d9..104ca13d7a0ce 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -28,11 +28,13 @@ #include "swift/SILOptimizer/Analysis/ValueTracking.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/Existential.h" +#include "swift/SILOptimizer/Utils/KeyPathProjector.h" #include "swift/SILOptimizer/Utils/ValueLifetime.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" +#include using namespace swift; using namespace swift::PatternMatch; @@ -199,92 +201,6 @@ SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI, return NAI; } -/// Ends the begin_access "scope" if a begin_access was inserted for optimizing -/// a keypath pattern. -static void insertEndAccess(BeginAccessInst *&beginAccess, bool isModify, - SILBuilder &builder) { - if (beginAccess) { - builder.createEndAccess(beginAccess->getLoc(), beginAccess, - /*aborted*/ false); - if (isModify) - beginAccess->setAccessKind(SILAccessKind::Modify); - beginAccess = nullptr; - } -} - -/// Creates the projection pattern for a keypath instruction. -/// -/// Currently only the StoredProperty pattern is handled. -/// TODO: handle other patterns, like getters/setters, optional chaining, etc. -/// -/// Returns false if \p keyPath is not a keypath instruction or if there is any -/// other reason why the optimization cannot be done. -static SILValue createKeypathProjections(SILValue keyPath, SILValue root, - SILLocation loc, - BeginAccessInst *&beginAccess, - SILBuilder &builder) { - if (auto *upCast = dyn_cast(keyPath)) - keyPath = upCast->getOperand(); - - // Is it a keypath instruction at all? - auto *kpInst = dyn_cast(keyPath); - if (!kpInst || !kpInst->hasPattern()) - return SILValue(); - - auto components = kpInst->getPattern()->getComponents(); - - // Check if the keypath only contains patterns which we support. - for (const KeyPathPatternComponent &comp : components) { - if (comp.getKind() != KeyPathPatternComponent::Kind::StoredProperty) - return SILValue(); - } - - SILValue addr = root; - for (const KeyPathPatternComponent &comp : components) { - assert(comp.getKind() == KeyPathPatternComponent::Kind::StoredProperty); - VarDecl *storedProperty = comp.getStoredPropertyDecl(); - SILValue elementAddr; - if (addr->getType().getStructOrBoundGenericStruct()) { - addr = builder.createStructElementAddr(loc, addr, storedProperty); - } else if (addr->getType().getClassOrBoundGenericClass()) { - SingleValueInstruction *Ref = builder.createLoad(loc, addr, - LoadOwnershipQualifier::Unqualified); - insertEndAccess(beginAccess, /*isModify*/ false, builder); - - // Handle the case where the storedProperty is in a super class. - while (Ref->getType().getClassOrBoundGenericClass() != - storedProperty->getDeclContext()) { - SILType superCl = Ref->getType().getSuperclass(); - if (!superCl) { - // This should never happen, because the property should be in the - // decl or in a superclass of it. Just handle this to be on the safe - // side. - return SILValue(); - } - Ref = builder.createUpcast(loc, Ref, superCl); - } - - addr = builder.createRefElementAddr(loc, Ref, storedProperty); - - // Class members need access enforcement. - if (builder.getModule().getOptions().EnforceExclusivityDynamic) { - beginAccess = builder.createBeginAccess(loc, addr, SILAccessKind::Read, - SILAccessEnforcement::Dynamic, - /*noNestedConflict*/ false, - /*fromBuiltin*/ false); - addr = beginAccess; - } - } else { - // This should never happen, as a stored-property pattern can only be - // applied to classes and structs. But to be safe - and future prove - - // let's handle this case and bail. - insertEndAccess(beginAccess, /*isModify*/ false, builder); - return SILValue(); - } - } - return addr; -} - /// Try to optimize a keypath application with an apply instruction. /// /// Replaces (simplified SIL): @@ -317,22 +233,26 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) { } else { return false; } - - BeginAccessInst *beginAccess = nullptr; - SILValue projectedAddr = createKeypathProjections(keyPath, rootAddr, - AI->getLoc(), beginAccess, - Builder); - if (!projectedAddr) + + auto projector = KeyPathProjector::create(keyPath, rootAddr, + AI->getLoc(), Builder); + if (!projector) return false; - - if (isModify) { - Builder.createCopyAddr(AI->getLoc(), valueAddr, projectedAddr, - IsTake, IsNotInitialization); - } else { - Builder.createCopyAddr(AI->getLoc(), projectedAddr, valueAddr, - IsNotTake, IsInitialization); - } - insertEndAccess(beginAccess, isModify, Builder); + + KeyPathProjector::AccessType accessType; + if (isModify) accessType = KeyPathProjector::AccessType::Set; + else accessType = KeyPathProjector::AccessType::Get; + + projector->project(accessType, [&](SILValue projectedAddr) { + if (isModify) { + Builder.createCopyAddr(AI->getLoc(), valueAddr, projectedAddr, + IsTake, IsNotInitialization); + } else { + Builder.createCopyAddr(AI->getLoc(), projectedAddr, valueAddr, + IsNotTake, IsInitialization); + } + }); + eraseInstFromFunction(*AI); ++NumOptimizedKeypaths; return true; @@ -377,19 +297,24 @@ bool SILCombiner::tryOptimizeInoutKeypath(BeginApplyInst *AI) { EndApplyInst *endApply = dyn_cast(AIUse->getUser()); if (!endApply) return false; - - BeginAccessInst *beginAccess = nullptr; - SILValue projectedAddr = createKeypathProjections(keyPath, rootAddr, - AI->getLoc(), beginAccess, - Builder); - if (!projectedAddr) + + auto projector = KeyPathProjector::create(keyPath, rootAddr, + AI->getLoc(), Builder); + if (!projector) return false; + + KeyPathProjector::AccessType accessType; + if (isModify) accessType = KeyPathProjector::AccessType::Modify; + else accessType = KeyPathProjector::AccessType::Get; + + projector->project(accessType, [&](SILValue projectedAddr) { + // Replace the projected address. + valueAddr->replaceAllUsesWith(projectedAddr); + + // Skip to the end of the key path application before cleaning up. + Builder.setInsertionPoint(endApply); + }); - // Replace the projected address. - valueAddr->replaceAllUsesWith(projectedAddr); - - Builder.setInsertionPoint(endApply); - insertEndAccess(beginAccess, isModify, Builder); eraseInstFromFunction(*endApply); eraseInstFromFunction(*AI); ++NumOptimizedKeypaths; diff --git a/lib/SILOptimizer/Utils/CMakeLists.txt b/lib/SILOptimizer/Utils/CMakeLists.txt index e8d871174e021..72587675a5500 100644 --- a/lib/SILOptimizer/Utils/CMakeLists.txt +++ b/lib/SILOptimizer/Utils/CMakeLists.txt @@ -11,6 +11,7 @@ silopt_register_sources( GenericCloner.cpp Generics.cpp InstOptUtils.cpp + KeyPathProjector.cpp LoadStoreOptUtils.cpp LoopUtils.cpp OptimizerStatsUtils.cpp diff --git a/lib/SILOptimizer/Utils/KeyPathProjector.cpp b/lib/SILOptimizer/Utils/KeyPathProjector.cpp new file mode 100644 index 0000000000000..e81a61650f4ad --- /dev/null +++ b/lib/SILOptimizer/Utils/KeyPathProjector.cpp @@ -0,0 +1,723 @@ +//===-- KeyPathProjector.cpp - Project a static key path --------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Utility class to project a statically known key path +/// expression to a direct property access sequence. +/// +//===----------------------------------------------------------------------===// + + +#include "swift/SILOptimizer/Utils/KeyPathProjector.h" + +#include "swift/SIL/SILInstruction.h" + +using namespace swift; + + +// Projectors to handle individual key path components. + +/// Projects the root of a key path application. +class RootProjector : public KeyPathProjector { +public: + RootProjector(SILValue root, SILLocation loc, SILBuilder &builder) + : KeyPathProjector(loc, builder), root(root) {} + + void project(AccessType accessType, + std::function callback) override { + callback(root); + } + + bool isStruct() override { + return root->getType().getStructOrBoundGenericStruct() != nullptr; + } +private: + SILValue root; +}; + +/// Projects a single key path component. +class ComponentProjector : public KeyPathProjector { +protected: + ComponentProjector(const KeyPathPatternComponent &component, + std::unique_ptr parent, + SILLocation loc, SILBuilder &builder) + : KeyPathProjector(loc, builder), + component(component), parent(std::move(parent)) {} + + /// The key path component. + const KeyPathPatternComponent &component; + + /// The projector for the previous components. + std::unique_ptr parent; + + bool isStruct() override { + auto type = component.getComponentType(); + return type.getStructOrBoundGenericStruct() != nullptr; + } + + ~ComponentProjector() override {}; +}; + + +/// Ends the begin_access "scope" if a begin_access was inserted for optimizing +/// a keypath pattern. +static void insertEndAccess(BeginAccessInst *&beginAccess, + SILBuilder &builder) { + if (beginAccess) { + builder.createEndAccess(beginAccess->getLoc(), beginAccess, + /*aborted*/ false); + beginAccess = nullptr; + } +} + +class StoredPropertyProjector : public ComponentProjector { +public: + StoredPropertyProjector(const KeyPathPatternComponent &component, + std::unique_ptr parent, + BeginAccessInst *&beginAccess, + SILLocation loc, SILBuilder &builder) + : ComponentProjector(component, std::move(parent), loc, builder), + beginAccess(beginAccess) {} + + void project(AccessType accessType, + std::function callback) override { + assert(component.getKind() == + KeyPathPatternComponent::Kind::StoredProperty); + + VarDecl *storedProperty = component.getStoredPropertyDecl(); + + if (parent->isStruct()) { + // Reading a struct field -> reading the struct + // Writing or modifying a struct field -> modifying the struct + AccessType parentAccessType; + if (accessType == AccessType::Get) + parentAccessType = AccessType::Get; + else + parentAccessType = AccessType::Modify; + + parent->project(parentAccessType, [&](SILValue parent) { + callback(builder.createStructElementAddr(loc, parent, storedProperty)); + }); + } else { + // Accessing a class member -> reading the class + parent->project(AccessType::Get, [&](SILValue parent) { + SingleValueInstruction *Ref = builder.createLoad(loc, parent, + LoadOwnershipQualifier::Unqualified); + + // If we were previously accessing a class member, we're done now. + insertEndAccess(beginAccess, builder); + + // Handle the case where the storedProperty is in a super class. + while (Ref->getType().getClassOrBoundGenericClass() != + storedProperty->getDeclContext()) { + SILType superCl = Ref->getType().getSuperclass(); + if (!superCl) { + // This should never happen, because the property should be in the + // decl or in a superclass of it. Just handle this to be on the safe + // side. + callback(SILValue()); + return; + } + Ref = builder.createUpcast(loc, Ref, superCl); + } + + SILValue addr = builder.createRefElementAddr(loc, Ref, storedProperty); + + // Class members need access enforcement. + if (builder.getModule().getOptions().EnforceExclusivityDynamic) { + beginAccess = builder.createBeginAccess(loc, addr, SILAccessKind::Read, + SILAccessEnforcement::Dynamic, + /*noNestedConflict*/ false, + /*fromBuiltin*/ false); + if (accessType != AccessType::Get) + beginAccess->setAccessKind(SILAccessKind::Modify); + addr = beginAccess; + } + + callback(addr); + + // if a child hasn't started a new access (i.e. beginAccess is unchanged), + // end the access now + if (beginAccess == addr) { + insertEndAccess(beginAccess, builder); + beginAccess = nullptr; + } + }); + } + } +private: + BeginAccessInst *&beginAccess; +}; + +class TupleElementProjector : public ComponentProjector { +public: + TupleElementProjector(const KeyPathPatternComponent &component, + std::unique_ptr parent, + SILLocation loc, SILBuilder &builder) + : ComponentProjector(component, std::move(parent), loc, builder) {} + + void project(AccessType accessType, + std::function callback) override { + assert(component.getKind() == + KeyPathPatternComponent::Kind::TupleElement); + + // Reading a tuple field -> reading the tuple + // Writing or modifying a tuple field -> modifying the tuple + AccessType parentAccessType; + if (accessType == AccessType::Get) + parentAccessType = AccessType::Get; + else + parentAccessType = AccessType::Modify; + + parent->project(parentAccessType, [&](SILValue parent) { + callback(builder.createTupleElementAddr(loc, parent, + component.getTupleIndex())); + }); + } +}; + +class GettablePropertyProjector : public ComponentProjector { +public: + GettablePropertyProjector(KeyPathInst *keyPath, + const KeyPathPatternComponent &component, + std::unique_ptr parent, + SubstitutionMap subs, BeginAccessInst *&beginAccess, + SILLocation loc, SILBuilder &builder) + : ComponentProjector(component, std::move(parent), loc, builder), + keyPath(keyPath), subs(subs), beginAccess(beginAccess) {} + + void project(AccessType accessType, + std::function callback) override { + assert(component.getKind() == + KeyPathPatternComponent::Kind::GettableProperty || + component.getKind() == + KeyPathPatternComponent::Kind::SettableProperty); + assert(accessType == AccessType::Get && "property is not settable"); + + parent->project(accessType, [&](SILValue parent) { + auto getter = component.getComputedPropertyGetter(); + + // The callback expects a memory address it can read from, + // so allocate a buffer. + auto &function = builder.getFunction(); + SILType type = function.getLoweredType(component.getComponentType()); + auto addr = builder.createAllocStack(loc, type); + + // Get subscript indices. + auto context = createContextContainer(); + SILValue contextPtr = SILValue(); + if (context) { + auto rawPtrType = builder.getASTContext().TheRawPointerType; + auto rawPtrLoweredType = function.getLoweredLoadableType(rawPtrType); + auto rawPtr = builder.createAddressToPointer(loc, context, rawPtrLoweredType); + + auto ptrDecl = builder.getASTContext().getUnsafeRawPointerDecl(); + SILType ptrType = function.getLoweredType(ptrDecl->getDeclaredType()); + contextPtr = builder.createStruct(loc, ptrType, {rawPtr}); + } + + auto ref = builder.createFunctionRef(loc, getter); + if (contextPtr) + builder.createApply(loc, ref, subs, {addr, parent, contextPtr}); + else + builder.createApply(loc, ref, subs, {addr, parent}); + + // If we were previously accessing a class member, we're done now. + insertEndAccess(beginAccess, builder); + beginAccess = nullptr; + + callback(addr); + + if (context) { + builder.createDestroyAddr(loc, context); + builder.createDeallocStack(loc, context); + } + + builder.createDestroyAddr(loc, addr); + builder.createDeallocStack(loc, addr); + }); + + } +protected: + KeyPathInst *keyPath; + SubstitutionMap subs; + BeginAccessInst *&beginAccess; + + // Creates the subscript index context for a computed property. + // Returns either null or a stack address that must be deallocated. + SILValue createContextContainer() { + auto indices = component.getSubscriptIndices(); + if (indices.empty()) return SILValue(); + + if (indices.size() == 1) { + auto index = indices.front(); + auto addr = builder.createAllocStack(loc, index.LoweredType); + auto value = keyPath->getAllOperands()[index.Operand].get(); + if (value->getType().isObject()) + builder.createStore(loc, value, addr, + StoreOwnershipQualifier::Unqualified); + else + builder.createCopyAddr(loc, value, addr, + IsNotTake, IsInitialization); + return addr; + } else { + // Create a tuple containing the context. + std::vector elementTypes; + elementTypes.reserve(indices.size()); + for (auto index : indices) { + elementTypes.push_back(TupleTypeElt(index.FormalType)); + } + auto tupleType = TupleType::get(ArrayRef(elementTypes), + builder.getASTContext()); + + auto &function = builder.getFunction(); + SILType lowered = function.getLoweredType(tupleType); + + auto tupleAddr = builder.createAllocStack(loc, lowered); + + // Copy the context elements into the tuple. + for (size_t i = 0; i < indices.size(); i++) { + auto index = indices[i]; + auto elementAddr = builder.createTupleElementAddr(loc, tupleAddr, i); + auto value = keyPath->getAllOperands()[index.Operand].get(); + if (value->getType().isObject()) + builder.createStore(loc, value, elementAddr, + StoreOwnershipQualifier::Unqualified); + else + builder.createCopyAddr(loc, value, elementAddr, + IsNotTake, IsInitialization); + } + return tupleAddr; + } + } +}; + +class SettablePropertyProjector : public GettablePropertyProjector { +public: + SettablePropertyProjector(KeyPathInst *keyPath, + const KeyPathPatternComponent &component, + std::unique_ptr parent, + SubstitutionMap subs, BeginAccessInst *&beginAccess, + SILLocation loc, SILBuilder &builder) + : GettablePropertyProjector(keyPath, component, std::move(parent), + subs, beginAccess, loc, builder) {} + + + void project(AccessType accessType, + std::function callback) override { + assert(component.getKind() == + KeyPathPatternComponent::Kind::GettableProperty || + component.getKind() == + KeyPathPatternComponent::Kind::SettableProperty); + + switch (accessType) { + case AccessType::Get: + GettablePropertyProjector::project(accessType, callback); + break; + + case AccessType::Modify: + case AccessType::Set: + AccessType parentAccessType; + if (component.isComputedSettablePropertyMutating()) { + // A mutating setter modifies the parent + parentAccessType = AccessType::Modify; + if (beginAccess) beginAccess->setAccessKind(SILAccessKind::Modify); + } else parentAccessType = AccessType::Get; + + parent->project(parentAccessType, [&](SILValue parent) { + auto getter = component.getComputedPropertyGetter(); + auto setter = component.getComputedPropertySetter(); + + // The callback expects a memory address it can write to, + // so allocate a writeback buffer. + auto &function = builder.getFunction(); + SILType type = function.getLoweredType(component.getComponentType()); + auto addr = builder.createAllocStack(loc, type); + + // Get subscript indices. + auto context = createContextContainer(); + SILValue contextPtr = SILValue(); + if (context) { + auto rawPtrType = builder.getASTContext().TheRawPointerType; + auto rawPtrLoweredType = function.getLoweredLoadableType(rawPtrType); + auto rawPtr = builder.createAddressToPointer(loc, context, rawPtrLoweredType); + + auto ptrDecl = builder.getASTContext().getUnsafeRawPointerDecl(); + SILType ptrType = function.getLoweredType(ptrDecl->getDeclaredType()); + contextPtr = builder.createStruct(loc, ptrType, {rawPtr}); + } + + // If this is a modify, we need to call the getter and + // store the result in the writeback buffer. + if (accessType == AccessType::Modify) { + auto getterRef = builder.createFunctionRef(loc, getter); + if (contextPtr) + builder.createApply(loc, getterRef, subs, {addr, parent, contextPtr}); + else + builder.createApply(loc, getterRef, subs, {addr, parent}); + } + + // The callback function will write into the writeback buffer. + callback(addr); + + // Pass the value from the writeback buffer to the setter. + auto setterRef = builder.createFunctionRef(loc, setter); + if (contextPtr) + builder.createApply(loc, setterRef, subs, {addr, parent, contextPtr}); + else + builder.createApply(loc, setterRef, subs, {addr, parent}); + + if (context) { + builder.createDestroyAddr(loc, context); + builder.createDeallocStack(loc, context); + } + + // Deallocate the writeback buffer. + builder.createDestroyAddr(loc, addr); + builder.createDeallocStack(loc, addr); + }); + break; + } + } +}; + + +class OptionalWrapProjector : public ComponentProjector { +public: + OptionalWrapProjector(const KeyPathPatternComponent &component, + std::unique_ptr parent, + SILLocation loc, SILBuilder &builder) + : ComponentProjector(component, std::move(parent), loc, builder) {} + + void project(AccessType accessType, + std::function callback) override { + assert(component.getKind() == + KeyPathPatternComponent::Kind::OptionalWrap); + assert(accessType == AccessType::Get && "optional wrap components are immutable"); + + parent->project(AccessType::Get, [&](SILValue parent) { + auto &function = builder.getFunction(); + SILType optType = function.getLoweredType(component.getComponentType()); + SILType objType = optType.getOptionalObjectType().getAddressType(); + + assert(objType && "optional wrap must return an optional"); + + // Allocate a buffer for the result. + auto optAddr = builder.createAllocStack(loc, optType); + + // Store the parent result in the enum payload address. + auto someDecl = builder.getASTContext().getOptionalSomeDecl(); + auto objAddr = builder.createInitEnumDataAddr(loc, optAddr, + someDecl, objType); + builder.createCopyAddr(loc, parent, objAddr, IsNotTake, IsInitialization); + + // Initialize the Optional enum. + builder.createInjectEnumAddr(loc, optAddr, someDecl); + + callback(optAddr); + + // Destroy the Optional. + builder.createDestroyAddr(loc, optAddr); + builder.createDeallocStack(loc, optAddr); + }); + } +}; + +class OptionalForceProjector : public ComponentProjector { +public: + OptionalForceProjector(const KeyPathPatternComponent &component, + std::unique_ptr parent, + SILLocation loc, SILBuilder &builder) + : ComponentProjector(component, std::move(parent), loc, builder) {} + + void project(AccessType accessType, + std::function callback) override { + assert(component.getKind() == + KeyPathPatternComponent::Kind::OptionalForce); + + parent->project(accessType, [&](SILValue optAddr) { + auto &ctx = builder.getASTContext(); + + auto noneDecl = ctx.getOptionalNoneDecl(); + auto someDecl = ctx.getOptionalSomeDecl(); + + SILType optType = optAddr->getType(); + SILType objType = optType.getOptionalObjectType(); + + if (accessType != AccessType::Set) { + // We're getting (or modifying), so we need to unwrap the optional. + auto int1Type = SILType::getBuiltinIntegerType(1, ctx); + auto falseLiteral = builder.createIntegerLiteral(loc, int1Type, false); + auto trueLiteral = builder.createIntegerLiteral(loc, int1Type, true); + + auto isNil = builder.createSelectEnumAddr(loc, optAddr, int1Type, SILValue(), { + {noneDecl, trueLiteral}, {someDecl, falseLiteral} + }); + builder.createCondFail(loc, isNil, "unexpectedly found nil while " + "unwrapping an Optional key-path expression"); + } + + switch (accessType) { + case AccessType::Get: { + // We have to copy the optional, since unwrapping is destructive. + auto tempAddr = builder.createAllocStack(loc, optType); + builder.createCopyAddr(loc, optAddr, tempAddr, IsNotTake, IsInitialization); + + // Unwrap the optional. + auto objAddr = builder.createUncheckedTakeEnumDataAddr(loc, tempAddr, someDecl, objType); + + callback(objAddr); + + builder.createDestroyAddr(loc, objAddr); + builder.createDeallocStack(loc, tempAddr); + break; + } + case AccessType::Set: { + // We can destroy the old value and write the new value in its place. + builder.createDestroyAddr(loc, optAddr); + auto objAddr = builder.createInitEnumDataAddr(loc, optAddr, someDecl, objType); + + callback(objAddr); + + // Finish creating the enum. + builder.createInjectEnumAddr(loc, optAddr, someDecl); + break; + } + case AccessType::Modify: { + // We have to copy the old value out, perform the modification, + // and copy the new value back in. + auto objAddr = builder.createAllocStack(loc, objType); + + // Unwrap the optional and copy it to the new buffer. + auto unwrappedAddr = builder.createUncheckedTakeEnumDataAddr(loc, optAddr, someDecl, objType); + builder.createCopyAddr(loc, unwrappedAddr, objAddr, IsTake, IsInitialization); + + callback(objAddr); + + auto initAddr = builder.createInitEnumDataAddr(loc, optAddr, someDecl, objType); + builder.createCopyAddr(loc, objAddr, initAddr, IsTake, IsInitialization); + builder.createDeallocStack(loc, objAddr); + builder.createInjectEnumAddr(loc, optAddr, someDecl); + break; + } + } + }); + } +}; + +class OptionalChainProjector : public ComponentProjector { +public: + OptionalChainProjector(const KeyPathPatternComponent &component, + std::unique_ptr parent, + SILValue optionalChainResult, + SILLocation loc, SILBuilder &builder) + : ComponentProjector(component, std::move(parent), loc, builder), + optionalChainResult(optionalChainResult) {} + + void project(AccessType accessType, + std::function callback) override { + assert(component.getKind() == + KeyPathPatternComponent::Kind::OptionalChain); + assert(accessType == AccessType::Get && + "Optional chain components are immutable"); + + parent->project(accessType, [&](SILValue optAddr) { + auto &ctx = builder.getASTContext(); + + auto noneDecl = ctx.getOptionalNoneDecl(); + auto someDecl = ctx.getOptionalSomeDecl(); + + SILType optType = optAddr->getType(); + SILType objType = optType.getOptionalObjectType(); + + // Continue projecting only if the optional is non-nil + // i.e. if let objAddr = optAddr { + auto continuation = builder.splitBlockForFallthrough(); + auto ifSome = builder.getFunction().createBasicBlockAfter(builder.getInsertionBB()); + auto ifNone = builder.getFunction().createBasicBlockAfter(ifSome); + builder.createSwitchEnumAddr(loc, optAddr, /*defaultBB*/ nullptr, + {{noneDecl, ifNone}, {someDecl, ifSome}}); + + assert(ifSome->empty()); + builder.setInsertionPoint(ifSome); + + // We have to copy the optional, since unwrapping is destructive. + auto tempAddr = builder.createAllocStack(loc, optType); + builder.createCopyAddr(loc, optAddr, tempAddr, IsNotTake, IsInitialization); + + // Unwrap the optional. + auto objAddr = builder.createUncheckedTakeEnumDataAddr(loc, tempAddr, someDecl, objType); + + // at the end of the projection, callback will store a value in optionalChainResult + callback(objAddr); + + builder.createDestroyAddr(loc, objAddr); + builder.createDeallocStack(loc, tempAddr); + + builder.createBranch(loc, continuation); + // else, store nil in the result + builder.setInsertionPoint(ifNone); + builder.createInjectEnumAddr(loc, optionalChainResult, noneDecl); + + builder.createBranch(loc, continuation); + // end if, allow parents to clean up regardless of whether the chain continued + builder.setInsertionPoint(continuation, continuation->begin()); + }); + } + +private: + SILValue optionalChainResult; +}; + + + + +/// A projector to handle a complete key path. +class CompleteKeyPathProjector : public KeyPathProjector { +public: + CompleteKeyPathProjector(KeyPathInst *keyPath, SILValue root, + SILLocation loc, SILBuilder &builder) + : KeyPathProjector(loc, builder), keyPath(keyPath), root(root) {} + + void project(AccessType accessType, + std::function callback) override { + auto components = keyPath->getPattern()->getComponents(); + + // Check if the keypath has an optional chain. + bool isOptionalChain = false; + for (const KeyPathPatternComponent &comp : components) { + if (comp.getKind() == KeyPathPatternComponent::Kind::OptionalChain) { + isOptionalChain = true; + break; + } + } + + // Root projector + auto rootProjector = std::make_unique(root, loc, builder); + + BeginAccessInst *beginAccess = nullptr; + + if (isOptionalChain) { + assert(accessType == AccessType::Get && "Optional chains are read-only"); + + // If we're reading an optional chain, create an optional result. + auto resultCanType = components.back().getComponentType(); + auto &function = builder.getFunction(); + auto optType = function.getLoweredType(resultCanType); + + assert(optType.getOptionalObjectType() && + "Optional-chained key path should result in an optional"); + SILValue optionalChainResult = builder.createAllocStack(loc, optType); + + // Get the (conditional) result projector. + auto projector = create(0, std::move(rootProjector), + beginAccess, optionalChainResult); + + projector->project(accessType, [&](SILValue result) { + // This will only run if all optional chains succeeded. + // Store the result in optionalChainResult. + builder.createCopyAddr(loc, result, optionalChainResult, + IsNotTake, IsInitialization); + }); + + // If the optional chain succeeded, optionalChainResult will have + // .some(result). Otherwise, projectOptionalChain will have written .none. + callback(optionalChainResult); + builder.createDestroyAddr(loc, optionalChainResult); + builder.createDeallocStack(loc, optionalChainResult); + } else { + // If we're not optional chaining, or we're writing to an optional chain, + // we don't need an optional result. + auto projector = create(0, std::move(rootProjector), + beginAccess, /*optionalChainResult*/ nullptr); + projector->project(accessType, callback); + } + assert(beginAccess == nullptr && + "key path projector returned with dangling access enforcement"); + } + + bool isStruct() override { + auto components = keyPath->getPattern()->getComponents(); + auto resultType = components.back().getComponentType(); + return resultType.getStructOrBoundGenericStruct() != nullptr; + } + +private: + KeyPathInst *keyPath; + SILValue root; + + /// Recursively creates a chain of key path projectors + /// for components from index.. + create(size_t index, std::unique_ptr parent, + BeginAccessInst *&beginAccess, SILValue optionalChainResult) { + auto components = keyPath->getPattern()->getComponents(); + + if (index >= components.size()) return parent; + + auto &comp = components[index]; + std::unique_ptr projector; + + // Create a projector for this component. + switch (comp.getKind()) { + case KeyPathPatternComponent::Kind::StoredProperty: + projector = std::make_unique + (comp, std::move(parent), beginAccess, loc, builder); + break; + case KeyPathPatternComponent::Kind::TupleElement: + projector = std::make_unique + (comp, std::move(parent), loc, builder); + break; + case KeyPathPatternComponent::Kind::GettableProperty: + projector = std::make_unique + (keyPath, comp, std::move(parent), keyPath->getSubstitutions(), + beginAccess, loc, builder); + break; + case KeyPathPatternComponent::Kind::SettableProperty: + projector = std::make_unique + (keyPath, comp, std::move(parent), keyPath->getSubstitutions(), + beginAccess, loc, builder); + break; + case KeyPathPatternComponent::Kind::OptionalWrap: + projector = std::make_unique + (comp, std::move(parent), loc, builder); + break; + case KeyPathPatternComponent::Kind::OptionalForce: + projector = std::make_unique + (comp, std::move(parent), loc, builder); + break; + case KeyPathPatternComponent::Kind::OptionalChain: + projector = std::make_unique + (comp, std::move(parent), optionalChainResult, loc, builder); + break; + } + + // Project the rest of the chain on top of this component. + return create(index + 1, std::move(projector), + beginAccess, optionalChainResult); + } +}; + +std::unique_ptr +KeyPathProjector::create(SILValue keyPath, SILValue root, + SILLocation loc, SILBuilder &builder) { + if (auto *upCast = dyn_cast(keyPath)) + keyPath = upCast->getOperand(); + + // Is it a keypath instruction at all? + auto *kpInst = dyn_cast(keyPath); + if (!kpInst || !kpInst->hasPattern()) + return nullptr; + + return std::make_unique(kpInst, root, + loc, builder); +} diff --git a/test/SILOptimizer/optimize_keypath.swift b/test/SILOptimizer/optimize_keypath.swift index 578270e19d937..d8faf81373f49 100644 --- a/test/SILOptimizer/optimize_keypath.swift +++ b/test/SILOptimizer/optimize_keypath.swift @@ -10,10 +10,16 @@ protocol P { func modifyIt() + var computed: Int { get set } + + subscript(a: Int, b: Bool, c: Int?) -> Int { get set } } struct GenStruct : P { var st: T + var computed: Int { get { st.computed } set { st.computed = newValue } } + + subscript(a: Int, b: Bool, c: Int?) -> Int { get { st[a, b, c] } set { st[a, b, c] = newValue } } init(_ st: T) { self.st = st } @@ -26,6 +32,9 @@ var numGenClassObjs = 0 final class GenClass : P { var ct: T + var computed: Int { get { ct.computed } set { ct.computed = newValue } } + + subscript(a: Int, b: Bool, c: Int?) -> Int { get { ct[a, b, c] } set { ct[a, b, c] = newValue } } init(_ ct: T) { self.ct = ct @@ -53,6 +62,7 @@ final class DerivedClass2 : DerivedClass { final class SimpleClass : P { var i: Int + var array = [1, 2, 3, 4, 5] static var numObjs = 0 init(_ i: Int) { @@ -67,6 +77,26 @@ final class SimpleClass : P { func modifyIt() { i += 10 } + + var computed: Int { get { i + 1 } set { i = newValue - 1} } + + subscript(a: Int, b: Bool, c: Int?) -> Int { + get { precondition(!b && c == nil, "arguments are corrupt"); return array[a] } + set { precondition(b && c == 42, "arguments are corrupt"); array[a] = newValue } + } +} + +struct SimpleStruct { + var tuple = (0, 1) + + struct Nested { + var i: Int + } + var opt: Nested? + + var i = 0 + + var computed: Int { get { i + 1 } set { i = newValue - 1} } } // Check if all keypath instructions have been optimized away @@ -252,6 +282,139 @@ func testNestedModify(_ s: inout GenStruct>>) { modifyGeneric(&s[keyPath: kp]) } +// CHECK-LABEL: sil {{.*}}testTuple +// CHECK: [[E:%[0-9]+]] = struct_element_addr +// CHECK: [[T1:%[0-9]+]] = tuple_element_addr [[E]] +// CHECK: [[I:%[0-9]+]] = load [[T1]] +// CHECK: [[T2:%[0-9]+]] = tuple_element_addr [[E]] +// CHECK: store [[I]] to [[T2]] +// CHECK: return +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testTuple(_ s: inout SimpleStruct) { + let first = \SimpleStruct.tuple.0 + let second = \SimpleStruct.tuple.1 + s[keyPath: first] = s[keyPath: second] +} + +// CHECK-LABEL: sil {{.*}} [noinline] {{.*}}testGetter +// CHECK: [[A:%[0-9]+]] = alloc_stack $Int +// CHECK: [[F:%[0-9]+]] = function_ref {{.*}}computed +// CHECK: apply [[F]]([[A]], %0) +// destroy_addr gets optimized out +// CHECK: dealloc_stack [[A]] +// CHECK: return +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testGetter(_ s: GenStruct) -> Int { + let kp = \GenStruct.computed + return s[keyPath: kp] +} + +// CHECK-LABEL: sil {{.*}}testComputedModify +// CHECK: [[A:%[0-9]+]] = alloc_stack $Int +// CHECK: [[G:%[0-9]+]] = function_ref {{.*}}computed +// CHECK: apply [[G]]([[A]], %0) +// CHECK: store {{%[0-9]+}} to [[A]] +// CHECK: [[S:%[0-9]+]] = function_ref {{.*}}computed +// CHECK: apply [[S]]([[A]], %0) +// destroy_addr gets optimized out +// CHECK: dealloc_stack [[A]] +// CHECK: return +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testComputedModify(_ s: inout GenStruct) { + let kp = \GenStruct.computed + s[keyPath: kp] += 10 +} + +// CHECK-LABEL: sil {{.*}}testSubscript +// CHECK: [[I1:%[0-9]+]] = alloc_stack $Int +// CHECK: [[C1:%[0-9]+]] = alloc_stack $(Int, Bool, Optional) +// CHECK: [[A1:%[0-9]+]] = address_to_pointer [[C1]] +// CHECK: [[R1:%[0-9]+]] = struct $UnsafeRawPointer ([[A1]] +// CHECK: [[G:%[0-9]+]] = function_ref +// CHECK: apply [[G]]([[I1]], {{%[0-9]+}}, [[R1]]) +// CHECK: dealloc_stack [[C1]] +// +// CHECK: [[I2:%[0-9]+]] = alloc_stack $Int +// CHECK: [[C2:%[0-9]+]] = alloc_stack $(Int, Bool, Optional) +// CHECK: [[A2:%[0-9]+]] = address_to_pointer [[C2]] +// CHECK: [[R2:%[0-9]+]] = struct $UnsafeRawPointer ([[A2]] +// CHECK: [[S:%[0-9]+]] = function_ref +// CHECK: apply [[S]]([[I2]], {{%[0-9]+}}, [[R2]]) +// CHECK: dealloc_stack [[C2]] +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testSubscript(_ s: inout GenStruct) { + let kp1 = \GenStruct[1, true, 42] + let kp2 = \GenStruct[2, false, nil] + s[keyPath: kp1] = s[keyPath: kp2] +} + +// CHECK-LABEL: sil {{.*}}testModifyOptionalForce +// CHECK: [[F:%[0-9]+]] = select_enum [[O:%[0-9]+]] +// CHECK: cond_fail [[F]] +// CHECK: unchecked_enum_data [[O]] +// CHECK: [[E2:%[0-9]+]] = init_enum_data_addr [[E1:%[0-9]+]] +// CHECK: store {{%[0-9]+}} to [[E2]] +// CHECK: inject_enum_addr [[E1]] +// CHECK: return +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testModifyOptionalForce(_ s: inout SimpleStruct) { + let kp = \SimpleStruct.opt!.i + s[keyPath: kp] += 10 +} + + + +// CHECK-LABEL: sil {{.*}}testOptionalChain +// By the time the test gets run, lots of stack +// allocations have been promoted to registers. +// +// Check if value is null +// CHECK: switch_enum [[O:%[0-9]+]] +// CHECK: {{bb.}}: +// Unwrap value +// CHECK: [[A1:%[0-9]+]] = alloc_stack +// CHECK: store [[O]] to [[A1]] +// CHECK: [[U:%[0-9]+]] = unchecked_take_enum_data_addr [[A1]] +// Access stored property & re-wrap result +// CHECK: [[I:%[0-9]+]] = struct_element_addr [[U]] +// CHECK: [[R1:%[0-9]+]] = enum +// CHECK: dealloc_stack [[A1]] +// CHECK: br [[CONTINUATION:bb.]]([[R1]] : $Optional) +// CHECK: {{bb.}}: +// Store nil in result +// CHECK: [[R2:%[0-9]+]] = enum +// CHECK: br [[CONTINUATION]]([[R2]] : $Optional) +// CHECK: [[CONTINUATION]]([[R:%[0-9]+]] : $Optional): +// CHECK: return [[R]] +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testOptionalChain(_ s: SimpleStruct) -> Int? { + let kp = \SimpleStruct.opt?.i + return s[keyPath: kp] +} + + +// CHECK-LABEL: sil {{.*}}testGetOptionalForce +// CHECK: [[F:%[0-9]+]] = select_enum [[O:%[0-9]+]] +// CHECK: cond_fail [[F]] +// CHECK: [[A:%[0-9]+]] = alloc_stack +// CHECK: store [[O]] to [[A]] +// CHECK: [[E2:%[0-9]+]] = unchecked_take_enum_data_addr [[A]] +// CHECK: [[E3:%[0-9]+]] = struct_element_addr [[E2]] +// CHECK: [[I:%[0-9]+]] = load [[E3]] +// CHECK: dealloc_stack [[A]] +// CHECK: return [[I]] +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testGetOptionalForce(_ s: SimpleStruct) -> Int { + let kp = \SimpleStruct.opt!.i + return s[keyPath: kp] +} // CHECK-LABEL: sil {{.*}}testit func testit() { @@ -300,6 +463,34 @@ func testit() { var s2 = GenStruct(GenClass(GenClass(SimpleClass(34)))) testNestedModify(&s2) print("NestedModify: \(s2.st.ct.ct.i)") + + // CHECK-OUTPUT: Getter: 51 + var s3 = GenStruct(SimpleClass(50)) + print("Getter: \(testGetter(s3))") + + // CHECK-OUTPUT: ComputedModify: 61 + testComputedModify(&s3) + print("ComputedModify: \(s3.computed)") + + // CHECK-OUTPUT: Subscript: [1, 3, 3, 4, 5] + testSubscript(&s3) + print("Subscript: \(s3.st.array)") + + var s4 = SimpleStruct() + // CHECK-OUTPUT: Tuple: 1 + testTuple(&s4) + print("Tuple: \(s4.tuple.0)") + + // CHECK-OUTPUT: OptionalChain1: nil + print("OptionalChain1: \(String(describing: testOptionalChain(s4)))") + + // CHECK-OUTPUT: OptionalChain2: Optional(70) + s4.opt = .init(i: 70) + print("OptionalChain2: \(String(describing: testOptionalChain(s4)))") + + // CHECK-OUTPUT: OptionalForce: 80 + testModifyOptionalForce(&s4) + print("OptionalForce: \(testGetOptionalForce(s4))") } testit() From 758f1f3208ca897ec2a8f53b93c8e323feb0b855 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 22 Dec 2019 13:54:10 -0800 Subject: [PATCH 016/381] Update getAddressOfStackInit and getStackInitInst to support store instructions --- lib/SILOptimizer/Utils/Existential.cpp | 52 ++++++++++---------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index d270189f2da57..3c5881e39c008 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -111,6 +111,19 @@ static SILInstruction *getStackInitInst(SILValue allocStackAddr, } continue; } + if (auto *store = dyn_cast(User)) { + if (store->getDest() == allocStackAddr) { + if (SingleWrite) + return nullptr; + SingleWrite = store; + // When we support OSSA here, we need to insert a new copy of the value + // before `store` (and make sure that the copy is destroyed when + // replacing the apply operand). + assert(store->getOwnershipQualifier() == + StoreOwnershipQualifier::Unqualified); + } + continue; + } if (isa(User)) { if (SingleWrite) return nullptr; @@ -144,6 +157,9 @@ static SILInstruction *getStackInitInst(SILValue allocStackAddr, if (BB != allocStackAddr->getParentBlock() && BB != ASIUser->getParent()) return nullptr; + if(auto *store = dyn_cast(SingleWrite)) + return store; + if (auto *IE = dyn_cast(SingleWrite)) return IE; @@ -194,6 +210,9 @@ static SILValue getAddressOfStackInit(SILValue allocStackAddr, if (auto *CAI = dyn_cast(initI)) return CAI->getSrc(); + + if (auto *store = dyn_cast(initI)) + return store->getSrc(); return SILValue(); } @@ -213,39 +232,6 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { getAddressOfStackInit(instance, user, isOpenedValueCopied)) { openedVal = stackInitVal; } - - // Handle: - // %4 = open_existential_ref - // %5 = alloc_stack - // store %4 to %5 - // apply (%5) - // It's important that the only uses of %5 (instance) are in - // a store and an apply. - StoreInst *store = nullptr; - ApplyInst *apply = nullptr; - DeallocStackInst *dealloc = nullptr; - bool shouldOptimize = true; - - for (auto *use : instance->getUses()) { - if (auto *foundStore = dyn_cast(use->getUser())) { - store = foundStore; - } else if (auto *foundApply = dyn_cast(use->getUser())) { - apply = foundApply; - } else if (auto *foundDealloc = dyn_cast(use->getUser())) { - dealloc = foundDealloc; - } else { - // TODO: this may be too harsh - shouldOptimize = false; - } - } - - if (shouldOptimize && store && apply && dealloc) { - DominanceInfo domInfo(store->getFunction()); - bool correctOrder = domInfo.dominates(store, apply) && - domInfo.dominates(apply, dealloc); - if (correctOrder) - openedVal = store->getSrc(); - } } if (auto *Open = dyn_cast(openedVal)) { OpenedArchetype = Open->getType().castTo(); From f4e3ca9999a422b2c2d907778e6cb56c4ee63833 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 22 Dec 2019 13:58:01 -0800 Subject: [PATCH 017/381] Formatting --- lib/SILOptimizer/Utils/Existential.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 3c5881e39c008..8053e32082017 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -157,9 +157,9 @@ static SILInstruction *getStackInitInst(SILValue allocStackAddr, if (BB != allocStackAddr->getParentBlock() && BB != ASIUser->getParent()) return nullptr; - if(auto *store = dyn_cast(SingleWrite)) + if (auto *store = dyn_cast(SingleWrite)) return store; - + if (auto *IE = dyn_cast(SingleWrite)) return IE; @@ -210,7 +210,7 @@ static SILValue getAddressOfStackInit(SILValue allocStackAddr, if (auto *CAI = dyn_cast(initI)) return CAI->getSrc(); - + if (auto *store = dyn_cast(initI)) return store->getSrc(); From a52fe57256f275e5897ae2d9af3d456f2c51e218 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 5 Jan 2020 11:51:39 -0800 Subject: [PATCH 018/381] Cleanup tests --- ...ualize_protocol_composition_two_stores.sil | 217 ++---------------- 1 file changed, 15 insertions(+), 202 deletions(-) diff --git a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil index e3639ed2f1224..ac7115966f202 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil +++ b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil @@ -31,7 +31,7 @@ func imp(x: A & P, y: A & P) -> Int public func test(x: B, y: C) -> Int -// CHECK-LABEL: sil shared [noinline] @${{.*}}impl{{.*}} +// CHECK-LABEL: sil shared [noinline] @{{.*}}impl{{.*}} // In the optimization pass we look for uses of alloc_stack (aka %5). // This test makes sure that we look at the correct uses with the // correct dominance order (store before apply before dealloc). @@ -59,54 +59,15 @@ bb0(%0 : $A & P, %1 : $A & P): return %8 : $Int } // end sil function 'impl' -// main -sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { -bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>): - %2 = integer_literal $Builtin.Int32, 0 // user: %3 - %3 = struct $Int32 (%2 : $Builtin.Int32) // user: %4 - return %3 : $Int32 // id: %4 -} // end sil function 'main' - -// A.deinit -sil @$s3run1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject { -// %0 // users: %2, %1 -bb0(%0 : $A): - debug_value %0 : $A, let, name "self", argno 1 // id: %1 - %2 = unchecked_ref_cast %0 : $A to $Builtin.NativeObject // user: %3 - return %2 : $Builtin.NativeObject // id: %3 -} // end sil function '$s3run1ACfd' - -// A.__deallocating_deinit -sil @$s3run1ACfD : $@convention(method) (@owned A) -> () { -// %0 // users: %3, %1 -bb0(%0 : $A): - debug_value %0 : $A, let, name "self", argno 1 // id: %1 - // function_ref A.deinit - %2 = function_ref @$s3run1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %3 - %3 = apply %2(%0) : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %4 - %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $A // user: %5 - dealloc_ref %4 : $A // id: %5 - %6 = tuple () // user: %7 - return %6 : $() // id: %7 -} // end sil function '$s3run1ACfD' - -// A.__allocating_init() -sil hidden [exact_self_class] @$s3run1ACACycfC : $@convention(method) (@thick A.Type) -> @owned A { -bb0(%0 : $@thick A.Type): - %1 = alloc_ref $A // user: %3 - // function_ref A.init() - %2 = function_ref @$s3run1ACACycfc : $@convention(method) (@owned A) -> @owned A // user: %3 - %3 = apply %2(%1) : $@convention(method) (@owned A) -> @owned A // user: %4 - return %3 : $A // id: %4 -} // end sil function '$s3run1ACACycfC' - -// A.init() -sil hidden @$s3run1ACACycfc : $@convention(method) (@owned A) -> @owned A { -// %0 // users: %2, %1 -bb0(%0 : $A): - debug_value %0 : $A, let, name "self", argno 1 // id: %1 - return %0 : $A // id: %2 -} // end sil function '$s3run1ACACycfc' +// protocol witness for P.foo() in conformance B +sil shared [transparent] [serialized] [thunk] @Pfoo : $@convention(witness_method: P) (@in_guaranteed B) -> Int { +// %0 // user: %1 +bb0(%0 : $*B): + %1 = load %0 : $*B // users: %2, %3 + %2 = class_method %1 : $B, #B.foo!1 : (B) -> () -> Int, $@convention(method) (@guaranteed B) -> Int // user: %3 + %3 = apply %2(%1) : $@convention(method) (@guaranteed B) -> Int // user: %4 + return %3 : $Int // id: %4 +} // end sil function 'Pfoo' // B.foo() sil [noinline] @foo : $@convention(method) (@guaranteed B) -> Int { @@ -116,149 +77,7 @@ bb0(%0 : $B): %2 = integer_literal $Builtin.Int64, 1 // user: %3 %3 = struct $Int (%2 : $Builtin.Int64) // user: %4 return %3 : $Int // id: %4 -} // end sil function '$s3run1BC3fooSiyF' - -// Int.init(_builtinIntegerLiteral:) -sil public_external [transparent] [serialized] @$sSi22_builtinIntegerLiteralSiBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int { -// %0 // user: %2 -bb0(%0 : $Builtin.IntLiteral, %1 : $@thin Int.Type): - %2 = builtin "s_to_s_checked_trunc_IntLiteral_Int64"(%0 : $Builtin.IntLiteral) : $(Builtin.Int64, Builtin.Int1) // user: %3 - %3 = tuple_extract %2 : $(Builtin.Int64, Builtin.Int1), 0 // user: %4 - %4 = struct $Int (%3 : $Builtin.Int64) // user: %5 - return %4 : $Int // id: %5 -} // end sil function '$sSi22_builtinIntegerLiteralSiBI_tcfC' - -// B.deinit -sil @$s3run1BCfd : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject { -// %0 // users: %2, %1 -bb0(%0 : $B): - debug_value %0 : $B, let, name "self", argno 1 // id: %1 - %2 = upcast %0 : $B to $A // user: %4 - // function_ref A.deinit - %3 = function_ref @$s3run1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %4 - %4 = apply %3(%2) : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // users: %5, %6 - %5 = unchecked_ref_cast %4 : $Builtin.NativeObject to $B - return %4 : $Builtin.NativeObject // id: %6 -} // end sil function '$s3run1BCfd' - -// B.__deallocating_deinit -sil @$s3run1BCfD : $@convention(method) (@owned B) -> () { -// %0 // users: %3, %1 -bb0(%0 : $B): - debug_value %0 : $B, let, name "self", argno 1 // id: %1 - // function_ref B.deinit - %2 = function_ref @$s3run1BCfd : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject // user: %3 - %3 = apply %2(%0) : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject // user: %4 - %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $B // user: %5 - dealloc_ref %4 : $B // id: %5 - %6 = tuple () // user: %7 - return %6 : $() // id: %7 -} // end sil function '$s3run1BCfD' - -// B.__allocating_init() -sil hidden [exact_self_class] @$s3run1BCACycfC : $@convention(method) (@thick B.Type) -> @owned B { -bb0(%0 : $@thick B.Type): - %1 = alloc_ref $B // user: %3 - // function_ref B.init() - %2 = function_ref @$s3run1BCACycfc : $@convention(method) (@owned B) -> @owned B // user: %3 - %3 = apply %2(%1) : $@convention(method) (@owned B) -> @owned B // user: %4 - return %3 : $B // id: %4 -} // end sil function '$s3run1BCACycfC' - -// B.init() -sil hidden @$s3run1BCACycfc : $@convention(method) (@owned B) -> @owned B { -// %0 // user: %2 -bb0(%0 : $B): - %1 = alloc_stack $B, let, name "self" // users: %9, %3, %2, %10, %11 - store %0 to %1 : $*B // id: %2 - %3 = load %1 : $*B // user: %4 - %4 = upcast %3 : $B to $A // user: %6 - // function_ref A.init() - %5 = function_ref @$s3run1ACACycfc : $@convention(method) (@owned A) -> @owned A // user: %6 - %6 = apply %5(%4) : $@convention(method) (@owned A) -> @owned A // user: %7 - %7 = unchecked_ref_cast %6 : $A to $B // users: %12, %9, %8 - strong_retain %7 : $B // id: %8 - store %7 to %1 : $*B // id: %9 - destroy_addr %1 : $*B // id: %10 - dealloc_stack %1 : $*B // id: %11 - return %7 : $B // id: %12 -} // end sil function '$s3run1BCACycfc' - -// C.foo() -sil @bar : $@convention(method) (@guaranteed C) -> Int { -// %0 // user: %1 -bb0(%0 : $C): - debug_value %0 : $C, let, name "self", argno 1 // id: %1 - %2 = integer_literal $Builtin.Int64, 0 // user: %3 - %3 = struct $Int (%2 : $Builtin.Int64) // user: %4 - return %3 : $Int // id: %4 -} // end sil function 'bar' - -// C.deinit -sil @$s3run1CCfd : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject { -// %0 // users: %2, %1 -bb0(%0 : $C): - debug_value %0 : $C, let, name "self", argno 1 // id: %1 - %2 = upcast %0 : $C to $A // user: %4 - // function_ref A.deinit - %3 = function_ref @$s3run1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %4 - %4 = apply %3(%2) : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // users: %5, %6 - %5 = unchecked_ref_cast %4 : $Builtin.NativeObject to $C - return %4 : $Builtin.NativeObject // id: %6 -} // end sil function '$s3run1CCfd' - -// C.__deallocating_deinit -sil @$s3run1CCfD : $@convention(method) (@owned C) -> () { -// %0 // users: %3, %1 -bb0(%0 : $C): - debug_value %0 : $C, let, name "self", argno 1 // id: %1 - // function_ref C.deinit - %2 = function_ref @$s3run1CCfd : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject // user: %3 - %3 = apply %2(%0) : $@convention(method) (@guaranteed C) -> @owned Builtin.NativeObject // user: %4 - %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $C // user: %5 - dealloc_ref %4 : $C // id: %5 - %6 = tuple () // user: %7 - return %6 : $() // id: %7 -} // end sil function '$s3run1CCfD' - -// C.__allocating_init() -sil hidden [exact_self_class] @$s3run1CCACycfC : $@convention(method) (@thick C.Type) -> @owned C { -bb0(%0 : $@thick C.Type): - %1 = alloc_ref $C // user: %3 - // function_ref C.init() - %2 = function_ref @$s3run1CCACycfc : $@convention(method) (@owned C) -> @owned C // user: %3 - %3 = apply %2(%1) : $@convention(method) (@owned C) -> @owned C // user: %4 - return %3 : $C // id: %4 -} // end sil function '$s3run1CCACycfC' - -// C.init() -sil hidden @$s3run1CCACycfc : $@convention(method) (@owned C) -> @owned C { -// %0 // user: %2 -bb0(%0 : $C): - %1 = alloc_stack $C, let, name "self" // users: %9, %3, %2, %10, %11 - store %0 to %1 : $*C // id: %2 - %3 = load %1 : $*C // user: %4 - %4 = upcast %3 : $C to $A // user: %6 - // function_ref A.init() - %5 = function_ref @$s3run1ACACycfc : $@convention(method) (@owned A) -> @owned A // user: %6 - %6 = apply %5(%4) : $@convention(method) (@owned A) -> @owned A // user: %7 - %7 = unchecked_ref_cast %6 : $A to $C // users: %12, %9, %8 - strong_retain %7 : $C // id: %8 - store %7 to %1 : $*C // id: %9 - destroy_addr %1 : $*C // id: %10 - dealloc_stack %1 : $*C // id: %11 - return %7 : $C // id: %12 -} // end sil function '$s3run1CCACycfc' - -// protocol witness for P.foo() in conformance B -sil shared [transparent] [serialized] [thunk] @$s3run1BCAA1PA2aDP3fooSiyFTW : $@convention(witness_method: P) (@in_guaranteed B) -> Int { -// %0 // user: %1 -bb0(%0 : $*B): - %1 = load %0 : $*B // users: %2, %3 - %2 = class_method %1 : $B, #B.foo!1 : (B) -> () -> Int, $@convention(method) (@guaranteed B) -> Int // user: %3 - %3 = apply %2(%1) : $@convention(method) (@guaranteed B) -> Int // user: %4 - return %3 : $Int // id: %4 -} // end sil function '$s3run1BCAA1PA2aDP3fooSiyFTW' +} // end sil function 'foo' // test(x:y:) sil @test :$@convention(thin) (@guaranteed B, @guaranteed C) -> Int { @@ -279,23 +98,17 @@ bb0(%0 : $B, %1 : $C): return %9 : $Int // id: %12 } // end sil function 'test' -sil_vtable [serialized] A { - #A.init!allocator.1: (A.Type) -> () -> A : @$s3run1ACACycfC // A.__allocating_init() - #A.deinit!deallocator.1: @$s3run1ACfD // A.__deallocating_deinit -} +// C.foo() +sil @bar : $@convention(method) (@guaranteed C) -> Int sil_vtable [serialized] B { - #A.init!allocator.1: (A.Type) -> () -> A : @$s3run1BCACycfC [override] // B.__allocating_init() #B.foo!1: (B) -> () -> Int : @foo // B.foo() - #B.deinit!deallocator.1: @$s3run1BCfD // B.__deallocating_deinit } sil_vtable [serialized] C { - #A.init!allocator.1: (A.Type) -> () -> A : @$s3run1CCACycfC [override] // C.__allocating_init() - #C.foo!1: (C) -> () -> Int : @bar // C.foo() - #C.deinit!deallocator.1: @$s3run1CCfD // C.__deallocating_deinit + #C.foo!1: (C) -> () -> Int : @bar } sil_witness_table [serialized] B: P module run { - method #P.foo!1: (Self) -> () -> Int : @$s3run1BCAA1PA2aDP3fooSiyFTW // protocol witness for P.foo() in conformance B + method #P.foo!1: (Self) -> () -> Int : @Pfoo // protocol witness for P.foo() in conformance B } From 4ced714e2508723cbfa6d550a53db6524d810bfb Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 9 Jan 2020 23:18:27 -0800 Subject: [PATCH 019/381] [CodeCompletion] Allow type variable in MakeAbstractConformanceForGenericType When typesSatisfyConstraint() is called with 'openArchetypes=true', archetypes are substituted with type variables. If they have conformances, they used to hit assertion in 'MakeAbstractConformanceForGenericType::operator()'. Adjust the assetion to accept 'TypeVariableType'. rdar://problem/56834798 --- lib/AST/Type.cpp | 3 ++- .../IDE/crashers_2_fixed/rdar56834798.swift | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 validation-test/IDE/crashers_2_fixed/rdar56834798.swift diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 00486319c22b1..2c4a7c51a1001 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3422,7 +3422,8 @@ operator()(CanType dependentType, Type conformingReplacementType, ProtocolDecl *conformedProtocol) const { assert((conformingReplacementType->is() || conformingReplacementType->is() - || conformingReplacementType->is()) + || conformingReplacementType->is() + || conformingReplacementType->is()) && "replacement requires looking up a concrete conformance"); return ProtocolConformanceRef(conformedProtocol); } diff --git a/validation-test/IDE/crashers_2_fixed/rdar56834798.swift b/validation-test/IDE/crashers_2_fixed/rdar56834798.swift new file mode 100644 index 0000000000000..cb31e7844efa8 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/rdar56834798.swift @@ -0,0 +1,23 @@ +// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s +// RUN: %target-swift-ide-test -code-completion -code-completion-token=B -source-filename=%s + +protocol Proto {} + +struct S { + typealias Value = T + + func test(arg: Int) -> Value { + return #^A^# + } +} + +class C: Proto { + init() {} +} +extension Proto { + typealias Nested = C +} +func receiver(arg: T) {} +func test() { + receiver(arg: .#^B^#) +} From 3335ba5fcacd47514feb95e0a2b9f46d2b1027f6 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 10 Jan 2020 09:31:34 -0800 Subject: [PATCH 020/381] Update isAssignedOnlyOnceInInitializer to be more correct --- lib/SILOptimizer/IPO/GlobalOpt.cpp | 20 +++++++++++------ .../globalopt_global_propagation.swift | 22 +++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index d79c3961731e5..7c4488c5c48e0 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -104,6 +104,9 @@ class SILGlobalOpt { bool run(); protected: + /// Checks if a given global variable is assigned only once. + bool isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG); + /// If this is a call to a global initializer, map it. void collectGlobalInitCall(ApplyInst *AI); @@ -595,15 +598,18 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder, return GetterF; } -/// Checks if a given global variable is assigned only once. -static bool isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG) { +bool SILGlobalOpt::isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG) { if (SILG->isLet()) return true; - // TODO: If we can prove that a given global variable - // is assigned only once, during initialization, then - // we can treat it as if it is a let. - // If this global is internal or private, it should be - return false; + + // If we should skip this, it is probably because there are multiple stores. + // Return false if there are multiple stores or no stores. + if (GlobalVarSkipProcessing.count(SILG) || !GlobalVarStore.count(SILG)) + return false; + + // Otherwise, return true if this can't be used externally (false, otherwise). + return !isPossiblyUsedExternally(SILG->getLinkage(), + SILG->getModule().isWholeModule()); } /// Replace load sequence which may contain diff --git a/test/SILOptimizer/globalopt_global_propagation.swift b/test/SILOptimizer/globalopt_global_propagation.swift index 0b9d375724d6e..3f8985f722559 100644 --- a/test/SILOptimizer/globalopt_global_propagation.swift +++ b/test/SILOptimizer/globalopt_global_propagation.swift @@ -227,3 +227,25 @@ let IT2 = (100, 200, 300) public func test_let_tuple_wrapped_ints() -> Int { return IT1.0.0 + IT2.1 } + +class Foo { + fileprivate static var x: Int = 0 +} + +// CHECK-LABEL: sil @$s28globalopt_global_propagation25test_optimize_init_staticSiyF +// CHECK: bb0: +// CHECK-NOT: global_addr +// CHECK-NEXT: integer_literal +// CHECK-NEXT: struct +// CHECK-NEXT: return + +// CHECK-WMO-LABEL: sil @$s28globalopt_global_propagation25test_optimize_init_staticSiyF +// CHECK-WMO: bb0: +// CHECK-WMO-NOT: global_addr +// CHECK-WMO-NEXT: integer_literal +// CHECK-WMO-NEXT: struct +// CHECK-WMO-NEXT: return +public func test_optimize_init_static() -> Int { + return Foo.x +} + From eda11c56632ae8ff532ff00afb0874c7bca47314 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sat, 11 Jan 2020 11:02:09 -0800 Subject: [PATCH 021/381] Fix access tests --- test/SILOptimizer/access_wmo.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/SILOptimizer/access_wmo.swift b/test/SILOptimizer/access_wmo.swift index 59625df2afa64..284f20d5d45ce 100644 --- a/test/SILOptimizer/access_wmo.swift +++ b/test/SILOptimizer/access_wmo.swift @@ -27,9 +27,7 @@ // PRIMARY-LABEL: } // end sil function '$s10access_wmo10readGlobalSiyF' // // WMO-LABEL: sil @$s10access_wmo10readGlobalSiyF : $@convention(thin) () -> Int { -// WMO: [[G1:%.*]] = global_addr @$s10access_wmo14internalGlobalSivp : $*Int -// WMO: [[A1:%.*]] = begin_access [read] [static] [no_nested_conflict] [[G1]] : $*Int -// WMO: end_access [[A1]] : $*Int +// WMO-NOT: global_addr // WMO: [[G2:%.*]] = global_addr @$s10access_wmo12publicGlobalSivp : $*Int // WMO: [[A2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[G2]] : $*Int // WMO: end_access [[A2]] : $*Int From 33987c5380e5a97efb3c93ab7f4d8751c1d73101 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 22 Jan 2020 21:33:41 -0800 Subject: [PATCH 022/381] Inline/remove getAddressOfStackInit and update tests to make them more clear --- lib/SILOptimizer/Utils/Existential.cpp | 36 +++-------- ...ualize_protocol_composition_two_stores.sil | 63 ++++++++++--------- 2 files changed, 45 insertions(+), 54 deletions(-) diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 8053e32082017..00edb27c1fc2c 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -69,7 +69,7 @@ findInitExistentialFromGlobalAddr(GlobalAddrInst *GAI, SILInstruction *Insn) { /// Returns the instruction that initializes the given stack address. This is /// currently either a init_existential_addr, unconditional_checked_cast_addr, -/// or copy_addr (if the instruction initializing the source of the copy cannot +/// store, or copy_addr (if the instruction initializing the source of the copy cannot /// be determined). Returns nullptr if the initializer does not dominate the /// alloc_stack user \p ASIUser. If the value is copied from another stack /// location, \p isCopied is set to true. @@ -196,27 +196,6 @@ static SILInstruction *getStackInitInst(SILValue allocStackAddr, return CAI; } -/// Return the address of the value used to initialize the given stack location. -/// If the value originates from init_existential_addr, then it will be a -/// different type than \p allocStackAddr. -static SILValue getAddressOfStackInit(SILValue allocStackAddr, - SILInstruction *ASIUser, bool &isCopied) { - SILInstruction *initI = getStackInitInst(allocStackAddr, ASIUser, isCopied); - if (!initI) - return SILValue(); - - if (auto *IEA = dyn_cast(initI)) - return IEA; - - if (auto *CAI = dyn_cast(initI)) - return CAI->getSrc(); - - if (auto *store = dyn_cast(initI)) - return store->getSrc(); - - return SILValue(); -} - /// Check if the given operand originates from a recognized OpenArchetype /// instruction. If so, return the Opened, otherwise return nullptr. OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { @@ -226,11 +205,16 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { // Handle: // %opened = open_existential_addr // %instance = alloc $opened - // copy_addr %opened to %stack + // %opened to %stack // %instance - if (auto stackInitVal = - getAddressOfStackInit(instance, user, isOpenedValueCopied)) { - openedVal = stackInitVal; + if (auto *initI = getStackInitInst(instance, user, isOpenedValueCopied)) { + if (auto *IEA = dyn_cast(initI)) + // TODO: this case doesn't need to exist beacuse openedVal will never be used + openedVal = IEA; + if (auto *CAI = dyn_cast(initI)) + openedVal = CAI->getSrc(); + if (auto *store = dyn_cast(initI)) + openedVal = store->getSrc(); } } if (auto *Open = dyn_cast(openedVal)) { diff --git a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil index ac7115966f202..0dcc2f1e2d624 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil +++ b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil @@ -31,34 +31,6 @@ func imp(x: A & P, y: A & P) -> Int public func test(x: B, y: C) -> Int -// CHECK-LABEL: sil shared [noinline] @{{.*}}impl{{.*}} -// In the optimization pass we look for uses of alloc_stack (aka %5). -// This test makes sure that we look at the correct uses with the -// correct dominance order (store before apply before dealloc). -// This function will be passed arguments of type B and C for arguments -// %0 and %1 respectively. We want to make sure that we call B's foo method -// and not C's foo method. -sil hidden [noinline] @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int { -bb0(%0 : $A & P, %1 : $A & P): - %2 = open_existential_ref %0 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - %3 = unchecked_ref_cast %1 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - - %5 = alloc_stack $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - store %2 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - - // We want to make sure that we picked up on the FIRST store and not the second one. - // class C's foo method is named "bar" whereas class B's foo method is named "foo". - // We want to make sure that we call a function named "foo" not "bar". - // CHECK: [[FN:%[0-9]+]] = function_ref @${{.*}}foo{{.*}} : $@convention(thin) () -> Int - %7 = witness_method $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P, #P.foo!1 : (Self) -> () -> Int, %2 : $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int - // CHECK: apply [[FN]] - %8 = apply %7<@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P>(%5) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int - - store %3 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - dealloc_stack %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - return %8 : $Int -} // end sil function 'impl' - // protocol witness for P.foo() in conformance B sil shared [transparent] [serialized] [thunk] @Pfoo : $@convention(witness_method: P) (@in_guaranteed B) -> Int { // %0 // user: %1 @@ -79,6 +51,10 @@ bb0(%0 : $B): return %3 : $Int // id: %4 } // end sil function 'foo' + +// This function calls @impl. The optimizer generates a specialization of impl +// based on the arguments passed to it here. Even though this function isn't +// directly checked, it is essentail for the optimization. // test(x:y:) sil @test :$@convention(thin) (@guaranteed B, @guaranteed C) -> Int { // %0 // users: %5, %4, %2 @@ -98,6 +74,37 @@ bb0(%0 : $B, %1 : $C): return %9 : $Int // id: %12 } // end sil function 'test' +// We're looking of an optimized spcialization of @impl, not @impl itself. +// CHECK-NOT: @impl +// CHECK-LABEL: sil shared [noinline] @{{.*}}impl{{.*}} +// In the optimization pass we look for uses of alloc_stack (aka %5). +// This test makes sure that we look at the correct uses with the +// correct dominance order (store before apply before dealloc). +// This function will be passed arguments of type B and C for arguments +// %0 and %1 respectively. We want to make sure that we call B's foo method +// and not C's foo method. +sil hidden [noinline] @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int { +bb0(%0 : $A & P, %1 : $A & P): + %2 = open_existential_ref %0 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + %3 = unchecked_ref_cast %1 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + + %5 = alloc_stack $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + store %2 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + + // We want to make sure that we picked up on the FIRST store and not the second one. + // class C's foo method is named "bar" whereas class B's foo method is named "foo". + // We want to make sure that we call a function named "foo" not "bar". + // CHECK: [[FN:%[0-9]+]] = function_ref @${{.*}}foo{{.*}} : $@convention(thin) () -> Int + %7 = witness_method $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P, #P.foo!1 : (Self) -> () -> Int, %2 : $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int + // CHECK: apply [[FN]] + %8 = apply %7<@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P>(%5) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int + + store %3 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + dealloc_stack %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P + return %8 : $Int +// CHECK-LABEL: end sil function {{.*}}impl{{.*}} +} // end sil function 'impl' + // C.foo() sil @bar : $@convention(method) (@guaranteed C) -> Int From 0992e482d4417a50a76d6956a2836285d07ac480 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 24 Jan 2020 11:07:31 -0800 Subject: [PATCH 023/381] Abort if not address type --- lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp | 7 ++++++- lib/SILOptimizer/Utils/Existential.cpp | 4 ++-- .../existential_specializer_indirect_class.sil | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 90607e637f7d9..8a602b68435b4 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -814,6 +814,10 @@ struct ConcreteArgumentCopy { if (!paramInfo.isFormalIndirect()) return None; + // TODO: Support non-address values (e.g. from stores). + if (!CEI.ConcreteValue->getType().isAddress()) + return None; + SILBuilderWithScope B(apply.getInstruction(), BuilderCtx); auto loc = apply.getLoc(); auto *ASI = B.createAllocStack(loc, CEI.ConcreteValue->getType()); @@ -882,12 +886,13 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( NewArgs.push_back(Apply.getArgument(ArgIdx)); continue; } - UpdatedArgs = true; + // Ensure that we have a concrete value to propagate. assert(CEI.ConcreteValue); auto argSub = ConcreteArgumentCopy::generate(CEI, Apply, ArgIdx, BuilderCtx); if (argSub) { + UpdatedArgs = true; concreteArgCopies.push_back(*argSub); NewArgs.push_back(argSub->tempArgCopy->getDest()); } else diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 00edb27c1fc2c..28fd2baec1b11 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -213,8 +213,8 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { openedVal = IEA; if (auto *CAI = dyn_cast(initI)) openedVal = CAI->getSrc(); - if (auto *store = dyn_cast(initI)) - openedVal = store->getSrc(); +// if (auto *store = dyn_cast(initI)) +// openedVal = store->getSrc(); } } if (auto *Open = dyn_cast(openedVal)) { diff --git a/test/SILOptimizer/existential_specializer_indirect_class.sil b/test/SILOptimizer/existential_specializer_indirect_class.sil index 384931e2f09a4..2aa96abf1fbbd 100644 --- a/test/SILOptimizer/existential_specializer_indirect_class.sil +++ b/test/SILOptimizer/existential_specializer_indirect_class.sil @@ -40,7 +40,7 @@ bb0(%0 : $*ClassProtocol): // CHECK-NEXT: %7 = witness_method $C, #ClassProtocol.method %f = witness_method $@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol, #ClassProtocol.method!1 : (Self) -> () -> (), %2 : $@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol : $@convention(witness_method : ClassProtocol) (@guaranteed Self) -> () // CHECK-NEXT: %8 = unchecked_ref_cast %6 - // CHECK-NEXT: %9 = apply %7(%8) + // CHECK-NEXT: %9 = apply %7(%6) apply %f<@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol>(%2) : $@convention(witness_method : ClassProtocol) (@guaranteed Self) -> () // CHECK-NEXT: dealloc_stack %3 // CHECK-NEXT: return undef From ae64719824621e2a70f1c574ba98f57ce4006e93 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 24 Jan 2020 12:22:13 -0800 Subject: [PATCH 024/381] Support store instructions in createApplyWithConcreteType Supporting stores in more places means that more code can be _completely_ devirtualized which also means other optimizations (e.g. inlining) can also happen. Also, updates tests. --- .../SILCombiner/SILCombinerApplyVisitors.cpp | 48 ++++++------ lib/SILOptimizer/Utils/Existential.cpp | 4 +- ...existential_specializer_indirect_class.sil | 74 +++++++++---------- 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 8a602b68435b4..6c0b9528358b1 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -775,10 +775,10 @@ bool SILCombiner::canReplaceArg(FullApplySite Apply, /// (@in or @owned convention). struct ConcreteArgumentCopy { SILValue origArg; - CopyAddrInst *tempArgCopy; + AllocStackInst *tempArg; - ConcreteArgumentCopy(SILValue origArg, CopyAddrInst *tempArgCopy) - : origArg(origArg), tempArgCopy(tempArgCopy) { + ConcreteArgumentCopy(SILValue origArg, AllocStackInst *tempArg) + : origArg(origArg), tempArg(tempArg) { assert(origArg->getType().isAddress()); } @@ -791,8 +791,8 @@ struct ConcreteArgumentCopy { assert(!paramInfo.isIndirectMutating() && "A mutated opened existential value can't be replaced"); - if (!paramInfo.isConsumed()) - return None; +// if (!paramInfo.isConsumed()) +// return None; SILValue origArg = apply.getArgument(argIdx); // FIXME_opaque: With SIL opaque values, a formally indirect argument may be @@ -814,16 +814,18 @@ struct ConcreteArgumentCopy { if (!paramInfo.isFormalIndirect()) return None; - // TODO: Support non-address values (e.g. from stores). - if (!CEI.ConcreteValue->getType().isAddress()) - return None; - SILBuilderWithScope B(apply.getInstruction(), BuilderCtx); auto loc = apply.getLoc(); auto *ASI = B.createAllocStack(loc, CEI.ConcreteValue->getType()); - auto *CAI = B.createCopyAddr(loc, CEI.ConcreteValue, ASI, IsNotTake, - IsInitialization_t::IsInitialization); - return ConcreteArgumentCopy(origArg, CAI); + + if (CEI.ConcreteValue->getType().isAddress()) { + B.createCopyAddr(loc, CEI.ConcreteValue, ASI, IsNotTake, + IsInitialization_t::IsInitialization); + } else { + B.createStore(loc, CEI.ConcreteValue, ASI, + StoreOwnershipQualifier::Unqualified); + } + return ConcreteArgumentCopy(origArg, ASI); } }; @@ -889,14 +891,19 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // Ensure that we have a concrete value to propagate. assert(CEI.ConcreteValue); - auto argSub = - ConcreteArgumentCopy::generate(CEI, Apply, ArgIdx, BuilderCtx); - if (argSub) { - UpdatedArgs = true; - concreteArgCopies.push_back(*argSub); - NewArgs.push_back(argSub->tempArgCopy->getDest()); - } else + + if (Apply.getArgument(ArgIdx)->getType().isAddress()) { + auto argSub = + ConcreteArgumentCopy::generate(CEI, Apply, ArgIdx, BuilderCtx); + if (argSub) { + UpdatedArgs = true; + concreteArgCopies.push_back(*argSub); + NewArgs.push_back(argSub->tempArg); + } + } else { NewArgs.push_back(CEI.ConcreteValue); + UpdatedArgs = true; + } // Form a new set of substitutions where the argument is // replaced with a concrete type. @@ -962,8 +969,7 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( auto cleanupLoc = RegularLocation::getAutoGeneratedLocation(); for (ConcreteArgumentCopy &argCopy : llvm::reverse(concreteArgCopies)) { cleanupBuilder.createDestroyAddr(cleanupLoc, argCopy.origArg); - cleanupBuilder.createDeallocStack(cleanupLoc, - argCopy.tempArgCopy->getDest()); + cleanupBuilder.createDeallocStack(cleanupLoc, argCopy.tempArg); } } return NewApply.getInstruction(); diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 28fd2baec1b11..00edb27c1fc2c 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -213,8 +213,8 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { openedVal = IEA; if (auto *CAI = dyn_cast(initI)) openedVal = CAI->getSrc(); -// if (auto *store = dyn_cast(initI)) -// openedVal = store->getSrc(); + if (auto *store = dyn_cast(initI)) + openedVal = store->getSrc(); } } if (auto *Open = dyn_cast(openedVal)) { diff --git a/test/SILOptimizer/existential_specializer_indirect_class.sil b/test/SILOptimizer/existential_specializer_indirect_class.sil index 2aa96abf1fbbd..5fb9696cfd986 100644 --- a/test/SILOptimizer/existential_specializer_indirect_class.sil +++ b/test/SILOptimizer/existential_specializer_indirect_class.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -wmo -enable-sil-verify-all -emit-sorted-sil %s -enable-existential-specializer -existential-specializer -inline -sil-combine -generic-specializer -devirtualizer 2>&1 | %FileCheck %s +// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all %s | %FileCheck %s sil_stage canonical @@ -8,74 +8,68 @@ protocol ClassProtocol: AnyObject { func method() } class C: ClassProtocol { func method() {} } -// CHECK-LABEL: sil shared @$s28test_indirect_class_protocolTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : ClassProtocol> (@in τ_0_0) -> () -sil hidden @test_indirect_class_protocol : $@convention(thin) (@in ClassProtocol) -> () { +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @test_indirect_class_protocol : $@convention(thin) (@in ClassProtocol) -> () +sil @test_indirect_class_protocol : $@convention(thin) (@in ClassProtocol) -> () { // CHECK-NEXT: // -// CHECK-NEXT: bb0(%0 : $*τ_0_0): -// CHECK-NEXT: %1 = load %0 -// CHECK-NEXT: %2 = init_existential_ref %1 -// CHECK-NEXT: %3 = alloc_stack $ClassProtocol -// CHECK-NEXT: store %2 to %3 +// CHECK-NEXT: bb0(%0 : $*ClassProtocol): bb0(%0 : $*ClassProtocol): - // CHECK-NEXT: destroy_addr %3 + // CHECK-NEXT: %1 = load %0 + // CHECK-NEXT: strong_release %1 + // CHECK-NEXT: strong_release %1 destroy_addr %0 : $*ClassProtocol - // CHECK-NEXT: dealloc_stack %3 // CHECK-NEXT: return undef return undef : $() } -// CHECK-LABEL: sil shared @$s39test_indirect_class_protocol_guaranteedTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : ClassProtocol> (@in_guaranteed τ_0_0) -> () -sil hidden @test_indirect_class_protocol_guaranteed : $@convention(thin) (@in_guaranteed ClassProtocol) -> () { +// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @test_indirect_class_protocol_guaranteed : $@convention(thin) (@in_guaranteed ClassProtocol) -> () +sil @test_indirect_class_protocol_guaranteed : $@convention(thin) (@in_guaranteed ClassProtocol) -> () { // CHECK-NEXT: // -// CHECK-NEXT: bb0(%0 : $*τ_0_0): -// CHECK-NEXT: %1 = load %0 -// CHECK-NEXT: %2 = init_existential_ref %1 -// CHECK-NEXT: %3 = alloc_stack $ClassProtocol -// CHECK-NEXT: store %2 to %3 +// CHECK-NEXT: bb0(%0 : $*ClassProtocol): bb0(%0 : $*ClassProtocol): - // CHECK-NEXT: %5 = load %3 : $*ClassProtocol +// CHECK-NEXT: [[INPUT:%1]] = load %0 %1 = load %0 : $*ClassProtocol - // CHECK-NEXT: %6 = open_existential_ref %5 + // CHECK-NEXT: [[OPENED:%.*]] = open_existential_ref [[INPUT]] + // CHECK-NEXT: [[CLOSED:%.*]] = unchecked_ref_cast [[OPENED]] %2 = open_existential_ref %1 : $ClassProtocol to $@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol - // CHECK-NEXT: %7 = witness_method $C, #ClassProtocol.method + // CHECK-NEXT: [[METHOD:%.*]] = witness_method $C, #ClassProtocol.method %f = witness_method $@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol, #ClassProtocol.method!1 : (Self) -> () -> (), %2 : $@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol : $@convention(witness_method : ClassProtocol) (@guaranteed Self) -> () - // CHECK-NEXT: %8 = unchecked_ref_cast %6 - // CHECK-NEXT: %9 = apply %7(%6) + // CHECK-NEXT: apply [[METHOD]]([[CLOSED]]) apply %f<@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol>(%2) : $@convention(witness_method : ClassProtocol) (@guaranteed Self) -> () - // CHECK-NEXT: dealloc_stack %3 + // CHECK-NEXT: strong_release [[INPUT]] // CHECK-NEXT: return undef return undef : $() } -// CHECK-LABEL: sil hidden @invoke_indirect_class_protocol -sil hidden @invoke_indirect_class_protocol : $@convention(thin) (@guaranteed C) -> () { +// CHECK-LABEL: sil @invoke_indirect_class_protocol : $@convention(thin) (@guaranteed C) -> () + +// Make sure both applies were inlined. + +// CHECK-NEXT: // +// CHECK-NEXT: bb0(%0 : $C): + +// CHECK-NEXT: [[METHOD:%.*]] = witness_method $C, #ClassProtocol.method +// CHECK-NEXT: strong_retain %0 +// CHECK-NEXT: apply [[METHOD]](%0) + +// CHECK-NEXT: strong_release %0 +// CHECK-NEXT: strong_release %0 +// CHECK-NEXT: strong_release %0 + +// CHECK-NEXT: return undef + +sil @invoke_indirect_class_protocol : $@convention(thin) (@guaranteed C) -> () { bb0(%0 : $C): %1 = init_existential_ref %0 : $C : $C, $ClassProtocol - // CHECK: [[INPUT:%.*]] = alloc_stack $ClassProtocol %z = alloc_stack $ClassProtocol retain_value %1 : $ClassProtocol store %1 to %z : $*ClassProtocol - // CHECK: [[SPECIALIZATION:%.*]] = function_ref @$s39test_indirect_class_protocol_guaranteedTf4e_n %f = function_ref @test_indirect_class_protocol_guaranteed : $@convention(thin) (@in_guaranteed ClassProtocol) -> () - // CHECK-NEXT: [[INPUT_LOAD:%.*]] = load [[INPUT]] - // CHECK-NEXT: [[INPUT_OPEN:%.*]] = open_existential_ref [[INPUT_LOAD]] : $ClassProtocol to $[[OPENED_TYPE:@opened(.*) ClassProtocol]] - // CHECK-NEXT: [[INPUT_OPEN_BUF:%.*]] = alloc_stack $[[OPENED_TYPE]] - // CHECK-NEXT: store [[INPUT_OPEN]] to [[INPUT_OPEN_BUF]] - // CHECK-NEXT: apply [[SPECIALIZATION]]<[[OPENED_TYPE]]>([[INPUT_OPEN_BUF]]) apply %f(%z) : $@convention(thin) (@in_guaranteed ClassProtocol) -> () - // CHECK-NEXT: dealloc_stack [[INPUT_OPEN_BUF]] - // CHECK: [[SPECIALIZATION:%.*]] = function_ref @$s28test_indirect_class_protocolTf4e_n %g = function_ref @test_indirect_class_protocol : $@convention(thin) (@in ClassProtocol) -> () - // CHECK-NEXT: [[INPUT_LOAD:%.*]] = load [[INPUT]] - // CHECK-NEXT: [[INPUT_OPEN:%.*]] = open_existential_ref [[INPUT_LOAD]] : $ClassProtocol to $[[OPENED_TYPE:@opened(.*) ClassProtocol]] - // CHECK-NEXT: [[INPUT_OPEN_BUF:%.*]] = alloc_stack $[[OPENED_TYPE]] - // CHECK-NEXT: store [[INPUT_OPEN]] to [[INPUT_OPEN_BUF]] - // CHECK-NEXT: apply [[SPECIALIZATION]]<[[OPENED_TYPE]]>([[INPUT_OPEN_BUF]]) apply %g(%z) : $@convention(thin) (@in ClassProtocol) -> () - // CHECK-NEXT: dealloc_stack [[INPUT_OPEN_BUF]] dealloc_stack %z : $*ClassProtocol return undef : $() From e93919b75495a56fbd94c9f59bb81598b5bd9a03 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 24 Jan 2020 12:44:11 -0800 Subject: [PATCH 025/381] Add comments, remove commented code, and other general cleanup --- .../SILCombiner/SILCombinerApplyVisitors.cpp | 17 ++++++++--------- lib/SILOptimizer/Utils/Existential.cpp | 13 +++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 6c0b9528358b1..cda4aaadcd594 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -791,9 +791,6 @@ struct ConcreteArgumentCopy { assert(!paramInfo.isIndirectMutating() && "A mutated opened existential value can't be replaced"); -// if (!paramInfo.isConsumed()) -// return None; - SILValue origArg = apply.getArgument(argIdx); // FIXME_opaque: With SIL opaque values, a formally indirect argument may be // passed as a SIL object. In this case, generate a copy_value for the new @@ -817,11 +814,13 @@ struct ConcreteArgumentCopy { SILBuilderWithScope B(apply.getInstruction(), BuilderCtx); auto loc = apply.getLoc(); auto *ASI = B.createAllocStack(loc, CEI.ConcreteValue->getType()); - + // If the type is an address, simple copy it. if (CEI.ConcreteValue->getType().isAddress()) { B.createCopyAddr(loc, CEI.ConcreteValue, ASI, IsNotTake, IsInitialization_t::IsInitialization); } else { + // Otherwise, we probably got the value from the source of a store + // instruction so, create a store into the temporary argument. B.createStore(loc, CEI.ConcreteValue, ASI, StoreOwnershipQualifier::Unqualified); } @@ -863,7 +862,6 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // Create the new set of arguments to apply including their substitutions. SubstitutionMap NewCallSubs = Apply.getSubstitutionMap(); SmallVector NewArgs; - bool UpdatedArgs = false; unsigned ArgIdx = 0; // Push the indirect result arguments. for (unsigned EndIdx = Apply.getSubstCalleeConv().getSILArgIndexOfFirstParam(); @@ -891,18 +889,19 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // Ensure that we have a concrete value to propagate. assert(CEI.ConcreteValue); - + + // If the parameter is expecting a pointer, then we need to create a + // alloc_stack to store the temporary value. if (Apply.getArgument(ArgIdx)->getType().isAddress()) { auto argSub = ConcreteArgumentCopy::generate(CEI, Apply, ArgIdx, BuilderCtx); if (argSub) { - UpdatedArgs = true; concreteArgCopies.push_back(*argSub); NewArgs.push_back(argSub->tempArg); } } else { + // Otherwise, we can just use the value itself. NewArgs.push_back(CEI.ConcreteValue); - UpdatedArgs = true; } // Form a new set of substitutions where the argument is @@ -926,7 +925,7 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( }); } - if (!UpdatedArgs) { + if (NewArgs.empty()) { // Remove any new instructions created while attempting to optimize this // apply. Since the apply was never rewritten, if they aren't removed here, // they will be removed later as dead when visited by SILCombine, causing diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 00edb27c1fc2c..626c717a640d2 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -69,9 +69,9 @@ findInitExistentialFromGlobalAddr(GlobalAddrInst *GAI, SILInstruction *Insn) { /// Returns the instruction that initializes the given stack address. This is /// currently either a init_existential_addr, unconditional_checked_cast_addr, -/// store, or copy_addr (if the instruction initializing the source of the copy cannot -/// be determined). Returns nullptr if the initializer does not dominate the -/// alloc_stack user \p ASIUser. If the value is copied from another stack +/// store, or copy_addr (if the instruction initializing the source of the copy +/// cannot be determined). Returns nullptr if the initializer does not dominate +/// the alloc_stack user \p ASIUser. If the value is copied from another stack /// location, \p isCopied is set to true. /// /// allocStackAddr may either itself be an AllocStackInst or an @@ -208,9 +208,10 @@ OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) { // %opened to %stack // %instance if (auto *initI = getStackInitInst(instance, user, isOpenedValueCopied)) { - if (auto *IEA = dyn_cast(initI)) - // TODO: this case doesn't need to exist beacuse openedVal will never be used - openedVal = IEA; + // init_existential_addr isn't handled here because it isn't considered an + // "opened" archtype. init_existential_addr should be handled by + // ConcreteExistentialInfo. + if (auto *CAI = dyn_cast(initI)) openedVal = CAI->getSrc(); if (auto *store = dyn_cast(initI)) From 2ac715c22f5d398c93b73bcab8d7417bc623214d Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 24 Jan 2020 12:48:34 -0800 Subject: [PATCH 026/381] Update devirtualize_(...)_two_stores RUN to use sil-opt --- .../devirtualize_protocol_composition_two_stores.sil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil index 0dcc2f1e2d624..86cf074464c15 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil +++ b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil @@ -1,4 +1,4 @@ -// RUN: %target-build-swift %s -O -wmo -emit-sil | %FileCheck %s +// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all %s | %FileCheck %s sil_stage canonical From b516d1930a5891a8a0f0c4593175ec1776e79630 Mon Sep 17 00:00:00 2001 From: Butta Date: Sat, 25 Jan 2020 01:38:45 +0530 Subject: [PATCH 027/381] [Docs] [android] Update to the latest NDK 21, fix commands, and remove cruft --- docs/Android.md | 81 ++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/docs/Android.md b/docs/Android.md index 02ff3267d4bcc..d0bbd19180910 100644 --- a/docs/Android.md +++ b/docs/Android.md @@ -1,11 +1,11 @@ # Getting Started with Swift on Android -The Swift stdlib can be compiled for Android armv7 targets, which makes it -possible to execute Swift code on a mobile device running Android. This guide -explains: +The Swift stdlib can be compiled for Android armv7 and aarch64 targets, which +makes it possible to execute Swift code on a mobile device running Android. +This guide explains: 1. How to run a simple "Hello, world" program on your Android device. -2. How to run the Swift test suite, targeting Android, and on an Android device. +2. How to run the Swift test suite on an Android device. If you encounter any problems following the instructions below, please file a bug using https://bugs.swift.org/. @@ -30,13 +30,12 @@ Swift-to-Java bridging. To follow along with this guide, you'll need: 1. A Linux environment capable of building Swift from source, specifically - Ubuntu 16.04 or Ubuntu 15.10 (Ubuntu 14.04 has not been tested recently). - The stdlib is currently only buildable for Android from a Linux environment. - Before attempting to build for Android, please make sure you are able to build - for Linux by following the instructions in the Swift project README. -2. The latest version of the Android NDK (r16 at the time of this writing), + Ubuntu 18.04 or Ubuntu 16.04. Before attempting to build for Android, + please make sure you are able to build for Linux by following the + instructions in the Swift project README. +2. The latest version of the Android NDK (r21 at the time of this writing), available to download here: - http://developer.android.com/ndk/downloads/index.html. + https://developer.android.com/ndk/downloads/index.html. 3. An Android device with remote debugging enabled. We require remote debugging in order to deploy built stdlib products to the device. You may turn on remote debugging by following the official instructions: @@ -74,16 +73,18 @@ Android NDK, as well as the directories that contain the `libicuucswift.so` and ``` $ ARM_DIR=path/to/libicu-libiconv-android -$ NDK_PATH=path/to/android-ndk16 +$ NDK_PATH=path/to/android-ndk21 $ utils/build-script \ -R \ # Build in ReleaseAssert mode. --android \ # Build for Android. - --android-ndk $NDK_PATH \ # Path to an Android NDK. + --android-ndk $NDK_PATH \ # Path to an Android NDK. + --android-arch armv7 \ # Optionally specify Android architecture, alternately aarch64 --android-api-level 21 \ # The Android API level to target. Swift only supports 21 or greater. --android-icu-uc ${ARM_DIR}/libicuucswift.so \ --android-icu-uc-include ${ARM_DIR}/icu/source/common \ --android-icu-i18n ${ARM_DIR}/libicui18nswift.so \ - --android-icu-i18n-include ${ARM_DIR}/icu/source/i18n + --android-icu-i18n-include ${ARM_DIR}/icu/source/i18n \ + --android-icu-data ${ARM_DIR}/libicudataswift.so ``` ### 3. Compiling `hello.swift` to run on an Android device @@ -94,27 +95,18 @@ Create a simple Swift file named `hello.swift`: print("Hello, Android") ``` -To compile it, we need to make sure the correct linker is used. Symlink the -gold linker in the Android NDK into your `PATH`: - -``` -$ sudo ln -s \ - /path/to/android-ndk-r16/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ld.gold \ - /usr/bin/armv7-none-linux-androideabi-ld.gold -``` - Then use the built Swift compiler from the previous step to compile a Swift source file, targeting Android: ``` -$ NDK_PATH="path/to/android-ndk16" -$ build/Ninja-ReleaseAssert/swift-linux-x86_64/bin/swiftc \ # The Swift compiler built in the previous step. - # The location of the tools used to build Android binaries - -tools-directory ${NDK_PATH}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin - -target armv7-none-linux-androideabi \ # Targeting android-armv7. - -sdk ${NDK_PATH}/platforms/android-21/arch-arm \ # Use the same NDK path and API version as you used to build the stdlib in the previous step. - -L ${NDK_PATH}/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a \ # Link the Android NDK's libc++ and libgcc. - -L ${NDK_PATH}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x \ +$ NDK_PATH="path/to/android-ndk21" +$ build/Ninja-ReleaseAssert/swift-linux-x86_64/bin/swiftc \ # The Swift compiler built in the previous step. + # The location of the tools used to build Android binaries + -tools-directory ${NDK_PATH}/toolchains/llvm/prebuilt/linux-x86_64/bin/ \ + -target armv7a-none-linux-androideabi \ # Targeting android-armv7. + -sdk ${NDK_PATH}/platforms/android-21/arch-arm \ # Use the same architecture and API version as you used to build the stdlib in the previous step. + -Xclang-linker -nostdlib++ \ # Don't link libc++, and supply the path to libgcc. + -L ${NDK_PATH}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/armv7-a \ hello.swift ``` @@ -149,7 +141,6 @@ $ adb push build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift/android/libswi $ adb push build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift/android/libswiftGlibc.so /data/local/tmp $ adb push build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift/android/libswiftSwiftOnoneSupport.so /data/local/tmp $ adb push build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift/android/libswiftRemoteMirror.so /data/local/tmp -$ adb push build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift/android/libswiftSwiftExperimental.so /data/local/tmp ``` You will also need to push the icu libraries: @@ -163,7 +154,7 @@ adb push /path/to/libicu-android/armeabi-v7a/libicuucswift.so /data/local/tmp In addition, you'll also need to copy the Android NDK's libc++: ``` -$ adb push /path/to/android-ndk-r14/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_shared.so /data/local/tmp +$ adb push /path/to/android-ndk-r21/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_shared.so /data/local/tmp ``` Finally, you'll need to copy the `hello` executable you built in the @@ -192,7 +183,7 @@ Congratulations! You've just run your first Swift program on Android. ## Running the Swift test suite hosted on an Android device When running the test suite, build products are automatically pushed to your -device. As in part one, you'll need to connect your Android device via USB: +device. As in part four, you'll need to connect your Android device via USB: 1. Connect your Android device to your computer via USB. Ensure that remote debugging is enabled for that device by following the official instructions: @@ -204,28 +195,14 @@ device. As in part one, you'll need to connect your Android device via USB: ``` $ utils/build-script \ -R \ # Build in ReleaseAssert mode. - -T \ # Run all tests. + -T --host-test \ # Run all tests, including on the Android host. --android \ # Build for Android. - --android-ndk ~/android-ndk-r13 \ # Path to an Android NDK. + --android-ndk ~/android-ndk-r21 \ # Path to an Android NDK. + --android-arch armv7 \ # Optionally specify Android architecture, alternately aarch64 --android-ndk-version 21 \ --android-icu-uc ~/libicu-android/armeabi-v7a/libicuuc.so \ --android-icu-uc-include ~/libicu-android/armeabi-v7a/icu/source/common \ --android-icu-i18n ~/libicu-android/armeabi-v7a/libicui18n.so \ - --android-icu-i18n-include ~/libicu-android/armeabi-v7a/icu/source/i18n/ -``` - -## Build Android Toolchain - -This toolchain will generate the .so and .swiftmodule files of the Swift standard library and Foundation framework for the Android environment, armv7 architecture. Those files are needed when building any Swift library to be included in an application for Android. - -To build the toolchain run: - -``` -$ utils/android/build-toolchain -``` - -It will be built on: - -``` -path/to/swift-source/swift-android-toolchain + --android-icu-i18n-include ~/libicu-android/armeabi-v7a/icu/source/i18n/ \ + --android-icu-data ~/libicu-android/armeabi-v7a/libicudata.so ``` From e38da06f7c4e86584fffd8ca94a75d5e2f763b36 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 26 Jan 2020 19:34:16 -0800 Subject: [PATCH 028/381] Stash debugging changes --- lib/IRGen/AllocStackHoisting.cpp | 3 +++ lib/SILOptimizer/PassManager/PassManager.cpp | 5 ++++ .../SILCombiner/SILCombinerApplyVisitors.cpp | 23 +++++++++++-------- lib/Sema/TypeChecker.cpp | 6 +++-- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/IRGen/AllocStackHoisting.cpp b/lib/IRGen/AllocStackHoisting.cpp index e4c125fdbe1cd..8e5fc1fbad84d 100644 --- a/lib/IRGen/AllocStackHoisting.cpp +++ b/lib/IRGen/AllocStackHoisting.cpp @@ -421,6 +421,8 @@ void HoistAllocStack::hoist() { /// Try to hoist generic alloc_stack instructions to the entry block. /// Returns true if the function was changed. bool HoistAllocStack::run() { + llvm::errs() << "Running hoist alloc stack on: " << F->getName() << "\n"; + collectHoistableInstructions(); // Nothing to hoist? @@ -441,6 +443,7 @@ class AllocStackHoisting : public SILFunctionTransform { if (Changed) { PM->invalidateAnalysis(F, SILAnalysis::InvalidationKind::Instructions); } + llvm::errs() << "Completed alloc stack hoisting pass.\n"; } }; } // end anonymous namespace diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index a1b5200e81ebb..93bc1506f81c9 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -407,6 +407,11 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { Mod->registerDeleteNotificationHandler(SFT); if (breakBeforeRunning(F->getName(), SFT)) LLVM_BUILTIN_DEBUGTRAP; + llvm::errs() << "Running SIL pass: " << SFT->getID() << "\n"; + llvm::errs() << "Function named: " << F->getName() << "\n"; + F->getLocation().dump(F->getModule().getSourceManager()); + llvm::errs() << "***** \n"; + SFT->run(); assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); Mod->removeDeleteNotificationHandler(SFT); diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index cda4aaadcd594..6526567750344 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -791,6 +791,9 @@ struct ConcreteArgumentCopy { assert(!paramInfo.isIndirectMutating() && "A mutated opened existential value can't be replaced"); + if (!paramInfo.isConsumed()) + return None; + SILValue origArg = apply.getArgument(argIdx); // FIXME_opaque: With SIL opaque values, a formally indirect argument may be // passed as a SIL object. In this case, generate a copy_value for the new @@ -821,6 +824,7 @@ struct ConcreteArgumentCopy { } else { // Otherwise, we probably got the value from the source of a store // instruction so, create a store into the temporary argument. + B.createStrongRetain(loc, CEI.ConcreteValue, B.getDefaultAtomicity()); B.createStore(loc, CEI.ConcreteValue, ASI, StoreOwnershipQualifier::Unqualified); } @@ -856,6 +860,11 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( const llvm::SmallDenseMap &COAIs, SILBuilderContext &BuilderCtx) { + // Bail on try_apply. + // TODO: support try_apply. + if (isa(Apply)) + return nullptr; + // Ensure that the callee is polymorphic. assert(Apply.getOrigCalleeType()->isPolymorphic()); @@ -898,6 +907,8 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( if (argSub) { concreteArgCopies.push_back(*argSub); NewArgs.push_back(argSub->tempArg); + } else { + NewArgs.clear(); } } else { // Otherwise, we can just use the value itself. @@ -936,15 +947,9 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( } // Now create the new apply instruction. SILBuilderWithScope ApplyBuilder(Apply.getInstruction(), BuilderCtx); - FullApplySite NewApply; - if (auto *TAI = dyn_cast(Apply)) - NewApply = ApplyBuilder.createTryApply( - Apply.getLoc(), Apply.getCallee(), NewCallSubs, NewArgs, - TAI->getNormalBB(), TAI->getErrorBB()); - else - NewApply = ApplyBuilder.createApply( - Apply.getLoc(), Apply.getCallee(), NewCallSubs, NewArgs, - cast(Apply)->isNonThrowing()); + FullApplySite NewApply = ApplyBuilder.createApply( + Apply.getLoc(), Apply.getCallee(), NewCallSubs, NewArgs, + cast(Apply)->isNonThrowing()); if (auto NewAI = dyn_cast(NewApply)) replaceInstUsesWith(*cast(Apply.getInstruction()), NewAI); diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 4918500c12e5f..8f1ea1b517438 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -559,12 +559,14 @@ swift::handleSILGenericParams(GenericParamList *genericParams, auto genericParams = nestedList[i]; genericParams->setDepth(i); } - + llvm::errs() << "Checking generic sig..."; auto sig = TypeChecker::checkGenericSignature(nestedList.back(), DC, /*parentSig=*/nullptr, /*allowConcreteGenericParams=*/true); - return (sig ? sig->getGenericEnvironment() : nullptr); + auto out = (sig ? sig->getGenericEnvironment() : nullptr); + llvm::errs() << "done.\n"; + return out; } void swift::typeCheckPatternBinding(PatternBindingDecl *PBD, From 31e9807fc86200da75260115167bad161f0b56fa Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Mon, 27 Jan 2020 22:00:33 -0500 Subject: [PATCH 029/381] Update AnyHashable documentation --- stdlib/public/core/AnyHashable.swift | 31 +++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index 722074bf05ed6..4812addee763c 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -107,22 +107,33 @@ internal struct _ConcreteHashableBox: _AnyHashableBox { /// A type-erased hashable value. /// /// The `AnyHashable` type forwards equality comparisons and hashing operations -/// to an underlying hashable value, hiding its specific underlying type. +/// to an underlying hashable value, hiding the type of the wrapped value. /// /// You can store mixed-type keys in dictionaries and other collections that /// require `Hashable` conformance by wrapping mixed-type keys in /// `AnyHashable` instances: /// /// let descriptions: [AnyHashable: Any] = [ -/// AnyHashable("😄"): "emoji", /// AnyHashable(42): "an Int", /// AnyHashable(Int8(43)): "an Int8", /// AnyHashable(Set(["a", "b"])): "a set of strings" /// ] -/// print(descriptions[AnyHashable(42)]!) // prints "an Int" -/// print(descriptions[AnyHashable(45)]) // prints "nil" +/// print(descriptions[AnyHashable(42)]!) // prints "an Int" +/// print(descriptions[AnyHashable(Int8(42))]!) // prints "an Int" /// print(descriptions[AnyHashable(Int8(43))]!) // prints "an Int8" +/// print(descriptions[AnyHashable(44)]) // prints "nil" /// print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings" +/// +/// If the wrapped value is of a type that can be bridged using `as` to a +/// different but compatible class defined in Foundation, or vice versa, then +/// the underlying hashable value to which operations are forwarded might not be +/// of the wrapped type itself but of a transitively bridged type: +/// +/// let x: AnyHashable = [1, 2, 3] as NSArray +/// let y: AnyHashable = [1, 2, 3] as [Double] +/// let z: AnyHashable = [1, 2, 3] as [Int8] +/// print(x == y, x.hashValue == y.hashValue) // prints "true true" +/// print(y == z, y.hashValue == z.hashValue) // prints "true true" @frozen public struct AnyHashable { internal var _box: _AnyHashableBox @@ -189,12 +200,22 @@ public struct AnyHashable { extension AnyHashable: Equatable { /// Returns a Boolean value indicating whether two type-erased hashable - /// instances wrap the same type and value. + /// instances wrap the same value. /// /// Two instances of `AnyHashable` compare as equal if and only if the /// underlying types have the same conformance to the `Equatable` protocol /// and the underlying values compare as equal. /// + /// If the wrapped values are of a type that can be bridged using `as` to a + /// different but compatible class defined in Foundation, or vice versa, then + /// the underlying values compared might not be of the wrapped type itself but + /// of a transitively bridged type: + /// + /// let a = (42 as NSNumber as AnyHashable) + /// let b = (42 as Double as AnyHashable) + /// let c = (42 as Int8 as AnyHashable) + /// print(a == b, b == c, a == c) // prints "true true true" + /// /// - Parameters: /// - lhs: A type-erased hashable value. /// - rhs: Another type-erased hashable value. From 809f569bccb3886ff6394bfd04ce7d2b0163c63e Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 28 Jan 2020 15:27:37 -0800 Subject: [PATCH 030/381] Remove debug code and update check for successful conversion of arguments. --- lib/IRGen/AllocStackHoisting.cpp | 3 --- lib/SILOptimizer/PassManager/PassManager.cpp | 5 ----- lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp | 4 +--- lib/Sema/TypeChecker.cpp | 6 ++---- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/IRGen/AllocStackHoisting.cpp b/lib/IRGen/AllocStackHoisting.cpp index 8e5fc1fbad84d..e4c125fdbe1cd 100644 --- a/lib/IRGen/AllocStackHoisting.cpp +++ b/lib/IRGen/AllocStackHoisting.cpp @@ -421,8 +421,6 @@ void HoistAllocStack::hoist() { /// Try to hoist generic alloc_stack instructions to the entry block. /// Returns true if the function was changed. bool HoistAllocStack::run() { - llvm::errs() << "Running hoist alloc stack on: " << F->getName() << "\n"; - collectHoistableInstructions(); // Nothing to hoist? @@ -443,7 +441,6 @@ class AllocStackHoisting : public SILFunctionTransform { if (Changed) { PM->invalidateAnalysis(F, SILAnalysis::InvalidationKind::Instructions); } - llvm::errs() << "Completed alloc stack hoisting pass.\n"; } }; } // end anonymous namespace diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index 93bc1506f81c9..a1b5200e81ebb 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -407,11 +407,6 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { Mod->registerDeleteNotificationHandler(SFT); if (breakBeforeRunning(F->getName(), SFT)) LLVM_BUILTIN_DEBUGTRAP; - llvm::errs() << "Running SIL pass: " << SFT->getID() << "\n"; - llvm::errs() << "Function named: " << F->getName() << "\n"; - F->getLocation().dump(F->getModule().getSourceManager()); - llvm::errs() << "***** \n"; - SFT->run(); assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); Mod->removeDeleteNotificationHandler(SFT); diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 6526567750344..9b68c3d6af41e 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -907,8 +907,6 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( if (argSub) { concreteArgCopies.push_back(*argSub); NewArgs.push_back(argSub->tempArg); - } else { - NewArgs.clear(); } } else { // Otherwise, we can just use the value itself. @@ -936,7 +934,7 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( }); } - if (NewArgs.empty()) { + if (NewArgs.size() != Apply.getNumArguments()) { // Remove any new instructions created while attempting to optimize this // apply. Since the apply was never rewritten, if they aren't removed here, // they will be removed later as dead when visited by SILCombine, causing diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 8f1ea1b517438..4918500c12e5f 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -559,14 +559,12 @@ swift::handleSILGenericParams(GenericParamList *genericParams, auto genericParams = nestedList[i]; genericParams->setDepth(i); } - llvm::errs() << "Checking generic sig..."; + auto sig = TypeChecker::checkGenericSignature(nestedList.back(), DC, /*parentSig=*/nullptr, /*allowConcreteGenericParams=*/true); - auto out = (sig ? sig->getGenericEnvironment() : nullptr); - llvm::errs() << "done.\n"; - return out; + return (sig ? sig->getGenericEnvironment() : nullptr); } void swift::typeCheckPatternBinding(PatternBindingDecl *PBD, From ddeef6483623af432fed5fdbbd6c7e84df351085 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 28 Jan 2020 17:09:52 -0800 Subject: [PATCH 031/381] Re-add support for try_apply instruction and update tests. Re-work tests based on updated optimizations and check specialized functions. --- .../SILCombiner/SILCombinerApplyVisitors.cpp | 18 ++++---- ...existential_specializer_indirect_class.sil | 42 +++++++++---------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 9b68c3d6af41e..d20998b4b9a5d 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -859,12 +859,6 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( FullApplySite Apply, const llvm::SmallDenseMap &COAIs, SILBuilderContext &BuilderCtx) { - - // Bail on try_apply. - // TODO: support try_apply. - if (isa(Apply)) - return nullptr; - // Ensure that the callee is polymorphic. assert(Apply.getOrigCalleeType()->isPolymorphic()); @@ -945,9 +939,15 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( } // Now create the new apply instruction. SILBuilderWithScope ApplyBuilder(Apply.getInstruction(), BuilderCtx); - FullApplySite NewApply = ApplyBuilder.createApply( - Apply.getLoc(), Apply.getCallee(), NewCallSubs, NewArgs, - cast(Apply)->isNonThrowing()); + FullApplySite NewApply; + if (auto *TAI = dyn_cast(Apply)) + NewApply = ApplyBuilder.createTryApply( + Apply.getLoc(), Apply.getCallee(), NewCallSubs, NewArgs, + TAI->getNormalBB(), TAI->getErrorBB()); + else + NewApply = ApplyBuilder.createApply( + Apply.getLoc(), Apply.getCallee(), NewCallSubs, NewArgs, + cast(Apply)->isNonThrowing()); if (auto NewAI = dyn_cast(NewApply)) replaceInstUsesWith(*cast(Apply.getInstruction()), NewAI); diff --git a/test/SILOptimizer/existential_specializer_indirect_class.sil b/test/SILOptimizer/existential_specializer_indirect_class.sil index 5fb9696cfd986..7f0ea56c56d81 100644 --- a/test/SILOptimizer/existential_specializer_indirect_class.sil +++ b/test/SILOptimizer/existential_specializer_indirect_class.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all %s | %FileCheck %s +// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all -sil-disable-pass=DeadFunctionElimination %s | %FileCheck %s sil_stage canonical @@ -15,46 +15,42 @@ sil @test_indirect_class_protocol : $@convention(thin) (@in ClassProtocol) -> () bb0(%0 : $*ClassProtocol): // CHECK-NEXT: %1 = load %0 // CHECK-NEXT: strong_release %1 - // CHECK-NEXT: strong_release %1 destroy_addr %0 : $*ClassProtocol // CHECK-NEXT: return undef return undef : $() } -// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @test_indirect_class_protocol_guaranteed : $@convention(thin) (@in_guaranteed ClassProtocol) -> () -sil @test_indirect_class_protocol_guaranteed : $@convention(thin) (@in_guaranteed ClassProtocol) -> () { +// Check that all the opened types are optimized away in the specialization of test_indirect_class_protocol_guaranteed +// CHECK-LABEL: sil shared @$s39test_indirect_class_protocol_guaranteedTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : ClassProtocol> (@in_guaranteed τ_0_0) -> () // CHECK-NEXT: // -// CHECK-NEXT: bb0(%0 : $*ClassProtocol): +// CHECK-NEXT: bb0(%0 : $*τ_0_0): + // CHECK-NEXT: [[INPUT:%1]] = load %0 + // CHECK-NEXT: [[METHOD:%.*]] = witness_method $C, #ClassProtocol.method + // CHECK-NEXT: [[ARG:%.*]] = unchecked_ref_cast [[INPUT]] + // CHECK-NEXT: apply [[METHOD]]([[ARG]]) + // CHECK-NEXT: return undef + +sil @test_indirect_class_protocol_guaranteed : $@convention(thin) (@in_guaranteed ClassProtocol) -> () { bb0(%0 : $*ClassProtocol): -// CHECK-NEXT: [[INPUT:%1]] = load %0 %1 = load %0 : $*ClassProtocol - // CHECK-NEXT: [[OPENED:%.*]] = open_existential_ref [[INPUT]] - // CHECK-NEXT: [[CLOSED:%.*]] = unchecked_ref_cast [[OPENED]] %2 = open_existential_ref %1 : $ClassProtocol to $@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol - // CHECK-NEXT: [[METHOD:%.*]] = witness_method $C, #ClassProtocol.method %f = witness_method $@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol, #ClassProtocol.method!1 : (Self) -> () -> (), %2 : $@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol : $@convention(witness_method : ClassProtocol) (@guaranteed Self) -> () - // CHECK-NEXT: apply [[METHOD]]([[CLOSED]]) apply %f<@opened("ABCDEF01-ABCD-ABCD-ABCD-ABCDEFABCDEF") ClassProtocol>(%2) : $@convention(witness_method : ClassProtocol) (@guaranteed Self) -> () - // CHECK-NEXT: strong_release [[INPUT]] - // CHECK-NEXT: return undef return undef : $() } -// CHECK-LABEL: sil @invoke_indirect_class_protocol : $@convention(thin) (@guaranteed C) -> () - -// Make sure both applies were inlined. - +// Check that a specialization of test_indirect_class_protocol is created. +// CHECK-LABEL: sil shared [signature_optimized_thunk] [always_inline] @$s28test_indirect_class_protocolTf4e_n4main1CC_Tg5 : $@convention(thin) (@owned C) -> () // CHECK-NEXT: // // CHECK-NEXT: bb0(%0 : $C): - -// CHECK-NEXT: [[METHOD:%.*]] = witness_method $C, #ClassProtocol.method -// CHECK-NEXT: strong_retain %0 -// CHECK-NEXT: apply [[METHOD]](%0) - -// CHECK-NEXT: strong_release %0 -// CHECK-NEXT: strong_release %0 // CHECK-NEXT: strong_release %0 +// CHECK-NEXT: return undef +// CHECK-LABEL: end sil function '$s28test_indirect_class_protocolTf4e_n4main1CC_Tg5' +// Check the generated specialization of test_indirect_class_protocol +// CHECK-LABEL: sil shared @$s28test_indirect_class_protocolTf4e_n4main1CC_Tg5Tf4d_n : $@convention(thin) () -> () +// Make sure *everything* was inlined / optimized away +// CHECK-NEXT: bb0: // CHECK-NEXT: return undef sil @invoke_indirect_class_protocol : $@convention(thin) (@guaranteed C) -> () { From 1e861acd1fcab4cac6728747ecd9e6a0f93ca2c9 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Tue, 28 Jan 2020 21:00:52 -0500 Subject: [PATCH 032/381] Incorporate review comments --- stdlib/public/core/AnyHashable.swift | 54 +++++++++++++--------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index 4812addee763c..08c1181280fb9 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -109,6 +109,11 @@ internal struct _ConcreteHashableBox: _AnyHashableBox { /// The `AnyHashable` type forwards equality comparisons and hashing operations /// to an underlying hashable value, hiding the type of the wrapped value. /// +/// For types that support conversions between each other using `as` or `as?` +/// (such as `Int` and `NSNumber`), `AnyHashable` treats their values as +/// equivalent when type-erased by forwarding operations to canonical +/// representations of the wrapped values. +/// /// You can store mixed-type keys in dictionaries and other collections that /// require `Hashable` conformance by wrapping mixed-type keys in /// `AnyHashable` instances: @@ -124,16 +129,9 @@ internal struct _ConcreteHashableBox: _AnyHashableBox { /// print(descriptions[AnyHashable(44)]) // prints "nil" /// print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings" /// -/// If the wrapped value is of a type that can be bridged using `as` to a -/// different but compatible class defined in Foundation, or vice versa, then -/// the underlying hashable value to which operations are forwarded might not be -/// of the wrapped type itself but of a transitively bridged type: -/// -/// let x: AnyHashable = [1, 2, 3] as NSArray -/// let y: AnyHashable = [1, 2, 3] as [Double] -/// let z: AnyHashable = [1, 2, 3] as [Int8] -/// print(x == y, x.hashValue == y.hashValue) // prints "true true" -/// print(y == z, y.hashValue == z.hashValue) // prints "true true" +/// Note that `AnyHashable` instances are not guaranteed to preserve the hash +/// encoding of their wrapped values, even in cases where hash values appear to +/// match in a particular release of the standard library. @frozen public struct AnyHashable { internal var _box: _AnyHashableBox @@ -165,7 +163,7 @@ public struct AnyHashable { /// The value wrapped by this instance. /// /// The `base` property can be cast back to its original type using one of - /// the casting operators (`as?`, `as!`, or `as`). + /// the type casting operators (`as?`, `as!`, or `as`). /// /// let anyMessage = AnyHashable("Hello world!") /// if let unwrappedMessage = anyMessage.base as? String { @@ -200,21 +198,23 @@ public struct AnyHashable { extension AnyHashable: Equatable { /// Returns a Boolean value indicating whether two type-erased hashable - /// instances wrap the same value. + /// instances wrap the same value of equivalent type. /// - /// Two instances of `AnyHashable` compare as equal if and only if the - /// underlying types have the same conformance to the `Equatable` protocol - /// and the underlying values compare as equal. + /// `AnyHashable` considers bridged counterparts (such as a `String` and an + /// `NSString`) of the same value to be equivalent when type-erased. Where + /// those compatible types have different definitions of equality comparisons, + /// values that originally compare as not equal may then compare as equal when + /// they are type-erased by conversion to `AnyHashable`: /// - /// If the wrapped values are of a type that can be bridged using `as` to a - /// different but compatible class defined in Foundation, or vice versa, then - /// the underlying values compared might not be of the wrapped type itself but - /// of a transitively bridged type: - /// - /// let a = (42 as NSNumber as AnyHashable) - /// let b = (42 as Double as AnyHashable) - /// let c = (42 as Int8 as AnyHashable) - /// print(a == b, b == c, a == c) // prints "true true true" + /// let string1 = "café" + /// let string2 = "cafe\u{301}" // U+301 COMBINING ACUTE ACCENT + /// let nsString1 = string1 as NSString + /// let nsString2 = string2 as NSString + /// let typeErased1 = nsString1 as AnyHashable + /// let typeErased2 = nsString2 as AnyHashable + /// print(string1 == string2) // prints "true" + /// print(nsString1 == nsString2) // prints "false" + /// print(typeErased1 == typeErased2) // prints "true" /// /// - Parameters: /// - lhs: A type-erased hashable value. @@ -225,16 +225,10 @@ extension AnyHashable: Equatable { } extension AnyHashable: Hashable { - /// The hash value. public var hashValue: Int { return _box._canonicalBox._hashValue } - /// Hashes the essential components of this value by feeding them into the - /// given hasher. - /// - /// - Parameter hasher: The hasher to use when combining the components - /// of this instance. public func hash(into hasher: inout Hasher) { _box._canonicalBox._hash(into: &hasher) } From 518ccce520a1509c24b783da7954b7e0c05f627b Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Tue, 28 Jan 2020 21:07:58 -0500 Subject: [PATCH 033/381] Optimize documentation wording for clarity --- stdlib/public/core/AnyHashable.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index 08c1181280fb9..507d6ca82d84c 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -110,8 +110,8 @@ internal struct _ConcreteHashableBox: _AnyHashableBox { /// to an underlying hashable value, hiding the type of the wrapped value. /// /// For types that support conversions between each other using `as` or `as?` -/// (such as `Int` and `NSNumber`), `AnyHashable` treats their values as -/// equivalent when type-erased by forwarding operations to canonical +/// (such as `Int` and `NSNumber`), `AnyHashable` treats their values +/// equivalently when type-erased by forwarding operations to canonical /// representations of the wrapped values. /// /// You can store mixed-type keys in dictionaries and other collections that From 4ee0cc16e1afe6dbf5e2a80b7b50e239e6dfad14 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Wed, 29 Jan 2020 21:15:04 -0500 Subject: [PATCH 034/381] Update stdlib/public/core/AnyHashable.swift Co-Authored-By: Karoy Lorentey --- stdlib/public/core/AnyHashable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index 507d6ca82d84c..044d52d443928 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -198,7 +198,7 @@ public struct AnyHashable { extension AnyHashable: Equatable { /// Returns a Boolean value indicating whether two type-erased hashable - /// instances wrap the same value of equivalent type. + /// instances wrap the same underlying value. /// /// `AnyHashable` considers bridged counterparts (such as a `String` and an /// `NSString`) of the same value to be equivalent when type-erased. Where From cb26625f5de905cc16f57b4a1d2b4e8f9b93da38 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 30 Jan 2020 15:46:43 -0800 Subject: [PATCH 035/381] [sourcekitd] Send a notification when semantic functionality is enabled again --- tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index ccade323104fe..6024b611b7d70 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -3106,6 +3106,13 @@ static bool isSemanticEditorDisabled() { NSEC_PER_SEC * Seconds); dispatch_after(When, dispatch_get_main_queue(), ^{ Toggle = SemaInfoToggle::Enable; + + static UIdent SemaEnabledNotificationUID( + "source.notification.sema_enabled"); + ResponseBuilder RespBuilder; + auto Dict = RespBuilder.getDictionary(); + Dict.set(KeyNotification, SemaEnabledNotificationUID); + sourcekitd::postNotification(RespBuilder.createResponse()); }); }); } From ac4fa332abfc5318a6c3ee4f346f613ed5d72b10 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 31 Jan 2020 09:17:07 -0800 Subject: [PATCH 036/381] Stash optimization debugging --- .../ExistentialSpecializer.cpp | 3 +++ lib/SILOptimizer/PassManager/PassManager.cpp | 15 +++++++++++++++ lib/SILOptimizer/Transforms/AllocBoxToStack.cpp | 2 ++ lib/SILOptimizer/Transforms/Devirtualizer.cpp | 5 ++++- .../Transforms/GenericSpecializer.cpp | 2 ++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp index 44954304de13c..667f4b47791e4 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp @@ -336,6 +336,9 @@ void ExistentialSpecializer::specializeExistentialArgsInAppliesWithinFunction( /// Update statistics on the number of functions specialized. ++NumFunctionsWithExistentialArgsSpecialized; + llvm::errs() << "Adding function to pass manger in existential\n"; + llvm::errs() << ET.getExistentialSpecializedFunction()->getName() << "\n"; + /// Make sure the PM knows about the new specialized inner function. addFunctionToPassManagerWorklist(ET.getExistentialSpecializedFunction(), Callee); diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index a1b5200e81ebb..73e77ca0aad9e 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -407,9 +407,24 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { Mod->registerDeleteNotificationHandler(SFT); if (breakBeforeRunning(F->getName(), SFT)) LLVM_BUILTIN_DEBUGTRAP; + + + llvm::errs() << "FUNC NAME: "; + F->printName(llvm::errs()); + llvm::errs() << "\nDEMA NAME: " << Demangle::demangleSymbolAsString(F->getName()) << "\n"; + llvm::errs() << "PASS NAME: " << SFT->getID() << "\n"; + SFT->run(); assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); Mod->removeDeleteNotificationHandler(SFT); + + llvm::errs() << "Finished running pass\n\t\t****\n"; + if (F->getName().contains("ss6UInt32VSjsSj9magnitude9MagnitudeQzvgTW") || + F->getName().contains("ss2geoiySbx_q_q0_q1_q2_q3_t_x_q_q0_q1_q2_q3_ttSLRzSLR_SLR0_SLR1_SLR2_SLR3_r4_lF")) { + llvm::errs() << "Dumping module to file..."; + F->getModule().dump("/Users/zoe/Developer/swift-source/build/buildbot_incremental/swift-macosx-x86_64/module_dump.txt"); + llvm::errs() << "done.\n"; + } auto Delta = (std::chrono::system_clock::now() - StartTime).count(); if (SILPrintPassTime) { diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index ddf770d2a0874..2e79bfcae2175 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -793,6 +793,8 @@ specializePartialApply(SILOptFunctionBuilder &FuncBuilder, ClonedName); Cloner.populateCloned(); ClonedFn = Cloner.getCloned(); + llvm::errs() << "Adding function to pass manger in alloc box\n"; + llvm::errs() << ClonedFn->getName() << "\n"; pass.T->addFunctionToPassManagerWorklist(ClonedFn, F); } diff --git a/lib/SILOptimizer/Transforms/Devirtualizer.cpp b/lib/SILOptimizer/Transforms/Devirtualizer.cpp index 27e140b6993be..b14f7fb12bc28 100644 --- a/lib/SILOptimizer/Transforms/Devirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/Devirtualizer.cpp @@ -102,8 +102,11 @@ bool Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F, // We may not have optimized these functions yet, and it could // be beneficial to rerun some earlier passes on the current // function now that we've made these direct references visible. - if (CalleeFn->isDefinition() && CalleeFn->shouldOptimize()) + if (CalleeFn->isDefinition() && CalleeFn->shouldOptimize()) { + llvm::errs() << "Adding function to pass manger in devirt\n"; + llvm::errs() << CalleeFn->getName() << "\n"; addFunctionToPassManagerWorklist(CalleeFn, nullptr); + } } return Changed; diff --git a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp index 82dd9d4d33ce8..5817677c58302 100644 --- a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp @@ -129,6 +129,8 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) { // (as opposed to returning a previous specialization), we need to notify // the pass manager so that the new functions get optimized. for (SILFunction *NewF : reverse(NewFunctions)) { + llvm::errs() << "Adding function to pass manger in generic spec\n"; + llvm::errs() << NewF->getName() << "\n"; addFunctionToPassManagerWorklist(NewF, Callee); } } From d913eefcc93f8c80d6d1a6de4ea898a2838d8b6f Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Fri, 31 Jan 2020 15:59:40 -0800 Subject: [PATCH 037/381] Remove dependency on libatomic on Linux Due to some unfortunate interplay between clang and libstdc++, clang was not able to correctly identify to alignment of PoolRange and SideTableRefCountBits, causing it to emit library calls instead of inlining atomic operations. This was fixed by adding the appropriate alignment to those types. In addition to that the march for the Linux target was set to 'core2', which is the earliest architecture to support cx16, which is necessary for the atomic operations on PoolRange. --- cmake/modules/AddSwift.cmake | 7 ++++++- stdlib/public/SwiftShims/RefCount.h | 2 +- stdlib/public/runtime/Metadata.cpp | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index a89442f485d85..81d62031ea970 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -349,6 +349,11 @@ function(_add_variant_c_compile_flags) list(APPEND result "-D__ANDROID_API__=${SWIFT_ANDROID_API_LEVEL}") endif() + if("${CFLAGS_SDK}" STREQUAL "LINUX") + # this is the minimum architecture that supports 16 byte CAS, which is necessary to avoid a dependency to libatomic + list(APPEND result "-march=core2") + endif() + set("${CFLAGS_RESULT_VAR_NAME}" "${result}" PARENT_SCOPE) endfunction() @@ -466,7 +471,7 @@ function(_add_variant_link_flags) RESULT_VAR_NAME result MACCATALYST_BUILD_FLAVOR "${LFLAGS_MACCATALYST_BUILD_FLAVOR}") if("${LFLAGS_SDK}" STREQUAL "LINUX") - list(APPEND link_libraries "pthread" "atomic" "dl") + list(APPEND link_libraries "pthread" "dl") elseif("${LFLAGS_SDK}" STREQUAL "FREEBSD") list(APPEND link_libraries "pthread") elseif("${LFLAGS_SDK}" STREQUAL "CYGWIN") diff --git a/stdlib/public/SwiftShims/RefCount.h b/stdlib/public/SwiftShims/RefCount.h index 1de032c56f49b..c4fee4c22dab0 100644 --- a/stdlib/public/SwiftShims/RefCount.h +++ b/stdlib/public/SwiftShims/RefCount.h @@ -631,7 +631,7 @@ class RefCountBitsT { typedef RefCountBitsT InlineRefCountBits; -class SideTableRefCountBits : public RefCountBitsT +class alignas(sizeof(void*) * 2) SideTableRefCountBits : public RefCountBitsT { uint32_t weakBits; diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 2546d90dec5cd..687dd83dbb7e4 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -5115,7 +5115,7 @@ void swift::checkMetadataDependencyCycle(const Metadata *startMetadata, /***************************************************************************/ namespace { - struct PoolRange { + struct alignas(sizeof(uintptr_t) * 2) PoolRange { static constexpr uintptr_t PageSize = 16 * 1024; static constexpr uintptr_t MaxPoolAllocationSize = PageSize / 2; From 6db05fdbca83f8d3cacdea94531c643bff94716c Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Sat, 1 Feb 2020 23:25:31 -0800 Subject: [PATCH 038/381] Set march for unittest on Linux --- cmake/modules/AddSwiftUnittests.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 5da0ae3ac44e3..2c46a289c4960 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -48,8 +48,8 @@ function(add_swift_unittest test_dirname) "${android_system_libs}") set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_LIBRARIES "log") elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") - set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_LIBRARIES - "atomic") + set_property(TARGET "${test_dirname}" APPEND PROPERTY COMPILE_FLAGS + "-march=core2") endif() find_program(LDLLD_PATH "ld.lld") From 34a331eb4d3d298066fd5f16ca66fd5cefb7f71a Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Sun, 2 Feb 2020 00:29:09 -0800 Subject: [PATCH 039/381] Fix march flag in unittest --- cmake/modules/AddSwiftUnittests.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 2c46a289c4960..773d603d80afa 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -49,7 +49,7 @@ function(add_swift_unittest test_dirname) set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_LIBRARIES "log") elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set_property(TARGET "${test_dirname}" APPEND PROPERTY COMPILE_FLAGS - "-march=core2") + "-Xcc -march=core2") endif() find_program(LDLLD_PATH "ld.lld") From 904bd0bf6377b011fd6196d9f0197769057f22a7 Mon Sep 17 00:00:00 2001 From: Vlasov Anton Date: Sat, 1 Jun 2019 18:11:21 +0300 Subject: [PATCH 040/381] SR-5740 Refactoring action to convert if statement to switch --- include/swift/IDE/RefactoringKinds.def | 2 + lib/IDE/Refactoring.cpp | 264 ++++++++++++++++++ .../Outputs/basic/L108-3.swift.expected | 148 ++++++++++ .../Outputs/basic/L118-3.swift.expected | 148 ++++++++++ .../Outputs/basic/L128-3.swift.expected | 151 ++++++++++ .../Outputs/basic/L20-3.swift.expected | 148 ++++++++++ .../Outputs/basic/L29-3.swift.expected | 150 ++++++++++ .../Outputs/basic/L39-3.swift.expected | 148 ++++++++++ .../Outputs/basic/L50-3.swift.expected | 150 ++++++++++ .../Outputs/basic/L54-3.swift.expected | 150 ++++++++++ .../Outputs/basic/L60-3.swift.expected | 150 ++++++++++ .../Outputs/basic/L64-3.swift.expected | 150 ++++++++++ .../Outputs/basic/L71-3.swift.expected | 148 ++++++++++ .../Outputs/basic/L82-3.swift.expected | 150 ++++++++++ .../Outputs/basic/L9-3.swift.expected | 147 ++++++++++ .../Outputs/basic/L99-3.swift.expected | 150 ++++++++++ .../ConvertToSwitchStmt/basic.swift | 176 ++++++++++++ test/refactoring/RefactoringKind/basic.swift | 15 +- .../convert_to_switch_statement.swift | 204 ++++++++++++++ tools/swift-refactor/swift-refactor.cpp | 3 +- 20 files changed, 2744 insertions(+), 8 deletions(-) create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L108-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L118-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L128-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L20-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L29-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L39-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L50-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L54-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L60-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L64-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L71-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L82-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L9-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/Outputs/basic/L99-3.swift.expected create mode 100644 test/refactoring/ConvertToSwitchStmt/basic.swift create mode 100644 test/refactoring/RefactoringKind/convert_to_switch_statement.swift diff --git a/include/swift/IDE/RefactoringKinds.def b/include/swift/IDE/RefactoringKinds.def index b6565439a2016..0fbd03dbb7053 100644 --- a/include/swift/IDE/RefactoringKinds.def +++ b/include/swift/IDE/RefactoringKinds.def @@ -72,6 +72,8 @@ RANGE_REFACTORING(ConvertGuardExprToIfLetExpr, "Convert To IfLet Expression", co RANGE_REFACTORING(ConvertToComputedProperty, "Convert To Computed Property", convert.to.computed.property) +RANGE_REFACTORING(ConvertToSwitchStmt, "Convert To Switch Statement", convert.switch.stmt) + // These internal refactorings are designed to be helpful for working on // the compiler/standard library, etc., but are likely to be just confusing and // noise for general development. diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 9ce2bc7739024..677296c0f9b1d 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -2243,6 +2243,270 @@ bool RefactoringActionConvertGuardExprToIfLetExpr::performChange() { return false; } +bool RefactoringActionConvertToSwitchStmt:: +isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) { + + class ConditionalChecker : public ASTWalker { + public: + bool ParamsUseSameVars = true; + bool ConditionUseOnlyAllowedFunctions = true; + StringRef ExpectName; + + Expr *walkToExprPost(Expr *E) { + if (E->getKind() != ExprKind::DeclRef) + return E; + auto D = dyn_cast(E)->getDecl(); + if (D->getKind() == DeclKind::Var || D->getKind() == DeclKind::Param) + ParamsUseSameVars = checkName(dyn_cast(D)); + if (D->getKind() == DeclKind::Func) + ConditionUseOnlyAllowedFunctions = checkName(dyn_cast(D)); + if (allCheckPassed()) + return E; + return nullptr; + } + + bool allCheckPassed() { + return ParamsUseSameVars && ConditionUseOnlyAllowedFunctions; + } + + private: + bool checkName(VarDecl *VD) { + auto Name = VD->getName().str(); + if (ExpectName.empty()) + ExpectName = Name; + return Name == ExpectName; + } + + bool checkName(FuncDecl *FD) { + auto Name = FD->getName().str(); + return Name == "~=" + || Name == "==" + || Name == "__derived_enum_equals" + || Name == "__derived_struct_equals" + || Name == "||" + || Name == "..."; + } + }; + + class SwitchConvertable { + public: + SwitchConvertable(ResolvedRangeInfo Info) { + this->Info = Info; + } + + bool isApplicable() { + if (Info.Kind != RangeKind::SingleStatement) + return false; + if (!findIfStmt()) + return false; + return checkEachCondition(); + } + + private: + ResolvedRangeInfo Info; + IfStmt *If = nullptr; + ConditionalChecker checker; + + bool findIfStmt() { + if (Info.ContainedNodes.size() != 1) + return false; + if (auto S = Info.ContainedNodes.front().dyn_cast()) + If = dyn_cast(S); + return If != nullptr; + } + + bool checkEachCondition() { + checker = ConditionalChecker(); + do { + if (!checkEachElement()) + return false; + } while ((If = dyn_cast_or_null(If->getElseStmt()))); + return true; + } + + bool checkEachElement() { + bool result = true; + auto ConditionalList = If->getCond(); + for (auto Element : ConditionalList) { + result &= check(Element); + } + return result; + } + + bool check(StmtConditionElement ConditionElement) { + if (ConditionElement.getKind() == StmtConditionElement::CK_Availability) + return false; + ConditionElement.walk(checker); + return checker.allCheckPassed(); + } + }; + return SwitchConvertable(Info).isApplicable(); +} + +bool RefactoringActionConvertToSwitchStmt::performChange() { + + class VarNameFinder : public ASTWalker { + public: + std::string VarName; + + Expr *walkToExprPost(Expr *E) { + if (E->getKind() != ExprKind::DeclRef) + return E; + auto D = dyn_cast(E)->getDecl(); + if (D->getKind() != DeclKind::Var && D->getKind() != DeclKind::Param) + return E; + VarName = dyn_cast(D)->getName().str().str(); + return nullptr; + } + }; + + class ConditionalPatternFinder : public ASTWalker { + public: + ConditionalPatternFinder(SourceManager &SM) : SM(SM) {} + + SmallString<64> ConditionalPattern = SmallString<64>(); + + Expr *walkToExprPost(Expr *E) { + if (E->getKind() != ExprKind::Binary) + return E; + auto BE = dyn_cast(E); + if (isFunctionNameAllowed(BE)) + appendPattern(dyn_cast(E)->getArg()); + return E; + } + + std::pair walkToPatternPre(Pattern *P) { + ConditionalPattern.append(Lexer::getCharSourceRangeFromSourceRange(SM, P->getSourceRange()).str()); + if (P->getKind() == PatternKind::OptionalSome) + ConditionalPattern.append("?"); + return { true, nullptr }; + } + + private: + + SourceManager &SM; + + bool isFunctionNameAllowed(BinaryExpr *E) { + auto FunctionBody = dyn_cast(E->getFn())->getFn(); + auto FunctionDeclaration = dyn_cast(FunctionBody)->getDecl(); + auto FunctionName = dyn_cast(FunctionDeclaration)->getName().str(); + return FunctionName == "~=" + || FunctionName == "==" + || FunctionName == "__derived_enum_equals" + || FunctionName == "__derived_struct_equals"; + } + + void appendPattern(TupleExpr *Tuple) { + auto PatternArgument = Tuple->getElements().back(); + if (PatternArgument->getKind() == ExprKind::DeclRef) + PatternArgument = Tuple->getElements().front(); + if (ConditionalPattern.size() > 0) + ConditionalPattern.append(", "); + ConditionalPattern.append(Lexer::getCharSourceRangeFromSourceRange(SM, PatternArgument->getSourceRange()).str()); + } + }; + + class ConverterToSwitch { + public: + ConverterToSwitch(ResolvedRangeInfo Info, SourceManager &SM) : SM(SM) { + this->Info = Info; + } + + void performConvert(SmallString<64> &Out) { + If = findIf(); + OptionalLabel = If->getLabelInfo().Name.str().str(); + ControlExpression = findControlExpression(); + findPatternsAndBodies(PatternsAndBodies); + DefaultStatements = findDefaultStatements(); + makeSwitchStatement(Out); + } + + private: + ResolvedRangeInfo Info; + SourceManager &SM; + + IfStmt *If; + IfStmt *PreviousIf; + + std::string OptionalLabel; + std::string ControlExpression; + SmallVector, 16> PatternsAndBodies; + std::string DefaultStatements; + + IfStmt *findIf() { + auto S = Info.ContainedNodes[0].dyn_cast(); + return dyn_cast(S); + } + + std::string findControlExpression() { + auto ConditionElement = If->getCond().front(); + auto Finder = VarNameFinder(); + ConditionElement.walk(Finder); + return Finder.VarName; + } + + void findPatternsAndBodies(SmallVectorImpl> &Out) { + do { + auto pattern = findPattern(); + auto body = findBodyStatements(); + Out.push_back(std::make_pair(pattern, body)); + PreviousIf = If; + } while ((If = dyn_cast_or_null(If->getElseStmt()))); + } + + std::string findPattern() { + auto ConditionElement = If->getCond().front(); + auto Finder = ConditionalPatternFinder(SM); + ConditionElement.walk(Finder); + return Finder.ConditionalPattern.str().str(); + } + + std::string findBodyStatements() { + return findBodyWithoutBraces(If->getThenStmt()); + } + + std::string findDefaultStatements() { + auto ElseBody = dyn_cast_or_null(PreviousIf->getElseStmt()); + if (!ElseBody) + return getTokenText(tok::kw_break); + return findBodyWithoutBraces(ElseBody); + } + + std::string findBodyWithoutBraces(Stmt *body) { + auto BS = dyn_cast(body); + if (!BS) + return Lexer::getCharSourceRangeFromSourceRange(SM, body->getSourceRange()).str().str(); + if (BS->getElements().empty()) + return getTokenText(tok::kw_break); + SourceRange BodyRange = BS->getElements().front().getSourceRange(); + BodyRange.widen(BS->getElements().back().getSourceRange()); + return Lexer::getCharSourceRangeFromSourceRange(SM, BodyRange).str().str(); + } + + void makeSwitchStatement(SmallString<64> &Out) { + StringRef Space = " "; + StringRef NewLine = "\n"; + llvm::raw_svector_ostream OS(Out); + if (OptionalLabel.size() > 0) + OS << OptionalLabel << ":" << Space; + OS << tok::kw_switch << Space << ControlExpression << Space << tok::l_brace << NewLine; + for (auto &pair : PatternsAndBodies) { + OS << tok::kw_case << Space << pair.first << tok::colon << NewLine; + OS << pair.second << NewLine; + } + OS << tok::kw_default << tok::colon << NewLine; + OS << DefaultStatements << NewLine; + OS << tok::r_brace; + } + + }; + + SmallString<64> result; + ConverterToSwitch(RangeInfo, SM).performConvert(result); + EditConsumer.accept(SM, RangeInfo.ContentRange, result.str()); + return false; +} + /// Struct containing info about an IfStmt that can be converted into an IfExpr. struct ConvertToTernaryExprInfo { ConvertToTernaryExprInfo() {} diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L108-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L108-3.swift.expected new file mode 100644 index 0000000000000..bdf7b109d4d48 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L108-3.swift.expected @@ -0,0 +1,148 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + switch x { +case 5: +print("5") +case 4: +print("4") +case 1...3: +print("1...3") +default: +print("default") +} + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L118-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L118-3.swift.expected new file mode 100644 index 0000000000000..09b545703144b --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L118-3.swift.expected @@ -0,0 +1,148 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + switch y { +case MyType(v: "bye"): +print("bye") +case MyType(v: "tchau"): +print("tchau") +default: +print("default") +} +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L128-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L128-3.swift.expected new file mode 100644 index 0000000000000..fa543f8022c5d --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L128-3.swift.expected @@ -0,0 +1,151 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + switch e { +case .first: +break +case .second: +print("second") +default: +break +} +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L20-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L20-3.swift.expected new file mode 100644 index 0000000000000..20008e668f86b --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L20-3.swift.expected @@ -0,0 +1,148 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + switch e { +case .first: +print("first string") + print("second string") +default: +print("default") +} +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L29-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L29-3.swift.expected new file mode 100644 index 0000000000000..3ff08f83271ab --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L29-3.swift.expected @@ -0,0 +1,150 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + switch e { +case .first: +print("first") +case .second: +print("second") +case .third: +print("third") +default: +break +} +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L39-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L39-3.swift.expected new file mode 100644 index 0000000000000..f330089b0fbf7 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L39-3.swift.expected @@ -0,0 +1,148 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + switch e { +case .first: +print("first") +case .second: +print("second") +default: +print("default") +} +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L50-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L50-3.swift.expected new file mode 100644 index 0000000000000..5af1ac6d3cac5 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L50-3.swift.expected @@ -0,0 +1,150 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + switch someOptional { +case .some(let x): +print(x) +default: +break +} + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L54-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L54-3.swift.expected new file mode 100644 index 0000000000000..037116d2e5f14 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L54-3.swift.expected @@ -0,0 +1,150 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + switch someOptional { +case let x?: +print(x) +default: +break +} +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L60-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L60-3.swift.expected new file mode 100644 index 0000000000000..4753405a9b75d --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L60-3.swift.expected @@ -0,0 +1,150 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + switch optional { +case let unwraped?: +print(unwraped) +default: +break +} + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L64-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L64-3.swift.expected new file mode 100644 index 0000000000000..1873a8397e190 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L64-3.swift.expected @@ -0,0 +1,150 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + switch optional { +case var unwraped?: +unwraped += "!" + print(unwraped) +default: +break +} +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L71-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L71-3.swift.expected new file mode 100644 index 0000000000000..298230bacfb01 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L71-3.swift.expected @@ -0,0 +1,148 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + switch e { +case .first, .second, .third: +print("1-3") +case .fourth: +print("4") +default: +print(">4") +} +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L82-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L82-3.swift.expected new file mode 100644 index 0000000000000..9106b0fe3f136 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L82-3.swift.expected @@ -0,0 +1,150 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: switch e { +case .a: +innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") +default: +break +} + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L9-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L9-3.swift.expected new file mode 100644 index 0000000000000..8725e893b57e4 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L9-3.swift.expected @@ -0,0 +1,147 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + switch e { +case .first: +print("first") +case .second: +print("second") +default: +print("default") +} +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L99-3.swift.expected b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L99-3.swift.expected new file mode 100644 index 0000000000000..d932752a36165 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/Outputs/basic/L99-3.swift.expected @@ -0,0 +1,150 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + switch productBarcode { +case .upc(let numberSystem, let manufacturer, let product, let check): +print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") +case let .qrCode(productCode): +print("QR code: \(productCode).") +default: +break +} +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + + + + + + + + + + + + + + + diff --git a/test/refactoring/ConvertToSwitchStmt/basic.swift b/test/refactoring/ConvertToSwitchStmt/basic.swift new file mode 100644 index 0000000000000..dd848549a7962 --- /dev/null +++ b/test/refactoring/ConvertToSwitchStmt/basic.swift @@ -0,0 +1,176 @@ +enum E { + case first + case second + case third + case fourth +} + +func someFunc(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } +} + +func twoStringBody(e: E) { + if e == .first { + print("first string") + print("second string") + } else { + print("default") + } +} + +func withoutElse(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } else if e == .third { + print("third") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + if case .some(let x) = someOptional { + print(x) + } + + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +enum Foo { case a, b, c } +func checkLabeledCondition(e: Foo, f: Foo) { + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) +} +func checkEnumWithAssociatedValues(productBarcode: Barcode) { + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +struct MyType: Equatable { let v: String } +func checkEquatableProtocol(x: Int, y: MyType) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +func checkEmptyBody(e: E) { + if e == .first {} + else if e == .second { + print("second") + } +} + +// RUN: rm -rf %t.result && mkdir -p %t.result + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=9:3 -end-pos=16:4 > %t.result/L9-3.swift +// RUN: diff -u %S/Outputs/basic/L9-3.swift.expected %t.result/L9-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=20:3 -end-pos=25:4 > %t.result/L20-3.swift +// RUN: diff -u %S/Outputs/basic/L20-3.swift.expected %t.result/L20-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=29:3 -end-pos=35:4 > %t.result/L29-3.swift +// RUN: diff -u %S/Outputs/basic/L29-3.swift.expected %t.result/L29-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=39:3 -end-pos=45:4 > %t.result/L39-3.swift +// RUN: diff -u %S/Outputs/basic/L39-3.swift.expected %t.result/L39-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=50:3 -end-pos=52:4 > %t.result/L50-3.swift +// RUN: diff -u %S/Outputs/basic/L50-3.swift.expected %t.result/L50-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=54:3 -end-pos=56:4 > %t.result/L54-3.swift +// RUN: diff -u %S/Outputs/basic/L54-3.swift.expected %t.result/L54-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=60:3 -end-pos=62:4 > %t.result/L60-3.swift +// RUN: diff -u %S/Outputs/basic/L60-3.swift.expected %t.result/L60-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=64:3 -end-pos=67:4 > %t.result/L64-3.swift +// RUN: diff -u %S/Outputs/basic/L64-3.swift.expected %t.result/L64-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=71:3 -end-pos=77:4 > %t.result/L71-3.swift +// RUN: diff -u %S/Outputs/basic/L71-3.swift.expected %t.result/L71-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=82:3 -end-pos=90:4 > %t.result/L82-3.swift +// RUN: diff -u %S/Outputs/basic/L82-3.swift.expected %t.result/L82-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=99:3 -end-pos=103:4 > %t.result/L99-3.swift +// RUN: diff -u %S/Outputs/basic/L99-3.swift.expected %t.result/L99-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=108:3 -end-pos=116:4 > %t.result/L108-3.swift +// RUN: diff -u %S/Outputs/basic/L108-3.swift.expected %t.result/L108-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=118:3 -end-pos=124:4 > %t.result/L118-3.swift +// RUN: diff -u %S/Outputs/basic/L118-3.swift.expected %t.result/L118-3.swift + +// RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=128:3 -end-pos=131:4 > %t.result/L128-3.swift +// RUN: diff -u %S/Outputs/basic/L128-3.swift.expected %t.result/L128-3.swift diff --git a/test/refactoring/RefactoringKind/basic.swift b/test/refactoring/RefactoringKind/basic.swift index 2ec7e032ef8e0..16bf14f604b10 100644 --- a/test/refactoring/RefactoringKind/basic.swift +++ b/test/refactoring/RefactoringKind/basic.swift @@ -392,10 +392,10 @@ struct S { // RUN: %refactor -source-filename %s -pos=272:3 -end-pos=275:13 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-IFLET-EXPRESSION // RUN: %refactor -source-filename %s -pos=288:3 -end-pos=288:16 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY -// RUN: %refactor -source-filename %s -pos=289:3 -end-pos=289:22 | %FileCheck %s -check-prefix=CHECK-NONE -// RUN: %refactor -source-filename %s -pos=290:3 -end-pos=290:32 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY2 -// RUN: %refactor -source-filename %s -pos=291:3 -end-pos=291:18 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY2 -// RUN: %refactor -source-filename %s -pos=292:3 -end-pos=296:4 | %FileCheck %s -check-prefix=CHECK-NONE +// RUN: %refactor -source-filename %s -pos=289:3 -end-pos=289:22 | %FileCheck %s -check-prefix=CHECK-IS-NOT-CONVERT-TO-COMPUTED-PROPERTY +// RUN: %refactor -source-filename %s -pos=290:3 -end-pos=290:32 | %FileCheck %s -check-prefix=CHECK-IS-NOT-CONVERT-TO-COMPUTED-PROPERTY +// RUN: %refactor -source-filename %s -pos=291:3 -end-pos=291:18 | %FileCheck %s -check-prefix=CHECK-IS-NOT-CONVERT-TO-COMPUTED-PROPERTY +// RUN: %refactor -source-filename %s -pos=292:3 -end-pos=296:4 | %FileCheck %s -check-prefix=CHECK-IS-NOT-CONVERT-TO-COMPUTED-PROPERTY // CHECK1: Action begins // CHECK1-NEXT: Extract Method @@ -450,6 +450,7 @@ struct S { // CHECK-CONVERT-TO-COMPUTED-PROPERTY: Convert To Computed Property -// CHECK-CONVERT-TO-COMPUTED-PROPERTY2: Action begins -// CHECK-CONVERT-TO-COMPUTED-PROPERTY2-NEXT: Move To Extension -// CHECK-CONVERT-TO-COMPUTED-PROPERTY2-NEXT: Action ends \ No newline at end of file +// CHECK-IS-NOT-CONVERT-TO-COMPUTED-PROPERTY: Action begins +// CHECK-IS-NOT-CONVERT-TO-COMPUTED-PROPERTY-NOT: Convert To Computed Property +// CHECK-IS-NOT-CONVERT-TO-COMPUTED-PROPERTY: Action ends + diff --git a/test/refactoring/RefactoringKind/convert_to_switch_statement.swift b/test/refactoring/RefactoringKind/convert_to_switch_statement.swift new file mode 100644 index 0000000000000..3db1d2a18d139 --- /dev/null +++ b/test/refactoring/RefactoringKind/convert_to_switch_statement.swift @@ -0,0 +1,204 @@ +enum E { + case first + case second + case third + case fourth +} + +func checkForEnum(e: E) { + if e == .first { + print("first") + } else if e == .second { + print("second") + } + else { + print("default") + } + + let x = "some other type" + if e == .first { + print("first") + } else if x == "some value" { + print("x") + } +} + +func checkBiggerOrSmallerSign(integer: Int) { + if integer > 0 { + print("Positive integer") + } else if integer < 0 { + print("Negative integer") + } else { + print("Zero") + } +} + +func checkInverseCondition(e: E) { + if .first == e { + print("first") + } else if .second == e { + print("second") + } else { + print("default") + } +} + +func checkAvailabilityCondition() { + if #available(iOS 13, *) { + print("#available may only be used as condition of an 'if', 'guard' or 'while' statement") + } +} + +func checkCaseCondition() { + let someOptional: Int? = 42 + // Match using an enumeration case pattern. + if case .some(let x) = someOptional { + print(x) + } + + // Match using an optional pattern. + if case let x? = someOptional { + print(x) + } +} + +func checkOptionalBindingCondition(optional: String?) { + if let unwraped = optional { + print(unwraped) + } + + if var unwraped = optional { + unwraped += "!" + print(unwraped) + } +} + +func checkConditionalList(x: Int, y: Int) -> Bool { + if x > 0, y < 0 { + return false + } + + if x > 0, x < 10 { + return true + } +} + +func checkFunctionCallExpression() { + if checkConditionalList(x: 0, y: 0) { + print("false") + } +} + +func checkMultipleOrConditions(e: E) { + if e == .first || e == .second || e == .third { + print("1-3") + } else if e == .fourth { + print("4") + } else { + print(">4") + } +} + +func checkMultipleAndConditions(e: E) { + if e == .first && e == .second && e == .third { + print("Never executed") + } else { + print("Whenever") + } +} + +func checkMultipleWithUnrelatedCondition(e: E, x: Int) { + if e == .first || x > 0 { + print("e is the first or x is positive") + } else { + print("Shouldn't convert to switch") + } +} + +func checkOperatorIsActuallyBeingUsed(e: E) { + if e != .first { + print("Can't use at case statement") + } +} + +func checkLabeledCondition() { + enum Foo { case a, b, c } + let e = Foo.a + let f = Foo.a + + outerLabel: if e == .a { + innerLabel: switch f { + case .a: + break outerLabel + default: + break innerLabel + } + print("outside switch") + } + print("outside if") +} + +func checkEnumWithAssociatedValues() { + enum Barcode { + case upc(Int, Int, Int, Int) + case qrCode(String) + } + + let productBarcode = Barcode.upc(8, 85909, 51226, 3) + if case .upc(let numberSystem, let manufacturer, let product, let check) = productBarcode { + print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") + } else if case let .qrCode(productCode) = productBarcode { + print("QR code: \(productCode).") + } +} + +func checkEquatableProtocol(x: Int) { + if x == 5 { + print("5") + } else if x == 4 { + print("4") + } else if 1...3 ~= x { + print("1...3") + } else { + print("default") + } + + struct MyType: Equatable { let v: String } + let y = MyType(v: "hello") + + if y == MyType(v: "bye") { + print("bye") + } else if y == MyType(v: "tchau") { + print("tchau") + } else { + print("default") + } +} + +// RUN: %refactor -pos=9:3 -end-pos=16:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=19:3 -end-pos=23:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=27:3 -end-pos=33:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=37:3 -end-pos=43:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=47:3 -end-pos=49:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=55:3 -end-pos=57:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=60:3 -end-pos=62:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=66:3 -end-pos=68:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=70:3 -end-pos=73:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=77:3 -end-pos=79:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=81:3 -end-pos=83:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=87:3 -end-pos=89:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=93:3 -end-pos=99:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=103:3 -end-pos=107:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=111:3 -end-pos=115:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=119:3 -end-pos=121:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT2 +// RUN: %refactor -pos=129:3 -end-pos=137:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=148:3 -end-pos=152:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=156:3 -end-pos=164:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT +// RUN: %refactor -pos=169:3 -end-pos=175:4 -source-filename %s | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-SWITCH-STATEMENT + +// CHECK-CONVERT-TO-SWITCH-STATEMENT: Convert To Switch Statement + +// CHECK-CONVERT-TO-SWITCH-STATEMENT2: Action begins +// CHECK-CONVERT-TO-SWITCH-STATEMENT2-NOT: Convert To Switch Statement +// CHECK-CONVERT-TO-SWITCH-STATEMENT2: Action ends + diff --git a/tools/swift-refactor/swift-refactor.cpp b/tools/swift-refactor/swift-refactor.cpp index 22c885b97c2cb..a995f6acc7b06 100644 --- a/tools/swift-refactor/swift-refactor.cpp +++ b/tools/swift-refactor/swift-refactor.cpp @@ -73,7 +73,8 @@ Action(llvm::cl::desc("kind:"), llvm::cl::init(RefactoringKind::None), "replace-bodies-with-fatalError", "Perform trailing closure refactoring"), clEnumValN(RefactoringKind::MemberwiseInitLocalRefactoring, "memberwise-init", "Generate member wise initializer"), clEnumValN(RefactoringKind::ConvertToComputedProperty, - "convert-to-computed-property", "Convert from field initialization to computed property"))); + "convert-to-computed-property", "Convert from field initialization to computed property"), + clEnumValN(RefactoringKind::ConvertToSwitchStmt, "convert-to-switch-stmt", "Perform convert to switch statement"))); static llvm::cl::opt From feb7bb1a0654384add15de1ba4005910c630f474 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Sun, 2 Feb 2020 09:53:18 -0800 Subject: [PATCH 041/381] one more try --- cmake/modules/AddSwiftUnittests.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 773d603d80afa..6de6909f1e1e7 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -49,7 +49,9 @@ function(add_swift_unittest test_dirname) set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_LIBRARIES "log") elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set_property(TARGET "${test_dirname}" APPEND PROPERTY COMPILE_FLAGS - "-Xcc -march=core2") + " -march=core2") + set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_FLAGS + " -march=core2") endif() find_program(LDLLD_PATH "ld.lld") From 4d2c342639057c5748e8b5c71e3a52b509e415c5 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 2 Feb 2020 12:39:30 -0800 Subject: [PATCH 042/381] Update how createApplyWithConcreteType checks if it can update Apply args createApplyWithConcreteType used to update Apply unconditionally. There may have been cases prior to supporting store instructions that miscompiled because of this but, there are certainly cases after supporting store instructions. Therefore, it is important that the apply is updated only when the substitution can accept the new args. --- .../ExistentialSpecializer.cpp | 3 --- lib/SILOptimizer/PassManager/PassManager.cpp | 15 ----------- .../SILCombiner/SILCombinerApplyVisitors.cpp | 27 +++++++++++-------- .../Transforms/AllocBoxToStack.cpp | 2 -- lib/SILOptimizer/Transforms/Devirtualizer.cpp | 5 +--- .../Transforms/GenericSpecializer.cpp | 2 -- 6 files changed, 17 insertions(+), 37 deletions(-) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp index 667f4b47791e4..44954304de13c 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp @@ -336,9 +336,6 @@ void ExistentialSpecializer::specializeExistentialArgsInAppliesWithinFunction( /// Update statistics on the number of functions specialized. ++NumFunctionsWithExistentialArgsSpecialized; - llvm::errs() << "Adding function to pass manger in existential\n"; - llvm::errs() << ET.getExistentialSpecializedFunction()->getName() << "\n"; - /// Make sure the PM knows about the new specialized inner function. addFunctionToPassManagerWorklist(ET.getExistentialSpecializedFunction(), Callee); diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index 73e77ca0aad9e..a1b5200e81ebb 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -407,24 +407,9 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { Mod->registerDeleteNotificationHandler(SFT); if (breakBeforeRunning(F->getName(), SFT)) LLVM_BUILTIN_DEBUGTRAP; - - - llvm::errs() << "FUNC NAME: "; - F->printName(llvm::errs()); - llvm::errs() << "\nDEMA NAME: " << Demangle::demangleSymbolAsString(F->getName()) << "\n"; - llvm::errs() << "PASS NAME: " << SFT->getID() << "\n"; - SFT->run(); assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); Mod->removeDeleteNotificationHandler(SFT); - - llvm::errs() << "Finished running pass\n\t\t****\n"; - if (F->getName().contains("ss6UInt32VSjsSj9magnitude9MagnitudeQzvgTW") || - F->getName().contains("ss2geoiySbx_q_q0_q1_q2_q3_t_x_q_q0_q1_q2_q3_ttSLRzSLR_SLR0_SLR1_SLR2_SLR3_r4_lF")) { - llvm::errs() << "Dumping module to file..."; - F->getModule().dump("/Users/zoe/Developer/swift-source/build/buildbot_incremental/swift-macosx-x86_64/module_dump.txt"); - llvm::errs() << "done.\n"; - } auto Delta = (std::chrono::system_clock::now() - StartTime).count(); if (SILPrintPassTime) { diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index d20998b4b9a5d..7d013e39589c0 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -893,17 +893,12 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // Ensure that we have a concrete value to propagate. assert(CEI.ConcreteValue); - // If the parameter is expecting a pointer, then we need to create a - // alloc_stack to store the temporary value. - if (Apply.getArgument(ArgIdx)->getType().isAddress()) { - auto argSub = - ConcreteArgumentCopy::generate(CEI, Apply, ArgIdx, BuilderCtx); - if (argSub) { - concreteArgCopies.push_back(*argSub); - NewArgs.push_back(argSub->tempArg); - } + auto argSub = + ConcreteArgumentCopy::generate(CEI, Apply, ArgIdx, BuilderCtx); + if (argSub) { + concreteArgCopies.push_back(*argSub); + NewArgs.push_back(argSub->tempArg); } else { - // Otherwise, we can just use the value itself. NewArgs.push_back(CEI.ConcreteValue); } @@ -928,7 +923,17 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( }); } - if (NewArgs.size() != Apply.getNumArguments()) { + bool canUpdateArgs = [&]() { + auto substTy = Apply.getCallee()->getType().substGenericArgs(Apply.getModule(), NewCallSubs, Apply.getFunction()->getTypeExpansionContext()).getAs(); + SILFunctionConventions conv(substTy, SILModuleConventions(Apply.getModule())); + bool canUpdate = true; + for (unsigned index = 0; index < conv.getNumSILArguments(); ++index) { + canUpdate &= conv.getSILArgumentType(index) == NewArgs[index]->getType(); + } + return canUpdate; + }(); + + if (!canUpdateArgs) { // Remove any new instructions created while attempting to optimize this // apply. Since the apply was never rewritten, if they aren't removed here, // they will be removed later as dead when visited by SILCombine, causing diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 2e79bfcae2175..ddf770d2a0874 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -793,8 +793,6 @@ specializePartialApply(SILOptFunctionBuilder &FuncBuilder, ClonedName); Cloner.populateCloned(); ClonedFn = Cloner.getCloned(); - llvm::errs() << "Adding function to pass manger in alloc box\n"; - llvm::errs() << ClonedFn->getName() << "\n"; pass.T->addFunctionToPassManagerWorklist(ClonedFn, F); } diff --git a/lib/SILOptimizer/Transforms/Devirtualizer.cpp b/lib/SILOptimizer/Transforms/Devirtualizer.cpp index b14f7fb12bc28..27e140b6993be 100644 --- a/lib/SILOptimizer/Transforms/Devirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/Devirtualizer.cpp @@ -102,11 +102,8 @@ bool Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F, // We may not have optimized these functions yet, and it could // be beneficial to rerun some earlier passes on the current // function now that we've made these direct references visible. - if (CalleeFn->isDefinition() && CalleeFn->shouldOptimize()) { - llvm::errs() << "Adding function to pass manger in devirt\n"; - llvm::errs() << CalleeFn->getName() << "\n"; + if (CalleeFn->isDefinition() && CalleeFn->shouldOptimize()) addFunctionToPassManagerWorklist(CalleeFn, nullptr); - } } return Changed; diff --git a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp index 5817677c58302..82dd9d4d33ce8 100644 --- a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp @@ -129,8 +129,6 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) { // (as opposed to returning a previous specialization), we need to notify // the pass manager so that the new functions get optimized. for (SILFunction *NewF : reverse(NewFunctions)) { - llvm::errs() << "Adding function to pass manger in generic spec\n"; - llvm::errs() << NewF->getName() << "\n"; addFunctionToPassManagerWorklist(NewF, Callee); } } From a0fb139260d7dae389037d96958d7aecdf02e621 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 2 Feb 2020 12:57:50 -0800 Subject: [PATCH 043/381] Format --- .../SILCombiner/SILCombinerApplyVisitors.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 7d013e39589c0..76f38c5e97de1 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -792,7 +792,7 @@ struct ConcreteArgumentCopy { && "A mutated opened existential value can't be replaced"); if (!paramInfo.isConsumed()) - return None; + return None; SILValue origArg = apply.getArgument(argIdx); // FIXME_opaque: With SIL opaque values, a formally indirect argument may be @@ -924,8 +924,14 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( } bool canUpdateArgs = [&]() { - auto substTy = Apply.getCallee()->getType().substGenericArgs(Apply.getModule(), NewCallSubs, Apply.getFunction()->getTypeExpansionContext()).getAs(); - SILFunctionConventions conv(substTy, SILModuleConventions(Apply.getModule())); + auto substTy = + Apply.getCallee() + ->getType() + .substGenericArgs(Apply.getModule(), NewCallSubs, + Apply.getFunction()->getTypeExpansionContext()) + .getAs(); + SILFunctionConventions conv(substTy, + SILModuleConventions(Apply.getModule())); bool canUpdate = true; for (unsigned index = 0; index < conv.getNumSILArguments(); ++index) { canUpdate &= conv.getSILArgumentType(index) == NewArgs[index]->getType(); From 65e8d37b6f011ee0231466c2d3cc254171444469 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 2 Feb 2020 19:15:01 -0800 Subject: [PATCH 044/381] Add a `madeUpdate` check to `createApplyWithConcreteType` to prevent infinite loop --- lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 76f38c5e97de1..b5115659411a1 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -871,6 +871,11 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( ArgIdx < EndIdx; ++ArgIdx) { NewArgs.push_back(Apply.getArgument(ArgIdx)); } + + // Keep track of weather we made any updates at all. Otherwise, we will + // have an infinite loop. + bool madeUpdate = false; + // Transform the parameter arguments. SmallVector concreteArgCopies; for (unsigned EndIdx = Apply.getNumArguments(); ArgIdx < EndIdx; ++ArgIdx) { @@ -898,6 +903,7 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( if (argSub) { concreteArgCopies.push_back(*argSub); NewArgs.push_back(argSub->tempArg); + madeUpdate = true; } else { NewArgs.push_back(CEI.ConcreteValue); } @@ -939,7 +945,7 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( return canUpdate; }(); - if (!canUpdateArgs) { + if (!canUpdateArgs || !madeUpdate) { // Remove any new instructions created while attempting to optimize this // apply. Since the apply was never rewritten, if they aren't removed here, // they will be removed later as dead when visited by SILCombine, causing From 37c0eb2a5cb48106ac1af5cc4276852e7f592973 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 3 Feb 2020 10:10:10 -0800 Subject: [PATCH 045/381] Manually check if types are the same when checking for change and update tests --- .../SILCombiner/SILCombinerApplyVisitors.cpp | 16 +++--- ...ualize_protocol_composition_two_stores.sil | 50 +++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index b5115659411a1..8612580196e8b 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -872,10 +872,6 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( NewArgs.push_back(Apply.getArgument(ArgIdx)); } - // Keep track of weather we made any updates at all. Otherwise, we will - // have an infinite loop. - bool madeUpdate = false; - // Transform the parameter arguments. SmallVector concreteArgCopies; for (unsigned EndIdx = Apply.getNumArguments(); ArgIdx < EndIdx; ++ArgIdx) { @@ -903,7 +899,6 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( if (argSub) { concreteArgCopies.push_back(*argSub); NewArgs.push_back(argSub->tempArg); - madeUpdate = true; } else { NewArgs.push_back(CEI.ConcreteValue); } @@ -929,7 +924,8 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( }); } - bool canUpdateArgs = [&]() { + bool canUpdateArgs, madeUpdate; + std::tie(canUpdateArgs, madeUpdate) = [&]() { auto substTy = Apply.getCallee() ->getType() @@ -939,12 +935,16 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( SILFunctionConventions conv(substTy, SILModuleConventions(Apply.getModule())); bool canUpdate = true; + // Keep track of weather we made any updates at all. Otherwise, we will + // have an infinite loop. + bool madeUpdate = false; for (unsigned index = 0; index < conv.getNumSILArguments(); ++index) { canUpdate &= conv.getSILArgumentType(index) == NewArgs[index]->getType(); + madeUpdate |= NewArgs[index]->getType() != Apply.getArgument(index)->getType(); } - return canUpdate; + return std::make_tuple(canUpdate, madeUpdate); }(); - + if (!canUpdateArgs || !madeUpdate) { // Remove any new instructions created while attempting to optimize this // apply. Since the apply was never rewritten, if they aren't removed here, diff --git a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil index 86cf074464c15..231a6a1c88e18 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil +++ b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil @@ -12,43 +12,43 @@ public class A { } public protocol P { - func foo() -> Int + func foo() -> Int64 } public class B : A, P { - public func foo() -> Int + public func foo() -> Int64 @objc deinit override init() } public class C : A, P { - public func foo() -> Int + public func foo() -> Int64 @objc deinit override init() } -func imp(x: A & P, y: A & P) -> Int +func imp(x: A & P, y: A & P) -> Int64 -public func test(x: B, y: C) -> Int +public func test(x: B, y: C) -> Int64 // protocol witness for P.foo() in conformance B -sil shared [transparent] [serialized] [thunk] @Pfoo : $@convention(witness_method: P) (@in_guaranteed B) -> Int { +sil shared [transparent] [serialized] [thunk] @Pfoo : $@convention(witness_method: P) (@in_guaranteed B) -> Int64 { // %0 // user: %1 bb0(%0 : $*B): %1 = load %0 : $*B // users: %2, %3 - %2 = class_method %1 : $B, #B.foo!1 : (B) -> () -> Int, $@convention(method) (@guaranteed B) -> Int // user: %3 - %3 = apply %2(%1) : $@convention(method) (@guaranteed B) -> Int // user: %4 - return %3 : $Int // id: %4 + %2 = class_method %1 : $B, #B.foo!1 : (B) -> () -> Int64, $@convention(method) (@guaranteed B) -> Int64 // user: %3 + %3 = apply %2(%1) : $@convention(method) (@guaranteed B) -> Int64 // user: %4 + return %3 : $Int64 // id: %4 } // end sil function 'Pfoo' // B.foo() -sil [noinline] @foo : $@convention(method) (@guaranteed B) -> Int { +sil [noinline] @foo : $@convention(method) (@guaranteed B) -> Int64 { // %0 // user: %1 bb0(%0 : $B): debug_value %0 : $B, let, name "self", argno 1 // id: %1 %2 = integer_literal $Builtin.Int64, 1 // user: %3 - %3 = struct $Int (%2 : $Builtin.Int64) // user: %4 - return %3 : $Int // id: %4 + %3 = struct $Int64 (%2 : $Builtin.Int64) // user: %4 + return %3 : $Int64 // id: %4 } // end sil function 'foo' @@ -56,7 +56,7 @@ bb0(%0 : $B): // based on the arguments passed to it here. Even though this function isn't // directly checked, it is essentail for the optimization. // test(x:y:) -sil @test :$@convention(thin) (@guaranteed B, @guaranteed C) -> Int { +sil @test :$@convention(thin) (@guaranteed B, @guaranteed C) -> Int64 { // %0 // users: %5, %4, %2 // %1 // users: %7, %6, %3 bb0(%0 : $B, %1 : $C): @@ -67,11 +67,11 @@ bb0(%0 : $B, %1 : $C): strong_retain %1 : $C // id: %6 %7 = init_existential_ref %1 : $C : $C, $A & P // users: %10, %9 // function_ref imp(x:y:) - %8 = function_ref @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int // user: %9 - %9 = apply %8(%5, %7) : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int // user: %12 + %8 = function_ref @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int64 // user: %9 + %9 = apply %8(%5, %7) : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int64 // user: %12 strong_release %7 : $A & P // id: %10 strong_release %5 : $A & P // id: %11 - return %9 : $Int // id: %12 + return %9 : $Int64 // id: %12 } // end sil function 'test' // We're looking of an optimized spcialization of @impl, not @impl itself. @@ -83,7 +83,7 @@ bb0(%0 : $B, %1 : $C): // This function will be passed arguments of type B and C for arguments // %0 and %1 respectively. We want to make sure that we call B's foo method // and not C's foo method. -sil hidden [noinline] @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int { +sil hidden [noinline] @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int64 { bb0(%0 : $A & P, %1 : $A & P): %2 = open_existential_ref %0 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P %3 = unchecked_ref_cast %1 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P @@ -94,28 +94,28 @@ bb0(%0 : $A & P, %1 : $A & P): // We want to make sure that we picked up on the FIRST store and not the second one. // class C's foo method is named "bar" whereas class B's foo method is named "foo". // We want to make sure that we call a function named "foo" not "bar". - // CHECK: [[FN:%[0-9]+]] = function_ref @${{.*}}foo{{.*}} : $@convention(thin) () -> Int - %7 = witness_method $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P, #P.foo!1 : (Self) -> () -> Int, %2 : $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int + // CHECK: [[FN:%[0-9]+]] = function_ref @${{.*}}foo{{.*}} : $@convention(thin) () -> Int64 + %7 = witness_method $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P, #P.foo!1 : (Self) -> () -> Int64, %2 : $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 // CHECK: apply [[FN]] - %8 = apply %7<@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P>(%5) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int + %8 = apply %7<@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P>(%5) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 store %3 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P dealloc_stack %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B") A & P - return %8 : $Int + return %8 : $Int64 // CHECK-LABEL: end sil function {{.*}}impl{{.*}} } // end sil function 'impl' // C.foo() -sil @bar : $@convention(method) (@guaranteed C) -> Int +sil @bar : $@convention(method) (@guaranteed C) -> Int64 sil_vtable [serialized] B { - #B.foo!1: (B) -> () -> Int : @foo // B.foo() + #B.foo!1: (B) -> () -> Int64 : @foo // B.foo() } sil_vtable [serialized] C { - #C.foo!1: (C) -> () -> Int : @bar + #C.foo!1: (C) -> () -> Int64 : @bar } sil_witness_table [serialized] B: P module run { - method #P.foo!1: (Self) -> () -> Int : @Pfoo // protocol witness for P.foo() in conformance B + method #P.foo!1: (Self) -> () -> Int64 : @Pfoo // protocol witness for P.foo() in conformance B } From 727cd962bc62800e94883a1446e4ad64823a2189 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 3 Feb 2020 10:12:57 -0800 Subject: [PATCH 046/381] Format --- lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 8612580196e8b..4e82cf92ccad8 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -940,11 +940,12 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( bool madeUpdate = false; for (unsigned index = 0; index < conv.getNumSILArguments(); ++index) { canUpdate &= conv.getSILArgumentType(index) == NewArgs[index]->getType(); - madeUpdate |= NewArgs[index]->getType() != Apply.getArgument(index)->getType(); + madeUpdate |= + NewArgs[index]->getType() != Apply.getArgument(index)->getType(); } return std::make_tuple(canUpdate, madeUpdate); }(); - + if (!canUpdateArgs || !madeUpdate) { // Remove any new instructions created while attempting to optimize this // apply. Since the apply was never rewritten, if they aren't removed here, From 525f408445181d58e0d4b63a6b114a88ff1d9f98 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Mon, 3 Feb 2020 11:28:22 -0800 Subject: [PATCH 047/381] Update AddSwiftUnittests.cmake --- cmake/modules/AddSwiftUnittests.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 6de6909f1e1e7..a939730f65172 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -50,8 +50,6 @@ function(add_swift_unittest test_dirname) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set_property(TARGET "${test_dirname}" APPEND PROPERTY COMPILE_FLAGS " -march=core2") - set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_FLAGS - " -march=core2") endif() find_program(LDLLD_PATH "ld.lld") From ddd003fd4d20e9954ec7ae7a54006d456942d7a1 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Mon, 3 Feb 2020 13:44:27 -0800 Subject: [PATCH 048/381] Update AddSwiftUnittests.cmake --- cmake/modules/AddSwiftUnittests.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index a939730f65172..6d506c9d91df7 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -48,8 +48,10 @@ function(add_swift_unittest test_dirname) "${android_system_libs}") set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_LIBRARIES "log") elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") - set_property(TARGET "${test_dirname}" APPEND PROPERTY COMPILE_FLAGS - " -march=core2") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64") + set_property(TARGET "${test_dirname}" APPEND_STRING PROPERTY COMPILE_FLAGS + " -march=core2") + endif() endif() find_program(LDLLD_PATH "ld.lld") From 62a5ebb6a2927c0976a00211470c6ac4c869fc86 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Mon, 3 Feb 2020 13:45:47 -0800 Subject: [PATCH 049/381] Update AddSwift.cmake --- cmake/modules/AddSwift.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 81d62031ea970..dee7278559026 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -350,8 +350,10 @@ function(_add_variant_c_compile_flags) endif() if("${CFLAGS_SDK}" STREQUAL "LINUX") - # this is the minimum architecture that supports 16 byte CAS, which is necessary to avoid a dependency to libatomic - list(APPEND result "-march=core2") + if(${CFLAGS_ARCH} STREQUAL x86_64) + # this is the minimum architecture that supports 16 byte CAS, which is necessary to avoid a dependency to libatomic + list(APPEND result "-march=core2") + endif() endif() set("${CFLAGS_RESULT_VAR_NAME}" "${result}" PARENT_SCOPE) From dbadd99b62531e844e7d63f0bff7a5af7a893e6f Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 3 Feb 2020 15:20:59 -0800 Subject: [PATCH 050/381] Add benchmark for devirtualization performance measurements --- benchmark/CMakeLists.txt | 1 + .../DevirtualizeProtocolComposition.swift | 65 +++++++++++++++++++ benchmark/utils/main.swift | 2 + 3 files changed, 68 insertions(+) create mode 100644 benchmark/single-source/DevirtualizeProtocolComposition.swift diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index fbf1709638d3b..58c85675ad2d1 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -62,6 +62,7 @@ set(SWIFT_BENCH_MODULES single-source/Combos single-source/DataBenchmarks single-source/DeadArray + single-source/DevirtualizeProtocolComposition single-source/DictOfArraysToArrayOfDicts single-source/DictTest single-source/DictTest2 diff --git a/benchmark/single-source/DevirtualizeProtocolComposition.swift b/benchmark/single-source/DevirtualizeProtocolComposition.swift new file mode 100644 index 0000000000000..4bb8efdc5ce6a --- /dev/null +++ b/benchmark/single-source/DevirtualizeProtocolComposition.swift @@ -0,0 +1,65 @@ +//===--- DevirtualizeProtocolComposition.swift -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +public let DevirtualizeProtocolComposition = [ + BenchmarkInfo(name: "DevirtualizeProtocolComposition", runFunction: run_DevirtualizeProtocolComposition, tags: [.validation, .api]), +] + +public class ClassA { } + +protocol ProtocolA { + func foo() -> Int +} + +protocol ProtocolB { + func bar() -> Int +} + +public class ClassB: ClassA { + func foo() -> Int { + return 10 + } +} + +extension ClassB: ProtocolA { } + +func quadratic(a: Int) -> Int { + var sum = 0 + for _ in 0..(_ x: ClassA & ProtocolA, count: Int) -> Int { + var sum = 0 + for _ in 0.. Int { + return shouldOptimize1(c, count: 25) +} + +@inline(never) +public func run_DevirtualizeProtocolComposition(N: Int) { + for _ in 0.. Date: Mon, 3 Feb 2020 15:39:06 -0800 Subject: [PATCH 051/381] Update added tests to require objec interop --- test/SILOptimizer/devirtualize_protocol_composition.swift | 2 ++ .../devirtualize_protocol_composition_two_stores.sil | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/SILOptimizer/devirtualize_protocol_composition.swift b/test/SILOptimizer/devirtualize_protocol_composition.swift index 75d6d7bf5dbb0..9c54736ef2177 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition.swift +++ b/test/SILOptimizer/devirtualize_protocol_composition.swift @@ -1,6 +1,8 @@ // RUN: %target-swift-frontend -parse-as-library -O -wmo -emit-sil %s | %FileCheck %s // RUN: %target-swift-frontend -parse-as-library -Osize -wmo -emit-sil %s | %FileCheck %s +// REQUIRES: objc_interop + // This is an end-to-end test to ensure that the optimizer devertualizes // calls to a protocol composition type. diff --git a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil index 231a6a1c88e18..cf16ae6c167da 100644 --- a/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil +++ b/test/SILOptimizer/devirtualize_protocol_composition_two_stores.sil @@ -1,5 +1,7 @@ // RUN: %target-sil-opt -O -wmo -enable-sil-verify-all %s | %FileCheck %s +// REQUIRES: objc_interop + sil_stage canonical import Builtin From ea821b1adb17d04eb8171dab70b2cf2986247f94 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 3 Feb 2020 16:48:01 -0800 Subject: [PATCH 052/381] Use blackHole to make sure benchmark isn't optimized away --- benchmark/single-source/DevirtualizeProtocolComposition.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark/single-source/DevirtualizeProtocolComposition.swift b/benchmark/single-source/DevirtualizeProtocolComposition.swift index 4bb8efdc5ce6a..97fc75b3a4011 100644 --- a/benchmark/single-source/DevirtualizeProtocolComposition.swift +++ b/benchmark/single-source/DevirtualizeProtocolComposition.swift @@ -59,7 +59,7 @@ public func entryPoint1(c: ClassB) -> Int { @inline(never) public func run_DevirtualizeProtocolComposition(N: Int) { - for _ in 0.. Date: Tue, 4 Feb 2020 16:02:43 -0800 Subject: [PATCH 053/381] [SILOptimizer] address key path projection review feedback --- lib/SILOptimizer/Utils/KeyPathProjector.cpp | 156 ++++-------- test/SILOptimizer/optimize_keypath.swift | 267 +++++++++++++++++--- 2 files changed, 273 insertions(+), 150 deletions(-) diff --git a/lib/SILOptimizer/Utils/KeyPathProjector.cpp b/lib/SILOptimizer/Utils/KeyPathProjector.cpp index e81a61650f4ad..3783882e0f5cd 100644 --- a/lib/SILOptimizer/Utils/KeyPathProjector.cpp +++ b/lib/SILOptimizer/Utils/KeyPathProjector.cpp @@ -104,13 +104,13 @@ class StoredPropertyProjector : public ComponentProjector { else parentAccessType = AccessType::Modify; - parent->project(parentAccessType, [&](SILValue parent) { - callback(builder.createStructElementAddr(loc, parent, storedProperty)); + parent->project(parentAccessType, [&](SILValue parentValue) { + callback(builder.createStructElementAddr(loc, parentValue, storedProperty)); }); } else { // Accessing a class member -> reading the class - parent->project(AccessType::Get, [&](SILValue parent) { - SingleValueInstruction *Ref = builder.createLoad(loc, parent, + parent->project(AccessType::Get, [&](SILValue parentValue) { + SingleValueInstruction *Ref = builder.createLoad(loc, parentValue, LoadOwnershipQualifier::Unqualified); // If we were previously accessing a class member, we're done now. @@ -149,7 +149,6 @@ class StoredPropertyProjector : public ComponentProjector { // end the access now if (beginAccess == addr) { insertEndAccess(beginAccess, builder); - beginAccess = nullptr; } }); } @@ -178,8 +177,8 @@ class TupleElementProjector : public ComponentProjector { else parentAccessType = AccessType::Modify; - parent->project(parentAccessType, [&](SILValue parent) { - callback(builder.createTupleElementAddr(loc, parent, + parent->project(parentAccessType, [&](SILValue parentValue) { + callback(builder.createTupleElementAddr(loc, parentValue, component.getTupleIndex())); }); } @@ -203,7 +202,7 @@ class GettablePropertyProjector : public ComponentProjector { KeyPathPatternComponent::Kind::SettableProperty); assert(accessType == AccessType::Get && "property is not settable"); - parent->project(accessType, [&](SILValue parent) { + parent->project(accessType, [&](SILValue parentValue) { auto getter = component.getComputedPropertyGetter(); // The callback expects a memory address it can read from, @@ -212,36 +211,17 @@ class GettablePropertyProjector : public ComponentProjector { SILType type = function.getLoweredType(component.getComponentType()); auto addr = builder.createAllocStack(loc, type); - // Get subscript indices. - auto context = createContextContainer(); - SILValue contextPtr = SILValue(); - if (context) { - auto rawPtrType = builder.getASTContext().TheRawPointerType; - auto rawPtrLoweredType = function.getLoweredLoadableType(rawPtrType); - auto rawPtr = builder.createAddressToPointer(loc, context, rawPtrLoweredType); - - auto ptrDecl = builder.getASTContext().getUnsafeRawPointerDecl(); - SILType ptrType = function.getLoweredType(ptrDecl->getDeclaredType()); - contextPtr = builder.createStruct(loc, ptrType, {rawPtr}); - } + assertHasNoContext(); + assert(getter->getArguments().size() == 2); auto ref = builder.createFunctionRef(loc, getter); - if (contextPtr) - builder.createApply(loc, ref, subs, {addr, parent, contextPtr}); - else - builder.createApply(loc, ref, subs, {addr, parent}); + builder.createApply(loc, ref, subs, {addr, parentValue}); // If we were previously accessing a class member, we're done now. insertEndAccess(beginAccess, builder); - beginAccess = nullptr; callback(addr); - if (context) { - builder.createDestroyAddr(loc, context); - builder.createDeallocStack(loc, context); - } - builder.createDestroyAddr(loc, addr); builder.createDeallocStack(loc, addr); }); @@ -252,52 +232,11 @@ class GettablePropertyProjector : public ComponentProjector { SubstitutionMap subs; BeginAccessInst *&beginAccess; - // Creates the subscript index context for a computed property. - // Returns either null or a stack address that must be deallocated. - SILValue createContextContainer() { - auto indices = component.getSubscriptIndices(); - if (indices.empty()) return SILValue(); - - if (indices.size() == 1) { - auto index = indices.front(); - auto addr = builder.createAllocStack(loc, index.LoweredType); - auto value = keyPath->getAllOperands()[index.Operand].get(); - if (value->getType().isObject()) - builder.createStore(loc, value, addr, - StoreOwnershipQualifier::Unqualified); - else - builder.createCopyAddr(loc, value, addr, - IsNotTake, IsInitialization); - return addr; - } else { - // Create a tuple containing the context. - std::vector elementTypes; - elementTypes.reserve(indices.size()); - for (auto index : indices) { - elementTypes.push_back(TupleTypeElt(index.FormalType)); - } - auto tupleType = TupleType::get(ArrayRef(elementTypes), - builder.getASTContext()); - - auto &function = builder.getFunction(); - SILType lowered = function.getLoweredType(tupleType); - - auto tupleAddr = builder.createAllocStack(loc, lowered); - - // Copy the context elements into the tuple. - for (size_t i = 0; i < indices.size(); i++) { - auto index = indices[i]; - auto elementAddr = builder.createTupleElementAddr(loc, tupleAddr, i); - auto value = keyPath->getAllOperands()[index.Operand].get(); - if (value->getType().isObject()) - builder.createStore(loc, value, elementAddr, - StoreOwnershipQualifier::Unqualified); - else - builder.createCopyAddr(loc, value, elementAddr, - IsNotTake, IsInitialization); - } - return tupleAddr; - } + void assertHasNoContext() { + assert(component.getSubscriptIndices().empty() && + component.getExternalSubstitutions().empty() && + "cannot yet optimize key path component with external context; " + "we should have checked for this before trying to project"); } }; @@ -330,10 +269,14 @@ class SettablePropertyProjector : public GettablePropertyProjector { if (component.isComputedSettablePropertyMutating()) { // A mutating setter modifies the parent parentAccessType = AccessType::Modify; - if (beginAccess) beginAccess->setAccessKind(SILAccessKind::Modify); - } else parentAccessType = AccessType::Get; + if (beginAccess) { + beginAccess->setAccessKind(SILAccessKind::Modify); + } + } else { + parentAccessType = AccessType::Get; + } - parent->project(parentAccessType, [&](SILValue parent) { + parent->project(parentAccessType, [&](SILValue parentValue) { auto getter = component.getComputedPropertyGetter(); auto setter = component.getComputedPropertySetter(); @@ -342,28 +285,16 @@ class SettablePropertyProjector : public GettablePropertyProjector { auto &function = builder.getFunction(); SILType type = function.getLoweredType(component.getComponentType()); auto addr = builder.createAllocStack(loc, type); - - // Get subscript indices. - auto context = createContextContainer(); - SILValue contextPtr = SILValue(); - if (context) { - auto rawPtrType = builder.getASTContext().TheRawPointerType; - auto rawPtrLoweredType = function.getLoweredLoadableType(rawPtrType); - auto rawPtr = builder.createAddressToPointer(loc, context, rawPtrLoweredType); - - auto ptrDecl = builder.getASTContext().getUnsafeRawPointerDecl(); - SILType ptrType = function.getLoweredType(ptrDecl->getDeclaredType()); - contextPtr = builder.createStruct(loc, ptrType, {rawPtr}); - } + + assertHasNoContext(); + assert(getter->getArguments().size() == 2); + assert(setter->getArguments().size() == 2); // If this is a modify, we need to call the getter and // store the result in the writeback buffer. if (accessType == AccessType::Modify) { auto getterRef = builder.createFunctionRef(loc, getter); - if (contextPtr) - builder.createApply(loc, getterRef, subs, {addr, parent, contextPtr}); - else - builder.createApply(loc, getterRef, subs, {addr, parent}); + builder.createApply(loc, getterRef, subs, {addr, parentValue}); } // The callback function will write into the writeback buffer. @@ -371,15 +302,7 @@ class SettablePropertyProjector : public GettablePropertyProjector { // Pass the value from the writeback buffer to the setter. auto setterRef = builder.createFunctionRef(loc, setter); - if (contextPtr) - builder.createApply(loc, setterRef, subs, {addr, parent, contextPtr}); - else - builder.createApply(loc, setterRef, subs, {addr, parent}); - - if (context) { - builder.createDestroyAddr(loc, context); - builder.createDeallocStack(loc, context); - } + builder.createApply(loc, setterRef, subs, {addr, parentValue}); // Deallocate the writeback buffer. builder.createDestroyAddr(loc, addr); @@ -390,7 +313,6 @@ class SettablePropertyProjector : public GettablePropertyProjector { } }; - class OptionalWrapProjector : public ComponentProjector { public: OptionalWrapProjector(const KeyPathPatternComponent &component, @@ -404,7 +326,7 @@ class OptionalWrapProjector : public ComponentProjector { KeyPathPatternComponent::Kind::OptionalWrap); assert(accessType == AccessType::Get && "optional wrap components are immutable"); - parent->project(AccessType::Get, [&](SILValue parent) { + parent->project(AccessType::Get, [&](SILValue parentValue) { auto &function = builder.getFunction(); SILType optType = function.getLoweredType(component.getComponentType()); SILType objType = optType.getOptionalObjectType().getAddressType(); @@ -418,7 +340,7 @@ class OptionalWrapProjector : public ComponentProjector { auto someDecl = builder.getASTContext().getOptionalSomeDecl(); auto objAddr = builder.createInitEnumDataAddr(loc, optAddr, someDecl, objType); - builder.createCopyAddr(loc, parent, objAddr, IsNotTake, IsInitialization); + builder.createCopyAddr(loc, parentValue, objAddr, IsNotTake, IsInitialization); // Initialize the Optional enum. builder.createInjectEnumAddr(loc, optAddr, someDecl); @@ -578,9 +500,6 @@ class OptionalChainProjector : public ComponentProjector { SILValue optionalChainResult; }; - - - /// A projector to handle a complete key path. class CompleteKeyPathProjector : public KeyPathProjector { public: @@ -717,6 +636,21 @@ KeyPathProjector::create(SILValue keyPath, SILValue root, auto *kpInst = dyn_cast(keyPath); if (!kpInst || !kpInst->hasPattern()) return nullptr; + + // Check if the keypath only contains patterns which we support. + auto components = kpInst->getPattern()->getComponents(); + for (const KeyPathPatternComponent &comp : components) { + if (comp.getKind() == KeyPathPatternComponent::Kind::GettableProperty || + comp.getKind() == KeyPathPatternComponent::Kind::SettableProperty) { + if (!comp.getExternalSubstitutions().empty() || + !comp.getSubscriptIndices().empty()) { + // TODO: right now we can't optimize computed properties that require + // additional context for subscript indices or generic environment + // See https://github.com/apple/swift/pull/28799#issuecomment-570299845 + return nullptr; + } + } + } return std::make_unique(kpInst, root, loc, builder); diff --git a/test/SILOptimizer/optimize_keypath.swift b/test/SILOptimizer/optimize_keypath.swift index d8faf81373f49..cc1a8db359547 100644 --- a/test/SILOptimizer/optimize_keypath.swift +++ b/test/SILOptimizer/optimize_keypath.swift @@ -9,21 +9,17 @@ // REQUIRES: CPU=arm64 || CPU=x86_64 protocol P { - func modifyIt() + mutating func modifyIt() var computed: Int { get set } - - subscript(a: Int, b: Bool, c: Int?) -> Int { get set } } struct GenStruct : P { var st: T var computed: Int { get { st.computed } set { st.computed = newValue } } - subscript(a: Int, b: Bool, c: Int?) -> Int { get { st[a, b, c] } set { st[a, b, c] = newValue } } - init(_ st: T) { self.st = st } - func modifyIt() { + mutating func modifyIt() { st.modifyIt() } } @@ -34,10 +30,11 @@ final class GenClass : P { var ct: T var computed: Int { get { ct.computed } set { ct.computed = newValue } } - subscript(a: Int, b: Bool, c: Int?) -> Int { get { ct[a, b, c] } set { ct[a, b, c] = newValue } } + var gs: GenStruct init(_ ct: T) { self.ct = ct + self.gs = .init(ct) numGenClassObjs += 1 } @@ -62,11 +59,21 @@ final class DerivedClass2 : DerivedClass { final class SimpleClass : P { var i: Int - var array = [1, 2, 3, 4, 5] static var numObjs = 0 + + var tuple = (0, 1) + + struct Nested { + var i: Int = 0 + + @inline(never) + var computedGenClass: GenClass { GenClass(SimpleStruct(i: i)) } + } + var opt: Nested? - init(_ i: Int) { + init(_ i: Int, nested: Int? = nil) { self.i = i + self.opt = nested.map { Nested(i: $0) } Self.numObjs += 1 } @@ -79,14 +86,9 @@ final class SimpleClass : P { } var computed: Int { get { i + 1 } set { i = newValue - 1} } - - subscript(a: Int, b: Bool, c: Int?) -> Int { - get { precondition(!b && c == nil, "arguments are corrupt"); return array[a] } - set { precondition(b && c == 42, "arguments are corrupt"); array[a] = newValue } - } } -struct SimpleStruct { +struct SimpleStruct: P { var tuple = (0, 1) struct Nested { @@ -94,8 +96,19 @@ struct SimpleStruct { } var opt: Nested? + struct Nested2 { + var opt: Nested? + } + var opt2: Nested2? + var i = 0 + init(i: Int = 0) { self.i = i } + + mutating func modifyIt() { + i += 10 + } + var computed: Int { get { i + 1 } set { i = newValue - 1} } } @@ -311,6 +324,23 @@ func testGetter(_ s: GenStruct) -> Int { return s[keyPath: kp] } +// CHECK-LABEL: sil {{.*}} [noinline] {{.*}}testClassMemberGetter +// CHECK: [[E:%[0-9]+]] = ref_element_addr +// CHECK: [[M:%[0-9]+]] = begin_access [read] [dynamic] [[E]] +// CHECK: [[A:%[0-9]+]] = alloc_stack $Int +// CHECK: [[F:%[0-9]+]] = function_ref {{.*}}computed +// CHECK: apply [[F]]([[A]], [[M]]) +// CHECK: end_access +// destroy_addr gets optimized out +// CHECK: dealloc_stack [[A]] +// CHECK: return +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testClassMemberGetter(_ c: GenClass) -> Int { + let kp = \GenClass.gs.computed + return c[keyPath: kp] +} + // CHECK-LABEL: sil {{.*}}testComputedModify // CHECK: [[A:%[0-9]+]] = alloc_stack $Int // CHECK: [[G:%[0-9]+]] = function_ref {{.*}}computed @@ -328,28 +358,24 @@ func testComputedModify(_ s: inout GenStruct) { s[keyPath: kp] += 10 } -// CHECK-LABEL: sil {{.*}}testSubscript -// CHECK: [[I1:%[0-9]+]] = alloc_stack $Int -// CHECK: [[C1:%[0-9]+]] = alloc_stack $(Int, Bool, Optional) -// CHECK: [[A1:%[0-9]+]] = address_to_pointer [[C1]] -// CHECK: [[R1:%[0-9]+]] = struct $UnsafeRawPointer ([[A1]] -// CHECK: [[G:%[0-9]+]] = function_ref -// CHECK: apply [[G]]([[I1]], {{%[0-9]+}}, [[R1]]) -// CHECK: dealloc_stack [[C1]] -// -// CHECK: [[I2:%[0-9]+]] = alloc_stack $Int -// CHECK: [[C2:%[0-9]+]] = alloc_stack $(Int, Bool, Optional) -// CHECK: [[A2:%[0-9]+]] = address_to_pointer [[C2]] -// CHECK: [[R2:%[0-9]+]] = struct $UnsafeRawPointer ([[A2]] -// CHECK: [[S:%[0-9]+]] = function_ref -// CHECK: apply [[S]]([[I2]], {{%[0-9]+}}, [[R2]]) -// CHECK: dealloc_stack [[C2]] +// CHECK-LABEL: sil {{.*}}testComputedModify +// CHECK: [[E:%[0-9]+]] = ref_element_addr +// CHECK: [[M:%[0-9]+]] = begin_access [modify] [dynamic] [[E]] +// CHECK: [[A:%[0-9]+]] = alloc_stack $Int +// CHECK: [[G:%[0-9]+]] = function_ref {{.*}}computed +// CHECK: apply [[G]]([[A]], [[M]]) +// CHECK: store {{%[0-9]+}} to [[A]] +// CHECK: [[S:%[0-9]+]] = function_ref {{.*}}computed +// CHECK: apply [[S]]([[A]], [[M]]) +// destroy_addr gets optimized out +// CHECK: dealloc_stack [[A]] +// CHECK: end_access +// CHECK: return @inline(never) @_semantics("optimize.sil.specialize.generic.never") -func testSubscript(_ s: inout GenStruct) { - let kp1 = \GenStruct[1, true, 42] - let kp2 = \GenStruct[2, false, nil] - s[keyPath: kp1] = s[keyPath: kp2] +func testClassMemberComputedModify(_ s: inout GenClass) { + let kp = \GenClass.gs.computed + s[keyPath: kp] += 10 } // CHECK-LABEL: sil {{.*}}testModifyOptionalForce @@ -367,6 +393,24 @@ func testModifyOptionalForce(_ s: inout SimpleStruct) { s[keyPath: kp] += 10 } +// CHECK-LABEL: sil {{.*}}testModifyOptionalForceClass +// CHECK: [[O:%[0-9]+]] = ref_element_addr +// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] [[O]] +// CHECK: [[F:%[0-9]+]] = select_enum +// CHECK: cond_fail [[F]] +// CHECK: unchecked_enum_data [[E1:%[0-9]+]] +// CHECK: [[E2:%[0-9]+]] = init_enum_data_addr [[E1:%[0-9]+]] +// CHECK: store {{%[0-9]+}} to [[E2]] +// CHECK: inject_enum_addr [[E1]] +// CHECK: end_access +// CHECK: return +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testModifyOptionalForceClass(_ s: inout SimpleClass) { + let kp = \SimpleClass.opt!.i + s[keyPath: kp] += 10 +} + // CHECK-LABEL: sil {{.*}}testOptionalChain @@ -398,6 +442,80 @@ func testOptionalChain(_ s: SimpleStruct) -> Int? { return s[keyPath: kp] } +// CHECK-LABEL: sil {{.*}}testOptionalChainClass +// By the time the test gets run, lots of stack +// allocations have been promoted to registers. +// +// CHECK: [[E1:%[0-9]+]] = ref_element_addr +// CHECK: [[E2:%[0-9]+]] = begin_access [read] [dynamic] [no_nested_conflict] [[E1]] +// Check if value is null +// CHECK: switch_enum [[O:%[0-9]+]] +// CHECK: {{bb.}}: +// Unwrap value +// CHECK: [[A1:%[0-9]+]] = alloc_stack +// CHECK: store [[O]] to [[A1]] +// CHECK: [[U:%[0-9]+]] = unchecked_take_enum_data_addr [[A1]] +// Access stored property & re-wrap result +// CHECK: [[I:%[0-9]+]] = struct_element_addr [[U]] +// CHECK: [[R1:%[0-9]+]] = enum +// CHECK: dealloc_stack [[A1]] +// CHECK: br [[CONTINUATION:bb.]]([[R1]] : $Optional) +// CHECK: {{bb.}}: +// Store nil in result +// CHECK: [[R2:%[0-9]+]] = enum +// CHECK: br [[CONTINUATION]]([[R2]] : $Optional) +// CHECK: [[CONTINUATION]]([[R:%[0-9]+]] : $Optional): +// CHECK: end_access [[E2]] +// CHECK: return [[R]] +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testOptionalChainClass(_ s: SimpleClass) -> Int? { + let kp = \SimpleClass.opt?.i + return s[keyPath: kp] +} + +// CHECK-LABEL: sil {{.*}}testNestedOptionalChain +// By the time the test gets run, lots of stack +// allocations have been promoted to registers. +// +// Check if value is null +// CHECK: switch_enum [[O:%[0-9]+]] +// CHECK: {{bb.}}: +// Unwrap value +// CHECK: [[A1:%[0-9]+]] = alloc_stack +// CHECK: store [[O]] to [[A1]] +// CHECK: [[U:%[0-9]+]] = unchecked_take_enum_data_addr [[A1]] +// +// Unwrap nested optional +// CHECK: switch_enum [[O2:%[0-9]+]] +// CHECK: {{bb.}}: +// CHECK: [[A2:%[0-9]+]] = alloc_stack +// CHECK: store [[O2]] to [[A2]] +// CHECK: [[U2:%[0-9]+]] = unchecked_take_enum_data_addr [[A2]] +// Access stored property & re-wrap result +// CHECK: [[I:%[0-9]+]] = struct_element_addr [[U2]] +// CHECK: [[R1:%[0-9]+]] = enum +// CHECK: dealloc_stack [[A2]] +// CHECK: br [[CONT2:bb.]]([[R1]] : $Optional +// CHECK: {{bb.}}: +// Store nil in result +// CHECK: [[R2:%[0-9]+]] = enum +// CHECK: br [[CONT2]]([[R2]] : $Optional +// CHECK: [[CONT2]]([[R3:%[0-9]+]] : $Optional): +// CHECK: dealloc_stack [[A1]] +// CHECK: br [[CONT1:bb.]]([[R3]] : $Optional) +// CHECK: {{bb.}}: +// Store nil in result +// CHECK: [[R2:%[0-9]+]] = enum +// CHECK: br [[CONT1]]([[R2]] : $Optional) +// CHECK: [[CONT1]]([[R:%[0-9]+]] : $Optional): +// CHECK: return [[R]] +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testNestedOptionalChain(_ s: SimpleStruct) -> Int? { + let kp = \SimpleStruct.opt2?.opt?.i + return s[keyPath: kp] +} // CHECK-LABEL: sil {{.*}}testGetOptionalForce // CHECK: [[F:%[0-9]+]] = select_enum [[O:%[0-9]+]] @@ -416,6 +534,53 @@ func testGetOptionalForce(_ s: SimpleStruct) -> Int { return s[keyPath: kp] } +// CHECK-LABEL: sil {{.*}}testGetOptionalForceClass +// CHECK: [[R1:%[0-9]+]] = ref_element_addr +// CHECK: [[R2:%[0-9]+]] = begin_access [read] [dynamic] [no_nested_conflict] [[R1]] +// CHECK: [[F:%[0-9]+]] = select_enum [[O:%[0-9]+]] +// CHECK: cond_fail [[F]] +// CHECK: [[A:%[0-9]+]] = alloc_stack +// CHECK: store [[O]] to [[A]] +// CHECK: [[E2:%[0-9]+]] = unchecked_take_enum_data_addr [[A]] +// CHECK: [[E3:%[0-9]+]] = struct_element_addr [[E2]] +// CHECK: [[I:%[0-9]+]] = load [[E3]] +// CHECK: dealloc_stack [[A]] +// CHECK: end_access [[R2]] +// CHECK: return [[I]] +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testGetOptionalForceClass(_ s: SimpleClass) -> Int { + let kp = \SimpleClass.opt!.i + return s[keyPath: kp] +} + +// CHECK-LABEL: sil {{.*}}testGetComplex +// opt +// CHECK: [[E1:%[0-9]+]] = ref_element_addr +// CHECK: [[B1:%[0-9]+]] = begin_access [read] [dynamic] [[E1]] +// ! +// CHECK: [[F:%[0-9]+]] = select_enum [[O:%[0-9]+]] +// CHECK: cond_fail [[F]] +// computedGenClass +// CHECK: [[F:%[0-9]+]] = function_ref +// CHECK: apply [[F]] +// CHECK: end_access [[B1]] +// ct +// CHECK: [[E3:%[0-9]+]] = ref_element_addr +// CHECK: [[B2:%[0-9]+]] = begin_access [read] [dynamic] [no_nested_conflict] [[E3]] +// tuple +// CHECK: [[E4:%[0-9]+]] = struct_element_addr +// 0 +// CHECK: [[E5:%[0-9]+]] = tuple_element_addr [[E4]] +// CHECK: load [[E5]] +// CHECK: end_access [[B2]] +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testGetComplex(_ s: SimpleClass) -> Int { + let kp = \SimpleClass.opt!.computedGenClass.ct.tuple.1 + return s[keyPath: kp] +} + // CHECK-LABEL: sil {{.*}}testit func testit() { // CHECK-OUTPUT: GenStructRead: 27 @@ -467,30 +632,54 @@ func testit() { // CHECK-OUTPUT: Getter: 51 var s3 = GenStruct(SimpleClass(50)) print("Getter: \(testGetter(s3))") + // CHECK-OUTPUT: ClassMemberGetter: 52 + var c3 = GenClass(SimpleClass(51)) + print("ClassMemberGetter: \(testClassMemberGetter(c3))") // CHECK-OUTPUT: ComputedModify: 61 testComputedModify(&s3) print("ComputedModify: \(s3.computed)") - - // CHECK-OUTPUT: Subscript: [1, 3, 3, 4, 5] - testSubscript(&s3) - print("Subscript: \(s3.st.array)") + // CHECK-OUTPUT: ClassComputedModify: 62 + testClassMemberComputedModify(&c3) + print("ClassComputedModify: \(c3.computed)") var s4 = SimpleStruct() // CHECK-OUTPUT: Tuple: 1 testTuple(&s4) print("Tuple: \(s4.tuple.0)") + var c4 = SimpleClass(0) + // CHECK-OUTPUT: OptionalChain1: nil print("OptionalChain1: \(String(describing: testOptionalChain(s4)))") + // CHECK-OUTPUT: ClassOptionalChain1: nil + print("ClassOptionalChain1: \(String(describing: testOptionalChainClass(c4)))") // CHECK-OUTPUT: OptionalChain2: Optional(70) s4.opt = .init(i: 70) print("OptionalChain2: \(String(describing: testOptionalChain(s4)))") + // CHECK-OUTPUT: ClassOptionalChain2: Optional(71) + c4.opt = .init(i: 71) + print("ClassOptionalChain2: \(String(describing: testOptionalChainClass(c4)))") // CHECK-OUTPUT: OptionalForce: 80 testModifyOptionalForce(&s4) print("OptionalForce: \(testGetOptionalForce(s4))") + // CHECK-OUTPUT: ClassOptionalForce: 81 + testModifyOptionalForceClass(&c4) + print("ClassOptionalForce: \(testGetOptionalForceClass(c4))") + + // CHECK-OUTPUT: NestedOptionalChain1: nil + print("NestedOptionalChain1: \(String(describing: testNestedOptionalChain(s4)))") + // CHECK-OUTPUT: NestedOptionalChain2: nil + s4.opt2 = .init() + print("NestedOptionalChain2: \(String(describing: testNestedOptionalChain(s4)))") + // CHECK-OUTPUT: NestedOptionalChain3: Optional(90) + s4.opt2!.opt = .init(i: 90) + print("NestedOptionalChain3: \(String(describing: testNestedOptionalChain(s4)))") + + // CHECK-OUTPUT: testGetComplex: 1 + print("testGetComplex: \(testGetComplex(c4))") } testit() From 209bfd085664896ccce46f9ccfcfb6fa08ab8685 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 4 Feb 2020 16:27:17 -0800 Subject: [PATCH 054/381] Break argument checking out of a lambda and add comments --- .../SILCombiner/SILCombinerApplyVisitors.cpp | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 2a389da526441..c76a6c3f43073 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -924,28 +924,32 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( }); } - bool canUpdateArgs, madeUpdate; - std::tie(canUpdateArgs, madeUpdate) = [&]() { - auto substTy = - Apply.getCallee() - ->getType() - .substGenericArgs(Apply.getModule(), NewCallSubs, - Apply.getFunction()->getTypeExpansionContext()) - .getAs(); - SILFunctionConventions conv(substTy, - SILModuleConventions(Apply.getModule())); - bool canUpdate = true; - // Keep track of weather we made any updates at all. Otherwise, we will - // have an infinite loop. - bool madeUpdate = false; - for (unsigned index = 0; index < conv.getNumSILArguments(); ++index) { - canUpdate &= conv.getSILArgumentType(index) == NewArgs[index]->getType(); - madeUpdate |= - NewArgs[index]->getType() != Apply.getArgument(index)->getType(); - } - return std::make_tuple(canUpdate, madeUpdate); - }(); + // We need to make sure that we can a) update Apply to use the new args and b) + // at least one argument has changed. If no arguments have changed, we need + // to return nullptr. Otherwise, we will have an infinite loop. + auto substTy = + Apply.getCallee() + ->getType() + .substGenericArgs(Apply.getModule(), NewCallSubs, + Apply.getFunction()->getTypeExpansionContext()) + .getAs(); + SILFunctionConventions conv(substTy, + SILModuleConventions(Apply.getModule())); + bool canUpdateArgs = true; + bool madeUpdate = false; + for (unsigned index = 0; index < conv.getNumSILArguments(); ++index) { + // Make sure that *all* the arguments in both the new substitution function + // and our vector of new arguments have the same type. + canUpdateArgs &= + conv.getSILArgumentType(index) == NewArgs[index]->getType(); + // Make sure that we have changed at least one argument. + madeUpdate |= + NewArgs[index]->getType() != Apply.getArgument(index)->getType(); + } + // If we can't update the args (because of a type mismatch) or the args don't + // change, bail out by removing the instructions we've added and returning + // nullptr. if (!canUpdateArgs || !madeUpdate) { // Remove any new instructions created while attempting to optimize this // apply. Since the apply was never rewritten, if they aren't removed here, From 29749e64d76e8ba606eb0e4d4676de7a71a35903 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Wed, 5 Feb 2020 08:51:00 -0800 Subject: [PATCH 055/381] Update AddSwiftUnittests.cmake --- cmake/modules/AddSwiftUnittests.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 6d506c9d91df7..3e53e6696e444 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -49,8 +49,8 @@ function(add_swift_unittest test_dirname) set_property(TARGET "${test_dirname}" APPEND PROPERTY LINK_LIBRARIES "log") elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64") - set_property(TARGET "${test_dirname}" APPEND_STRING PROPERTY COMPILE_FLAGS - " -march=core2") + target_compile_options(${test_dirname} PRIVATE + -march=core2) endif() endif() From 5d086e7862daa44ae96b36912272ecc32be8922c Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 5 Feb 2020 13:16:49 -0800 Subject: [PATCH 056/381] Check that there is only one use of a given global addr function --- lib/SILOptimizer/IPO/GlobalOpt.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 6d6d31189a8de..faca4f30eeeb1 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -122,7 +122,8 @@ class SILGlobalOpt { protected: /// Checks if a given global variable is assigned only once. - bool isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG); + bool isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG, + SILFunction *globalAddrF); /// Reset all the maps of global variables. void reset(); @@ -640,13 +641,21 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder, return GetterF; } -bool SILGlobalOpt::isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG) { +bool SILGlobalOpt::isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG, + SILFunction *globalAddrF) { if (SILG->isLet()) return true; // If we should skip this, it is probably because there are multiple stores. // Return false if there are multiple stores or no stores. - if (GlobalVarSkipProcessing.count(SILG) || !GlobalVarStore.count(SILG)) + if (GlobalVarSkipProcessing.count(SILG) || !GlobalVarStore.count(SILG) || + // Check if there is more than one use the global addr function. If there + // is only one use, it must be the use that we are trying to optimize, so + // that is OK. If there is more than one use, one of the other uses may + // have a store attached to it which means there may be more than one + // assignment, so return false. + (GlobalInitCallMap.count(globalAddrF) && + GlobalInitCallMap[globalAddrF].size() != 1)) return false; // Otherwise, return true if this can't be used externally (false, otherwise). @@ -697,7 +706,7 @@ replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF, LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: replacing loads with known value for " << SILG->getName() << '\n'); - assert(isAssignedOnlyOnceInInitializer(SILG) && + assert(isAssignedOnlyOnceInInitializer(SILG, AddrF) && "The value of the initializer should be known at compile-time"); assert(SILG->getDecl() && "Decl corresponding to the global variable should be known"); @@ -810,7 +819,7 @@ void SILGlobalOpt::optimizeInitializer(SILFunction *AddrF, << SILG->getName() << '\n'); // Remove "once" call from the addressor. - if (!isAssignedOnlyOnceInInitializer(SILG) || !SILG->getDecl()) { + if (!isAssignedOnlyOnceInInitializer(SILG, AddrF) || !SILG->getDecl()) { LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: building static initializer for " << SILG->getName() << '\n'); From f8c4c450fe42ed2f6ed6128ce968349622dfd22f Mon Sep 17 00:00:00 2001 From: zoecarver Date: Wed, 5 Feb 2020 15:08:32 -0800 Subject: [PATCH 057/381] Reset changes from improperly modified test --- test/SILOptimizer/access_wmo.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/SILOptimizer/access_wmo.swift b/test/SILOptimizer/access_wmo.swift index 284f20d5d45ce..59625df2afa64 100644 --- a/test/SILOptimizer/access_wmo.swift +++ b/test/SILOptimizer/access_wmo.swift @@ -27,7 +27,9 @@ // PRIMARY-LABEL: } // end sil function '$s10access_wmo10readGlobalSiyF' // // WMO-LABEL: sil @$s10access_wmo10readGlobalSiyF : $@convention(thin) () -> Int { -// WMO-NOT: global_addr +// WMO: [[G1:%.*]] = global_addr @$s10access_wmo14internalGlobalSivp : $*Int +// WMO: [[A1:%.*]] = begin_access [read] [static] [no_nested_conflict] [[G1]] : $*Int +// WMO: end_access [[A1]] : $*Int // WMO: [[G2:%.*]] = global_addr @$s10access_wmo12publicGlobalSivp : $*Int // WMO: [[A2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[G2]] : $*Int // WMO: end_access [[A2]] : $*Int From 06f12fc268ae9a6ae805885b92838c82d2679a0a Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 5 Feb 2020 20:30:33 -0500 Subject: [PATCH 058/381] Incorporate further review comments --- stdlib/public/core/AnyHashable.swift | 42 +++++++++++++++------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index 044d52d443928..e0d4846880a8b 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -109,29 +109,31 @@ internal struct _ConcreteHashableBox: _AnyHashableBox { /// The `AnyHashable` type forwards equality comparisons and hashing operations /// to an underlying hashable value, hiding the type of the wrapped value. /// -/// For types that support conversions between each other using `as` or `as?` -/// (such as `Int` and `NSNumber`), `AnyHashable` treats their values -/// equivalently when type-erased by forwarding operations to canonical -/// representations of the wrapped values. +/// Where conversion using `as` or `as?` is possible between two types (such as +/// `Int` and `NSNumber`), `AnyHashable` uses a canonical representation of the +/// type-erased value so that instances wrapping the same value of either type +/// compare as equal. For example, `AnyHashable(42)` compares as equal to +/// `AnyHashable(42 as NSNumber)`. /// /// You can store mixed-type keys in dictionaries and other collections that /// require `Hashable` conformance by wrapping mixed-type keys in /// `AnyHashable` instances: /// /// let descriptions: [AnyHashable: Any] = [ -/// AnyHashable(42): "an Int", -/// AnyHashable(Int8(43)): "an Int8", -/// AnyHashable(Set(["a", "b"])): "a set of strings" +/// 42: "an Int", +/// 43 as Int8: "an Int8", +/// ["a", "b"] as Set: "a set of strings" /// ] -/// print(descriptions[AnyHashable(42)]!) // prints "an Int" -/// print(descriptions[AnyHashable(Int8(42))]!) // prints "an Int" -/// print(descriptions[AnyHashable(Int8(43))]!) // prints "an Int8" -/// print(descriptions[AnyHashable(44)]) // prints "nil" -/// print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings" +/// print(descriptions[42]!) // prints "an Int" +/// print(descriptions[42 as Int8]!) // prints "an Int" +/// print(descriptions[43 as Int8]!) // prints "an Int8" +/// print(descriptions[44]) // prints "nil" +/// print(descriptions[["a", "b"] as Set]!) // prints "a set of strings" /// -/// Note that `AnyHashable` instances are not guaranteed to preserve the hash -/// encoding of their wrapped values, even in cases where hash values appear to -/// match in a particular release of the standard library. +/// Note that `AnyHashable` does not guarantee that it preserves the hash +/// encoding of wrapped values. Do not rely on `AnyHashable` generating such +/// compatible hashes, as the hash encoding that it uses may change between any +/// two releases of the standard library. @frozen public struct AnyHashable { internal var _box: _AnyHashableBox @@ -198,13 +200,13 @@ public struct AnyHashable { extension AnyHashable: Equatable { /// Returns a Boolean value indicating whether two type-erased hashable - /// instances wrap the same underlying value. + /// instances wrap the same value. /// /// `AnyHashable` considers bridged counterparts (such as a `String` and an - /// `NSString`) of the same value to be equivalent when type-erased. Where - /// those compatible types have different definitions of equality comparisons, - /// values that originally compare as not equal may then compare as equal when - /// they are type-erased by conversion to `AnyHashable`: + /// `NSString`) of the same value to be equivalent when type-erased. If those + /// compatible types use different definitions for equality, values that were + /// originally distinct might compare as equal when they are converted to + /// `AnyHashable`: /// /// let string1 = "café" /// let string2 = "cafe\u{301}" // U+301 COMBINING ACUTE ACCENT From 464b585c9cfb79b0249e8add9607859faa06ec45 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 6 Feb 2020 14:14:51 -0800 Subject: [PATCH 059/381] Update benchmark to be more similar to other devirtualization benchmarks --- .../DevirtualizeProtocolComposition.swift | 42 +++++-------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/benchmark/single-source/DevirtualizeProtocolComposition.swift b/benchmark/single-source/DevirtualizeProtocolComposition.swift index 97fc75b3a4011..d90bbd1da71e6 100644 --- a/benchmark/single-source/DevirtualizeProtocolComposition.swift +++ b/benchmark/single-source/DevirtualizeProtocolComposition.swift @@ -16,50 +16,30 @@ public let DevirtualizeProtocolComposition = [ BenchmarkInfo(name: "DevirtualizeProtocolComposition", runFunction: run_DevirtualizeProtocolComposition, tags: [.validation, .api]), ] -public class ClassA { } +protocol Pingable { func ping() -> Int; func pong() -> Int} -protocol ProtocolA { - func foo() -> Int +public class Game { + func length() -> Int { return 10 } } -protocol ProtocolB { - func bar() -> Int -} +public class PingPong: Game { } -public class ClassB: ClassA { - func foo() -> Int { - return 10 - } +extension PingPong : Pingable { + func ping() -> Int { return 1 } + func pong() -> Int { return 2 } } -extension ClassB: ProtocolA { } - -func quadratic(a: Int) -> Int { +func playGame(_ x: Game & Pingable) -> Int { var sum = 0 - for _ in 0..(_ x: ClassA & ProtocolA, count: Int) -> Int { - var sum = 0 - for _ in 0.. Int { - return shouldOptimize1(c, count: 25) -} - @inline(never) public func run_DevirtualizeProtocolComposition(N: Int) { for _ in 0.. Date: Fri, 7 Feb 2020 16:05:01 -0800 Subject: [PATCH 060/381] [Gardening] dyn_cast -> isa --- include/swift/SIL/AbstractionPattern.h | 2 +- lib/AST/ASTScopeCreation.cpp | 6 +++--- lib/AST/AccessRequests.cpp | 2 +- lib/AST/Decl.cpp | 8 ++++---- lib/AST/Type.cpp | 4 ++-- lib/Driver/Driver.cpp | 2 +- lib/IDE/CodeCompletion.cpp | 2 +- lib/Parse/ParseDecl.cpp | 2 +- lib/SILOptimizer/Transforms/CopyPropagation.cpp | 2 +- lib/Sema/BuilderTransform.cpp | 2 +- lib/Sema/CSBindings.cpp | 6 +++--- lib/Sema/CSDiagnostics.cpp | 12 ++++++------ lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/TypeCheckAttr.cpp | 2 +- lib/Sema/TypeCheckPattern.cpp | 5 +++-- lib/Sema/TypeCheckPropertyWrapper.cpp | 1 + lib/Sema/TypeCheckProtocol.cpp | 2 +- lib/Sema/TypeCheckStorage.cpp | 4 ++-- stdlib/public/Reflection/TypeLowering.cpp | 2 +- 19 files changed, 35 insertions(+), 33 deletions(-) diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index 1cf4bc42ee17e..890b6222225f7 100644 --- a/include/swift/SIL/AbstractionPattern.h +++ b/include/swift/SIL/AbstractionPattern.h @@ -675,7 +675,7 @@ class AbstractionPattern { isa(type)) { return true; } - if (auto archetype = dyn_cast(type)) { + if (isa(type)) { return true; } return false; diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index f81ebe47bc7ab..6a3265a606b6c 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -523,7 +523,7 @@ class ScopeCreator final { // get inactive nodes that nest in active clauses for (auto n : clause.Elements) { if (auto *const d = n.dyn_cast()) - if (auto *const icd = dyn_cast(d)) + if (isa(d)) expandIfConfigClausesInto(expansion, {d}, true); } } else if (includeInactiveIfConfigClauses) { @@ -1776,7 +1776,7 @@ void ScopeCreator::forEachClosureIn( return {true, E}; } std::pair walkToStmtPre(Stmt *S) override { - if (auto *bs = dyn_cast(S)) { // closures hidden in here + if (isa(S)) { // closures hidden in here return {true, S}; } return {false, S}; @@ -2132,7 +2132,7 @@ class LocalizableDeclContextCollector : public ASTWalker { auto f = SM.getIdentifierForBuffer(bufID); auto lin = SM.getLineNumber(loc); if (f.endswith(file) && lin == line) - if (auto *v = dyn_cast(D)) + if (isa(D)) llvm::errs() << "*** catchForDebugging: " << lin << " ***\n"; } }; diff --git a/lib/AST/AccessRequests.cpp b/lib/AST/AccessRequests.cpp index 8a14df9f95f4d..8c6dc9b61ce95 100644 --- a/lib/AST/AccessRequests.cpp +++ b/lib/AST/AccessRequests.cpp @@ -68,7 +68,7 @@ AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const { // Special case for generic parameters; we just give them a dummy // access level. - if (auto genericParam = dyn_cast(D)) { + if (isa(D)) { return AccessLevel::Internal; } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 8bdb94abff1c9..333f95d276cb4 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -645,7 +645,7 @@ Expr *AbstractFunctionDecl::getSingleExpressionBody() const { if (auto *stmt = body.dyn_cast()) { if (auto *returnStmt = dyn_cast(stmt)) { return returnStmt->getResult(); - } else if (auto *failStmt = dyn_cast(stmt)) { + } else if (isa(stmt)) { // We can only get to this point if we're a type-checked ConstructorDecl // which was originally spelled init?(...) { nil }. // @@ -663,7 +663,7 @@ void AbstractFunctionDecl::setSingleExpressionBody(Expr *NewBody) { if (auto *returnStmt = dyn_cast(stmt)) { returnStmt->setResult(NewBody); return; - } else if (auto *failStmt = dyn_cast(stmt)) { + } else if (isa(stmt)) { // We can only get to this point if we're a type-checked ConstructorDecl // which was originally spelled init?(...) { nil }. // @@ -780,7 +780,7 @@ bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { FU->getKind() != FileUnitKind::SerializedAST) return false; - if (auto PD = dyn_cast(D)) { + if (isa(D)) { if (treatNonBuiltinProtocolsAsPublic) return false; } @@ -2657,7 +2657,7 @@ static Type mapSignatureFunctionType(ASTContext &ctx, Type type, // Functions and subscripts cannot overload differing only in opaque return // types. Replace the opaque type with `Any`. - if (auto opaque = type->getAs()) { + if (type->is()) { type = ProtocolCompositionType::get(ctx, {}, /*hasAnyObject*/ false); } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 679962777c587..a7c7aabf68cf1 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3442,7 +3442,7 @@ Type DependentMemberType::substBaseType(Type substBase, Type DependentMemberType::substRootParam(Type newRoot, LookupConformanceFn lookupConformance){ auto base = getBase(); - if (auto param = base->getAs()) { + if (base->is()) { return substBaseType(newRoot, lookupConformance); } if (auto depMem = base->getAs()) { @@ -3633,7 +3633,7 @@ static Type substType(Type derivedType, if (isa(substOrig)) return ErrorType::get(type); - if (auto primaryArchetype = dyn_cast(substOrig)) + if (isa(substOrig)) return ErrorType::get(type); // Opened existentials cannot be substituted in this manner, diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index d596d4ebdbb5f..49227f577d353 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -2347,7 +2347,7 @@ static StringRef baseNameForImage(const JobAction *JA, const OutputInfo &OI, if (JA->size() == 1 && OI.ModuleNameIsFallback && BaseInput != "-") return llvm::sys::path::stem(BaseInput); - if (auto link = dyn_cast(JA)) { + if (isa(JA)) { Buffer = "lib"; Buffer.append(BaseName); Buffer.append(Triple.isOSWindows() ? ".lib" : ".a"); diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 155cdb72299bf..32500f16f0949 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -5239,7 +5239,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { } if (auto *DRE = dyn_cast_or_null(ParsedExpr)) { Lookup.setIsSelfRefExpr(DRE->getDecl()->getFullName() == Context.Id_self); - } else if (auto *SRE = dyn_cast_or_null(ParsedExpr)) { + } else if (ParsedExpr && isa(ParsedExpr)) { Lookup.setIsSuperRefExpr(); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index a23cf13c9d71c..288198f3eab95 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6397,7 +6397,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { return; } } - if (auto F = dyn_cast(AFD)) { + if (isa(AFD)) { auto RS = new (Context) ReturnStmt(SourceLoc(), E); BS->setFirstElement(RS); AFD->setHasSingleExpressionBody(); diff --git a/lib/SILOptimizer/Transforms/CopyPropagation.cpp b/lib/SILOptimizer/Transforms/CopyPropagation.cpp index 52158b4c6abd7..82bb2269f0d4f 100644 --- a/lib/SILOptimizer/Transforms/CopyPropagation.cpp +++ b/lib/SILOptimizer/Transforms/CopyPropagation.cpp @@ -615,7 +615,7 @@ static bool computeLiveness(CopyPropagationState &pass) { // point of the end_borrow. if (auto *BBI = dyn_cast(user)) { for (Operand *use : BBI->getUses()) { - if (auto *EBI = dyn_cast(use->getUser())) + if (isa(use->getUser())) computeUseLiveness(use, pass); } continue; diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 770c39bac7939..8372133545f6c 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -266,7 +266,7 @@ class BuilderClosureVisitor continue; // Skip #warning/#error; we'll handle them when applying the builder. - if (auto poundDiag = dyn_cast(decl)) { + if (isa(decl)) { continue; } diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 0153ccc3f9b2c..016697ec13515 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -904,7 +904,7 @@ Optional ConstraintSystem::checkTypeOfBinding(TypeVariableType *typeVar, } // If the type is a type variable itself, don't permit the binding. - if (auto *bindingTypeVar = type->getRValueType()->getAs()) + if (type->getRValueType()->is()) return None; // Don't bind to a dependent member type, even if it's currently @@ -1082,8 +1082,8 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { cs, TypeVar->getImpl().getLocator()); if (cs.recordFix(fix)) return true; - } else if (auto *OLE = dyn_cast_or_null( - srcLocator->getAnchor())) { + } else if (srcLocator->getAnchor() && + isa(srcLocator->getAnchor())) { auto *fix = SpecifyObjectLiteralTypeImport::create( cs, TypeVar->getImpl().getLocator()); if (cs.recordFix(fix)) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index cfecbcb68ec2a..86c57e4cc5752 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -728,7 +728,7 @@ bool LabelingFailure::diagnoseAsNote() { return false; SmallVector argLabels; - if (auto *paren = dyn_cast(argExpr)) { + if (isa(argExpr)) { argLabels.push_back(Identifier()); } else if (auto *tuple = dyn_cast(argExpr)) { argLabels.append(tuple->getElementNames().begin(), @@ -1304,7 +1304,7 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { diag::assignment_dynamic_property_has_immutable_base; } } - } else if (auto sub = dyn_cast(diagExpr)) { + } else if (isa(diagExpr)) { subElementDiagID = diag::assignment_subscript_has_immutable_base; } else { subElementDiagID = diag::assignment_lhs_is_immutable_variable; @@ -2169,7 +2169,7 @@ bool ContextualFailure::diagnoseConversionToNil() const { if (auto *TE = dyn_cast(parentExpr)) { // In case of dictionary e.g. `[42: nil]` we need to figure // out whether nil is a "key" or a "value". - if (auto *DE = dyn_cast(enclosingExpr)) { + if (isa(enclosingExpr)) { assert(TE->getNumElements() == 2); CTP = TE->getElement(0) == anchor ? CTP_DictionaryKey : CTP_DictionaryValue; @@ -2185,7 +2185,7 @@ bool ContextualFailure::diagnoseConversionToNil() const { if (isa(enclosingExpr) || isa(enclosingExpr) || isa(enclosingExpr)) CTP = CTP_CallArgument; - } else if (auto *CE = dyn_cast(parentExpr)) { + } else if (isa(parentExpr)) { // `nil` is passed as a left-hand side of the coercion // operator e.g. `nil as Foo` CTP = CTP_CoerceOperand; @@ -3601,10 +3601,10 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { // Give a customized message if we're accessing a member type // of a protocol -- otherwise a diagnostic talking about // static members doesn't make a whole lot of sense - if (auto TAD = dyn_cast(Member)) { + if (isa(Member)) { Diag.emplace(emitDiagnostic(loc, diag::typealias_outside_of_protocol, Name)); - } else if (auto ATD = dyn_cast(Member)) { + } else if (isa(Member)) { Diag.emplace(emitDiagnostic(loc, diag::assoc_type_outside_of_protocol, Name)); } else if (isa(Member)) { diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index b81236a8e163e..97c6b04561e27 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2355,7 +2355,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } } - if (auto *SD = dyn_cast(decl)) { + if (isa(decl)) { if (locator->isResultOfKeyPathDynamicMemberLookup() || locator->isKeyPathSubscriptComponent()) { // Subscript type has a format of (Self[.Type) -> (Arg...) -> Result diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 5ee8756e6dbc6..ea54a43ef59e0 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -3891,7 +3891,7 @@ llvm::Expected DifferentiableAttributeTypeCheckRequest::evaluate( // TODO(TF-654): Class initializers are not yet supported. // Extra JVP/VJP type calculation logic is necessary because classes have // both allocators and initializers. - if (auto *initDecl = dyn_cast(original)) { + if (isa(original)) { diags.diagnose(attr->getLocation(), diag::differentiable_attr_class_init_not_yet_supported); attr->setInvalid(); diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 7a4bd42d0eff5..10691fc857e92 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -676,7 +676,8 @@ static Type validateTypedPattern(TypeResolution resolution, // variable binding, then we can bind the opaque return type from the // property definition. auto &Context = resolution.getASTContext(); - if (auto opaqueRepr = dyn_cast_or_null(TL.getTypeRepr())) { + auto *Repr = TL.getTypeRepr(); + if (Repr && isa(Repr)) { auto named = dyn_cast( TP->getSubPattern()->getSemanticsProvidingPattern()); if (named) { @@ -700,7 +701,7 @@ static Type validateTypedPattern(TypeResolution resolution, return ErrorType::get(Context); } - assert(!dyn_cast_or_null(TL.getTypeRepr())); + assert(!dyn_cast_or_null(Repr)); return TL.getType(); } diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 7f6869de91197..428fa51990efc 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -463,6 +463,7 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, // Properties with wrappers must not override another property. if (auto classDecl = dyn_cast(dc)) { if (auto overrideAttr = var->getAttrs().getAttribute()) { + if (isa(dc)) { var->diagnose(diag::property_with_wrapper_overrides, var->getFullName()) .highlight(attr->getRange()); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 40caf3c5310cf..24e3de6e09633 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -725,7 +725,7 @@ static Optional findMissingGenericRequirementForSolutionFix( requirement); }; - if (auto memberTy = type->getAs()) + if (type->is()) return missingRequirementMatch(type); type = type->mapTypeOutOfContext(); diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 6d490fda71ac0..57f92b2888192 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -712,7 +712,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor, // Check for availability of wrappedValue. if (accessor->getAccessorKind() == AccessorKind::Get || accessor->getAccessorKind() == AccessorKind::Read) { - if (auto *attr = wrappedValue->getAttrs().getUnavailable(ctx)) { + if (wrappedValue->getAttrs().getUnavailable(ctx)) { diagnoseExplicitUnavailability( wrappedValue, var->getAttachedPropertyWrappers()[i]->getRangeWithAt(), @@ -862,7 +862,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor, lookupExpr, lookupExpr->getType()->getRValueType()); } } - } else if (auto subscript = dyn_cast(storage)) { + } else if (isa(storage)) { Expr *indices = buildSubscriptIndexReference(ctx, accessor); lookupExpr = SubscriptExpr::create(ctx, selfDRE, indices, memberRef, /*IsImplicit=*/true, semantics); diff --git a/stdlib/public/Reflection/TypeLowering.cpp b/stdlib/public/Reflection/TypeLowering.cpp index c93afa1d25067..b373b57f699dc 100644 --- a/stdlib/public/Reflection/TypeLowering.cpp +++ b/stdlib/public/Reflection/TypeLowering.cpp @@ -337,7 +337,7 @@ class ExistentialTypeInfoBuilder { } // Don't look up field info for imported Objective-C classes. - if (auto *OC = dyn_cast(T)) { + if (isa(T)) { addAnyObject(); return; } From 0e6dc0cd72fee297f33f6fd5d33999a0c0ee6a10 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 7 Feb 2020 16:06:00 -0800 Subject: [PATCH 061/381] [Gardening] getAttribute -> hasAttribute --- lib/Sema/TypeCheckDeclObjC.cpp | 3 +-- lib/Sema/TypeCheckPropertyWrapper.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 54fe278987e52..e109fd507e09d 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -1043,8 +1043,7 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { // Infer @objc for @_dynamicReplacement(for:) when replaced decl is @objc. if (isa(VD) || isa(VD)) - if (auto *replacementAttr = - VD->getAttrs().getAttribute()) { + if (VD->getAttrs().hasAttribute()) { if (auto *replaced = VD->getDynamicallyReplacedDecl()) { if (replaced->isObjC()) return ObjCReason(ObjCReason::ImplicitlyObjC); diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 428fa51990efc..97a756b172acc 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -461,9 +461,8 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, } // Properties with wrappers must not override another property. - if (auto classDecl = dyn_cast(dc)) { - if (auto overrideAttr = var->getAttrs().getAttribute()) { if (isa(dc)) { + if (var->getAttrs().hasAttribute()) { var->diagnose(diag::property_with_wrapper_overrides, var->getFullName()) .highlight(attr->getRange()); From 66f2733045725a904babb9536e056111d9001642 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 7 Feb 2020 16:06:42 -0800 Subject: [PATCH 062/381] [Gardening] Use cast results where possible --- lib/SIL/LinearLifetimeChecker.cpp | 2 +- lib/Sema/CSDiag.cpp | 4 ++-- lib/Sema/TypeCheckAttr.cpp | 2 +- lib/Sema/TypeCheckDecl.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/SIL/LinearLifetimeChecker.cpp b/lib/SIL/LinearLifetimeChecker.cpp index 1697965957cc3..2bf81ce7458a4 100644 --- a/lib/SIL/LinearLifetimeChecker.cpp +++ b/lib/SIL/LinearLifetimeChecker.cpp @@ -219,7 +219,7 @@ void State::initializeConsumingUse(BranchPropagatedUser consumingUser, llvm::errs() << "Function: '" << beginBlock->getParent()->getName() << "'\n" << "Found over consume?!\n"; if (auto v = value) { - llvm::errs() << "Value: " << *value; + llvm::errs() << "Value: " << *v; } else { llvm::errs() << "Value: N/A\n"; } diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 8982a62545e44..c1b2b596a316c 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -1297,8 +1297,8 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { // Unresolved/Anonymous ClosureExprs are common enough that we should give // them tailored diagnostics. if (auto CE = dyn_cast(E->getValueProvidingExpr())) { - diagnose(E->getLoc(), diag::cannot_infer_closure_type) - .highlight(E->getSourceRange()); + diagnose(CE->getLoc(), diag::cannot_infer_closure_type) + .highlight(CE->getSourceRange()); return; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index ea54a43ef59e0..cd7f50ac3bd87 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2931,7 +2931,7 @@ DynamicallyReplacedDeclRequest::evaluate(Evaluator &evaluator, // If we can lazily resolve the function, do so now. if (auto *LazyResolver = attr->Resolver) { - auto decl = attr->Resolver->loadDynamicallyReplacedFunctionDecl( + auto decl = LazyResolver->loadDynamicallyReplacedFunctionDecl( attr, attr->ResolverContextData); attr->Resolver = nullptr; return decl; diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index a394d8671136a..ed0d7b27e212d 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -578,7 +578,7 @@ IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { VD->getOriginalWrappedProperty(PropertyWrapperSynthesizedPropertyKind::Backing)) return true; - if (auto *nominalDecl = VD->getDeclContext()->getSelfClassDecl()) { + if (VD->getDeclContext()->getSelfClassDecl()) { // If this variable is a class member, mark it final if the // class is final, or if it was declared with 'let'. auto *PBD = VD->getParentPatternBinding(); From 3bd00441432d9d668bc188edf214ad7bc4d2ec23 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 7 Feb 2020 16:07:08 -0800 Subject: [PATCH 063/381] [Gardening] Remove remaining casting artifacts --- lib/AST/ASTDumper.cpp | 2 +- lib/AST/AccessRequests.cpp | 2 +- lib/AST/Module.cpp | 2 +- lib/AST/Type.cpp | 4 ++-- lib/AST/TypeCheckRequests.cpp | 2 +- lib/IRGen/IRGenMangler.cpp | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 2a34900d480df..d3d4770a4cc2c 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -716,7 +716,7 @@ namespace { if (auto *var = dyn_cast(VD)) { PrintWithColorRAII(OS, TypeColor) << " type='"; - if (auto varTy = var->hasInterfaceType()) + if (var->hasInterfaceType()) var->getType().print(PrintWithColorRAII(OS, TypeColor).getOS()); else PrintWithColorRAII(OS, TypeColor) << ""; diff --git a/lib/AST/AccessRequests.cpp b/lib/AST/AccessRequests.cpp index 8c6dc9b61ce95..29908cfdddfe8 100644 --- a/lib/AST/AccessRequests.cpp +++ b/lib/AST/AccessRequests.cpp @@ -213,7 +213,7 @@ DefaultAndMaxAccessLevelRequest::evaluate(Evaluator &evaluator, AccessLevel maxAccess = AccessLevel::Public; - if (GenericParamList *genericParams = ED->getGenericParams()) { + if (ED->getGenericParams()) { // Only check the trailing 'where' requirements. Other requirements come // from the extended type and have already been checked. DirectlyReferencedTypeDecls typeDecls = diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index a478cb81a4514..e0565de351dce 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1218,7 +1218,7 @@ ModuleDecl::ReverseFullNameIterator::operator++() { if (!current) return *this; - if (auto *swiftModule = current.dyn_cast()) { + if (current.is()) { current = nullptr; return *this; } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index a7c7aabf68cf1..bb9cc373edee7 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2023,7 +2023,7 @@ getObjCObjectRepresentable(Type type, const DeclContext *dc) { // DynamicSelfType is always representable in Objective-C, even if // the class is not @objc, allowing Self-returning methods to witness // @objc protocol requirements. - if (auto dynSelf = type->getAs()) + if (type->is()) return ForeignRepresentableKind::Object; // @objc classes. @@ -4098,7 +4098,7 @@ case TypeKind::Id: auto fnTy = cast(base); bool changed = false; - if (auto subs = fnTy->getSubstitutions()) { + if (fnTy->getSubstitutions()) { #ifndef NDEBUG // This interface isn't suitable for updating the substitution map in a // substituted SILFunctionType. diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 267980c92d5fe..8261ffd62d232 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -363,7 +363,7 @@ SourceLoc WhereClauseOwner::getLoc() const { void swift::simple_display(llvm::raw_ostream &out, const WhereClauseOwner &owner) { - if (auto where = owner.source.dyn_cast()) { + if (owner.source.is()) { simple_display(out, owner.dc->getAsDecl()); } else if (owner.source.is()) { out << "@_specialize"; diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 06f85871b7b36..54e3934a807ef 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -120,7 +120,7 @@ IRGenMangler::withSymbolicReferences(IRGenModule &IGM, } return true; - } else if (auto opaque = s.dyn_cast()) { + } else if (s.is()) { // Always symbolically reference opaque types. return true; } else { From a8119c57718bf52bd0624f0671ee1dc57196c00e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 7 Feb 2020 16:08:00 -0800 Subject: [PATCH 064/381] [Gardening] Add an explicit use of an error code --- lib/Driver/DriverIncrementalRanges.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Driver/DriverIncrementalRanges.cpp b/lib/Driver/DriverIncrementalRanges.cpp index b7ff68b91d462..738d13b82d629 100644 --- a/lib/Driver/DriverIncrementalRanges.cpp +++ b/lib/Driver/DriverIncrementalRanges.cpp @@ -84,10 +84,14 @@ Optional SourceRangeBasedInfo::loadInfoForOnePrimary( DiagnosticEngine &diags) { auto removeSupplementaryPaths = [&] { - if (auto ec = llvm::sys::fs::remove(compiledSourcePath)) + if (auto ec = llvm::sys::fs::remove(compiledSourcePath)) { + (void)ec; llvm::errs() << "WARNING could not remove: " << compiledSourcePath; - if (auto ec = llvm::sys::fs::remove(swiftRangesPath)) + } + if (auto ec = llvm::sys::fs::remove(swiftRangesPath)) { + (void)ec; llvm::errs() << "WARNING could not remove: " << swiftRangesPath; + } }; assert(!primaryInputPath.empty() && "Must have a primary to load info."); From bd1dce89b85742415e032b440cb784c28c62ab5c Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 7 Feb 2020 16:08:32 -0800 Subject: [PATCH 065/381] [Gardening] Silence needless double-brace-initialization warnings See also https://wg21.cmeerw.net/cwg/issue1270 --- lib/IRGen/GenType.cpp | 2 +- lib/IRGen/MetadataRequest.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 54798e8093483..36c2f9cfab935 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -440,7 +440,7 @@ static void emitSpecializedMemOperation( auto &Builder = IGF.Builder; // Sizes to try. Tested in order. - const auto sizes = std::array{ + const std::array sizes = { Size(0), Size(1), Size(2), diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 5dcb3cdd05d02..a93a0d188bf8e 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1935,7 +1935,7 @@ MetadataResponse irgen::emitGenericTypeMetadataAccessFunction( ? IGF.Builder.CreateBitCast(params.claimNext(), IGM.Int8PtrTy) : llvm::UndefValue::get(IGM.Int8PtrTy); - std::array argValues{arg0, arg1, arg2}; + std::array argValues = {arg0, arg1, arg2}; emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( IGF, params, nominal, genericArgs, From 67297904acf5f2a2b525611f4f6017e3c1a3e017 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 29 Jan 2020 16:49:59 +0000 Subject: [PATCH 066/381] [WebAssembly] Add ifdefs for the WASI target --- include/swift/Basic/Lazy.h | 2 ++ include/swift/SwiftRemoteMirror/Platform.h | 6 ++--- lib/IRGen/IRGen.cpp | 6 +++++ .../private/StdlibUnittest/InterceptTraps.cpp | 10 +++++++- stdlib/private/StdlibUnittest/RaceTest.swift | 6 +++-- .../StdlibUnittest/StdlibCoreExtras.swift | 2 +- .../StdlibUnittest/StdlibUnittest.swift | 10 +++++++- .../private/StdlibUnittest/SymbolLookup.swift | 6 ++++- .../SwiftPrivateLibcExtras/Subprocess.c | 6 ++--- .../SwiftPrivateLibcExtras/Subprocess.swift | 19 ++++++++++++++- .../SwiftPrivateLibcExtras.swift | 6 +++-- .../SwiftPrivateThreadExtras/CMakeLists.txt | 1 + stdlib/public/Platform/Platform.swift | 4 +++- stdlib/public/Platform/glibc.modulemap.gyb | 24 ++++++++++++++++++- stdlib/public/SwiftShims/Visibility.h | 2 +- stdlib/public/core/AtomicInt.swift.gyb | 8 +++---- stdlib/public/core/BridgeStorage.swift | 2 +- stdlib/public/core/Builtin.swift | 4 ++-- stdlib/public/core/DictionaryVariant.swift | 2 +- stdlib/public/core/Hasher.swift | 4 ++-- stdlib/public/core/SetVariant.swift | 2 +- stdlib/public/core/StringBridge.swift | 2 +- stdlib/public/core/StringGuts.swift | 2 +- stdlib/public/core/StringObject.swift | 24 +++++++++---------- stdlib/public/core/StringStorage.swift | 12 +++++----- stdlib/public/runtime/Casting.cpp | 13 +++++++++- stdlib/public/runtime/Errors.cpp | 2 +- stdlib/public/runtime/Metadata.cpp | 3 +++ stdlib/public/runtime/MutexPThread.cpp | 2 +- 29 files changed, 139 insertions(+), 53 deletions(-) diff --git a/include/swift/Basic/Lazy.h b/include/swift/Basic/Lazy.h index 594e2ed8b9fcd..6c899abeeffd6 100644 --- a/include/swift/Basic/Lazy.h +++ b/include/swift/Basic/Lazy.h @@ -16,6 +16,8 @@ #include #ifdef __APPLE__ #include +#elif defined(__wasi__) +// No pthread on wasi, see https://bugs.swift.org/browse/SR-12097 for more details. #else #include #endif diff --git a/include/swift/SwiftRemoteMirror/Platform.h b/include/swift/SwiftRemoteMirror/Platform.h index cefdea2c4de6f..96ed451f854eb 100644 --- a/include/swift/SwiftRemoteMirror/Platform.h +++ b/include/swift/SwiftRemoteMirror/Platform.h @@ -18,7 +18,7 @@ extern "C" { #endif #if defined(swiftRemoteMirror_EXPORTS) -# if defined(__ELF__) +# if defined(__ELF__) || defined(__WASM__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("protected"))) # elif defined(__MACH__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) @@ -30,9 +30,7 @@ extern "C" { # endif # endif #else -# if defined(__ELF__) -# define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) -# elif defined(__MACH__) +# if defined(__ELF__) || defined(__MACH__) || defined(__WASM__) # define SWIFT_REMOTE_MIRROR_LINKAGE __attribute__((__visibility__("default"))) # else # if defined(_WINDLL) diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index a39ff7e63e4cd..b2d9b38b7bd50 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -165,6 +165,12 @@ swift::getIRTargetOptions(const IRGenOptions &Opts, ASTContext &Ctx) { TargetOpts.FunctionSections = Opts.FunctionSections; auto *Clang = static_cast(Ctx.getClangModuleLoader()); + + // WebAssembly doesn't support atomics yet, see https://bugs.swift.org/browse/SR-12097 + // for more details. + if (Clang->getTargetInfo().getTriple().isOSBinFormatWasm()) + TargetOpts.ThreadModel = llvm::ThreadModel::Single; + clang::TargetOptions &ClangOpts = Clang->getTargetInfo().getTargetOpts(); return std::make_tuple(TargetOpts, ClangOpts.CPU, ClangOpts.Features, ClangOpts.Triple); } diff --git a/stdlib/private/StdlibUnittest/InterceptTraps.cpp b/stdlib/private/StdlibUnittest/InterceptTraps.cpp index 377397bebb9bf..80c3fa5979571 100644 --- a/stdlib/private/StdlibUnittest/InterceptTraps.cpp +++ b/stdlib/private/StdlibUnittest/InterceptTraps.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +// No signals support on WASI yet, see https://github.com/WebAssembly/WASI/issues/166. +#if !defined(__wasi__) #include #include #include @@ -48,6 +50,8 @@ static void CrashCatcher(int Sig) { _exit(0); } +#endif // __wasi__ + #if defined(_WIN32) static LONG WINAPI VectoredCrashHandler(PEXCEPTION_POINTERS ExceptionInfo) { @@ -63,13 +67,16 @@ VectoredCrashHandler(PEXCEPTION_POINTERS ExceptionInfo) { return EXCEPTION_CONTINUE_SEARCH; } -#endif +#endif // _WIN32 SWIFT_CC(swift) SWIFT_RUNTIME_LIBRARY_VISIBILITY extern "C" void installTrapInterceptor() { // Disable buffering on stdout so that everything is printed before crashing. setbuf(stdout, 0); +// No signals support on WASI yet, see https://github.com/WebAssembly/WASI/issues/166. +#if !defined(__wasi__) + #if defined(_WIN32) _set_abort_behavior(0, _WRITE_ABORT_MSG); #endif @@ -87,3 +94,4 @@ void installTrapInterceptor() { #endif } +#endif // !defined(__wasi__) diff --git a/stdlib/private/StdlibUnittest/RaceTest.swift b/stdlib/private/StdlibUnittest/RaceTest.swift index 3bdee4f03d32f..c883b03a8b85b 100644 --- a/stdlib/private/StdlibUnittest/RaceTest.swift +++ b/stdlib/private/StdlibUnittest/RaceTest.swift @@ -41,7 +41,7 @@ import SwiftPrivateLibcExtras import SwiftPrivateThreadExtras #if os(macOS) || os(iOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -562,7 +562,9 @@ class _InterruptibleSleep { return } - var timeout = timeval(tv_sec: duration, tv_usec: 0) + // WebAssembly/WASI on wasm32 is the only 32-bit platform with Int64 time_t, + // needs an explicit conversion to `time_t` because of this. + var timeout = timeval(tv_sec: time_t(duration), tv_usec: 0) var readFDs = _stdlib_fd_set() var writeFDs = _stdlib_fd_set() diff --git a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift index 20bb2b2c9376f..bc8bd4fe980e0 100644 --- a/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift +++ b/stdlib/private/StdlibUnittest/StdlibCoreExtras.swift @@ -14,7 +14,7 @@ import SwiftPrivate import SwiftPrivateLibcExtras #if os(macOS) || os(iOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index c67f1778d8a3b..353fed99deb67 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -18,7 +18,7 @@ import SwiftPrivateLibcExtras #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Foundation import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -748,6 +748,9 @@ extension ProcessTerminationStatus { case .signal(let signal): #if os(Windows) return CInt(signal) == SIGILL +#elseif os(WASI) + // No signals support on WASI yet, see https://github.com/WebAssembly/WASI/issues/166. + return false #else return CInt(signal) == SIGILL || CInt(signal) == SIGTRAP #endif @@ -1746,6 +1749,7 @@ public enum OSVersion : CustomStringConvertible { case windowsCygnus case windows case haiku + case wasi public var description: String { switch self { @@ -1777,6 +1781,8 @@ public enum OSVersion : CustomStringConvertible { return "Windows" case .haiku: return "Haiku" + case .wasi: + return "WASI" } } } @@ -1821,6 +1827,8 @@ func _getOSVersion() -> OSVersion { return .windows #elseif os(Haiku) return .haiku +#elseif os(WASI) + return .wasi #else let productVersion = _getSystemVersionPlistProperty("ProductVersion")! let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion) diff --git a/stdlib/private/StdlibUnittest/SymbolLookup.swift b/stdlib/private/StdlibUnittest/SymbolLookup.swift index 2e9c627487a4f..ef211aa3db7ba 100644 --- a/stdlib/private/StdlibUnittest/SymbolLookup.swift +++ b/stdlib/private/StdlibUnittest/SymbolLookup.swift @@ -12,7 +12,7 @@ #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT @@ -35,6 +35,8 @@ #endif #elseif os(Windows) let hStdlibCore: HMODULE = GetModuleHandleA("swiftCore.dll")! +#elseif os(WASI) +// WASI doesn't support dynamic linking yet. #else #error("Unsupported platform") #endif @@ -43,6 +45,8 @@ public func pointerToSwiftCoreSymbol(name: String) -> UnsafeMutableRawPointer? { #if os(Windows) return unsafeBitCast(GetProcAddress(hStdlibCore, name), to: UnsafeMutableRawPointer?.self) +#elseif os(WASI) + fatalError("\(#function) is not supported on WebAssembly/WASI") #else return dlsym(RTLD_DEFAULT, name) #endif diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c index 4ef6fdbff8537..877dcd847b719 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.c @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -// posix_spawn is not available on Android or Windows (MSVC). -#if !defined(__ANDROID__) && !defined(__HAIKU__) && (!defined(_WIN32) || defined(__CYGWIN__)) +// posix_spawn is not available on Android, HAIKU, WASI or Windows (MSVC). +#if !defined(__ANDROID__) && !defined(__HAIKU__) && (!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__wasi__) #include "swift/Runtime/Config.h" @@ -54,5 +54,5 @@ int _stdlib_posix_spawn(pid_t *__restrict pid, const char * __restrict path, return posix_spawn(pid, path, file_actions, attrp, argv, envp); } -#endif // !defined(__ANDROID__) && !defined(__HAIKU__) && (!defined(_WIN32) || defined(__CGYWIN__)) +#endif // !defined(__ANDROID__) && !defined(__HAIKU__) && (!defined(_WIN32) || defined(__CGYWIN__)) && !defined(__wasi__) diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift index e95e07e142c3c..7142e1750fcf4 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift @@ -13,13 +13,15 @@ import SwiftPrivate #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT import WinSDK #endif +#if !os(WASI) +// No signals support on WASI yet, see https://github.com/WebAssembly/WASI/issues/166. internal func _signalToString(_ signal: Int) -> String { switch CInt(signal) { case SIGILL: return "SIGILL" @@ -34,6 +36,7 @@ internal func _signalToString(_ signal: Int) -> String { default: return "SIG???? (\(signal))" } } +#endif public enum ProcessTerminationStatus : CustomStringConvertible { case exit(Int) @@ -44,7 +47,12 @@ public enum ProcessTerminationStatus : CustomStringConvertible { case .exit(let status): return "Exit(\(status))" case .signal(let signal): +#if os(WASI) + // No signals support on WASI yet, see https://github.com/WebAssembly/WASI/issues/166. + fatalError("Signals are not supported on WebAssembly/WASI") +#else return "Signal(\(_signalToString(signal)))" +#endif } } } @@ -141,6 +149,15 @@ public func waitProcess(_ process: HANDLE) -> ProcessTerminationStatus { } return .exit(Int(status)) } +#elseif os(WASI) +// WASI doesn't support child processes +public func spawnChild(_ args: [String]) + -> (pid: pid_t, stdinFD: CInt, stdoutFD: CInt, stderrFD: CInt) { + fatalError("\(#function) is not supported on WebAssembly/WASI") +} +public func posixWaitpid(_ pid: pid_t) -> ProcessTerminationStatus { + fatalError("\(#function) is not supported on WebAssembly/WASI") +} #else // posix_spawn is not available on Android. // posix_spawn is not available on Haiku. diff --git a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift index 6fa2c06b6f6ee..ff2e2d9443ed0 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift +++ b/stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift @@ -13,14 +13,14 @@ import SwiftPrivate #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) import Glibc #elseif os(Windows) import MSVCRT #endif public func _stdlib_mkstemps(_ template: inout String, _ suffixlen: CInt) -> CInt { -#if os(Android) || os(Haiku) || os(Windows) +#if os(Android) || os(Haiku) || os(Windows) || os(WASI) preconditionFailure("mkstemps doesn't work on your platform") #else var utf8CStr = template.utf8CString @@ -125,6 +125,8 @@ public func _stdlib_pipe() -> (readEnd: CInt, writeEnd: CInt, error: CInt) { let ret = fds.withUnsafeMutableBufferPointer { unsafeFds -> CInt in #if os(Windows) return _pipe(unsafeFds.baseAddress, 0, 0) +#elseif os(WASI) + preconditionFailure("No pipes available on WebAssembly/WASI") #else return pipe(unsafeFds.baseAddress) #endif diff --git a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt index 97e2cc0d2af5b..3a72dd196729f 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt @@ -16,6 +16,7 @@ add_swift_target_library(swiftSwiftPrivateThreadExtras ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_HAIKU Glibc SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + TARGET_SDKS ALL_APPLE_PLATFORMS CYGWIN FREEBSD HAIKU LINUX WINDOWS INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/public/Platform/Platform.swift b/stdlib/public/Platform/Platform.swift index f5e54c7da1283..b9a2abaca082f 100644 --- a/stdlib/public/Platform/Platform.swift +++ b/stdlib/public/Platform/Platform.swift @@ -366,6 +366,8 @@ public var SIG_IGN: _crt_signal_t { public var SIG_ERR: _crt_signal_t { return unsafeBitCast(-1, to: _crt_signal_t.self) } +#elseif os(WASI) +// No signals support on WASI yet, see https://github.com/WebAssembly/WASI/issues/166. #else internal var _ignore = _UnsupportedPlatformError() #endif @@ -380,7 +382,7 @@ public var SEM_FAILED: UnsafeMutablePointer? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) // The value is ABI. Value verified to be correct for OS X, iOS, watchOS, tvOS. return UnsafeMutablePointer(bitPattern: -1) -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) || os(WASI) // The value is ABI. Value verified to be correct on Glibc. return UnsafeMutablePointer(bitPattern: 0) #else diff --git a/stdlib/public/Platform/glibc.modulemap.gyb b/stdlib/public/Platform/glibc.modulemap.gyb index 12de1073b6317..9a88a108c74c3 100644 --- a/stdlib/public/Platform/glibc.modulemap.gyb +++ b/stdlib/public/Platform/glibc.modulemap.gyb @@ -126,10 +126,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/math.h" export * } +% if CMAKE_SDK != "WASI": module setjmp { header "${GLIBC_INCLUDE_PATH}/setjmp.h" export * } +% end module signal { header "${GLIBC_INCLUDE_PATH}/signal.h" export * @@ -319,6 +321,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/dirent.h" export * } +% if CMAKE_SDK != "WASI": module dl { header "${GLIBC_INCLUDE_PATH}/link.h" export * @@ -327,6 +330,7 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/dlfcn.h" export * } +% end module fcntl { header "${GLIBC_INCLUDE_PATH}/fcntl.h" export * @@ -335,10 +339,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/fnmatch.h" export * } +% if CMAKE_SDK != "WASI": module grp { header "${GLIBC_INCLUDE_PATH}/grp.h" export * } +% end module ioctl { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/ioctl.h" export * @@ -347,12 +353,14 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/libgen.h" export * } +% if CMAKE_SDK != "WASI": module net { module if { header "${GLIBC_INCLUDE_PATH}/net/if.h" export * } } +% end module netinet { module in { header "${GLIBC_INCLUDE_PATH}/netinet/in.h" @@ -369,10 +377,12 @@ module SwiftGlibc [system] { header "${GLIBC_INCLUDE_PATH}/poll.h" export * } +% if CMAKE_SDK != "WASI": module pthread { header "${GLIBC_INCLUDE_PATH}/pthread.h" export * } +% end module pwd { header "${GLIBC_INCLUDE_PATH}/pwd.h" export * @@ -422,18 +432,22 @@ module SwiftGlibc [system] { } % end +% if CMAKE_SDK != "WASI": module ipc { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/ipc.h" export * } +% end module mman { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/mman.h" export * } +% if CMAKE_SDK != "WASI": module msg { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/msg.h" export * } +% end module resource { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/resource.h" export * @@ -442,7 +456,7 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/select.h" export * } -% if CMAKE_SDK != "FREEBSD" and CMAKE_SDK != "HAIKU": +% if CMAKE_SDK != "FREEBSD" and CMAKE_SDK != "HAIKU" and CMAKE_SDK != "WASI": module sendfile { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/sendfile.h" export * @@ -492,34 +506,42 @@ module SwiftGlibc [system] { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/utsname.h" export * } +% if CMAKE_SDK != "WASI": module wait { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/wait.h" export * } } +% end % if CMAKE_SDK in ["LINUX", "FREEBSD"]: module sysexits { header "${GLIBC_INCLUDE_PATH}/sysexits.h" export * } % end +% if CMAKE_SDK != "WASI": module termios { header "${GLIBC_INCLUDE_PATH}/termios.h" export * } +% end module unistd { header "${GLIBC_INCLUDE_PATH}/unistd.h" export * } +% if CMAKE_SDK != "WASI": module utime { header "${GLIBC_INCLUDE_PATH}/utime.h" export * } } +% end } +% if CMAKE_SDK != "WASI": module CUUID [system] { header "${GLIBC_INCLUDE_PATH}/uuid/uuid.h" link "uuid" export * } +% end diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index 8577fad1653b9..ebe73edd650fd 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -76,7 +76,7 @@ // SWIFT_RUNTIME_EXPORT on the library it's exported from. /// Attribute used to export symbols from the runtime. -#if defined(__MACH__) +#if defined(__MACH__) || defined(__wasi__) # define SWIFT_EXPORT_ATTRIBUTE __attribute__((__visibility__("default"))) diff --git a/stdlib/public/core/AtomicInt.swift.gyb b/stdlib/public/core/AtomicInt.swift.gyb index 62217f282ccc5..80514f42c147c 100644 --- a/stdlib/public/core/AtomicInt.swift.gyb +++ b/stdlib/public/core/AtomicInt.swift.gyb @@ -65,7 +65,7 @@ internal func _swift_stdlib_atomicCompareExchangeStrongInt( object target: UnsafeMutablePointer, expected: UnsafeMutablePointer, desired: Int) -> Bool { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_Int32( target._rawValue, expected.pointee._value, desired._value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) @@ -82,7 +82,7 @@ internal func _swift_stdlib_atomicCompareExchangeStrongInt( public // Existing uses outside stdlib func _swift_stdlib_atomicLoadInt( object target: UnsafeMutablePointer) -> Int { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let value = Builtin.atomicload_seqcst_Int32(target._rawValue) return Int(value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) @@ -95,7 +95,7 @@ func _swift_stdlib_atomicLoadInt( internal func _swift_stdlib_atomicStoreInt( object target: UnsafeMutablePointer, desired: Int) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) Builtin.atomicstore_seqcst_Int32(target._rawValue, desired._value) #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) Builtin.atomicstore_seqcst_Int64(target._rawValue, desired._value) @@ -111,7 +111,7 @@ func _swift_stdlib_atomicFetch${operation}Int( object target: UnsafeMutablePointer, operand: Int) -> Int { let rawTarget = UnsafeMutableRawPointer(target) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let value = _swift_stdlib_atomicFetch${operation}Int32( object: rawTarget.assumingMemoryBound(to: Int32.self), operand: Int32(operand)) diff --git a/stdlib/public/core/BridgeStorage.swift b/stdlib/public/core/BridgeStorage.swift index 4b100f28f7cfe..fcc971f8b6f76 100644 --- a/stdlib/public/core/BridgeStorage.swift +++ b/stdlib/public/core/BridgeStorage.swift @@ -61,7 +61,7 @@ internal struct _BridgeStorage { rawValue = Builtin.reinterpretCast(native) } -#if !(arch(i386) || arch(arm)) +#if !(arch(i386) || arch(arm) || arch(wasm32)) @inlinable @inline(__always) internal init(taggedPayload: UInt) { diff --git a/stdlib/public/core/Builtin.swift b/stdlib/public/core/Builtin.swift index 07c9ce14af6a7..8aceb9014df08 100644 --- a/stdlib/public/core/Builtin.swift +++ b/stdlib/public/core/Builtin.swift @@ -378,8 +378,8 @@ internal var _objectPointerLowSpareBitShift: UInt { } } -#if arch(i386) || arch(arm) || arch(powerpc64) || arch(powerpc64le) || arch( - s390x) +#if arch(i386) || arch(arm) || arch(wasm32) || arch(powerpc64) || arch( + powerpc64le) || arch(s390x) @inlinable internal var _objectPointerIsObjCBit: UInt { @inline(__always) get { return 0x0000_0002 } diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index 988ef9f7d0679..5ffaacc45ef32 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -46,7 +46,7 @@ extension Dictionary { @inlinable @inline(__always) init(dummy: Void) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init(native: _NativeDictionary()) #else self.object = _BridgeStorage(taggedPayload: 0) diff --git a/stdlib/public/core/Hasher.swift b/stdlib/public/core/Hasher.swift index f7098db934aa3..202c2830610ab 100644 --- a/stdlib/public/core/Hasher.swift +++ b/stdlib/public/core/Hasher.swift @@ -160,7 +160,7 @@ extension Hasher { @inline(__always) internal mutating func combine(_ value: UInt) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) combine(UInt32(truncatingIfNeeded: value)) #else combine(UInt64(truncatingIfNeeded: value)) @@ -423,7 +423,7 @@ public struct Hasher { @usableFromInline internal static func _hash(seed: Int, _ value: UInt) -> Int { var state = _State(seed: seed) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(UInt.bitWidth < UInt64.bitWidth) let tbc = _TailBuffer( tail: UInt64(truncatingIfNeeded: value), diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 0092fc74ca45e..ae3149002c7c4 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -36,7 +36,7 @@ extension Set { @inlinable @inline(__always) init(dummy: ()) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init(native: _NativeSet()) #else self.object = _BridgeStorage(taggedPayload: 0) diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index 1896d974204b6..2b892c2d52867 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -285,7 +285,7 @@ internal enum _KnownCocoaString { case storage case shared case cocoa -#if !(arch(i386) || arch(arm)) +#if !(arch(i386) || arch(arm) || arch(wasm32)) case tagged #endif diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 2b7765cd4093e..cc6ed08226e67 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -178,7 +178,7 @@ extension _StringGuts { #else @usableFromInline @inline(never) @_effects(releasenone) internal func _invariantCheck() { - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(MemoryLayout.size == 12, """ the runtime is depending on this, update Reflection.mm and \ this if you change it diff --git a/stdlib/public/core/StringObject.swift b/stdlib/public/core/StringObject.swift index 501d81b66ff53..7173d8d8b55be 100644 --- a/stdlib/public/core/StringObject.swift +++ b/stdlib/public/core/StringObject.swift @@ -169,7 +169,7 @@ extension _StringObject { @usableFromInline internal typealias RawBitPattern = (UInt64, UInt64) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // On 32-bit platforms, raw bit conversion is one-way only and uses the same // layout as on 64-bit platforms. @usableFromInline @@ -245,7 +245,7 @@ extension _StringObject { @inlinable @_transparent internal var discriminatedObjectRawBits: UInt64 { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) let low32: UInt switch _variant { case .immortal(let bitPattern): @@ -387,7 +387,7 @@ extension _StringObject.Nibbles { extension _StringObject { @inlinable @inline(__always) internal static var nativeBias: UInt { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) return 20 #else return 32 @@ -556,7 +556,7 @@ extension _StringObject { @inlinable @inline(__always) internal init(empty:()) { // Canonical empty pattern: small zero-length string -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( count: 0, variant: .immortal(0), @@ -832,7 +832,7 @@ extension _StringObject { @inline(__always) internal var sharedStorage: __SharedStringStorage { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) guard case .native(let storage) = _variant else { _internalInvariantFailure() } @@ -846,7 +846,7 @@ extension _StringObject { @inline(__always) internal var cocoaObject: AnyObject { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) guard case .bridged(let object) = _variant else { _internalInvariantFailure() } @@ -935,7 +935,7 @@ extension _StringObject { internal init(immortal bufPtr: UnsafeBufferPointer, isASCII: Bool) { let countAndFlags = CountAndFlags( immortalCount: bufPtr.count, isASCII: isASCII) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .immortal(start: bufPtr.baseAddress._unsafelyUnwrappedUnchecked), discriminator: Nibbles.largeImmortal(), @@ -955,7 +955,7 @@ extension _StringObject { @inline(__always) internal init(_ storage: __StringStorage) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .native(storage), discriminator: Nibbles.largeMortal(), @@ -969,7 +969,7 @@ extension _StringObject { } internal init(_ storage: __SharedStringStorage) { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self.init( variant: .native(storage), discriminator: Nibbles.largeMortal(), @@ -1009,7 +1009,7 @@ extension _StringObject { #else @usableFromInline @inline(never) @_effects(releasenone) internal func _invariantCheck() { - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) _internalInvariant(MemoryLayout<_StringObject>.size == 12) _internalInvariant(MemoryLayout<_StringObject>.stride == 12) _internalInvariant(MemoryLayout<_StringObject>.alignment == 4) @@ -1079,7 +1079,7 @@ extension _StringObject { } } - #if arch(i386) || arch(arm) + #if arch(i386) || arch(arm) || arch(wasm32) switch _variant { case .immortal: _internalInvariant(isImmortal) @@ -1099,7 +1099,7 @@ extension _StringObject { let raw = self.rawBits let word0 = ("0000000000000000" + String(raw.0, radix: 16)).suffix(16) let word1 = ("0000000000000000" + String(raw.1, radix: 16)).suffix(16) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) print(""" StringObject(\ <\(word0) \(word1)> \ diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift index 8e8a3d29d2133..a80937d1f7141 100644 --- a/stdlib/public/core/StringStorage.swift +++ b/stdlib/public/core/StringStorage.swift @@ -46,7 +46,7 @@ private typealias CountAndFlags = _StringObject.CountAndFlags // renamed. The old name must not be used in the new runtime. final internal class __StringStorage : __SwiftNativeNSString, _AbstractStringStorage { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // The total allocated storage capacity. Note that this includes the required // nul-terminator. internal var _realCapacity: Int @@ -106,7 +106,7 @@ final internal class __StringStorage // for Strings ~1KB or larger, though at this point we're well into our growth // curve. private func determineCodeUnitCapacity(_ desiredCapacity: Int) -> Int { -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) // FIXME: Adapt to actual 32-bit allocator. For now, let's arrange things so // that the instance size will be a multiple of 4. let bias = Int(bitPattern: _StringObject.nativeBias) @@ -139,7 +139,7 @@ extension __StringStorage { __StringStorage.self, realCodeUnitCapacity._builtinWordValue, UInt8.self, 1._builtinWordValue, Optional<_StringBreadcrumbs>.self) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) storage._realCapacity = realCodeUnitCapacity storage._count = countAndFlags.count storage._flags = countAndFlags.flags @@ -319,7 +319,7 @@ extension __StringStorage { internal func _updateCountAndFlags(newCount: Int, newIsASCII: Bool) { let countAndFlags = CountAndFlags( mortalCount: newCount, isASCII: newIsASCII) -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self._count = countAndFlags.count self._flags = countAndFlags.flags #else @@ -463,7 +463,7 @@ final internal class __SharedStringStorage internal var _owner: AnyObject? internal var start: UnsafePointer -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) internal var _count: Int internal var _flags: UInt16 @@ -485,7 +485,7 @@ final internal class __SharedStringStorage ) { self._owner = nil self.start = ptr -#if arch(i386) || arch(arm) +#if arch(i386) || arch(arm) || arch(wasm32) self._count = countAndFlags.count self._flags = countAndFlags.flags #else diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 2c516a9952fb4..5058f7cb6eb9c 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -30,7 +30,12 @@ #include "swift/Runtime/ExistentialContainer.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" -#include "swift/Runtime/Mutex.h" +#if defined(__wasi__) +# define SWIFT_CASTING_SUPPORTS_MUTEX 0 +#else +# define SWIFT_CASTING_SUPPORTS_MUTEX 1 +# include "swift/Runtime/Mutex.h" +#endif #include "swift/Runtime/Unreachable.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerIntPair.h" @@ -125,7 +130,9 @@ TypeNamePair swift::swift_getTypeName(const Metadata *type, bool qualified) { using Key = llvm::PointerIntPair; + #if SWIFT_CASTING_SUPPORTS_MUTEX static StaticReadWriteLock TypeNameCacheLock; + #endif static Lazy>> TypeNameCache; @@ -134,7 +141,9 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) { // Attempt read-only lookup of cache entry. { + #if SWIFT_CASTING_SUPPORTS_MUTEX StaticScopedReadLock guard(TypeNameCacheLock); + #endif auto found = cache.find(key); if (found != cache.end()) { @@ -145,7 +154,9 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) { // Read-only lookup failed to find item, we may need to create it. { + #if SWIFT_CASTING_SUPPORTS_MUTEX StaticScopedWriteLock guard(TypeNameCacheLock); + #endif // Do lookup again just to make sure it wasn't created by another // thread before we acquired the write lock. diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index 6e202434750f0..48201d54d6a2a 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -14,7 +14,7 @@ // //===----------------------------------------------------------------------===// -#if defined(__CYGWIN__) || defined(__HAIKU__) +#if defined(__CYGWIN__) || defined(__HAIKU__) || defined(__wasi__) #define SWIFT_SUPPORTS_BACKTRACE_REPORTING 0 #else #define SWIFT_SUPPORTS_BACKTRACE_REPORTING 1 diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 2546d90dec5cd..7a23376cf8f11 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -44,7 +44,10 @@ #else #include #include +// WASI doesn't support dynamic linking yet. +#if !defined(__wasi__) #include +#endif // !defined(__wasi__) #endif #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" diff --git a/stdlib/public/runtime/MutexPThread.cpp b/stdlib/public/runtime/MutexPThread.cpp index 62e718c16abaa..92db58226cd09 100644 --- a/stdlib/public/runtime/MutexPThread.cpp +++ b/stdlib/public/runtime/MutexPThread.cpp @@ -15,7 +15,7 @@ // //===----------------------------------------------------------------------===// -#if !defined(_WIN32) +#if !defined(_WIN32) && !defined(__wasi__) #include "swift/Runtime/Mutex.h" #include "swift/Runtime/Debug.h" From 6013431ecdcab19816b010442ddb6cfb10786c67 Mon Sep 17 00:00:00 2001 From: Vlasov Anton Date: Sat, 8 Feb 2020 16:23:41 +0300 Subject: [PATCH 067/381] SR-5740 Refactoring action to convert if statement to switch --- lib/IDE/Refactoring.cpp | 4 +++- test/refactoring/ConvertToSwitchStmt/basic.swift | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 677296c0f9b1d..5ea755d61b6c9 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -2249,7 +2249,7 @@ isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) { class ConditionalChecker : public ASTWalker { public: bool ParamsUseSameVars = true; - bool ConditionUseOnlyAllowedFunctions = true; + bool ConditionUseOnlyAllowedFunctions = false; StringRef ExpectName; Expr *walkToExprPost(Expr *E) { @@ -2336,6 +2336,8 @@ isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) { bool check(StmtConditionElement ConditionElement) { if (ConditionElement.getKind() == StmtConditionElement::CK_Availability) return false; + if (ConditionElement.getKind() == StmtConditionElement::CK_PatternBinding) + checker.ConditionUseOnlyAllowedFunctions = true; ConditionElement.walk(checker); return checker.allCheckPassed(); } diff --git a/test/refactoring/ConvertToSwitchStmt/basic.swift b/test/refactoring/ConvertToSwitchStmt/basic.swift index dd848549a7962..481bdca091d9d 100644 --- a/test/refactoring/ConvertToSwitchStmt/basic.swift +++ b/test/refactoring/ConvertToSwitchStmt/basic.swift @@ -134,43 +134,57 @@ func checkEmptyBody(e: E) { // RUN: rm -rf %t.result && mkdir -p %t.result // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=9:3 -end-pos=16:4 > %t.result/L9-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L9-3.swift // RUN: diff -u %S/Outputs/basic/L9-3.swift.expected %t.result/L9-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=20:3 -end-pos=25:4 > %t.result/L20-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L20-3.swift // RUN: diff -u %S/Outputs/basic/L20-3.swift.expected %t.result/L20-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=29:3 -end-pos=35:4 > %t.result/L29-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L29-3.swift // RUN: diff -u %S/Outputs/basic/L29-3.swift.expected %t.result/L29-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=39:3 -end-pos=45:4 > %t.result/L39-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L39-3.swift // RUN: diff -u %S/Outputs/basic/L39-3.swift.expected %t.result/L39-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=50:3 -end-pos=52:4 > %t.result/L50-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L50-3.swift // RUN: diff -u %S/Outputs/basic/L50-3.swift.expected %t.result/L50-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=54:3 -end-pos=56:4 > %t.result/L54-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L54-3.swift // RUN: diff -u %S/Outputs/basic/L54-3.swift.expected %t.result/L54-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=60:3 -end-pos=62:4 > %t.result/L60-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L60-3.swift // RUN: diff -u %S/Outputs/basic/L60-3.swift.expected %t.result/L60-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=64:3 -end-pos=67:4 > %t.result/L64-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L64-3.swift // RUN: diff -u %S/Outputs/basic/L64-3.swift.expected %t.result/L64-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=71:3 -end-pos=77:4 > %t.result/L71-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L71-3.swift // RUN: diff -u %S/Outputs/basic/L71-3.swift.expected %t.result/L71-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=82:3 -end-pos=90:4 > %t.result/L82-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L82-3.swift // RUN: diff -u %S/Outputs/basic/L82-3.swift.expected %t.result/L82-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=99:3 -end-pos=103:4 > %t.result/L99-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L99-3.swift // RUN: diff -u %S/Outputs/basic/L99-3.swift.expected %t.result/L99-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=108:3 -end-pos=116:4 > %t.result/L108-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L108-3.swift // RUN: diff -u %S/Outputs/basic/L108-3.swift.expected %t.result/L108-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=118:3 -end-pos=124:4 > %t.result/L118-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L118-3.swift // RUN: diff -u %S/Outputs/basic/L118-3.swift.expected %t.result/L118-3.swift // RUN: %refactor -convert-to-switch-stmt -source-filename %s -pos=128:3 -end-pos=131:4 > %t.result/L128-3.swift +// RUN: %target-swift-frontend-typecheck %t.result/L128-3.swift // RUN: diff -u %S/Outputs/basic/L128-3.swift.expected %t.result/L128-3.swift From 1c156dba8fa753d1dd393df4fa42d6665b59f892 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 7 Feb 2020 16:08:40 -0800 Subject: [PATCH 068/381] [Gardening] Remove tautological comparison --- lib/AST/IndexSubset.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/AST/IndexSubset.cpp b/lib/AST/IndexSubset.cpp index d5be9aa53363e..f9fdedef6b78c 100644 --- a/lib/AST/IndexSubset.cpp +++ b/lib/AST/IndexSubset.cpp @@ -16,7 +16,6 @@ using namespace swift; IndexSubset * IndexSubset::getFromString(ASTContext &ctx, StringRef string) { - if (string.size() < 0) return nullptr; unsigned capacity = string.size(); llvm::SmallBitVector indices(capacity); for (unsigned i : range(capacity)) { From f4086d842801137f61512443712a2044dc252778 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 24 Jan 2020 15:00:54 -0800 Subject: [PATCH 069/381] build: migrate playground support to post-build artifact This migrates the playground support out of the build-script-impl and into the python based build system. This makes it build more similarly to the Swift Package Manager and SourceKit-LSP. More importantly, it reduces the dependency on build-script-impl. --- utils/build-presets.ini | 3 - utils/build-script | 22 ++-- utils/build-script-impl | 94 --------------- .../build_swift/driver_arguments.py | 8 +- utils/build_swift/tests/expected_options.py | 6 + .../swift_build_support/products/__init__.py | 2 + .../products/playgroundsupport.py | 112 ++++++++++++++++++ 7 files changed, 139 insertions(+), 108 deletions(-) create mode 100644 utils/swift_build_support/swift_build_support/products/playgroundsupport.py diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 367c43944f7fd..7bb23f3415e87 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -371,7 +371,6 @@ swift-primary-variant-arch=x86_64 skip-build-llbuild skip-test-llbuild skip-test-swiftpm -skip-build-playgroundsupport skip-test-playgroundsupport # This preset is used by CI to test swift-corelibs-xctest. @@ -870,7 +869,6 @@ install-libcxx skip-test-linux skip-build-benchmarks -skip-build-playgroundsupport reconfigure @@ -1174,7 +1172,6 @@ lldb-build-type=Release verbose-build build-ninja build-swift-stdlib-unittest-extra -playgroundsupport-build-type=Release install-swift install-lldb diff --git a/utils/build-script b/utils/build-script index 1404db6132d4a..168bc5f6fe4b0 100755 --- a/utils/build-script +++ b/utils/build-script @@ -220,6 +220,7 @@ def validate_arguments(toolchain, args): targets_needing_toolchain = [ 'build_indexstoredb', + 'build_playgroundsupport', 'build_pythonkit', 'build_sourcekitlsp', 'build_toolchainbenchmarks', @@ -548,8 +549,6 @@ class BuildScriptInvocation(object): impl_args += ["--skip-build-libdispatch"] if not args.build_libicu: impl_args += ["--skip-build-libicu"] - if not args.build_playgroundsupport: - impl_args += ["--skip-build-playgroundsupport"] if args.build_swift_dynamic_stdlib: impl_args += ["--build-swift-dynamic-stdlib"] if args.build_swift_static_stdlib: @@ -589,14 +588,15 @@ class BuildScriptInvocation(object): if not args.test and not args.long_test and not args.stress_test: impl_args += ["--skip-test-swift"] if not args.test: - impl_args += ["--skip-test-cmark", - "--skip-test-lldb", - "--skip-test-llbuild", - "--skip-test-xctest", - "--skip-test-foundation", - "--skip-test-libdispatch", - "--skip-test-libicu", - "--skip-test-playgroundsupport"] + impl_args += [ + "--skip-test-cmark", + "--skip-test-lldb", + "--skip-test-llbuild", + "--skip-test-xctest", + "--skip-test-foundation", + "--skip-test-libdispatch", + "--skip-test-libicu", + ] if not args.test_linux: impl_args += ["--skip-test-linux"] if not args.test_freebsd: @@ -819,6 +819,8 @@ class BuildScriptInvocation(object): product_classes.append(products.SwiftEvolve) if self.args.build_indexstoredb: product_classes.append(products.IndexStoreDB) + if self.args.build_playgroundsupport: + product_classes.append(products.PlaygroundSupport) if self.args.build_pythonkit: product_classes.append(products.PythonKit) if self.args.build_sourcekitlsp: diff --git a/utils/build-script-impl b/utils/build-script-impl index 255ac4beb3844..2b55ad38a7533 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -253,7 +253,6 @@ components=( llbuild lldb llvm - playgroundsupport static-foundation static-libdispatch swift @@ -1036,12 +1035,6 @@ LIBDISPATCH_SOURCE_DIR="${WORKSPACE}/swift-corelibs-libdispatch" LIBDISPATCH_STATIC_SOURCE_DIR="${WORKSPACE}/swift-corelibs-libdispatch" LIBICU_SOURCE_DIR="${WORKSPACE}/icu" LIBCXX_SOURCE_DIR="${WORKSPACE}/llvm-project/libcxx" -PLAYGROUNDSUPPORT_SOURCE_DIR="${WORKSPACE}/swift-xcode-playground-support" - -if [[ ! "${SKIP_BUILD_PLAYGROUNDSUPPORT}" && ! -d ${PLAYGROUNDSUPPORT_SOURCE_DIR} ]]; then - echo "Couldn't find PlaygroundSupport source directory." - exit 1 -fi [[ "${SKIP_BUILD_CMARK}" ]] || PRODUCTS+=(cmark) [[ "${SKIP_BUILD_LIBCXX}" ]] || PRODUCTS+=(libcxx) @@ -1057,7 +1050,6 @@ fi [[ "${SKIP_BUILD_STATIC_FOUNDATION}" ]] || PRODUCTS+=(foundation_static) [[ "${SKIP_BUILD_LLBUILD}" ]] || PRODUCTS+=(llbuild) [[ "${SKIP_BUILD_XCTEST}" ]] || PRODUCTS+=(xctest) -[[ "${SKIP_BUILD_PLAYGROUNDSUPPORT}" ]] || PRODUCTS+=(playgroundsupport) # get_host_specific_variable(host, name) # @@ -2114,38 +2106,6 @@ for host in "${ALL_HOSTS[@]}"; do # libicu builds itself and doesn't use cmake continue ;; - playgroundsupport) - if [[ "$(uname -s)" != "Darwin" ]]; then - echo "error: unable to build PlaygroundLogger and PlaygroundSupport on this platform" - exit 1 - fi - - SWIFTC_BIN="$(build_directory_bin ${host} swift)/swiftc" - SWIFT_LIB_DIR="$(build_directory ${host} swift)"/lib/swift/ - - set -x - pushd "${PLAYGROUNDSUPPORT_SOURCE_DIR}" - if [[ $(not ${SKIP_BUILD_OSX}) ]]; then - call "xcodebuild" -configuration "${PLAYGROUNDSUPPORT_BUILD_TYPE}" -workspace swift-xcode-playground-support.xcworkspace -scheme BuildScript-macOS -sdk macosx -arch x86_64 -derivedDataPath "${build_dir}"/DerivedData SWIFT_EXEC="${SWIFTC_BIN}" SWIFT_LIBRARY_PATH="${SWIFT_LIB_DIR}/\$(PLATFORM_NAME)" ONLY_ACTIVE_ARCH=NO - - if [[ $(not ${SKIP_TEST_PLAYGROUNDSUPPORT}) ]]; then - # If we're going to end up testing PlaygroundLogger/PlaygroundSupport, then we need to build the tests too. - # Note that this *always* needs to run in Debug configuration. - call "xcodebuild" build-for-testing -configuration Debug -workspace swift-xcode-playground-support.xcworkspace -scheme BuildScript-Test-PlaygroundLogger-macOS -sdk macosx -arch x86_64 -derivedDataPath "${build_dir}"/DerivedData SWIFT_EXEC="${SWIFTC_BIN}" SWIFT_LIBRARY_PATH="${SWIFT_LIB_DIR}/\$(PLATFORM_NAME)" ONLY_ACTIVE_ARCH=NO - fi - fi - - if [[ $(not ${SKIP_BUILD_IOS_SIMULATOR}) ]]; then - call "xcodebuild" -configuration "${PLAYGROUNDSUPPORT_BUILD_TYPE}" -workspace swift-xcode-playground-support.xcworkspace -scheme BuildScript-iOS -sdk iphonesimulator -arch x86_64 -derivedDataPath "${build_dir}"/DerivedData SWIFT_EXEC="${SWIFTC_BIN}" SWIFT_LIBRARY_PATH="${SWIFT_LIB_DIR}/\$(PLATFORM_NAME)" ONLY_ACTIVE_ARCH=NO - fi - - if [[ $(not ${SKIP_BUILD_TVOS_SIMULATOR}) ]]; then - call "xcodebuild" -configuration "${PLAYGROUNDSUPPORT_BUILD_TYPE}" -workspace swift-xcode-playground-support.xcworkspace -scheme BuildScript-tvOS -sdk appletvsimulator -arch x86_64 -derivedDataPath "${build_dir}"/DerivedData SWIFT_EXEC="${SWIFTC_BIN}" SWIFT_LIBRARY_PATH="${SWIFT_LIB_DIR}/\$(PLATFORM_NAME)" ONLY_ACTIVE_ARCH=NO - fi - popd - { set +x; } 2>/dev/null - continue - ;; *) echo "error: unknown product: ${product}" exit 1 @@ -2528,26 +2488,6 @@ for host in "${ALL_HOSTS[@]}"; do echo "--- Finished tests for ${product} ---" continue ;; - playgroundsupport) - if [[ "${SKIP_TEST_PLAYGROUNDSUPPORT}" ]]; then - continue - fi - - if [[ "${host}" != "macosx"* ]]; then - echo "Skipping PlaygroundLogger tests on non-macOS platform" - continue - fi - - PLAYGROUNDSUPPORT_BUILD_DIR=$(build_directory ${host} ${product}) - SWIFTC_BIN="$(build_directory_bin ${host} swift)/swiftc" - SWIFT_LIB_DIR="$(build_directory ${host} swift)"/lib/swift/ - - set -x - with_pushd "${PLAYGROUNDSUPPORT_SOURCE_DIR}" \ - call "xcodebuild" test-without-building -configuration Debug -workspace swift-xcode-playground-support.xcworkspace -scheme BuildScript-Test-PlaygroundLogger-macOS -sdk macosx -arch x86_64 -derivedDataPath "${PLAYGROUNDSUPPORT_BUILD_DIR}"/DerivedData SWIFT_EXEC="${SWIFTC_BIN}" SWIFT_LIBRARY_PATH="${SWIFT_LIB_DIR}/\$(PLATFORM_NAME)" ONLY_ACTIVE_ARCH=NO - { set +x; } 2>/dev/null - continue - ;; *) echo "error: unknown product: ${product}" exit 1 @@ -2760,40 +2700,6 @@ for host in "${ALL_HOSTS[@]}"; do call cp -a "${ICU_TMP_INSTALL_DIR}/share/icuswift" "${ICU_INSTALL_DIR}share" continue ;; - playgroundsupport) - set -x - if [[ -z "${INSTALL_PLAYGROUNDSUPPORT}" ]] ; then - continue - fi - - echo "--- Installing ${product} ---" - - PLAYGROUNDSUPPORT_BUILD_DIR=$(build_directory ${host} ${product}) - - case "$(uname -s)" in - Darwin) - pushd "${PLAYGROUNDSUPPORT_SOURCE_DIR}" - if [[ $(not ${SKIP_BUILD_OSX}) ]]; then - call "xcodebuild" install -configuration "${PLAYGROUNDSUPPORT_BUILD_TYPE}" -workspace swift-xcode-playground-support.xcworkspace -scheme BuildScript-macOS -sdk macosx -arch x86_64 -derivedDataPath "${PLAYGROUNDSUPPORT_BUILD_DIR}"/DerivedData SWIFT_EXEC="${SWIFTC_BIN}" SWIFT_LIBRARY_PATH="${SWIFT_LIB_DIR}/\$(PLATFORM_NAME)" ONLY_ACTIVE_ARCH=NO DSTROOT="$(get_host_install_destdir ${host})" TOOLCHAIN_INSTALL_DIR="${TOOLCHAIN_PREFIX}" BUILD_PLAYGROUNDLOGGER_TESTS=NO - fi - - if [[ $(not ${SKIP_BUILD_IOS_SIMULATOR}) ]]; then - call "xcodebuild" install -configuration "${PLAYGROUNDSUPPORT_BUILD_TYPE}" -workspace swift-xcode-playground-support.xcworkspace -scheme BuildScript-iOS -sdk iphonesimulator -arch x86_64 -derivedDataPath "${PLAYGROUNDSUPPORT_BUILD_DIR}"/DerivedData SWIFT_EXEC="${SWIFTC_BIN}" SWIFT_LIBRARY_PATH="${SWIFT_LIB_DIR}/\$(PLATFORM_NAME)" ONLY_ACTIVE_ARCH=NO DSTROOT="$(get_host_install_destdir ${host})" TOOLCHAIN_INSTALL_DIR="${TOOLCHAIN_PREFIX}" BUILD_PLAYGROUNDLOGGER_TESTS=NO - fi - - if [[ $(not ${SKIP_BUILD_TVOS_SIMULATOR}) ]]; then - call "xcodebuild" install -configuration "${PLAYGROUNDSUPPORT_BUILD_TYPE}" -workspace swift-xcode-playground-support.xcworkspace -scheme BuildScript-tvOS -sdk appletvsimulator -arch x86_64 -derivedDataPath "${PLAYGROUNDSUPPORT_BUILD_DIR}"/DerivedData SWIFT_EXEC="${SWIFTC_BIN}" SWIFT_LIBRARY_PATH="${SWIFT_LIB_DIR}/\$(PLATFORM_NAME)" ONLY_ACTIVE_ARCH=NO DSTROOT="$(get_host_install_destdir ${host})" TOOLCHAIN_INSTALL_DIR="${TOOLCHAIN_PREFIX}" BUILD_PLAYGROUNDLOGGER_TESTS=NO - fi - popd - continue - ;; - *) - echo "error: --install-playgroundsupport is not supported on this platform" - exit 1 - ;; - esac - { set +x; } 2>/dev/null - ;; *) echo "error: unknown product: ${product}" exit 1 diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index becd8a02db234..4b50a422d8e1b 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -596,8 +596,11 @@ def create_argument_parser(): option('--libicu', toggle_true('build_libicu'), help='build libicu') - option('--playgroundsupport', store_true('build_playgroundsupport'), + option('--playgroundsupport', toggle_true('build_playgroundsupport'), help='build PlaygroundSupport') + option('--install-playgroundsupport', + store_true('install_playgroundsupport'), + help='install playground support') option('--pythonkit', store_true('build_pythonkit'), help='build PythonKit') @@ -981,6 +984,9 @@ def create_argument_parser(): help='skip testing indexstore-db') option('--skip-test-sourcekit-lsp', toggle_false('test_sourcekitlsp'), help='skip testing sourcekit-lsp') + option('--skip-test-playgroundsupport', + toggle_false('test_playgroundsupport'), + help='skip testing PlaygroundSupport') option('--skip-test-skstresstester', toggle_false('test_skstresstester'), help='skip testing the SourceKit Stress tester') option('--skip-test-swiftevolve', toggle_false('test_swiftevolve'), diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index 0b8e75e1eae6d..9d2ebbff58bd7 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -94,6 +94,7 @@ 'install_swiftpm': False, 'install_swiftsyntax': False, 'swiftsyntax_verify_generated_files': False, + 'install_playgroundsupport': False, 'install_pythonkit': False, 'install_sourcekitlsp': False, 'install_skstresstester': False, @@ -215,6 +216,7 @@ 'test_watchos': False, 'test_watchos_host': False, 'test_watchos_simulator': False, + 'test_playgroundsupport': True, 'test_swiftpm': False, 'test_swiftsyntax': False, 'test_indexstoredb': False, @@ -449,6 +451,8 @@ class BuildScriptImplOption(_BaseOption): SetTrueOption('--maccatalyst-ios-tests', dest='maccatalyst_ios_tests'), SetTrueOption('--playgroundsupport', dest='build_playgroundsupport'), SetTrueOption('--pythonkit', dest='build_pythonkit'), + SetTrueOption('--install-playgroundsupport', + dest='install_playgroundsupport'), SetTrueOption('--install-pythonkit', dest='install_pythonkit'), SetTrueOption('--test-pythonkit', dest='test_pythonkit'), SetTrueOption('--skip-build'), @@ -554,6 +558,8 @@ class BuildScriptImplOption(_BaseOption): DisableOption('--skip-test-watchos-host', dest='test_watchos_host'), DisableOption('--skip-test-watchos-simulator', dest='test_watchos_simulator'), + DisableOption('--skip-test-playgroundsupport', + dest='test_playgroundsupport'), DisableOption('--skip-test-swiftpm', dest='test_swiftpm'), DisableOption('--skip-test-swiftsyntax', dest='test_swiftsyntax'), DisableOption('--skip-test-indexstore-db', dest='test_indexstoredb'), diff --git a/utils/swift_build_support/swift_build_support/products/__init__.py b/utils/swift_build_support/swift_build_support/products/__init__.py index 3bc1e7d6ec857..d48378a055975 100644 --- a/utils/swift_build_support/swift_build_support/products/__init__.py +++ b/utils/swift_build_support/swift_build_support/products/__init__.py @@ -21,6 +21,7 @@ from .lldb import LLDB from .llvm import LLVM from .ninja import Ninja +from .playgroundsupport import PlaygroundSupport from .pythonkit import PythonKit from .skstresstester import SKStressTester from .sourcekitlsp import SourceKitLSP @@ -42,6 +43,7 @@ 'LLDB', 'LLVM', 'Ninja', + 'PlaygroundSupport', 'PythonKit', 'Swift', 'SwiftPM', diff --git a/utils/swift_build_support/swift_build_support/products/playgroundsupport.py b/utils/swift_build_support/swift_build_support/products/playgroundsupport.py new file mode 100644 index 0000000000000..81d53d841664b --- /dev/null +++ b/utils/swift_build_support/swift_build_support/products/playgroundsupport.py @@ -0,0 +1,112 @@ +# swift_build_support/products/playgroundsupport.py -------------*- python -*- +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +# +# ---------------------------------------------------------------------------- + +import os +import re + +from . import product +from .. import shell +from .. import targets + + +def get_os_spelling(os): + return { + 'macosx': 'macOS', + 'iphonesimulator': 'iOS', + 'appletvsimulator': 'tvOS', + }[os] + + +class PlaygroundSupport(product.Product): + @classmethod + def product_source_name(cls): + return "swift-xcode-playground-support" + + @classmethod + def is_build_script_impl_product(cls): + return False + + def should_build(self, host_target): + return self.args.build_playgroundsupport + + def build(self, host_target): + root = os.path.dirname(os.path.dirname(self.toolchain.swiftc)) + swift_lib_dir = os.path.join(root, 'lib', 'swift') + (host_os, host_arch) = host_target.split('-') + + with shell.pushd(self.source_dir): + shell.call([ + "xcodebuild", + "-configuration", self.args.build_variant, + "-workspace", "swift-xcode-playground-support.xcworkspace", + "-scheme", "BuildScript-{}".format(get_os_spelling(host_os)), + "-sdk", host_os, + "-arch", host_arch, + "-derivedDataPath", os.path.join(self.build_dir, "DerivedData"), + "SWIFT_EXEC={}".format(self.toolchain.swiftc), + "SWIFT_LIBRARY_PATH={}/$(PLATFORM_NAME)".format(swift_lib_dir), + "ONLY_ACTIVE_ARCH=NO", + ]) + + def should_test(self, host_target): + return re.match('macosx', host_target) and \ + self.args.test_playgroundsupport + + def test(self, host_target): + root = os.path.dirname(os.path.dirname(self.toolchain.swiftc)) + swift_lib_dir = os.path.join(root, 'lib', 'swift') + (host_os, host_arch) = host_target.split('-') + + with shell.pushd(self.source_dir): + shell.call([ + "xcodebuild", + "test", + # NOTE: this *always* needs to run in Debug configuration + "-configuration", "Debug", + "-workspace", "swift-xcode-playground-support.xcworkspace", + "-scheme", "BuildScript-Test-PlaygroundLogger-{}".format( + get_os_spelling(host_os)), + "-sdk", host_os, + "-arch", host_arch, + "-derivedDataPath", os.path.join(self.build_dir, "DerivedData"), + "SWIFT_EXEC={}".format(self.toolchain.swiftc), + "SWIFT_LIBRARY_PATH={}/$(PLATFORM_NAME)".format(swift_lib_dir), + "ONLY_ACTIVE_ARCH=NO", + ]) + + def should_install(self, host_target): + return self.args.install_playgroundsupport + + def install(self, host_target): + root = os.path.dirname(os.path.dirname(self.toolchain.swiftc)) + swift_lib_dir = os.path.join(root, 'lib', 'swift') + (host_os, host_arch) = host_target.split('-') + toolchain_prefix = \ + targets.darwin_toolchain_prefix(self.args.install_prefix) + + with shell.pushd(self.source_dir): + shell.call([ + "xcodebuild", + "install", + "-configuration", self.args.build_variant, + "-workspace", "swift-xcode-playground-support.xcworkspace", + "-scheme", "BuildScript-{}".format(get_os_spelling(host_os)), + "-sdk", host_os, + "-arch", host_arch, + "-derivedDataPath", os.path.join(self.build_dir, "DerivedData"), + "SWIFT_EXEC={}".format(self.toolchain.swiftc), + "SWIFT_LIBRARY_PATH={}/$(PLATFORM_NAME)".format(swift_lib_dir), + "ONLY_ACTIVE_ARCH=NO", + "DSTROOT={}".format(self.args.install_destdir), + "TOOLCHAIN_INSTALL_DIR={}".format(toolchain_prefix), + "BUILD_PLAYGROUND_LOGGER_TESTS=NO", + ]) From 299454db5b22fb349e3071872fddd02702776049 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sat, 8 Feb 2020 22:51:29 +0000 Subject: [PATCH 070/381] Add python-six as required on Ubuntu to README The current `master` doesn't build on Ubuntu 18.04 Docker image without installing `python-six` package. This should be reflected in README to make the build process on Ubuntu easier. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d998e0197640..13686bab2adb3 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Instructions for installing CMake and Ninja directly can be found [below](#build For Ubuntu, you'll need the following development dependencies: - sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config libcurl4-openssl-dev systemtap-sdt-dev tzdata rsync + sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config libcurl4-openssl-dev systemtap-sdt-dev tzdata rsync python-six **Note:** LLDB currently requires at least `swig-1.3.40` but will successfully build with version 2 shipped with Ubuntu. From bfff4825b46ba550802cbdb02abb8ed5cce3ec86 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 11 Feb 2020 18:15:11 +0100 Subject: [PATCH 071/381] SILCombine: directly get the SILBuilderContext from SILBuilder for tryOptimizeApplyOfPartialApply It's a NFC --- lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index c5ead61df1019..56edf84019de7 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -104,10 +104,8 @@ SILInstruction *SILCombiner::visitPartialApplyInst(PartialApplyInst *PAI) { if (foldInverseReabstractionThunks(PAI, this)) return nullptr; - SILBuilderContext BuilderCtxt(Builder.getModule(), Builder.getTrackingList()); - BuilderCtxt.setOpenedArchetypesTracker(Builder.getOpenedArchetypesTracker()); - bool argsAreKeptAlive = - tryOptimizeApplyOfPartialApply(PAI, BuilderCtxt, getInstModCallbacks()); + bool argsAreKeptAlive = tryOptimizeApplyOfPartialApply( + PAI, Builder.getBuilderContext(), getInstModCallbacks()); if (argsAreKeptAlive) needUpdateStackNesting = true; From 40e5955193f3399ca7d45be8aa62bc7bec3dc448 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 11 Feb 2020 18:26:04 +0100 Subject: [PATCH 072/381] SILOptimizer: rename needUpdateStackNesting (and similar) -> invalidatedStackNesting NFC --- include/swift/SILOptimizer/Utils/SILInliner.h | 6 +++--- lib/SILOptimizer/IPO/ClosureSpecializer.cpp | 18 +++++++++--------- .../Mandatory/MandatoryCombine.cpp | 6 +++--- .../Mandatory/MandatoryInlining.cpp | 16 ++++++++-------- lib/SILOptimizer/SILCombiner/SILCombine.cpp | 2 +- lib/SILOptimizer/SILCombiner/SILCombiner.h | 2 +- .../SILCombiner/SILCombinerApplyVisitors.cpp | 4 ++-- .../Transforms/PerformanceInliner.cpp | 6 +++--- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/include/swift/SILOptimizer/Utils/SILInliner.h b/include/swift/SILOptimizer/Utils/SILInliner.h index 7115267bd642c..87a0370450346 100644 --- a/include/swift/SILOptimizer/Utils/SILInliner.h +++ b/include/swift/SILOptimizer/Utils/SILInliner.h @@ -72,7 +72,7 @@ class SILInliner { /// /// In this case stack nesting must be corrected after inlining with the /// StackNesting utility. - static bool needsUpdateStackNesting(FullApplySite apply) { + static bool invalidatesStackNesting(FullApplySite apply) { // Inlining of coroutines can result in improperly nested stack // allocations. return isa(apply); @@ -111,7 +111,7 @@ class SILInliner { /// function. /// /// *NOTE*: Inlining can result in improperly nested stack allocations, which - /// must be corrected after inlining. See needsUpdateStackNesting(). + /// must be corrected after inlining. See invalidatesStackNesting(). /// /// Returns an iterator to the first inlined instruction (or the end of the /// caller block for empty functions) and the last block in function order @@ -134,7 +134,7 @@ class SILInliner { /// function. /// /// *NOTE*: Inlining can result in improperly nested stack allocations, which - /// must be corrected after inlining. See needsUpdateStackNesting(). + /// must be corrected after inlining. See invalidatesStackNesting(). /// /// Returns an iterator to the first inlined instruction (or the end of the /// caller block for empty functions) and the last block in function order diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index d57bd3e9a5fde..6adcf3bd88da2 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -780,7 +780,7 @@ SILValue ClosureSpecCloner::cloneCalleeConversion( /// Populate the body of the cloned closure, modifying instructions as /// necessary. This is where we create the actual specialized BB Arguments void ClosureSpecCloner::populateCloned() { - bool needToUpdateStackNesting = false; + bool invalidatedStackNesting = false; SILFunction *Cloned = getCloned(); SILFunction *ClosureUser = CallSiteDesc.getApplyCallee(); @@ -880,11 +880,11 @@ void ClosureSpecCloner::populateCloned() { Builder.createReleaseValue(Loc, SILValue(NewClosure), Builder.getDefaultAtomicity()); else - needToUpdateStackNesting |= + invalidatedStackNesting |= CallSiteDesc.destroyIfPartialApplyStack(Builder, NewClosure); for (auto PAI : NeedsRelease) { if (PAI->isOnStack()) - needToUpdateStackNesting |= + invalidatedStackNesting |= CallSiteDesc.destroyIfPartialApplyStack(Builder, PAI); else Builder.createReleaseValue(Loc, SILValue(PAI), @@ -909,11 +909,11 @@ void ClosureSpecCloner::populateCloned() { Builder.createReleaseValue(Loc, SILValue(NewClosure), Builder.getDefaultAtomicity()); else - needToUpdateStackNesting |= + invalidatedStackNesting |= CallSiteDesc.destroyIfPartialApplyStack(Builder, NewClosure); for (auto PAI : NeedsRelease) { if (PAI->isOnStack()) - needToUpdateStackNesting |= + invalidatedStackNesting |= CallSiteDesc.destroyIfPartialApplyStack(Builder, PAI); else Builder.createReleaseValue(Loc, SILValue(PAI), @@ -921,7 +921,7 @@ void ClosureSpecCloner::populateCloned() { } } } - if (needToUpdateStackNesting) { + if (invalidatedStackNesting) { StackNesting().correctStackNesting(Cloned); } } @@ -971,7 +971,7 @@ void SILClosureSpecializerTransform::run() { // specialized all of their uses. LLVM_DEBUG(llvm::dbgs() << "Trying to remove dead closures!\n"); sortUnique(PropagatedClosures); - bool needUpdateStackNesting = false; + bool invalidatedStackNesting = false; for (auto *Closure : PropagatedClosures) { LLVM_DEBUG(llvm::dbgs() << " Visiting: " << *Closure); @@ -983,10 +983,10 @@ void SILClosureSpecializerTransform::run() { LLVM_DEBUG(llvm::dbgs() << " Deleted closure!\n"); ++NumPropagatedClosuresEliminated; - needUpdateStackNesting = true; + invalidatedStackNesting = true; } - if (needUpdateStackNesting) { + if (invalidatedStackNesting) { StackNesting().correctStackNesting(F); } } diff --git a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp index a0baab9687927..0f26e376f691f 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp @@ -72,7 +72,7 @@ class MandatoryCombiner final /// Set to true if some alloc/dealloc_stack instruction are inserted and at /// the end of the run stack nesting needs to be corrected. - bool needUpdateStackNesting; + bool invalidatedStackNesting; /// The number of times that the worklist has been processed. unsigned iteration; @@ -119,7 +119,7 @@ class MandatoryCombiner final ++iteration; } - if (needUpdateStackNesting) { + if (invalidatedStackNesting) { StackNesting().correctStackNesting(&function); } @@ -279,7 +279,7 @@ SILInstruction *MandatoryCombiner::visitApplyInst(ApplyInst *instruction) { #endif ); if (tryDeleteDeadClosure(partialApply, instModCallbacks)) { - needUpdateStackNesting = true; + invalidatedStackNesting = true; } return nullptr; } diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp index 0d8165774e031..b323bbb69642e 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp @@ -343,7 +343,7 @@ static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) { /// Removes instructions that create the callee value if they are no /// longer necessary after inlining. static void cleanupCalleeValue(SILValue calleeValue, - bool &needUpdateStackNesting) { + bool &invalidatedStackNesting) { // Handle the case where the callee of the apply is a load instruction. If we // fail to optimize, return. Otherwise, see if we can look through other // abstractions on our callee. @@ -383,7 +383,7 @@ static void cleanupCalleeValue(SILValue calleeValue, return; calleeValue = callee; } - needUpdateStackNesting = true; + invalidatedStackNesting = true; calleeValue = stripCopiesAndBorrows(calleeValue); @@ -450,7 +450,7 @@ class ClosureCleanup { public: /// Set to true if some alloc/dealloc_stack instruction are inserted and at /// the end of the run stack nesting needs to be corrected. - bool needUpdateStackNesting = false; + bool invalidatedStackNesting = false; /// This regular instruction deletion callback checks for any function-type /// values that may be unused after deleting the given instruction. @@ -481,7 +481,7 @@ class ClosureCleanup { continue; if (auto *SVI = dyn_cast(I.getValue())) - cleanupCalleeValue(SVI, needUpdateStackNesting); + cleanupCalleeValue(SVI, invalidatedStackNesting); } } }; @@ -812,7 +812,7 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder, SmallVector CapturedArgConventions; SmallVector FullArgs; - bool needUpdateStackNesting = false; + bool invalidatedStackNesting = false; // Visiting blocks in reverse order avoids revisiting instructions after block // splitting, which would be quadratic. @@ -930,7 +930,7 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder, closureCleanup.recordDeadFunction(I); }); - needUpdateStackNesting |= Inliner.needsUpdateStackNesting(InnerAI); + invalidatedStackNesting |= Inliner.invalidatesStackNesting(InnerAI); // Inlining deletes the apply, and can introduce multiple new basic // blocks. After this, CalleeValue and other instructions may be invalid. @@ -944,7 +944,7 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder, // we may be able to remove dead callee computations (e.g. dead // partial_apply closures). closureCleanup.cleanupDeadClosures(F); - needUpdateStackNesting |= closureCleanup.needUpdateStackNesting; + invalidatedStackNesting |= closureCleanup.invalidatedStackNesting; // Resume inlining within nextBB, which contains only the inlined // instructions and possibly instructions in the original call block that @@ -953,7 +953,7 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder, } } - if (needUpdateStackNesting) { + if (invalidatedStackNesting) { StackNesting().correctStackNesting(F); } diff --git a/lib/SILOptimizer/SILCombiner/SILCombine.cpp b/lib/SILOptimizer/SILCombiner/SILCombine.cpp index 9893093a7d4ba..98d7e92ce0b72 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombine.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombine.cpp @@ -230,7 +230,7 @@ bool SILCombiner::runOnFunction(SILFunction &F) { Iteration++; } - if (needUpdateStackNesting) { + if (invalidatedStackNesting) { StackNesting().correctStackNesting(&F); } diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index 66c7013309296..5d7733c287a45 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -67,7 +67,7 @@ class SILCombiner : /// Set to true if some alloc/dealloc_stack instruction are inserted and at /// the end of the run stack nesting needs to be corrected. - bool needUpdateStackNesting = false; + bool invalidatedStackNesting = false; /// The current iteration of the SILCombine. unsigned Iteration; diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 56edf84019de7..1c7b5424d1961 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -107,14 +107,14 @@ SILInstruction *SILCombiner::visitPartialApplyInst(PartialApplyInst *PAI) { bool argsAreKeptAlive = tryOptimizeApplyOfPartialApply( PAI, Builder.getBuilderContext(), getInstModCallbacks()); if (argsAreKeptAlive) - needUpdateStackNesting = true; + invalidatedStackNesting = true; // Try to delete the partial_apply. // In case it became dead because of tryOptimizeApplyOfPartialApply, we don't // need to copy all arguments again (to extend their lifetimes), because it // was already done in tryOptimizeApplyOfPartialApply. if (tryDeleteDeadClosure(PAI, getInstModCallbacks(), !argsAreKeptAlive)) - needUpdateStackNesting = true; + invalidatedStackNesting = true; return nullptr; } diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index da6696bde8090..f03e5bd1d7f40 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -888,7 +888,7 @@ bool SILPerformanceInliner::inlineCallsIntoFunction(SILFunction *Caller) { // remains valid. SmallVector AppliesToInline; collectAppliesToInline(Caller, AppliesToInline); - bool needUpdateStackNesting = false; + bool invalidatedStackNesting = false; if (AppliesToInline.empty()) return false; @@ -918,7 +918,7 @@ bool SILPerformanceInliner::inlineCallsIntoFunction(SILFunction *Caller) { // Note that this must happen before inlining as the apply instruction // will be deleted after inlining. - needUpdateStackNesting |= SILInliner::needsUpdateStackNesting(AI); + invalidatedStackNesting |= SILInliner::invalidatesStackNesting(AI); // We've already determined we should be able to inline this, so // unconditionally inline the function. @@ -933,7 +933,7 @@ bool SILPerformanceInliner::inlineCallsIntoFunction(SILFunction *Caller) { // reestablish a canonical CFG. mergeBasicBlocks(Caller); - if (needUpdateStackNesting) { + if (invalidatedStackNesting) { StackNesting().correctStackNesting(Caller); } From 949029a788dcc1586a41c8aa62bd5a79f403901b Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 12 Dec 2019 18:13:56 -0800 Subject: [PATCH 073/381] [metadata prespecialization] Create enum records. Extracted implementation of SpecializedGenericStructMetadataBuilder into SpecializedGenericNominalMetadataBuilderBase, a CRTP with a template template argument for the CRTP superclass and a template argument for the implementation. That new type is now subclassed by SpecializedGenericStructMetadataBuilder. Additionally, this new type is also subclassed by the newly added SpecializedGenericEnumMetadataBuilder which is responsible for build the prespecialization of generic enum metadata. rdar://problem/56960887 --- include/swift/ABI/Metadata.h | 26 ++++ lib/IRGen/EnumMetadataVisitor.h | 9 ++ lib/IRGen/GenMeta.cpp | 235 +++++++++++++++++++++++------ lib/IRGen/GenMeta.h | 6 +- stdlib/public/runtime/Metadata.cpp | 2 + 5 files changed, 233 insertions(+), 45 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 951abc2cc3070..61482ffa4f29c 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -1426,6 +1426,32 @@ struct TargetEnumMetadata : public TargetValueMetadata { return *asWords; } + bool isCanonicalStaticallySpecializedGenericMetadata() const { + auto *description = getDescription(); + if (!description->isGeneric()) + return false; + + auto *trailingFlags = getTrailingFlags(); + if (trailingFlags == nullptr) + return false; + + return trailingFlags->isCanonicalStaticSpecialization(); + } + + const MetadataTrailingFlags *getTrailingFlags() const { + auto description = getDescription(); + auto flags = description->getFullGenericContextHeader() + .DefaultInstantiationPattern->PatternFlags; + if (!flags.hasTrailingFlags()) + return nullptr; + auto offset = + getGenericArgumentOffset() + + description->getFullGenericContextHeader().Base.getNumArguments() + + (hasPayloadSize() ? 1 : 0); + auto asWords = reinterpret_cast(this); + return reinterpret_cast(asWords + offset); + } + static constexpr int32_t getGenericArgumentOffset() { return sizeof(TargetEnumMetadata) / sizeof(StoredPointer); } diff --git a/lib/IRGen/EnumMetadataVisitor.h b/lib/IRGen/EnumMetadataVisitor.h index ab6690c42dac8..6f45d8a66e24a 100644 --- a/lib/IRGen/EnumMetadataVisitor.h +++ b/lib/IRGen/EnumMetadataVisitor.h @@ -62,6 +62,14 @@ template class EnumMetadataVisitor Target->getDeclaredTypeInContext()->getCanonicalType()); if (strategy.needsPayloadSizeInMetadata()) asImpl().addPayloadSize(); + + if (asImpl().hasTrailingFlags()) + asImpl().addTrailingFlags(); + } + + bool hasTrailingFlags() { + return Target->isGenericContext() && + IGM.shouldPrespecializeGenericMetadata(); } }; @@ -86,6 +94,7 @@ class EnumMetadataScanner : public EnumMetadataVisitor { void addGenericWitnessTable(GenericRequirement requirement) { addPointer(); } void addPayloadSize() { addPointer(); } void noteStartOfTypeSpecificMembers() {} + void addTrailingFlags() { addPointer(); } private: void addPointer() { diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index d494eaeb84ee8..9bb2799b9ff6c 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1859,7 +1859,21 @@ void irgen::emitLazyMetadataAccessor(IRGenModule &IGM, void irgen::emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, CanType type) { - emitSpecializedGenericStructMetadata(IGM, type); + switch (type->getKind()) { + case TypeKind::Struct: + case TypeKind::BoundGenericStruct: + emitSpecializedGenericStructMetadata(IGM, type, + *type.getStructOrBoundGenericStruct()); + break; + case TypeKind::Enum: + case TypeKind::BoundGenericEnum: + emitSpecializedGenericEnumMetadata(IGM, type, + *type.getEnumOrBoundGenericEnum()); + break; + default: + llvm_unreachable("Cannot statically specialize types of kind other than " + "struct and enum."); + } } llvm::Constant * @@ -3472,8 +3486,12 @@ namespace { : public ValueMetadataBuilderBase> { using super = ValueMetadataBuilderBase>; + bool HasUnfilledFieldOffset = false; + protected: - ConstantStructBuilder &B; + using ConstantBuilder = ConstantStructBuilder; + ConstantBuilder &B; + using NominalDecl = StructDecl; using super::IGM; using super::Target; using super::asImpl; @@ -3562,16 +3580,6 @@ namespace { return flags; } - }; - - class StructMetadataBuilder : - public StructMetadataBuilderBase { - - bool HasUnfilledFieldOffset = false; - public: - StructMetadataBuilder(IRGenModule &IGM, StructDecl *theStruct, - ConstantStructBuilder &B) - : StructMetadataBuilderBase(IGM, theStruct, B) {} void flagUnfilledFieldOffset() { HasUnfilledFieldOffset = true; @@ -3580,13 +3588,21 @@ namespace { bool canBeConstant() { return !HasUnfilledFieldOffset; } + }; + + class StructMetadataBuilder + : public StructMetadataBuilderBase { + public: + StructMetadataBuilder(IRGenModule &IGM, StructDecl *theStruct, + ConstantStructBuilder &B) + : StructMetadataBuilderBase(IGM, theStruct, B) {} void createMetadataAccessFunction() { createNonGenericMetadataAccessFunction(IGM, Target); maybeCreateSingletonMetadataInitialization(); } }; - + /// Emit a value witness table for a fixed-layout generic type, or a template /// if the value witness table is dependent on generic parameters. static ConstantReference @@ -3710,24 +3726,25 @@ namespace { } }; - class SpecializedGenericStructMetadataBuilder - : public StructMetadataBuilderBase< - SpecializedGenericStructMetadataBuilder> { - using super = - StructMetadataBuilderBase; + template