diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index e6a08be1305f0..da0b8d36ebd19 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -37,6 +37,9 @@ class SILGlobalVariable : public llvm::ilist_node, public SILAllocated { +public: + using const_iterator = SILBasicBlock::const_iterator; + private: friend class SILModule; friend class SILBuilder; @@ -157,6 +160,9 @@ class SILGlobalVariable return dyn_cast_or_null(getStaticInitializerValue()) != nullptr; } + const_iterator begin() const { return StaticInitializerBlock.begin(); } + const_iterator end() const { return StaticInitializerBlock.end(); } + /// Returns true if \p I is a valid instruction to be contained in the /// static initializer. static bool isValidStaticInitializerInst(const SILInstruction *I, diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index a0613879a627c..47f38422cf207 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2701,7 +2701,9 @@ void SILGlobalVariable::print(llvm::raw_ostream &OS, bool Verbose) const { printClangQualifiedNameCommentIfPresent(OS, getClangDecl()); OS << "sil_global "; - printLinkage(OS, getLinkage(), isDefinition()); + // Passing true for 'isDefinition' lets print the (external) linkage if it's + // not a definition. + printLinkage(OS, getLinkage(), /*isDefinition*/ true); if (isSerialized()) OS << "[serialized] "; diff --git a/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp b/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp index a37810ca715f5..ea8429c854876 100644 --- a/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp +++ b/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp @@ -23,11 +23,17 @@ #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/SILInliner.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; +/// Functions up to this (abstract) size are serialized, even if they are not +/// generic. +static llvm::cl::opt CMOFunctionSizeLimit("cmo-function-size-limit", + llvm::cl::init(20)); + namespace { /// Scans a whole module and marks functions and types as inlinable or usable @@ -54,6 +60,8 @@ class CrossModuleSerializationSetup { bool canSerialize(SILFunction *F, bool lookIntoThunks); + bool canSerialize(SILInstruction *inst, bool lookIntoThunks); + void setUpForSerialization(SILFunction *F); void prepareInstructionForSerialization(SILInstruction *inst); @@ -193,11 +201,6 @@ static llvm::cl::opt SerializeEverything( /// Decide whether to serialize a function. static bool shouldSerialize(SILFunction *F) { - // The basic heursitic: serialize all generic functions, because it makes a - // huge difference if generic functions can be specialized or not. - if (!F->getLoweredFunctionType()->isPolymorphic() && !SerializeEverything) - return false; - // Check if we already handled this function before. if (F->isSerialized() == IsSerialized) return false; @@ -205,6 +208,24 @@ static bool shouldSerialize(SILFunction *F) { if (F->hasSemanticsAttr("optimize.no.crossmodule")) return false; + if (SerializeEverything) + return true; + + // The basic heursitic: serialize all generic functions, because it makes a + // huge difference if generic functions can be specialized or not. + if (F->getLoweredFunctionType()->isPolymorphic()) + return true; + + // Also serialize "small" non-generic functions. + int size = 0; + for (SILBasicBlock &block : *F) { + for (SILInstruction &inst : block) { + size += (int)instructionInlineCost(inst); + if (size >= CMOFunctionSizeLimit) + return false; + } + } + return true; } @@ -227,6 +248,11 @@ prepareInstructionForSerialization(SILInstruction *inst) { handleReferencedFunction(callee); return; } + if (auto *GAI = dyn_cast(inst)) { + GAI->getReferencedGlobal()->setSerialized(IsSerialized); + GAI->getReferencedGlobal()->setLinkage(SILLinkage::Public); + return; + } if (auto *MI = dyn_cast(inst)) { handleReferencedMethod(MI->getMember()); return; @@ -285,26 +311,42 @@ bool CrossModuleSerializationSetup::canSerialize(SILFunction *F, // First step: check if serializing F is even possible. for (SILBasicBlock &block : *F) { for (SILInstruction &inst : block) { - if (auto *FRI = dyn_cast(&inst)) { - SILFunction *callee = FRI->getReferencedFunctionOrNull(); - if (!canUseFromInline(callee, lookIntoThunks)) - return false; - } else if (auto *KPI = dyn_cast(&inst)) { - bool canUse = true; - KPI->getPattern()->visitReferencedFunctionsAndMethods( - [&](SILFunction *func) { - if (!canUseFromInline(func, lookIntoThunks)) - canUse = false; - }, - [](SILDeclRef method) { }); - if (!canUse) - return false; - } + if (!canSerialize(&inst, lookIntoThunks)) + return false; } } return true; } +bool CrossModuleSerializationSetup::canSerialize(SILInstruction *inst, + bool lookIntoThunks) { + if (auto *FRI = dyn_cast(inst)) { + SILFunction *callee = FRI->getReferencedFunctionOrNull(); + return canUseFromInline(callee, lookIntoThunks); + } + if (auto *KPI = dyn_cast(inst)) { + bool canUse = true; + KPI->getPattern()->visitReferencedFunctionsAndMethods( + [&](SILFunction *func) { + if (!canUseFromInline(func, lookIntoThunks)) + canUse = false; + }, + [&](SILDeclRef method) { + if (method.isForeign) + canUse = false; + }); + return canUse; + } + if (auto *GAI = dyn_cast(inst)) { + return !GAI->getReferencedGlobal()->getName().startswith("globalinit_"); + } + if (auto *MI = dyn_cast(inst)) { + return !MI->getMember().isForeign; + } + + return true; +} + /// Returns true if the function \p func can be used from a serialized function. /// /// If \p lookIntoThunks is true, serializable shared thunks are also accepted. @@ -351,12 +393,17 @@ void CrossModuleSerializationSetup::setUpForSerialization(SILFunction *F) { } F->setSerialized(IsSerialized); - // As a code size optimization, make serialized functions - // @alwaysEmitIntoClient. - // Also, for shared thunks it's required to make them @alwaysEmitIntoClient. - // SILLinkage::Public would not work for shared functions, because it could - // result in duplicate-symbol linker errors. - F->setLinkage(SILLinkage::PublicNonABI); + if (F->getLoweredFunctionType()->isPolymorphic() || + F->getLinkage() != SILLinkage::Public) { + // As a code size optimization, make serialized functions + // @alwaysEmitIntoClient. + // Also, for shared thunks it's required to make them @alwaysEmitIntoClient. + // SILLinkage::Public would not work for shared functions, because it could + // result in duplicate-symbol linker errors. + F->setLinkage(SILLinkage::PublicNonABI); + } else { + F->setLinkage(SILLinkage::Public); + } } /// Select functions in the module which should be serialized. diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 25ca8699e9c68..932479f16b425 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -447,6 +447,10 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) { // is linked in from the stdlib. P.addTempRValueOpt(); + // Needed to serialize static initializers of globals for cross-module + // optimization. + P.addGlobalOpt(); + // Add the outliner pass (Osize). P.addOutliner(); diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 01c9237526742..e476210f9d48c 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -26,6 +26,7 @@ #include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/Devirtualize.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" @@ -807,6 +808,31 @@ static bool isZeroLoadFromEmptyCollection(LoadInst *LI) { } } +static SingleValueInstruction *getValueFromStaticLet(SILValue v) { + if (auto *globalAddr = dyn_cast(v)) { + SILGlobalVariable *global = globalAddr->getReferencedGlobal(); + if (!global->isLet()) + return nullptr; + return dyn_cast_or_null( + global->getStaticInitializerValue()); + } + if (auto *seai = dyn_cast(v)) { + auto *structVal = getValueFromStaticLet(seai->getOperand()); + if (!structVal) + return nullptr; + return cast( + cast(structVal)->getOperandForField(seai->getField())->get()); + } + if (auto *teai = dyn_cast(v)) { + auto *tupleVal = getValueFromStaticLet(teai->getOperand()); + if (!tupleVal) + return nullptr; + return cast( + cast(tupleVal)->getElement(teai->getFieldNo())); + } + return nullptr; +} + SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) { if (LI->getFunction()->hasOwnership()) return nullptr; @@ -833,6 +859,15 @@ SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) { if (isZeroLoadFromEmptyCollection(LI)) return Builder.createIntegerLiteral(LI->getLoc(), LI->getType(), 0); + // Propagate a value from a static "let" global variable. + // This optimization is also done by GlobalOpt, but not with de-serialized + // globals, which can occur with cross-module optimization. + if (SingleValueInstruction *initVal = getValueFromStaticLet(LI->getOperand())) { + StaticInitCloner cloner(LI); + cloner.add(initVal); + return cloner.clone(initVal); + } + return nullptr; } diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 7e9dfeb71a698..65bd1ec30b028 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -799,9 +799,10 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // occurred and this is a declaration. Work around that for now. if (!CurrentBB) return fn; + Builder.setInsertionPoint(CurrentBB); // Handle a SILInstruction record. - if (readSILInstruction(fn, CurrentBB, Builder, kind, scratch)) { + if (readSILInstruction(fn, Builder, kind, scratch)) { LLVM_DEBUG(llvm::dbgs() << "readSILInstruction returns error.\n"); MF->fatal(); } @@ -1031,16 +1032,12 @@ SILDeserializer::readKeyPathComponent(ArrayRef ListOfValues, llvm_unreachable("invalid key path component kind encoding"); } -bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, +bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBuilder &Builder, unsigned RecordKind, SmallVectorImpl &scratch) { - // Return error if Basic Block is null. - if (!BB) - return true; - - Builder.setInsertionPoint(BB); - Builder.setCurrentDebugScope(Fn->getDebugScope()); + if (Fn) + Builder.setCurrentDebugScope(Fn->getDebugScope()); unsigned RawOpCode = 0, TyCategory = 0, TyCategory2 = 0, TyCategory3 = 0, Attr = 0, Attr2 = 0, Attr3 = 0, Attr4 = 0, NumSubs = 0, NumConformances = 0, IsNonThrowingApply = 0; @@ -2096,7 +2093,22 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, break; } case SILInstructionKind::ObjectInst: { - llvm_unreachable("Serialization of global initializers not supported"); + assert(RecordKind == SIL_ONE_TYPE_VALUES && + "Layout should be OneTypeValues."); + unsigned NumVals = ListOfValues.size(); + assert(NumVals >= 1 && "Not enough values"); + unsigned numBaseElements = ListOfValues[0]; + SILType ClassTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + SmallVector elements; + for (unsigned i = 1; i < NumVals; i += 2) { + SILType elementType = getSILType(MF->getType(ListOfValues[i + 1]), + SILValueCategory::Object, Fn); + SILValue elementVal = getLocalValue(ListOfValues[i], elementType); + elements.push_back(elementVal); + } + ResultVal = Builder.createObject(Loc, ClassTy, elements, numBaseElements); + break; } case SILInstructionKind::BranchInst: { SmallVector Args; @@ -2927,7 +2939,60 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) { globalVarOrOffset = v; v->setDeclaration(IsDeclaration); - if (Callback) Callback->didDeserialize(MF->getAssociatedModule(), v); + if (Callback) + Callback->didDeserialize(MF->getAssociatedModule(), v); + + scratch.clear(); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); + if (entry.Kind == llvm::BitstreamEntry::EndBlock) + return v; + + maybeKind = SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); + + SILBuilder Builder(v); + + llvm::DenseMap SavedLocalValues; + llvm::DenseMap SavedForwardLocalValues; + serialization::ValueID SavedLastValueID = 1; + + SavedLocalValues.swap(LocalValues); + SavedForwardLocalValues.swap(ForwardLocalValues); + std::swap(SavedLastValueID, LastValueID); + + while (kind != SIL_FUNCTION && kind != SIL_VTABLE && kind != SIL_GLOBALVAR && + kind != SIL_WITNESS_TABLE && kind != SIL_DIFFERENTIABILITY_WITNESS) { + if (readSILInstruction(nullptr, Builder, kind, scratch)) { + LLVM_DEBUG(llvm::dbgs() << "readSILInstruction returns error.\n"); + MF->fatal(); + } + + // Fetch the next record. + scratch.clear(); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); + + // EndBlock means the end of this SILFunction. + if (entry.Kind == llvm::BitstreamEntry::EndBlock) + break; + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); + } + + SavedLocalValues.swap(LocalValues); + SavedForwardLocalValues.swap(ForwardLocalValues); + std::swap(SavedLastValueID, LastValueID); + return v; } diff --git a/lib/Serialization/DeserializeSIL.h b/lib/Serialization/DeserializeSIL.h index 368b26740a468..df35889d1290c 100644 --- a/lib/Serialization/DeserializeSIL.h +++ b/lib/Serialization/DeserializeSIL.h @@ -98,8 +98,8 @@ namespace swift { SILBasicBlock *readSILBasicBlock(SILFunction *Fn, SILBasicBlock *Prev, SmallVectorImpl &scratch); - /// Read a SIL instruction within a given SIL basic block. - bool readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, + /// Read a SIL instruction. + bool readSILInstruction(SILFunction *Fn, SILBuilder &Builder, unsigned RecordKind, SmallVectorImpl &scratch); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 065150c5d4d3b..42d5001304a2c 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 560; // SILVTable flag for non-overridden entries +const uint16_t SWIFTMODULE_VERSION_MINOR = 561; // Initializers of globals. /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index aad7fa2b7d3c8..75e47c8b2decc 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -734,8 +734,26 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { PrettyStackTraceSILNode stackTrace("Serializing", &SI); switch (SI.getKind()) { - case SILInstructionKind::ObjectInst: - llvm_unreachable("static initializers of sil_global are not serialized"); + case SILInstructionKind::ObjectInst: { + const ObjectInst *OI = cast(&SI); + unsigned abbrCode = SILAbbrCodes[SILOneTypeValuesLayout::Code]; + SmallVector Args; + Args.push_back((unsigned)OI->getBaseElements().size()); + for (const Operand &op : OI->getAllOperands()) { + SILValue OpVal = op.get(); + Args.push_back(addValueRef(OpVal)); + SILType OpType = OpVal->getType(); + assert(OpType.isObject()); + Args.push_back(S.addTypeRef(OpType.getASTType())); + } + SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, abbrCode, + (unsigned)SI.getKind(), + S.addTypeRef( + OI->getType().getASTType()), + (unsigned)OI->getType().getCategory(), + Args); + break; + } case SILInstructionKind::DebugValueInst: case SILInstructionKind::DebugValueAddrInst: @@ -2349,6 +2367,19 @@ void SILSerializer::writeSILGlobalVar(const SILGlobalVariable &g) { (unsigned)!g.isDefinition(), (unsigned)g.isLet(), TyID, dID); + + ValueIDs.clear(); + InstID = 0; + unsigned ValueID = 2; + for (const SILInstruction &initInst : g) { + for (auto result : initInst.getResults()) { + ValueIDs[result] = ValueID++; + } + } + + for (const SILInstruction &initInst : g) { + writeSILInstruction(initInst); + } } void SILSerializer::writeSILVTable(const SILVTable &vt) { diff --git a/test/Interop/Cxx/extern-var/extern-var-silgen.swift b/test/Interop/Cxx/extern-var/extern-var-silgen.swift index 08d06c316e3dd..ccc81163cab78 100644 --- a/test/Interop/Cxx/extern-var/extern-var-silgen.swift +++ b/test/Interop/Cxx/extern-var/extern-var-silgen.swift @@ -7,9 +7,9 @@ func getCounter() -> CInt { } // CHECK: // clang name: counter -// CHECK: sil_global @counter : $Int32 +// CHECK: sil_global public_external @counter : $Int32 // CHECK: // clang name: Namespaced::counter -// CHECK: sil_global @{{_ZN10Namespaced7counterE|\?counter@Namespaced@@3HA}} : $Int32 +// CHECK: sil_global public_external @{{_ZN10Namespaced7counterE|\?counter@Namespaced@@3HA}} : $Int32 // CHECK: sil hidden @$s4main10getCounters5Int32VyF : $@convention(thin) () -> Int32 // CHECK: [[COUNTER:%.*]] = global_addr @counter : $*Int32 diff --git a/test/Interop/Cxx/static/inline-static-member-var-silgen.swift b/test/Interop/Cxx/static/inline-static-member-var-silgen.swift index 4779f3b68cce1..9def284cffd19 100644 --- a/test/Interop/Cxx/static/inline-static-member-var-silgen.swift +++ b/test/Interop/Cxx/static/inline-static-member-var-silgen.swift @@ -7,7 +7,7 @@ func readStaticMember() -> CInt { } // CHECK: // clang name: WithInlineStaticMember::staticMember -// CHECK: sil_global @{{_ZN22WithInlineStaticMember12staticMemberE|\?staticMember@WithInlineStaticMember@@2HA}} : $Int32 +// CHECK: sil_global public_external @{{_ZN22WithInlineStaticMember12staticMemberE|\?staticMember@WithInlineStaticMember@@2HA}} : $Int32 // CHECK: sil hidden @$s4main16readStaticMembers5Int32VyF : $@convention(thin) () -> Int32 // CHECK: [[ADDR:%.*]] = global_addr @{{_ZN22WithInlineStaticMember12staticMemberE|\?staticMember@WithInlineStaticMember@@2HA}} : $*Int32 diff --git a/test/Interop/Cxx/static/static-member-var-silgen.swift b/test/Interop/Cxx/static/static-member-var-silgen.swift index 9723daa86652c..946928e048f8e 100644 --- a/test/Interop/Cxx/static/static-member-var-silgen.swift +++ b/test/Interop/Cxx/static/static-member-var-silgen.swift @@ -1,15 +1,15 @@ // RUN: %target-swift-emit-sil -I %S/Inputs -enable-cxx-interop %s | %FileCheck %s // CHECK: // clang name: WithStaticMember::staticMember -// CHECK: sil_global @{{_ZN16WithStaticMember12staticMemberE|\?staticMember@WithStaticMember@@2HA}} : $Int32 +// CHECK: sil_global public_external @{{_ZN16WithStaticMember12staticMemberE|\?staticMember@WithStaticMember@@2HA}} : $Int32 // CHECK: // clang name: WithIncompleteStaticMember::selfMember -// CHECK: sil_global @{{_ZN26WithIncompleteStaticMember10selfMemberE|\?selfMember@WithIncompleteStaticMember@@2V1@A}} : $WithIncompleteStaticMember +// CHECK: sil_global public_external @{{_ZN26WithIncompleteStaticMember10selfMemberE|\?selfMember@WithIncompleteStaticMember@@2V1@A}} : $WithIncompleteStaticMember // CHECK: // clang name: WithConstStaticMember::defined -// CHECK: sil_global [let] @{{_ZN21WithConstStaticMember7definedE|\?defined@WithConstStaticMember@@2HB}} : $Int32 +// CHECK: sil_global public_external [let] @{{_ZN21WithConstStaticMember7definedE|\?defined@WithConstStaticMember@@2HB}} : $Int32 // CHECK: // clang name: WithConstStaticMember::definedOutOfLine -// CHECK: sil_global [let] @{{_ZN21WithConstStaticMember16definedOutOfLineE|\?definedOutOfLine@WithConstStaticMember@@2HB}} : $Int32 +// CHECK: sil_global public_external [let] @{{_ZN21WithConstStaticMember16definedOutOfLineE|\?definedOutOfLine@WithConstStaticMember@@2HB}} : $Int32 // CHECK: // clang name: WithConstexprStaticMember::definedInline -// CHECK: sil_global [let] @{{_ZN25WithConstexprStaticMember13definedInlineE|\?definedInline@WithConstexprStaticMember@@2HB}} : $Int32 +// CHECK: sil_global public_external [let] @{{_ZN25WithConstexprStaticMember13definedInlineE|\?definedInline@WithConstexprStaticMember@@2HB}} : $Int32 import StaticMemberVar diff --git a/test/Interop/Cxx/static/static-var-silgen.swift b/test/Interop/Cxx/static/static-var-silgen.swift index 285bcfea4e8be..f76792b41b77c 100644 --- a/test/Interop/Cxx/static/static-var-silgen.swift +++ b/test/Interop/Cxx/static/static-var-silgen.swift @@ -9,25 +9,25 @@ func initStaticVars() -> CInt { } // CHECK: // clang name: staticVar -// CHECK: sil_global @staticVar : $Int32 +// CHECK: sil_global public_external @staticVar : $Int32 // CHECK: // clang name: staticVarInit -// CHECK: sil_global @staticVarInit : $Int32 +// CHECK: sil_global public_external @staticVarInit : $Int32 // CHECK: // clang name: staticVarInlineInit -// CHECK: sil_global @staticVarInlineInit : $Int32 +// CHECK: sil_global public_external @staticVarInlineInit : $Int32 // CHECK: // clang name: staticConst -// CHECK: sil_global [let] @staticConst : $Int32 +// CHECK: sil_global public_external [let] @staticConst : $Int32 // CHECK: // clang name: staticConstInit -// CHECK: sil_global [let] @staticConstInit : $Int32 +// CHECK: sil_global public_external [let] @staticConstInit : $Int32 // CHECK: // clang name: staticConstInlineInit -// CHECK: sil_global [let] @staticConstInlineInit : $Int32 +// CHECK: sil_global public_external [let] @staticConstInlineInit : $Int32 // CHECK: // clang name: staticConstexpr -// CHECK: sil_global [let] @staticConstexpr : $Int32 +// CHECK: sil_global public_external [let] @staticConstexpr : $Int32 // CHECK: // clang name: staticNonTrivial -// CHECK: sil_global @staticNonTrivial : $NonTrivial +// CHECK: sil_global public_external @staticNonTrivial : $NonTrivial // CHECK: // clang name: staticConstNonTrivial -// CHECK: sil_global [let] @staticConstNonTrivial : $NonTrivial +// CHECK: sil_global public_external [let] @staticConstNonTrivial : $NonTrivial // CHECK: // clang name: staticConstexprNonTrivial -// CHECK: sil_global [let] @staticConstexprNonTrivial : $NonTrivial +// CHECK: sil_global public_external [let] @staticConstexprNonTrivial : $NonTrivial func readStaticVar() -> CInt { return staticVar diff --git a/test/SIL/Serialization/globals.sil b/test/SIL/Serialization/globals.sil index 7ceffc910d283..5dadb7b1d7a4b 100644 --- a/test/SIL/Serialization/globals.sil +++ b/test/SIL/Serialization/globals.sil @@ -5,6 +5,7 @@ sil_stage canonical import Swift +import Builtin // CHECK-NOT: sil_global hidden @hidden_global_unused sil_global hidden @hidden_global_unused : $Int @@ -15,6 +16,17 @@ sil_global @public_global_unused : $Int // CHECK: sil_global @public_global_used sil_global @public_global_used : $Int +// CHECK: sil_global [serialized] @initialized_global_object : $Int64 = { +// CHECK-NEXT: %0 = integer_literal $Builtin.Int64, 27 +// CHECK-NEXT: %1 = integer_literal $Builtin.Int64, 28 +// CHECK-NEXT: %initval = object $GlobalObject (%0 : $Builtin.Int64, [tail_elems] %1 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK-NEXT: } +sil_global [serialized] @initialized_global_object : $Int64 = { + %0 = integer_literal $Builtin.Int64, 27 + %1 = integer_literal $Builtin.Int64, 28 + %initval = object $GlobalObject (%0 : $Builtin.Int64, [tail_elems] %1 : $Builtin.Int64, %1 : $Builtin.Int64) +} + // CHECK: sil_global [serialized] @serialized_global sil_global [serialized] @serialized_global : $Int @@ -24,3 +36,19 @@ bb0: %2 = tuple () return %2 : $() } + +// CHECK: sil_global [serialized] @initialized_global_int : $Int64 = { +// CHECK-NEXT: %0 = integer_literal $Builtin.Int64, 27 +// CHECK-NEXT: %initval = struct $Int64 (%0 : $Builtin.Int64) +// CHECK-NEXT: } +sil_global [serialized] @initialized_global_int : $Int64 = { + %0 = integer_literal $Builtin.Int64, 27 + %initval = struct $Int64 (%0 : $Builtin.Int64) +} + +class GlobalObject { + @_hasStorage let e: Builtin.Int64 + +} + + diff --git a/test/SILGen/dso_handle.swift b/test/SILGen/dso_handle.swift index e7fec3557f27d..4568ea44e7d68 100644 --- a/test/SILGen/dso_handle.swift +++ b/test/SILGen/dso_handle.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle %s | %FileCheck %s -// CHECK: sil_global [[DSO:@__dso_handle|@__ImageBase]] : $Builtin.RawPointer +// CHECK: sil_global public_external [[DSO:@__dso_handle|@__ImageBase]] : $Builtin.RawPointer // CHECK-LABEL: sil [ossa] @main : $@convention(c) // CHECK: bb0 diff --git a/test/SILOptimizer/Inputs/cross-module.swift b/test/SILOptimizer/Inputs/cross-module.swift index 8c2f1f21fe9c2..2fba5c8c229c0 100644 --- a/test/SILOptimizer/Inputs/cross-module.swift +++ b/test/SILOptimizer/Inputs/cross-module.swift @@ -130,6 +130,7 @@ extension Int : PrivateProtocol { } @inline(never) +@_semantics("optimize.no.crossmodule") private func printFooExistential(_ p: PrivateProtocol) { print(p.foo()) } @@ -266,3 +267,4 @@ public func callUnrelated(_ t: T) -> T { return t } +public let globalLet = 529387 diff --git a/test/SILOptimizer/cross-module-optimization.swift b/test/SILOptimizer/cross-module-optimization.swift index 87939e20c07bd..df1689c391719 100644 --- a/test/SILOptimizer/cross-module-optimization.swift +++ b/test/SILOptimizer/cross-module-optimization.swift @@ -17,7 +17,9 @@ // Second test: check if CMO really imports the SIL of functions in other modules. -// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil | %FileCheck %s -check-prefix=CHECK-SIL +// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil -o %t/out.sil +// RUN: %FileCheck %s -check-prefix=CHECK-SIL < %t/out.sil +// RUN: %FileCheck %s -check-prefix=CHECK-SIL2 < %t/out.sil import Test @@ -54,13 +56,18 @@ func testClass() { print(createClass_gen(0)) } +// CHECK-SIL2-LABEL: sil hidden [noinline] @$s4Main9testErroryyF +@inline(never) func testError() { // CHECK-OUTPUT: PrivateError() - // CHECK-SIL-DAG: sil @$s4Test12PrivateError33_{{.*}} : $@convention(method) (@thin PrivateError.Type) -> PrivateError{{$}} + // CHECK-SIL2: struct $PrivateError () + // CHECK-SIL2: alloc_existential_box $Error, $PrivateError print(returnPrivateError(27)) // CHECK-OUTPUT: InternalError() - // CHECK-SIL-DAG: sil @$s4Test13InternalErrorVACycfC : $@convention(method) (@thin InternalError.Type) -> InternalError{{$}} + // CHECK-SIL2: struct $InternalError () + // CHECK-SIL2: alloc_existential_box $Error, $InternalError print(returnInternalError(27)) + // CHECK-SIL2: } // end sil function '$s4Main9testErroryyF' } class DerivedFromOpen : OpenClass { } @@ -123,6 +130,15 @@ func testMisc() { print(classWithPublicProperty(33)) } +// CHECK-SIL2-LABEL: sil hidden [noinline] @$s4Main10testGlobalyyF +@inline(never) +func testGlobal() { + // CHECK-OUTPUT: 529387 + // CHECK-SIL2: integer_literal $Builtin.Int{{[0-9]+}}, 529387 + print(globalLet) + // CHECK-SIL2: } // end sil function '$s4Main10testGlobalyyF' +} + testNestedTypes() testClass() testError() @@ -131,3 +147,5 @@ testSubModule() testClosures() testKeypath() testMisc() +testGlobal() + diff --git a/test/SILOptimizer/sil_combine.sil b/test/SILOptimizer/sil_combine.sil index cfb4377c29346..3cd288f74e822 100644 --- a/test/SILOptimizer/sil_combine.sil +++ b/test/SILOptimizer/sil_combine.sil @@ -3739,3 +3739,80 @@ bb0(%0 : $@thick SpecialEnum.Type): %4 = struct $Bool (%3 : $Builtin.Int1) return %4 : $Bool } + +struct IntTuple { + @_hasStorage var t: (Int64, Int64) +} + +sil_global [let] @initialized_let_global : $Int64 = { + %0 = integer_literal $Builtin.Int64, 27 + %initval = struct $Int64 (%0 : $Builtin.Int64) +} + +sil_global @initialized_var_global : $Int64 = { + %0 = integer_literal $Builtin.Int64, 27 + %initval = struct $Int64 (%0 : $Builtin.Int64) +} + +sil_global [let] @initialized_tuple_global : $IntTuple = { + %0 = integer_literal $Builtin.Int64, 27 + %1 = integer_literal $Builtin.Int64, 28 + %2 = struct $Int64 (%0 : $Builtin.Int64) + %3 = struct $Int64 (%1 : $Builtin.Int64) + %4 = tuple (%2 : $Int64, %3 : $Int64) + %initval = struct $IntTuple (%4 : $(Int64, Int64)) +} + +// CHECK-LABEL: sil @load_from_global_let +// CHECK: [[I:%.*]] = integer_literal $Builtin.Int64, 27 +// CHECK: [[R:%.*]] = struct $Int64 ([[I]] : $Builtin.Int64) +// CHECK: return [[R]] +// CHECK: } // end sil function 'load_from_global_let' +sil @load_from_global_let : $@convention(thin) () -> Int64 { +bb0: + %0 = global_addr @initialized_let_global : $*Int64 + %1 = load %0 : $*Int64 + return %1 : $Int64 +} + +// CHECK-LABEL: sil @load_from_global_with_projections +// CHECK: [[I:%.*]] = integer_literal $Builtin.Int64, 28 +// CHECK: return [[I]] +// CHECK: } // end sil function 'load_from_global_with_projections' +sil @load_from_global_with_projections : $@convention(thin) () -> Builtin.Int64 { +bb0: + %0 = global_addr @initialized_tuple_global : $*IntTuple + %1 = struct_element_addr %0 : $*IntTuple, #IntTuple.t + %2 = tuple_element_addr %1 : $*(Int64, Int64), 1 + %3 = struct_element_addr %2 : $*Int64, #Int64._value + %4 = load %3 : $*Builtin.Int64 + return %4 : $Builtin.Int64 +} + +// CHECK-LABEL: sil @load_from_global_without_projections +// CHECK-DAG: [[I1:%.*]] = integer_literal $Builtin.Int64, 27 +// CHECK-DAG: [[I2:%.*]] = integer_literal $Builtin.Int64, 28 +// CHECK-DAG: [[S1:%.*]] = struct $Int64 ([[I1]] : $Builtin.Int64) +// CHECK-DAG: [[S2:%.*]] = struct $Int64 ([[I2]] : $Builtin.Int64) +// CHECK-DAG: [[T:%.*]] = tuple ([[S1]] : $Int64, [[S2]] : $Int64) +// CHECK-DAG: [[IT:%.*]] = struct $IntTuple ([[T]] : $(Int64, Int64)) +// CHECK: return [[IT]] +// CHECK: } // end sil function 'load_from_global_without_projections' +sil @load_from_global_without_projections : $@convention(thin) () -> IntTuple { +bb0: + %0 = global_addr @initialized_tuple_global : $*IntTuple + %1 = load %0 : $*IntTuple + return %1 : $IntTuple +} + +// CHECK-LABEL: sil @load_from_global_var +// CHECK: global_addr +// CHECK-NEXT: load +// CHECK-NEXT: return +// CHECK: } // end sil function 'load_from_global_var' +sil @load_from_global_var : $@convention(thin) () -> Int64 { +bb0: + %0 = global_addr @initialized_var_global : $*Int64 + %1 = load %0 : $*Int64 + return %1 : $Int64 +}