From 16a8f7fca09a473003a97c05a872f742b1a762f3 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Fri, 27 Mar 2020 10:34:58 -0700 Subject: [PATCH 01/17] Make object instructions valid --- lib/SIL/SILVerifier.cpp | 2 +- lib/SILOptimizer/Utils/SILInliner.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index d7660d79bf4c8..4bf8cbc93a397 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -1810,7 +1810,7 @@ class SILVerifier : public SILVerifierBase { } void checkObjectInst(ObjectInst *) { - require(false, "object instruction is only allowed in a static initializer"); + require(true, "object instruction is only allowed in a static initializer"); } void checkIntegerLiteralInst(IntegerLiteralInst *ILI) { diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index cf760936987e8..8fe65bcbcf448 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -911,7 +911,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::MarkUninitializedInst: llvm_unreachable("not valid in canonical sil"); case SILInstructionKind::ObjectInst: - llvm_unreachable("not valid in a function"); + return InlineCost::Free; } llvm_unreachable("Unhandled ValueKind in switch."); From 70f76e98e47714dbc6c82def52ca44244b3ad317 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sat, 28 Mar 2020 10:24:45 -0700 Subject: [PATCH 02/17] Add IRGen for ObjectInst --- lib/IRGen/IRGenSIL.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index c3b2fd60cc206..23ee133c4cafa 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -877,9 +877,7 @@ class IRGenSILFunction : void visitDestroyValueInst(DestroyValueInst *i); void visitAutoreleaseValueInst(AutoreleaseValueInst *i); void visitSetDeallocatingInst(SetDeallocatingInst *i); - void visitObjectInst(ObjectInst *i) { - llvm_unreachable("object instruction cannot appear in a function"); - } + void visitObjectInst(ObjectInst *i); void visitStructInst(StructInst *i); void visitTupleInst(TupleInst *i); void visitEnumInst(EnumInst *i); @@ -3450,6 +3448,13 @@ void IRGenSILFunction::visitDestroyValueInst(swift::DestroyValueInst *i) { .consume(*this, in, getDefaultAtomicity()); } +void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { + Explosion out; + for (SILValue elt : i->getAllElements()) + out.add(getLoweredExplosion(elt).claimAll()); + setLoweredExplosion(i, out); +} + void IRGenSILFunction::visitStructInst(swift::StructInst *i) { Explosion out; for (SILValue elt : i->getElements()) From 60f4cdf93e98ac85c5eee97c485760c6f266c7fd Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 29 Mar 2020 12:18:25 -0700 Subject: [PATCH 03/17] Fix IRGen and add 'real' stack promotion --- lib/IRGen/FixedTypeInfo.h | 2 + lib/IRGen/GenClass.cpp | 18 +++ lib/IRGen/GenInit.cpp | 7 +- lib/IRGen/GenType.cpp | 22 +++ lib/IRGen/IRGenSIL.cpp | 13 +- lib/SIL/SILVerifier.cpp | 2 +- .../Transforms/DeadStoreElimination.cpp | 3 + .../Transforms/StackPromotion.cpp | 150 +++++++++++++++++- 8 files changed, 210 insertions(+), 7 deletions(-) diff --git a/lib/IRGen/FixedTypeInfo.h b/lib/IRGen/FixedTypeInfo.h index 21db44bd926a0..3812f7a80b4d0 100644 --- a/lib/IRGen/FixedTypeInfo.h +++ b/lib/IRGen/FixedTypeInfo.h @@ -84,6 +84,8 @@ class FixedTypeInfo : public TypeInfo { // We can give these reasonable default implementations. + void initialize(IRGenFunction &IGF, Address dest, Address src, + IsTake_t isTake, SILType T, bool isOutlined) const; void initializeWithTake(IRGenFunction &IGF, Address destAddr, Address srcAddr, SILType T, bool isOutlined) const override; diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 6e371750f7465..5079650130194 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -102,6 +102,10 @@ namespace { StructLayout *createLayoutWithTailElems(IRGenModule &IGM, SILType classType, ArrayRef tailTypes) const; + + using HeapTypeInfo::initialize; + void initialize(IRGenFunction &IGF, Explosion &e, Address addr, + bool isOutlined) const override; }; } // end anonymous namespace @@ -483,6 +487,20 @@ ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType, return *Layout; } +void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, + bool isOutlined) const { + auto classType = IGF.IGM.getLoweredType(getClass()->TypeDecl::getDeclaredInterfaceType()); + for (auto *prop : getClass()->getStoredProperties()) { + auto propType = IGF.IGM.getLoweredType(prop->getType()); + const auto &propTypeInfo = cast(IGF.getTypeInfo(propType)); + auto propAddr = projectPhysicalClassMemberAddress(IGF, addr.getAddress(), classType, propType, prop); + Explosion propExplosion; + propExplosion.add(src.claimNext()); + propTypeInfo.initialize(IGF, propExplosion, propAddr, isOutlined); + } +} + + /// Cast the base to i8*, apply the given inbounds offset (in bytes, /// as a size_t), and cast to a pointer to the given type. llvm::Value *IRGenFunction::emitByteOffsetGEP(llvm::Value *base, diff --git a/lib/IRGen/GenInit.cpp b/lib/IRGen/GenInit.cpp index a0f01daf3ec72..02d72f4d67687 100644 --- a/lib/IRGen/GenInit.cpp +++ b/lib/IRGen/GenInit.cpp @@ -65,8 +65,13 @@ StackAddress FixedTypeInfo::allocateStack(IRGenFunction &IGF, SILType T, return { addr }; } + auto *type = getStorageType(); + // Classes will return a pointer type but we want the element type. + if (T.getClassOrBoundGenericClass()) + type = type->getPointerElementType(); + Address alloca = - IGF.createAlloca(getStorageType(), getFixedAlignment(), name); + IGF.createAlloca(type, getFixedAlignment(), name); IGF.Builder.CreateLifetimeStart(alloca, getFixedSize()); return { alloca }; diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 8fafe77ade077..349d2dc959f16 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -141,6 +141,28 @@ TypeInfo::nativeParameterValueSchema(IRGenModule &IGM) const { return *nativeParameterSchema; } +void FixedTypeInfo::initialize(IRGenFunction &IGF, Address dest, Address src, + IsTake_t isTake, SILType T, bool isOutlined) const { + if (isTake == IsTake) { + initializeWithTake(IGF, dest, src, T, isOutlined); + return; + } + + // Prefer loads and stores if we won't make a million of them. + // Maybe this should also require the scalars to have a fixed offset. + ExplosionSchema schema = getSchema(); + if (!schema.containsAggregate() && schema.size() <= 4) { + auto &loadableTI = cast(*this); + Explosion copy; + loadableTI.loadAsCopy(IGF, src, copy); + loadableTI.initialize(IGF, copy, dest, isOutlined); + return; + } + + // Otherwise, use a memcpy. + IGF.emitMemCpy(dest, src, getFixedSize()); +} + /// Copy a value from one object to a new object, directly taking /// responsibility for anything it might have. This is like C++ /// move-initialization, except the old object will not be destroyed. diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 23ee133c4cafa..ac4f0cbc1087a 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3560,10 +3560,18 @@ void IRGenSILFunction::visitStructElementAddrInst( } void IRGenSILFunction::visitRefElementAddrInst(swift::RefElementAddrInst *i) { + SILType baseTy = i->getOperand()->getType(); + + if (i->getOperand()->getType().isAddress()) { + Address base = getLoweredAddress(i->getOperand()); + auto fieldAddr = projectPhysicalClassMemberAddress(*this, base.getAddress(), baseTy, i->getType(), i->getField()); + setLoweredAddress(i, fieldAddr.getAddress()); + return; + } + Explosion base = getLoweredExplosion(i->getOperand()); llvm::Value *value = base.claimNext(); - SILType baseTy = i->getOperand()->getType(); Address field = projectPhysicalClassMemberAddress(*this, value, baseTy, i->getType(), i->getField()) .getAddress(); @@ -3626,6 +3634,9 @@ void IRGenSILFunction::visitStoreInst(swift::StoreInst *i) { SILType objType = i->getSrc()->getType().getObjectType(); const auto &typeInfo = cast(getTypeInfo(objType)); + if (objType.getClassOrBoundGenericClass()) { + // projectPhysicalClassMemberAddress + } switch (i->getOwnershipQualifier()) { case StoreOwnershipQualifier::Unqualified: case StoreOwnershipQualifier::Init: diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 4bf8cbc93a397..4e501fd9ca1de 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -2805,7 +2805,7 @@ class SILVerifier : public SILVerifierBase { } void checkRefElementAddrInst(RefElementAddrInst *EI) { - requireReferenceValue(EI->getOperand(), "Operand of ref_element_addr"); + // requireReferenceValue(EI->getOperand(), "Operand of ref_element_addr"); require(EI->getType().isAddress(), "result of ref_element_addr must be lvalue"); require(!EI->getField()->isStatic(), diff --git a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp index 266859152516a..e371e55ae7a3f 100644 --- a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp @@ -1038,6 +1038,9 @@ void DSEContext::processLoadInst(SILInstruction *I, DSEKind Kind) { void DSEContext::processStoreInst(SILInstruction *I, DSEKind Kind) { auto *SI = cast(I); + // TODO: for some reason these stores are removed when they shouldn't be. + if (isa(SI->getSrc())) + return; processWrite(I, SI->getSrc(), SI->getDest(), Kind); } diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index a7579edc39094..13560fcda0266 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -12,6 +12,7 @@ #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/CFG.h" +#include "swift/SIL/DebugUtils.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SILOptimizer/Analysis/EscapeAnalysis.h" @@ -51,6 +52,13 @@ class StackPromotion : public SILFunctionTransform { /// Tries to promote the allocation \p ARI. bool tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, DeadEndBlocks &DEBlocks); + + /// Tries to promote the allocation \p ARI to an object. This optimization will only happen if the class type + /// has a compiler-generated constructor and destructor. The promotion happens by scanning all uses in + /// dominance order. If all members are accounted for by ref_element_addr instruction before we find any + /// other use, then we can use those values to promote this alloc_ref to an object. + bool tryPromoteToObject(AllocRefInst *allocRef, + ValueLifetimeAnalysis::Frontier &frontier); }; void StackPromotion::run() { @@ -84,10 +92,11 @@ void StackPromotion::run() { bool StackPromotion::promoteInBlock(SILBasicBlock *BB, EscapeAnalysis *EA, DeadEndBlocks &DEBlocks) { bool Changed = false; - for (auto Iter = BB->begin(); Iter != BB->end();) { - // The allocation instruction may be moved, so increment Iter prior to - // doing the optimization. - SILInstruction *I = &*Iter++; + SmallVector allInstructions; + for (SILInstruction &inst : *BB) { + allInstructions.push_back(&inst); + } + for (auto *I : allInstructions) { if (auto *ARI = dyn_cast(I)) { // Don't stack promote any allocation inside a code region which ends up // in a no-return block. Such allocations may missing their final release. @@ -141,6 +150,9 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, return false; } NumStackPromoted++; + + if (tryPromoteToObject(ARI, Frontier)) + return true; // We set the [stack] attribute in the alloc_ref. ARI->setStackAllocatable(); @@ -153,6 +165,136 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, return true; } +static void getOrderedNonDebugUses(SILValue v, DominanceInfo *domInfo, + SmallVectorImpl &uses) { + auto unsorted = getNonDebugUses(v); + uses.append(unsorted.begin(), unsorted.end()); + llvm::sort(uses, [&domInfo](Operand *a, Operand *b) { + return domInfo->dominates(a->getUser(), b->getUser()); + }); +} + +bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, + ValueLifetimeAnalysis::Frontier &frontier) { + DominanceInfo *domInfo = PM->getAnalysis()->get(allocRef->getFunction()); + auto *classDecl = allocRef->getType().getClassOrBoundGenericClass(); + if (!classDecl || !classDecl->getDestructor()->isImplicit()) + return false; + + SmallVector uses; + getOrderedNonDebugUses(allocRef, domInfo, uses); + if (llvm::any_of(uses, [](Operand *use) { + auto user = use->getUser(); + return !isa(user) && !isa(user) && !isa(user) && !isa(user) && !isa(user); + })) + return false; + + SmallVector props; + for (auto *prop : classDecl->getStoredProperties()) { + props.push_back(prop); + } + + llvm::reverse(props); + SmallVector propertyInitializers; + for (auto *use : uses) { + auto propRef = dyn_cast(use->getUser()); + if (!propRef) + return false; + + if (propRef->getField() != props.pop_back_val()) + return false; + + propertyInitializers.push_back(propRef); + + if (props.empty()) + break; + } + + SmallVector deadStores; + SmallVector elements; + for (auto *init : propertyInitializers) { + SmallVector refElementUses; + getOrderedNonDebugUses(init, domInfo, refElementUses); + auto frontUser = refElementUses.front()->getUser(); + if (auto *beginAccess = dyn_cast(frontUser)) { + SmallVector beginAccessUses; + getOrderedNonDebugUses(beginAccess, domInfo, beginAccessUses); + frontUser = beginAccessUses.front()->getUser(); + } + if (auto *store = dyn_cast(frontUser)) { + elements.push_back(store->getSrc()); + deadStores.push_back(store); + } else { + return false; + } + } + + SILInstruction *lastElement = nullptr; + for (auto first = elements.rbegin(); first != elements.rend(); ++first) { + auto inst = first->getDefiningInstruction(); + if (!inst) + continue; + + if (!lastElement || domInfo->dominates(lastElement, inst)) + lastElement = inst; + } + + // If we didn't find anything, that means that all the elements are arguments, + // or there aren't any elements. Either way, we know that putting where the + // alloc_ref is will work. + if (!lastElement) + lastElement = allocRef; + + for (auto *init : propertyInitializers) { + if (llvm::any_of(init->getUses(), [&domInfo, &lastElement](Operand *use) { + return domInfo->dominates(use->getUser(), lastElement); + })) + return false; + } + + SILBuilder builder(std::next(lastElement->getIterator())); + auto object = builder.createObject(allocRef->getLoc(), allocRef->getType(), + elements, elements.size()); + auto allocStack = builder.createAllocStack(allocRef->getLoc(), allocRef->getType()); + builder.createStore(allocRef->getLoc(), object, allocStack, + StoreOwnershipQualifier::Unqualified); + + SmallVector users(allocRef->use_begin(), allocRef->use_end()); + for (auto *use : users) { + auto user = use->getUser(); + + // The only user that still may not dominate object is a debug_value. + if (isa(user) || isa(user) || + isa(user) || isa(user)) + user->eraseFromParent(); + + if (auto ref = dyn_cast(user)) { + // We need to move the ref_element_addr up. + if (domInfo->dominates(ref, allocStack)) { + auto newRef = builder.createRefElementAddr(ref->getLoc(), allocStack, + ref->getField()); + ref->replaceAllUsesWith(newRef); + ref->eraseFromParent(); + } + } + } + + for (auto *store : deadStores) { + store->eraseFromParent(); + } + + for (SILInstruction *frontierInst : frontier) { + SILBuilderWithScope destroyAddrBuilder(frontierInst); + destroyAddrBuilder.createDestroyAddr(allocStack->getLoc(), allocStack); + destroyAddrBuilder.createDeallocStack(allocStack->getLoc(), allocStack); + } + + allocRef->replaceAllUsesWith(allocStack); + allocRef->eraseFromParent(); + + return true; +} + } // end anonymous namespace SILTransform *swift::createStackPromotion() { From 7c97582da0e420b2f35a673640d4014b1e3a579e Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 29 Mar 2020 13:10:34 -0700 Subject: [PATCH 04/17] Use old behavior when we have a pointer type in class init --- lib/IRGen/GenClass.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 5079650130194..b9809da9dd099 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -491,11 +491,19 @@ void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, bool isOutlined) const { auto classType = IGF.IGM.getLoweredType(getClass()->TypeDecl::getDeclaredInterfaceType()); for (auto *prop : getClass()->getStoredProperties()) { + auto *exploded = src.claimNext(); + // If the src is an address, just project and store. Then we're done. + if (exploded->getType()->isPointerTy()) { + addr = asDerived().projectScalar(IGF, addr); + IGF.Builder.CreateStore(exploded, addr); + return; + } + // Otherwise, create GEP/init for each element in src. auto propType = IGF.IGM.getLoweredType(prop->getType()); const auto &propTypeInfo = cast(IGF.getTypeInfo(propType)); auto propAddr = projectPhysicalClassMemberAddress(IGF, addr.getAddress(), classType, propType, prop); Explosion propExplosion; - propExplosion.add(src.claimNext()); + propExplosion.add(exploded); propTypeInfo.initialize(IGF, propExplosion, propAddr, isOutlined); } } From 4372e8066b9256b9eec6d32515d8eb8899d4681b Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 29 Mar 2020 13:11:20 -0700 Subject: [PATCH 05/17] Remove unneeded change to fixed type init --- lib/IRGen/FixedTypeInfo.h | 2 -- lib/IRGen/GenType.cpp | 22 ---------------------- 2 files changed, 24 deletions(-) diff --git a/lib/IRGen/FixedTypeInfo.h b/lib/IRGen/FixedTypeInfo.h index 3812f7a80b4d0..21db44bd926a0 100644 --- a/lib/IRGen/FixedTypeInfo.h +++ b/lib/IRGen/FixedTypeInfo.h @@ -84,8 +84,6 @@ class FixedTypeInfo : public TypeInfo { // We can give these reasonable default implementations. - void initialize(IRGenFunction &IGF, Address dest, Address src, - IsTake_t isTake, SILType T, bool isOutlined) const; void initializeWithTake(IRGenFunction &IGF, Address destAddr, Address srcAddr, SILType T, bool isOutlined) const override; diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 349d2dc959f16..8fafe77ade077 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -141,28 +141,6 @@ TypeInfo::nativeParameterValueSchema(IRGenModule &IGM) const { return *nativeParameterSchema; } -void FixedTypeInfo::initialize(IRGenFunction &IGF, Address dest, Address src, - IsTake_t isTake, SILType T, bool isOutlined) const { - if (isTake == IsTake) { - initializeWithTake(IGF, dest, src, T, isOutlined); - return; - } - - // Prefer loads and stores if we won't make a million of them. - // Maybe this should also require the scalars to have a fixed offset. - ExplosionSchema schema = getSchema(); - if (!schema.containsAggregate() && schema.size() <= 4) { - auto &loadableTI = cast(*this); - Explosion copy; - loadableTI.loadAsCopy(IGF, src, copy); - loadableTI.initialize(IGF, copy, dest, isOutlined); - return; - } - - // Otherwise, use a memcpy. - IGF.emitMemCpy(dest, src, getFixedSize()); -} - /// Copy a value from one object to a new object, directly taking /// responsibility for anything it might have. This is like C++ /// move-initialization, except the old object will not be destroyed. From 2a5aa37a3c819993c745ece8890125fc566cbe17 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sun, 29 Mar 2020 14:28:15 -0700 Subject: [PATCH 06/17] Fix init of class pointer --- lib/IRGen/GenClass.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index b9809da9dd099..9140a92dc50c7 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -489,16 +489,18 @@ ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType, void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, bool isOutlined) const { - auto classType = IGF.IGM.getLoweredType(getClass()->TypeDecl::getDeclaredInterfaceType()); + // If the src is an address, just project and store. Then we're done. + auto *exploded = src.getAll().front(); + if (src.size() == 1 && exploded->getType() == addr->getType()->getPointerElementType()) { + IGF.Builder.CreateStore(exploded, addr); + src.reset(); + return; + } + + // Otherwise, create GEP/init for each element in src. + auto classType = IGF.IGM.getLoweredType(getClass()->getDeclaredInterfaceType()); for (auto *prop : getClass()->getStoredProperties()) { - auto *exploded = src.claimNext(); - // If the src is an address, just project and store. Then we're done. - if (exploded->getType()->isPointerTy()) { - addr = asDerived().projectScalar(IGF, addr); - IGF.Builder.CreateStore(exploded, addr); - return; - } - // Otherwise, create GEP/init for each element in src. + exploded = src.claimNext(); auto propType = IGF.IGM.getLoweredType(prop->getType()); const auto &propTypeInfo = cast(IGF.getTypeInfo(propType)); auto propAddr = projectPhysicalClassMemberAddress(IGF, addr.getAddress(), classType, propType, prop); From c1ade18e794702f60bc3d6b4ef4e63da27510e5b Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 30 Mar 2020 11:27:23 -0700 Subject: [PATCH 07/17] Use double pointer for class stack_alloc to maintian compatibility with current behavior --- lib/IRGen/GenClass.cpp | 20 ++++++++++++--- lib/IRGen/GenInit.cpp | 2 +- lib/IRGen/IRGenSIL.cpp | 25 +++++++++++++++++-- .../Transforms/StackPromotion.cpp | 1 + 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 9140a92dc50c7..e4336067bd0c3 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -491,19 +491,31 @@ void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, bool isOutlined) const { // If the src is an address, just project and store. Then we're done. auto *exploded = src.getAll().front(); - if (src.size() == 1 && exploded->getType() == addr->getType()->getPointerElementType()) { + if (exploded->getType() == addr->getType()->getPointerElementType()) { IGF.Builder.CreateStore(exploded, addr); - src.reset(); + (void)src.claimNext(); + return; + } + + auto classType = IGF.IGM.getLoweredType(getClass()->getDeclaredInterfaceType()); + if (exploded->getType() == addr->getType()) { + // `HeapTypeInfo::initialize(IGF, src, addr, isOutlined); + IGF.emitMemCpy(addr.getAddress(), exploded, getFixedSize(), addr.getAlignment()); + (void)src.claimNext(); +// cast(this)->initializeWithCopy(IGF, addr, Address(exploded, addr.getAlignment()), classType, isOutlined); return; } // Otherwise, create GEP/init for each element in src. - auto classType = IGF.IGM.getLoweredType(getClass()->getDeclaredInterfaceType()); for (auto *prop : getClass()->getStoredProperties()) { exploded = src.claimNext(); auto propType = IGF.IGM.getLoweredType(prop->getType()); const auto &propTypeInfo = cast(IGF.getTypeInfo(propType)); - auto propAddr = projectPhysicalClassMemberAddress(IGF, addr.getAddress(), classType, propType, prop); + auto offset = getClassFieldOffset(IGF.IGM, classType, prop); + auto propAddr = IGF.emitByteOffsetGEP(addr.getAddress(), + IGF.IGM.getInt32(offset.getValue()), + propTypeInfo); + // auto propAddr = projectPhysicalClassMemberAddress(IGF, addr.getAddress(), classType, propType, prop); Explosion propExplosion; propExplosion.add(exploded); propTypeInfo.initialize(IGF, propExplosion, propAddr, isOutlined); diff --git a/lib/IRGen/GenInit.cpp b/lib/IRGen/GenInit.cpp index 02d72f4d67687..7ae4e76fab9ab 100644 --- a/lib/IRGen/GenInit.cpp +++ b/lib/IRGen/GenInit.cpp @@ -67,7 +67,7 @@ StackAddress FixedTypeInfo::allocateStack(IRGenFunction &IGF, SILType T, auto *type = getStorageType(); // Classes will return a pointer type but we want the element type. - if (T.getClassOrBoundGenericClass()) + if (T.getClassOrBoundGenericClass() && false) type = type->getPointerElementType(); Address alloca = diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index ac4f0cbc1087a..83c33c2e8312d 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3452,7 +3452,19 @@ void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { Explosion out; for (SILValue elt : i->getAllElements()) out.add(getLoweredExplosion(elt).claimAll()); - setLoweredExplosion(i, out); + + SILType objType = i->getType().getObjectType(); + const auto &typeInfo = cast(getTypeInfo(objType)); + // TODO: this shouldn't be a stack alloc but, this is the easiest way to + // maintain compatibility with alloc_ref. + Address alloca = createAlloca(typeInfo.getStorageType()->getPointerElementType(), + typeInfo.getFixedAlignment()); + Builder.CreateLifetimeStart(alloca, typeInfo.getFixedSize()); + typeInfo.initialize(*this, out, alloca, false); + + Explosion e; + e.add(alloca.getAddress()); + setLoweredExplosion(i, e); } void IRGenSILFunction::visitStructInst(swift::StructInst *i) { @@ -3564,7 +3576,16 @@ void IRGenSILFunction::visitRefElementAddrInst(swift::RefElementAddrInst *i) { if (i->getOperand()->getType().isAddress()) { Address base = getLoweredAddress(i->getOperand()); - auto fieldAddr = projectPhysicalClassMemberAddress(*this, base.getAddress(), baseTy, i->getType(), i->getField()); + // + // assert(getIndexedTypeX(IGM.Int8PtrPtrTy, { IGM.getInt32(0), IGM.getInt32(0) })); + // auto bytePtr = Builder.CreateBitCast(base.getAddress(), base->getType()->getPointerElementType()); +// auto newAddr = Builder.CreateInBoundsGEP(base.getAddress(), { IGM.getInt32(0), IGM.getInt32(0), IGM.getInt32(0) }); +// auto newAddr = emitByteOffsetGEP(base.getAddress(), IGM.getInt32(0), base->getType()->getPointerElementType()->getPointerElementType()); + // newAddr = Builder.CreateGEP(newAddr, { IGM.getInt32(0), IGM.getInt32(0) }); + // newAddr = Builder.CreateBitCast(newAddr, base->getType()->getPointerElementType()); + // For dumb reasons, if we get an address here it means we're taking the address of the element of a class in a stack alloc which means we're taking the address of an element of a class double pointer. It's OK to load the first pointer because the pointer we load will still allow us to have a reference to the element. + auto classAddr = Builder.CreateLoad(base); + auto fieldAddr = projectPhysicalClassMemberAddress(*this, classAddr, baseTy, i->getType(), i->getField()); setLoweredAddress(i, fieldAddr.getAddress()); return; } diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index 13560fcda0266..d20dc763fce4d 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -116,6 +116,7 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, if (ARI->isObjC() || ARI->canAllocOnStack()) return false; + EA->invalidate(ARI->getFunction(), swift::AliasAnalysis::InvalidationKind::Everything); auto *ConGraph = EA->getConnectionGraph(ARI->getFunction()); auto *contentNode = ConGraph->getValueContent(ARI); if (!contentNode) From 2a0b0df9040303f30512a58ca1c9f560fe12a1f6 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 30 Mar 2020 14:29:05 -0700 Subject: [PATCH 08/17] Manually initialize each member in visitObjectInst --- lib/IRGen/GenClass.cpp | 16 +++++++++++----- lib/IRGen/IRGenSIL.cpp | 16 +++++++++++----- lib/SILOptimizer/Transforms/StackPromotion.cpp | 5 +++++ 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index e4336067bd0c3..01d48dde57164 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -489,6 +489,9 @@ ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType, void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, bool isOutlined) const { + HeapTypeInfo::initialize(IGF, src, addr, isOutlined); + return; + // If the src is an address, just project and store. Then we're done. auto *exploded = src.getAll().front(); if (exploded->getType() == addr->getType()->getPointerElementType()) { @@ -511,13 +514,16 @@ void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, exploded = src.claimNext(); auto propType = IGF.IGM.getLoweredType(prop->getType()); const auto &propTypeInfo = cast(IGF.getTypeInfo(propType)); - auto offset = getClassFieldOffset(IGF.IGM, classType, prop); - auto propAddr = IGF.emitByteOffsetGEP(addr.getAddress(), - IGF.IGM.getInt32(offset.getValue()), - propTypeInfo); - // auto propAddr = projectPhysicalClassMemberAddress(IGF, addr.getAddress(), classType, propType, prop); +// auto offset = getClassFieldOffset(IGF.IGM, classType, prop); +// auto propAddr = IGF.emitByteOffsetGEP(addr.getAddress(), +// IGF.IGM.getInt32(offset.getValue()), +// propTypeInfo); + auto propAddr = projectPhysicalClassMemberAddress(IGF, addr.getAddress(), classType, propType, prop); Explosion propExplosion; propExplosion.add(exploded); + llvm::errs() << "prop explo and addr respectfully: \n"; + propExplosion.dump(); + propAddr.getAddress()->dump(); propTypeInfo.initialize(IGF, propExplosion, propAddr, isOutlined); } } diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 83c33c2e8312d..76d18194c268b 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3449,10 +3449,6 @@ void IRGenSILFunction::visitDestroyValueInst(swift::DestroyValueInst *i) { } void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { - Explosion out; - for (SILValue elt : i->getAllElements()) - out.add(getLoweredExplosion(elt).claimAll()); - SILType objType = i->getType().getObjectType(); const auto &typeInfo = cast(getTypeInfo(objType)); // TODO: this shouldn't be a stack alloc but, this is the easiest way to @@ -3460,8 +3456,18 @@ void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { Address alloca = createAlloca(typeInfo.getStorageType()->getPointerElementType(), typeInfo.getFixedAlignment()); Builder.CreateLifetimeStart(alloca, typeInfo.getFixedSize()); - typeInfo.initialize(*this, out, alloca, false); + auto propsArr = i->getType().getClassOrBoundGenericClass()->getStoredProperties(); + SmallVector props(propsArr.begin(), propsArr.end()); + for (SILValue elt : i->getAllElements()) { + auto prop = props.pop_back_val(); + auto elementExplosion = getLoweredExplosion(elt); + auto propType = IGM.getLoweredType(prop->getType()); + const auto &propTypeInfo = cast(getTypeInfo(propType)); + auto propAddr = projectPhysicalClassMemberAddress(*this, alloca.getAddress(), objType, propType, prop); + propTypeInfo.initialize(*this, elementExplosion, propAddr, false); + } + Explosion e; e.add(alloca.getAddress()); setLoweredExplosion(i, e); diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index d20dc763fce4d..17fa87a9c4c9c 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -211,6 +211,9 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, break; } + if (!props.empty()) + return false; + SmallVector deadStores; SmallVector elements; for (auto *init : propertyInitializers) { @@ -292,6 +295,8 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, allocRef->replaceAllUsesWith(allocStack); allocRef->eraseFromParent(); + + llvm::errs() << "Promoted to object in stack. Function: " << object->getFunction()->getName() << "\n"; return true; } From 419960d4f9664d1d678e201d67580bc52cf81836 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 30 Mar 2020 16:08:54 -0700 Subject: [PATCH 09/17] Init the class on stack --- lib/IRGen/IRGenSIL.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 76d18194c268b..dabeff5153b7d 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3451,11 +3451,16 @@ void IRGenSILFunction::visitDestroyValueInst(swift::DestroyValueInst *i) { void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { SILType objType = i->getType().getObjectType(); const auto &typeInfo = cast(getTypeInfo(objType)); + llvm::Value *metadata = emitClassHeapMetadataRef(*this, objType.getASTType(), + MetadataValueType::TypeMetadata, + MetadataState::Complete); // TODO: this shouldn't be a stack alloc but, this is the easiest way to // maintain compatibility with alloc_ref. Address alloca = createAlloca(typeInfo.getStorageType()->getPointerElementType(), typeInfo.getFixedAlignment()); - Builder.CreateLifetimeStart(alloca, typeInfo.getFixedSize()); + auto classAddr = Builder.CreateBitCast(alloca, IGM.RefCountedPtrTy); + auto classVal = emitInitStackObjectCall(metadata, classAddr.getAddress(), "reference.new"); + classVal = Builder.CreateBitCast(classVal, typeInfo.getStorageType()); auto propsArr = i->getType().getClassOrBoundGenericClass()->getStoredProperties(); SmallVector props(propsArr.begin(), propsArr.end()); @@ -3464,12 +3469,12 @@ void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { auto elementExplosion = getLoweredExplosion(elt); auto propType = IGM.getLoweredType(prop->getType()); const auto &propTypeInfo = cast(getTypeInfo(propType)); - auto propAddr = projectPhysicalClassMemberAddress(*this, alloca.getAddress(), objType, propType, prop); + auto propAddr = projectPhysicalClassMemberAddress(*this, classVal, objType, propType, prop); propTypeInfo.initialize(*this, elementExplosion, propAddr, false); } Explosion e; - e.add(alloca.getAddress()); + e.add(classVal); setLoweredExplosion(i, e); } From 59947bce2cee6b187ee64fd998fdfce33e4008b4 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 30 Mar 2020 22:38:26 -0700 Subject: [PATCH 10/17] Bail on generic classes --- lib/IRGen/GenClass.cpp | 8 ++--- lib/IRGen/IRGenSIL.cpp | 3 ++ .../Transforms/StackPromotion.cpp | 29 +++++-------------- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 01d48dde57164..30649af490657 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -489,10 +489,6 @@ ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType, void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, bool isOutlined) const { - HeapTypeInfo::initialize(IGF, src, addr, isOutlined); - return; - - // If the src is an address, just project and store. Then we're done. auto *exploded = src.getAll().front(); if (exploded->getType() == addr->getType()->getPointerElementType()) { IGF.Builder.CreateStore(exploded, addr); @@ -500,6 +496,7 @@ void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, return; } + // If the src is an address, just project and store. Then we're done. auto classType = IGF.IGM.getLoweredType(getClass()->getDeclaredInterfaceType()); if (exploded->getType() == addr->getType()) { // `HeapTypeInfo::initialize(IGF, src, addr, isOutlined); @@ -509,6 +506,9 @@ void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, return; } + HeapTypeInfo::initialize(IGF, src, addr, isOutlined); + return; + // Otherwise, create GEP/init for each element in src. for (auto *prop : getClass()->getStoredProperties()) { exploded = src.claimNext(); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index dabeff5153b7d..c8b7ffd18e858 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3450,6 +3450,8 @@ void IRGenSILFunction::visitDestroyValueInst(swift::DestroyValueInst *i) { void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { SILType objType = i->getType().getObjectType(); + // TODO: we could support this in the future. + assert(!objType.getClassOrBoundGenericClass()->getAsGenericContext() || !objType.getClassOrBoundGenericClass()->getAsGenericContext()->isGeneric() && "Generics are not yet supported"); const auto &typeInfo = cast(getTypeInfo(objType)); llvm::Value *metadata = emitClassHeapMetadataRef(*this, objType.getASTType(), MetadataValueType::TypeMetadata, @@ -3586,6 +3588,7 @@ void IRGenSILFunction::visitRefElementAddrInst(swift::RefElementAddrInst *i) { SILType baseTy = i->getOperand()->getType(); if (i->getOperand()->getType().isAddress()) { + assert(false); Address base = getLoweredAddress(i->getOperand()); // // assert(getIndexedTypeX(IGM.Int8PtrPtrTy, { IGM.getInt32(0), IGM.getInt32(0) })); diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index 17fa87a9c4c9c..1441eacf2c910 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -179,15 +179,8 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, ValueLifetimeAnalysis::Frontier &frontier) { DominanceInfo *domInfo = PM->getAnalysis()->get(allocRef->getFunction()); auto *classDecl = allocRef->getType().getClassOrBoundGenericClass(); - if (!classDecl || !classDecl->getDestructor()->isImplicit()) - return false; - - SmallVector uses; - getOrderedNonDebugUses(allocRef, domInfo, uses); - if (llvm::any_of(uses, [](Operand *use) { - auto user = use->getUser(); - return !isa(user) && !isa(user) && !isa(user) && !isa(user) && !isa(user); - })) + if (!classDecl || !classDecl->getDestructor()->isImplicit() || + (classDecl->getAsGenericContext() && classDecl->getAsGenericContext()->isGeneric())) return false; SmallVector props; @@ -195,6 +188,9 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, props.push_back(prop); } + SmallVector uses; + getOrderedNonDebugUses(allocRef, domInfo, uses); + llvm::reverse(props); SmallVector propertyInitializers; for (auto *use : uses) { @@ -259,9 +255,6 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, SILBuilder builder(std::next(lastElement->getIterator())); auto object = builder.createObject(allocRef->getLoc(), allocRef->getType(), elements, elements.size()); - auto allocStack = builder.createAllocStack(allocRef->getLoc(), allocRef->getType()); - builder.createStore(allocRef->getLoc(), object, allocStack, - StoreOwnershipQualifier::Unqualified); SmallVector users(allocRef->use_begin(), allocRef->use_end()); for (auto *use : users) { @@ -274,8 +267,8 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, if (auto ref = dyn_cast(user)) { // We need to move the ref_element_addr up. - if (domInfo->dominates(ref, allocStack)) { - auto newRef = builder.createRefElementAddr(ref->getLoc(), allocStack, + if (domInfo->dominates(ref, object)) { + auto newRef = builder.createRefElementAddr(ref->getLoc(), object, ref->getField()); ref->replaceAllUsesWith(newRef); ref->eraseFromParent(); @@ -286,14 +279,8 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, for (auto *store : deadStores) { store->eraseFromParent(); } - - for (SILInstruction *frontierInst : frontier) { - SILBuilderWithScope destroyAddrBuilder(frontierInst); - destroyAddrBuilder.createDestroyAddr(allocStack->getLoc(), allocStack); - destroyAddrBuilder.createDeallocStack(allocStack->getLoc(), allocStack); - } - allocRef->replaceAllUsesWith(allocStack); + allocRef->replaceAllUsesWith(object); allocRef->eraseFromParent(); llvm::errs() << "Promoted to object in stack. Function: " << object->getFunction()->getName() << "\n"; From 8ace31172e4c59833c98da9254cf350383f2444d Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 31 Mar 2020 09:50:38 -0700 Subject: [PATCH 11/17] Cleanup --- lib/IRGen/GenClass.cpp | 31 ++++++------------------------- lib/IRGen/GenInit.cpp | 7 +------ lib/IRGen/IRGenSIL.cpp | 34 ++++++++-------------------------- lib/SIL/SILVerifier.cpp | 2 +- 4 files changed, 16 insertions(+), 58 deletions(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 30649af490657..53dbc47a7f967 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -489,6 +489,8 @@ ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType, void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, bool isOutlined) const { + // If the address is a poitner to the exploded type then we can simply emit a + // store and be done. auto *exploded = src.getAll().front(); if (exploded->getType() == addr->getType()->getPointerElementType()) { IGF.Builder.CreateStore(exploded, addr); @@ -496,36 +498,15 @@ void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, return; } - // If the src is an address, just project and store. Then we're done. - auto classType = IGF.IGM.getLoweredType(getClass()->getDeclaredInterfaceType()); + // If both are the same (address) type then just emit a memcpy. if (exploded->getType() == addr->getType()) { - // `HeapTypeInfo::initialize(IGF, src, addr, isOutlined); - IGF.emitMemCpy(addr.getAddress(), exploded, getFixedSize(), addr.getAlignment()); - (void)src.claimNext(); -// cast(this)->initializeWithCopy(IGF, addr, Address(exploded, addr.getAlignment()), classType, isOutlined); + IGF.emitMemCpy(addr.getAddress(), exploded, getFixedSize(), addr.getAlignment()); + (void)src.claimNext(); return; } + // Otherwise, bail to the default implementation. HeapTypeInfo::initialize(IGF, src, addr, isOutlined); - return; - - // Otherwise, create GEP/init for each element in src. - for (auto *prop : getClass()->getStoredProperties()) { - exploded = src.claimNext(); - auto propType = IGF.IGM.getLoweredType(prop->getType()); - const auto &propTypeInfo = cast(IGF.getTypeInfo(propType)); -// auto offset = getClassFieldOffset(IGF.IGM, classType, prop); -// auto propAddr = IGF.emitByteOffsetGEP(addr.getAddress(), -// IGF.IGM.getInt32(offset.getValue()), -// propTypeInfo); - auto propAddr = projectPhysicalClassMemberAddress(IGF, addr.getAddress(), classType, propType, prop); - Explosion propExplosion; - propExplosion.add(exploded); - llvm::errs() << "prop explo and addr respectfully: \n"; - propExplosion.dump(); - propAddr.getAddress()->dump(); - propTypeInfo.initialize(IGF, propExplosion, propAddr, isOutlined); - } } diff --git a/lib/IRGen/GenInit.cpp b/lib/IRGen/GenInit.cpp index 7ae4e76fab9ab..2c0d8cf90b128 100644 --- a/lib/IRGen/GenInit.cpp +++ b/lib/IRGen/GenInit.cpp @@ -64,14 +64,9 @@ StackAddress FixedTypeInfo::allocateStack(IRGenFunction &IGF, SILType T, auto addr = getUndefAddress(); return { addr }; } - - auto *type = getStorageType(); - // Classes will return a pointer type but we want the element type. - if (T.getClassOrBoundGenericClass() && false) - type = type->getPointerElementType(); Address alloca = - IGF.createAlloca(type, getFixedAlignment(), name); + IGF.createAlloca(getStorageType(), getFixedAlignment(), name); IGF.Builder.CreateLifetimeStart(alloca, getFixedSize()); return { alloca }; diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index c8b7ffd18e858..2b210d53cd493 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3450,20 +3450,23 @@ void IRGenSILFunction::visitDestroyValueInst(swift::DestroyValueInst *i) { void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { SILType objType = i->getType().getObjectType(); - // TODO: we could support this in the future. + // TODO: Currently generic classes aren't allowed but in the future we could + // support this. assert(!objType.getClassOrBoundGenericClass()->getAsGenericContext() || !objType.getClassOrBoundGenericClass()->getAsGenericContext()->isGeneric() && "Generics are not yet supported"); + const auto &typeInfo = cast(getTypeInfo(objType)); llvm::Value *metadata = emitClassHeapMetadataRef(*this, objType.getASTType(), MetadataValueType::TypeMetadata, MetadataState::Complete); - // TODO: this shouldn't be a stack alloc but, this is the easiest way to - // maintain compatibility with alloc_ref. + // TODO: this shouldn't be a stack alloc but, in order to maintain + // compatibility with alloc_ref we have to be a pointer. Address alloca = createAlloca(typeInfo.getStorageType()->getPointerElementType(), typeInfo.getFixedAlignment()); auto classAddr = Builder.CreateBitCast(alloca, IGM.RefCountedPtrTy); auto classVal = emitInitStackObjectCall(metadata, classAddr.getAddress(), "reference.new"); classVal = Builder.CreateBitCast(classVal, typeInfo.getStorageType()); - + // Match each property in the class decl to elements in the object + // instruction. auto propsArr = i->getType().getClassOrBoundGenericClass()->getStoredProperties(); SmallVector props(propsArr.begin(), propsArr.end()); for (SILValue elt : i->getAllElements()) { @@ -3585,28 +3588,10 @@ void IRGenSILFunction::visitStructElementAddrInst( } void IRGenSILFunction::visitRefElementAddrInst(swift::RefElementAddrInst *i) { - SILType baseTy = i->getOperand()->getType(); - - if (i->getOperand()->getType().isAddress()) { - assert(false); - Address base = getLoweredAddress(i->getOperand()); - // - // assert(getIndexedTypeX(IGM.Int8PtrPtrTy, { IGM.getInt32(0), IGM.getInt32(0) })); - // auto bytePtr = Builder.CreateBitCast(base.getAddress(), base->getType()->getPointerElementType()); -// auto newAddr = Builder.CreateInBoundsGEP(base.getAddress(), { IGM.getInt32(0), IGM.getInt32(0), IGM.getInt32(0) }); -// auto newAddr = emitByteOffsetGEP(base.getAddress(), IGM.getInt32(0), base->getType()->getPointerElementType()->getPointerElementType()); - // newAddr = Builder.CreateGEP(newAddr, { IGM.getInt32(0), IGM.getInt32(0) }); - // newAddr = Builder.CreateBitCast(newAddr, base->getType()->getPointerElementType()); - // For dumb reasons, if we get an address here it means we're taking the address of the element of a class in a stack alloc which means we're taking the address of an element of a class double pointer. It's OK to load the first pointer because the pointer we load will still allow us to have a reference to the element. - auto classAddr = Builder.CreateLoad(base); - auto fieldAddr = projectPhysicalClassMemberAddress(*this, classAddr, baseTy, i->getType(), i->getField()); - setLoweredAddress(i, fieldAddr.getAddress()); - return; - } - Explosion base = getLoweredExplosion(i->getOperand()); llvm::Value *value = base.claimNext(); + SILType baseTy = i->getOperand()->getType(); Address field = projectPhysicalClassMemberAddress(*this, value, baseTy, i->getType(), i->getField()) .getAddress(); @@ -3669,9 +3654,6 @@ void IRGenSILFunction::visitStoreInst(swift::StoreInst *i) { SILType objType = i->getSrc()->getType().getObjectType(); const auto &typeInfo = cast(getTypeInfo(objType)); - if (objType.getClassOrBoundGenericClass()) { - // projectPhysicalClassMemberAddress - } switch (i->getOwnershipQualifier()) { case StoreOwnershipQualifier::Unqualified: case StoreOwnershipQualifier::Init: diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 4e501fd9ca1de..4bf8cbc93a397 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -2805,7 +2805,7 @@ class SILVerifier : public SILVerifierBase { } void checkRefElementAddrInst(RefElementAddrInst *EI) { - // requireReferenceValue(EI->getOperand(), "Operand of ref_element_addr"); + requireReferenceValue(EI->getOperand(), "Operand of ref_element_addr"); require(EI->getType().isAddress(), "result of ref_element_addr must be lvalue"); require(!EI->getField()->isStatic(), From a929c842daaa5c341bbd0b9b456bd6da0a6ee75d Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 31 Mar 2020 10:15:48 -0700 Subject: [PATCH 12/17] Pull IRGen changes from optimize/alloc-ref-to-object --- lib/IRGen/GenClass.cpp | 27 ++++++++++++++ lib/IRGen/GenInit.cpp | 2 +- lib/IRGen/IRGenSIL.cpp | 36 ++++++++++++++++--- .../Transforms/DeadStoreElimination.cpp | 3 ++ 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 6e371750f7465..53dbc47a7f967 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -102,6 +102,10 @@ namespace { StructLayout *createLayoutWithTailElems(IRGenModule &IGM, SILType classType, ArrayRef tailTypes) const; + + using HeapTypeInfo::initialize; + void initialize(IRGenFunction &IGF, Explosion &e, Address addr, + bool isOutlined) const override; }; } // end anonymous namespace @@ -483,6 +487,29 @@ ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType, return *Layout; } +void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, + bool isOutlined) const { + // If the address is a poitner to the exploded type then we can simply emit a + // store and be done. + auto *exploded = src.getAll().front(); + if (exploded->getType() == addr->getType()->getPointerElementType()) { + IGF.Builder.CreateStore(exploded, addr); + (void)src.claimNext(); + return; + } + + // If both are the same (address) type then just emit a memcpy. + if (exploded->getType() == addr->getType()) { + IGF.emitMemCpy(addr.getAddress(), exploded, getFixedSize(), addr.getAlignment()); + (void)src.claimNext(); + return; + } + + // Otherwise, bail to the default implementation. + HeapTypeInfo::initialize(IGF, src, addr, isOutlined); +} + + /// Cast the base to i8*, apply the given inbounds offset (in bytes, /// as a size_t), and cast to a pointer to the given type. llvm::Value *IRGenFunction::emitByteOffsetGEP(llvm::Value *base, diff --git a/lib/IRGen/GenInit.cpp b/lib/IRGen/GenInit.cpp index a0f01daf3ec72..2c0d8cf90b128 100644 --- a/lib/IRGen/GenInit.cpp +++ b/lib/IRGen/GenInit.cpp @@ -64,7 +64,7 @@ StackAddress FixedTypeInfo::allocateStack(IRGenFunction &IGF, SILType T, auto addr = getUndefAddress(); return { addr }; } - + Address alloca = IGF.createAlloca(getStorageType(), getFixedAlignment(), name); IGF.Builder.CreateLifetimeStart(alloca, getFixedSize()); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 23ee133c4cafa..2b210d53cd493 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3449,10 +3449,38 @@ void IRGenSILFunction::visitDestroyValueInst(swift::DestroyValueInst *i) { } void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { - Explosion out; - for (SILValue elt : i->getAllElements()) - out.add(getLoweredExplosion(elt).claimAll()); - setLoweredExplosion(i, out); + SILType objType = i->getType().getObjectType(); + // TODO: Currently generic classes aren't allowed but in the future we could + // support this. + assert(!objType.getClassOrBoundGenericClass()->getAsGenericContext() || !objType.getClassOrBoundGenericClass()->getAsGenericContext()->isGeneric() && "Generics are not yet supported"); + + const auto &typeInfo = cast(getTypeInfo(objType)); + llvm::Value *metadata = emitClassHeapMetadataRef(*this, objType.getASTType(), + MetadataValueType::TypeMetadata, + MetadataState::Complete); + // TODO: this shouldn't be a stack alloc but, in order to maintain + // compatibility with alloc_ref we have to be a pointer. + Address alloca = createAlloca(typeInfo.getStorageType()->getPointerElementType(), + typeInfo.getFixedAlignment()); + auto classAddr = Builder.CreateBitCast(alloca, IGM.RefCountedPtrTy); + auto classVal = emitInitStackObjectCall(metadata, classAddr.getAddress(), "reference.new"); + classVal = Builder.CreateBitCast(classVal, typeInfo.getStorageType()); + // Match each property in the class decl to elements in the object + // instruction. + auto propsArr = i->getType().getClassOrBoundGenericClass()->getStoredProperties(); + SmallVector props(propsArr.begin(), propsArr.end()); + for (SILValue elt : i->getAllElements()) { + auto prop = props.pop_back_val(); + auto elementExplosion = getLoweredExplosion(elt); + auto propType = IGM.getLoweredType(prop->getType()); + const auto &propTypeInfo = cast(getTypeInfo(propType)); + auto propAddr = projectPhysicalClassMemberAddress(*this, classVal, objType, propType, prop); + propTypeInfo.initialize(*this, elementExplosion, propAddr, false); + } + + Explosion e; + e.add(classVal); + setLoweredExplosion(i, e); } void IRGenSILFunction::visitStructInst(swift::StructInst *i) { diff --git a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp index 266859152516a..e371e55ae7a3f 100644 --- a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp @@ -1038,6 +1038,9 @@ void DSEContext::processLoadInst(SILInstruction *I, DSEKind Kind) { void DSEContext::processStoreInst(SILInstruction *I, DSEKind Kind) { auto *SI = cast(I); + // TODO: for some reason these stores are removed when they shouldn't be. + if (isa(SI->getSrc())) + return; processWrite(I, SI->getSrc(), SI->getDest(), Kind); } From f5172bcfa86eebd3c74858af21697018b8012793 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 31 Mar 2020 10:19:32 -0700 Subject: [PATCH 13/17] Update verifier to check type is a class and is not generic --- lib/SIL/SILVerifier.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 4bf8cbc93a397..7179ba3f6159f 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -1809,8 +1809,12 @@ class SILVerifier : public SILVerifierBase { checkGlobalAccessInst(GVI); } - void checkObjectInst(ObjectInst *) { - require(true, "object instruction is only allowed in a static initializer"); + void checkObjectInst(ObjectInst *objectInst) { + auto *classDecl = objectInst->getType().getClassOrBoundGenericClass(); + require(classDecl, "Type of object instruction must be a class"); + require(!classDecl->getAsGenericContext() || + !classDecl->getAsGenericContext()->isGeneric(), + "Generics are not yet supported in object instructions"); } void checkIntegerLiteralInst(IntegerLiteralInst *ILI) { From cfc19d9fc0638c019982b7e7e509bd8ac3f2b9ea Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 31 Mar 2020 10:25:28 -0700 Subject: [PATCH 14/17] Run clang format over changes --- lib/IRGen/GenClass.cpp | 8 ++++---- lib/IRGen/GenInit.cpp | 2 +- lib/IRGen/IRGenSIL.cpp | 30 +++++++++++++++++++----------- lib/SIL/SILVerifier.cpp | 2 +- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 53dbc47a7f967..26de51e966f37 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -102,7 +102,7 @@ namespace { StructLayout *createLayoutWithTailElems(IRGenModule &IGM, SILType classType, ArrayRef tailTypes) const; - + using HeapTypeInfo::initialize; void initialize(IRGenFunction &IGF, Explosion &e, Address addr, bool isOutlined) const override; @@ -497,10 +497,11 @@ void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, (void)src.claimNext(); return; } - + // If both are the same (address) type then just emit a memcpy. if (exploded->getType() == addr->getType()) { - IGF.emitMemCpy(addr.getAddress(), exploded, getFixedSize(), addr.getAlignment()); + IGF.emitMemCpy(addr.getAddress(), exploded, getFixedSize(), + addr.getAlignment()); (void)src.claimNext(); return; } @@ -508,7 +509,6 @@ void ClassTypeInfo::initialize(IRGenFunction &IGF, Explosion &src, Address addr, // Otherwise, bail to the default implementation. HeapTypeInfo::initialize(IGF, src, addr, isOutlined); } - /// Cast the base to i8*, apply the given inbounds offset (in bytes, /// as a size_t), and cast to a pointer to the given type. diff --git a/lib/IRGen/GenInit.cpp b/lib/IRGen/GenInit.cpp index 2c0d8cf90b128..a0f01daf3ec72 100644 --- a/lib/IRGen/GenInit.cpp +++ b/lib/IRGen/GenInit.cpp @@ -64,7 +64,7 @@ StackAddress FixedTypeInfo::allocateStack(IRGenFunction &IGF, SILType T, auto addr = getUndefAddress(); return { addr }; } - + Address alloca = IGF.createAlloca(getStorageType(), getFixedAlignment(), name); IGF.Builder.CreateLifetimeStart(alloca, getFixedSize()); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 2b210d53cd493..27c392c80a370 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3452,32 +3452,40 @@ void IRGenSILFunction::visitObjectInst(swift::ObjectInst *i) { SILType objType = i->getType().getObjectType(); // TODO: Currently generic classes aren't allowed but in the future we could // support this. - assert(!objType.getClassOrBoundGenericClass()->getAsGenericContext() || !objType.getClassOrBoundGenericClass()->getAsGenericContext()->isGeneric() && "Generics are not yet supported"); + assert(!objType.getClassOrBoundGenericClass()->getAsGenericContext() || + !objType.getClassOrBoundGenericClass() + ->getAsGenericContext() + ->isGeneric() && + "Generics are not yet supported"); const auto &typeInfo = cast(getTypeInfo(objType)); - llvm::Value *metadata = emitClassHeapMetadataRef(*this, objType.getASTType(), - MetadataValueType::TypeMetadata, - MetadataState::Complete); + llvm::Value *metadata = emitClassHeapMetadataRef( + *this, objType.getASTType(), MetadataValueType::TypeMetadata, + MetadataState::Complete); // TODO: this shouldn't be a stack alloc but, in order to maintain // compatibility with alloc_ref we have to be a pointer. - Address alloca = createAlloca(typeInfo.getStorageType()->getPointerElementType(), - typeInfo.getFixedAlignment()); + Address alloca = + createAlloca(typeInfo.getStorageType()->getPointerElementType(), + typeInfo.getFixedAlignment()); auto classAddr = Builder.CreateBitCast(alloca, IGM.RefCountedPtrTy); - auto classVal = emitInitStackObjectCall(metadata, classAddr.getAddress(), "reference.new"); + auto classVal = emitInitStackObjectCall(metadata, classAddr.getAddress(), + "reference.new"); classVal = Builder.CreateBitCast(classVal, typeInfo.getStorageType()); // Match each property in the class decl to elements in the object // instruction. - auto propsArr = i->getType().getClassOrBoundGenericClass()->getStoredProperties(); - SmallVector props(propsArr.begin(), propsArr.end()); + auto propsArr = + i->getType().getClassOrBoundGenericClass()->getStoredProperties(); + SmallVector props(propsArr.begin(), propsArr.end()); for (SILValue elt : i->getAllElements()) { auto prop = props.pop_back_val(); auto elementExplosion = getLoweredExplosion(elt); auto propType = IGM.getLoweredType(prop->getType()); const auto &propTypeInfo = cast(getTypeInfo(propType)); - auto propAddr = projectPhysicalClassMemberAddress(*this, classVal, objType, propType, prop); + auto propAddr = projectPhysicalClassMemberAddress(*this, classVal, objType, + propType, prop); propTypeInfo.initialize(*this, elementExplosion, propAddr, false); } - + Explosion e; e.add(classVal); setLoweredExplosion(i, e); diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 7179ba3f6159f..05f1ddee5fdaf 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -1813,7 +1813,7 @@ class SILVerifier : public SILVerifierBase { auto *classDecl = objectInst->getType().getClassOrBoundGenericClass(); require(classDecl, "Type of object instruction must be a class"); require(!classDecl->getAsGenericContext() || - !classDecl->getAsGenericContext()->isGeneric(), + !classDecl->getAsGenericContext()->isGeneric(), "Generics are not yet supported in object instructions"); } From a3a1eed2a04079667c1bbbee6833d46b72dec6e4 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 31 Mar 2020 11:25:12 -0700 Subject: [PATCH 15/17] Add object.sil test --- test/IRGen/object.sil | 105 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 test/IRGen/object.sil diff --git a/test/IRGen/object.sil b/test/IRGen/object.sil new file mode 100644 index 0000000000000..b25edadfe9be8 --- /dev/null +++ b/test/IRGen/object.sil @@ -0,0 +1,105 @@ +// RUN: %swift -disable-legacy-type-info -module-name run %s -emit-ir -o - | %FileCheck %s + +// REQUIRES: CODEGENERATOR=X86 + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +class Foo { + @_hasStorage @_hasInitialValue var x: Int { get set } + @objc deinit + init() +} + +class Bar { + @_hasStorage @_hasInitialValue var x: Int { get set } + @_hasStorage @_hasInitialValue var y: Int { get set } + @_hasStorage @_hasInitialValue var z: Int { get set } + @objc deinit + init() +} + +// CHECK-LABEL: define swiftcc i64 @foouser +// CHECK: entry +// CHECK: [[ALLOC:%.*]] = alloca %T3run3FooC +// CHECK: [[META:%.*]] = call swiftcc %swift.metadata_response @"$s3run3FooCMa" +// CHECK: [[META_E:%.*]] = extractvalue %swift.metadata_response [[META]] +// CHECK: [[REF:%.*]] = bitcast %T3run3FooC* [[ALLOC]] to %swift.refcounted* +// CHECK: [[OBJ_R:%.*]] = call %swift.refcounted* @swift_initStackObject({{.*}}[[META_E]], {{.*}}[[REF]]) +// CHECK: [[OBJ:%.*]] = bitcast %swift.refcounted* [[OBJ_R]] to %T3run3FooC* + +// CHECK: [[X_PTR:%.*]] = getelementptr inbounds %T3run3FooC, %T3run3FooC* [[OBJ]], i32 0, i32 1 +// CHECK: [[X:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR]], i32 0, i32 0 +// CHECK: store i64 0, i64* [[X]] + +// CHECK: [[X_PTR1:%.*]] = getelementptr inbounds %T3run3FooC, %T3run3FooC* [[OBJ]], i32 0, i32 1 +// CHECK: [[X1:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR1]], i32 0, i32 0 +// CHECK: [[X_VAL:%.*]] = load i64, i64* [[X1]] +// CHECK: [[ADDED:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[X_VAL]], i64 1) +// CHECK: [[SUM:%.*]] = extractvalue { i64, i1 } [[ADDED]], 0 + +// CHECK: [[X2:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR1]], i32 0, i32 0 +// CHECK: store i64 [[SUM]], i64* [[X2]] +// CHECK: [[X3:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR1]], i32 0, i32 0 +// CHECK: [[OUT:%.*]] = load i64, i64* [[X3]] +// CHECK: ret i64 [[OUT]] +sil @foouser : $@convention(thin) () -> Int { +bb0: + %1 = integer_literal $Builtin.Int64, 0 // user: %2 + %2 = struct $Int (%1 : $Builtin.Int64) // user: %3 + %3 = object $Foo (%2 : $Int) // user: %4 + %4 = ref_element_addr %3 : $Foo, #Foo.x // users: %17, %5 + %5 = begin_access [modify] [dynamic] [no_nested_conflict] %4 : $*Int // users: %7, %15, %16 + %6 = integer_literal $Builtin.Int64, 1 // user: %10 + %7 = struct_element_addr %5 : $*Int, #Int._value // user: %8 + %8 = load %7 : $*Builtin.Int64 // user: %10 + %9 = integer_literal $Builtin.Int1, -1 // user: %10 + %10 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int64, %6 : $Builtin.Int64, %9 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // users: %12, %11 + %11 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 0 // user: %14 + %12 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 1 // user: %13 + cond_fail %12 : $Builtin.Int1, "arithmetic overflow" // id: %13 + %14 = struct $Int (%11 : $Builtin.Int64) // user: %15 + store %14 to %5 : $*Int // id: %15 + end_access %5 : $*Int // id: %16 + %17 = begin_access [read] [static] [no_nested_conflict] %4 : $*Int // users: %19, %18 + %18 = load %17 : $*Int // user: %20 + end_access %17 : $*Int // id: %19 + return %18 : $Int // id: %20 +} // end sil function 'foouser' + +sil_vtable Foo { + +} + +// CHECK-LABEL: define swiftcc i64 @baruser +// CHECK: [[ALLOC:%.*]] = alloca %T3run3BarC +// CHECK: [[META:%.*]] = call swiftcc %swift.metadata_response @"$s3run3BarCMa" +// CHECK: [[META_E:%.*]] = extractvalue %swift.metadata_response [[META]] +// CHECK: [[REF:%.*]] = bitcast %T3run3BarC* [[ALLOC]] to %swift.refcounted* +// CHECK: [[OBJ_R:%.*]] = call %swift.refcounted* @swift_initStackObject({{.*}}[[META_E]], {{.*}}[[REF]]) +// CHECK: [[OBJ:%.*]] = bitcast %swift.refcounted* [[OBJ_R]] to %T3run3BarC* +// CHECK: [[X_PTR:%.*]] = getelementptr inbounds %T3run3BarC, %T3run3BarC* [[OBJ]], i32 0, i32 3 +// CHECK: [[X:%.*]] = getelementptr inbounds %TSi, %TSi* [[X_PTR]], i32 0, i32 0 +// CHECK: store i64 0, i64* [[X]] +// CHECK: [[Y_PTR:%.*]] = getelementptr inbounds %T3run3BarC, %T3run3BarC* [[OBJ]], i32 0, i32 2 +// CHECK: [[Y:%.*]] = getelementptr inbounds %TSi, %TSi* [[Y_PTR]], i32 0, i32 0 +// CHECK: store i64 0, i64* [[Y]] +// CHECK: [[Z_PTR:%.*]] = getelementptr inbounds %T3run3BarC, %T3run3BarC* [[OBJ]], i32 0, i32 1 +// CHECK: [[Z:%.*]] = getelementptr inbounds %TSi, %TSi* [[Z_PTR]], i32 0, i32 0 +// CHECK: store i64 0, i64* [[Z]] +sil @baruser : $@convention(thin) () -> Int { +bb0: + %1 = integer_literal $Builtin.Int64, 0 + %2 = struct $Int (%1 : $Builtin.Int64) + %3 = object $Bar (%2 : $Int, %2 : $Int, %2 : $Int) + + return %2 : $Int +} + + +sil_vtable Bar { + +} From 0d2ffb8d5962eb04a1466426b50623e012c1607f Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 31 Mar 2020 13:43:55 -0700 Subject: [PATCH 16/17] Run clang format over changes --- lib/IRGen/GenInit.cpp | 2 +- .../Transforms/StackPromotion.cpp | 72 ++++++++++--------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/lib/IRGen/GenInit.cpp b/lib/IRGen/GenInit.cpp index 2c0d8cf90b128..a0f01daf3ec72 100644 --- a/lib/IRGen/GenInit.cpp +++ b/lib/IRGen/GenInit.cpp @@ -64,7 +64,7 @@ StackAddress FixedTypeInfo::allocateStack(IRGenFunction &IGF, SILType T, auto addr = getUndefAddress(); return { addr }; } - + Address alloca = IGF.createAlloca(getStorageType(), getFixedAlignment(), name); IGF.Builder.CreateLifetimeStart(alloca, getFixedSize()); diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index 1441eacf2c910..7b1d027f2e123 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -53,10 +53,12 @@ class StackPromotion : public SILFunctionTransform { bool tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, DeadEndBlocks &DEBlocks); - /// Tries to promote the allocation \p ARI to an object. This optimization will only happen if the class type - /// has a compiler-generated constructor and destructor. The promotion happens by scanning all uses in - /// dominance order. If all members are accounted for by ref_element_addr instruction before we find any - /// other use, then we can use those values to promote this alloc_ref to an object. + /// Tries to promote the allocation \p ARI to an object. This optimization + /// will only happen if the class type has a compiler-generated constructor + /// and destructor. The promotion happens by scanning all uses in dominance + /// order. If all members are accounted for by ref_element_addr instruction + /// before we find any other use, then we can use those values to promote this + /// alloc_ref to an object. bool tryPromoteToObject(AllocRefInst *allocRef, ValueLifetimeAnalysis::Frontier &frontier); }; @@ -92,7 +94,7 @@ void StackPromotion::run() { bool StackPromotion::promoteInBlock(SILBasicBlock *BB, EscapeAnalysis *EA, DeadEndBlocks &DEBlocks) { bool Changed = false; - SmallVector allInstructions; + SmallVector allInstructions; for (SILInstruction &inst : *BB) { allInstructions.push_back(&inst); } @@ -116,7 +118,8 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, if (ARI->isObjC() || ARI->canAllocOnStack()) return false; - EA->invalidate(ARI->getFunction(), swift::AliasAnalysis::InvalidationKind::Everything); + EA->invalidate(ARI->getFunction(), + swift::AliasAnalysis::InvalidationKind::Everything); auto *ConGraph = EA->getConnectionGraph(ARI->getFunction()); auto *contentNode = ConGraph->getValueContent(ARI); if (!contentNode) @@ -151,7 +154,7 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, return false; } NumStackPromoted++; - + if (tryPromoteToObject(ARI, Frontier)) return true; @@ -167,7 +170,7 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, } static void getOrderedNonDebugUses(SILValue v, DominanceInfo *domInfo, - SmallVectorImpl &uses) { + SmallVectorImpl &uses) { auto unsorted = getNonDebugUses(v); uses.append(unsorted.begin(), unsorted.end()); llvm::sort(uses, [&domInfo](Operand *a, Operand *b) { @@ -175,29 +178,31 @@ static void getOrderedNonDebugUses(SILValue v, DominanceInfo *domInfo, }); } -bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, - ValueLifetimeAnalysis::Frontier &frontier) { - DominanceInfo *domInfo = PM->getAnalysis()->get(allocRef->getFunction()); +bool StackPromotion::tryPromoteToObject( + AllocRefInst *allocRef, ValueLifetimeAnalysis::Frontier &frontier) { + DominanceInfo *domInfo = + PM->getAnalysis()->get(allocRef->getFunction()); auto *classDecl = allocRef->getType().getClassOrBoundGenericClass(); if (!classDecl || !classDecl->getDestructor()->isImplicit() || - (classDecl->getAsGenericContext() && classDecl->getAsGenericContext()->isGeneric())) + (classDecl->getAsGenericContext() && + classDecl->getAsGenericContext()->isGeneric())) return false; - - SmallVector props; + + SmallVector props; for (auto *prop : classDecl->getStoredProperties()) { props.push_back(prop); } - - SmallVector uses; + + SmallVector uses; getOrderedNonDebugUses(allocRef, domInfo, uses); - + llvm::reverse(props); SmallVector propertyInitializers; for (auto *use : uses) { auto propRef = dyn_cast(use->getUser()); if (!propRef) return false; - + if (propRef->getField() != props.pop_back_val()) return false; @@ -206,18 +211,18 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, if (props.empty()) break; } - + if (!props.empty()) return false; - - SmallVector deadStores; + + SmallVector deadStores; SmallVector elements; for (auto *init : propertyInitializers) { - SmallVector refElementUses; + SmallVector refElementUses; getOrderedNonDebugUses(init, domInfo, refElementUses); auto frontUser = refElementUses.front()->getUser(); if (auto *beginAccess = dyn_cast(frontUser)) { - SmallVector beginAccessUses; + SmallVector beginAccessUses; getOrderedNonDebugUses(beginAccess, domInfo, beginAccessUses); frontUser = beginAccessUses.front()->getUser(); } @@ -228,7 +233,7 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, return false; } } - + SILInstruction *lastElement = nullptr; for (auto first = elements.rbegin(); first != elements.rend(); ++first) { auto inst = first->getDefiningInstruction(); @@ -238,24 +243,24 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, if (!lastElement || domInfo->dominates(lastElement, inst)) lastElement = inst; } - + // If we didn't find anything, that means that all the elements are arguments, // or there aren't any elements. Either way, we know that putting where the // alloc_ref is will work. if (!lastElement) lastElement = allocRef; - + for (auto *init : propertyInitializers) { if (llvm::any_of(init->getUses(), [&domInfo, &lastElement](Operand *use) { - return domInfo->dominates(use->getUser(), lastElement); - })) + return domInfo->dominates(use->getUser(), lastElement); + })) return false; } SILBuilder builder(std::next(lastElement->getIterator())); auto object = builder.createObject(allocRef->getLoc(), allocRef->getType(), elements, elements.size()); - + SmallVector users(allocRef->use_begin(), allocRef->use_end()); for (auto *use : users) { auto user = use->getUser(); @@ -264,7 +269,7 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, if (isa(user) || isa(user) || isa(user) || isa(user)) user->eraseFromParent(); - + if (auto ref = dyn_cast(user)) { // We need to move the ref_element_addr up. if (domInfo->dominates(ref, object)) { @@ -275,7 +280,7 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, } } } - + for (auto *store : deadStores) { store->eraseFromParent(); } @@ -283,8 +288,9 @@ bool StackPromotion::tryPromoteToObject(AllocRefInst *allocRef, allocRef->replaceAllUsesWith(object); allocRef->eraseFromParent(); - llvm::errs() << "Promoted to object in stack. Function: " << object->getFunction()->getName() << "\n"; - + llvm::errs() << "Promoted to object in stack. Function: " + << object->getFunction()->getName() << "\n"; + return true; } From 487c69fb82cc21e367a212ba38389e08f3c4f486 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 31 Mar 2020 19:32:03 -0700 Subject: [PATCH 17/17] Add tests --- test/SILOptimizer/object.sil | 51 ++++++++++++++++++++ test/SILOptimizer/object.swift | 88 ++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 test/SILOptimizer/object.sil create mode 100644 test/SILOptimizer/object.swift diff --git a/test/SILOptimizer/object.sil b/test/SILOptimizer/object.sil new file mode 100644 index 0000000000000..3b34ecb12069d --- /dev/null +++ b/test/SILOptimizer/object.sil @@ -0,0 +1,51 @@ +// RUN: %target-sil-opt -O -enable-sil-verify-all %s | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +class Foo { + @_hasStorage @_hasInitialValue var x: Int { get set } + init() + deinit +} + +// CHECK-LABEL: sil @simple_stack_to_obj +// CHECK-NOT: alloc_ref +// CHECK: [[I:%.*]] = integer_literal +// CHECK-NEXT: [[X:%.*]] = struct $Int ([[I]] +// CHECK-NEXT: [[OBJ:%.*]] = object $Foo (%3 : $Int) +// CHECK-NEXT: [[E:%.*]] = ref_element_addr [[OBJ]] : $Foo, #Foo.x +// CHECK-NEXT: begin_access +// CHECK-NEXT: [[VAL:%.*]] = load +// CHECK-NEXT: end_access +// CHECK-NEXT: return [[VAL]] +// CHECK-LABEL: end sil function 'simple_stack_to_obj' +sil @simple_stack_to_obj : $@convention(thin) () -> Int { +bb0: + %1 = alloc_ref [stack] $Foo // users: %17, %16, %15, %14, %18, %10, %9, %5, %2 + debug_value %1 : $Foo, let, name "self", argno 1 // id: %2 + %3 = integer_literal $Builtin.Int64, 0 // user: %4 + %4 = struct $Int (%3 : $Builtin.Int64) // user: %7 + %5 = ref_element_addr %1 : $Foo, #Foo.x // users: %11, %6 + %6 = begin_access [modify] [dynamic] [no_nested_conflict] %5 : $*Int // users: %8, %7 + store %4 to %6 : $*Int // id: %7 + end_access %6 : $*Int // id: %8 + debug_value %1 : $Foo, let, name "f" // id: %9 + debug_value %1 : $Foo, let, name "self", argno 1 // id: %10 + %11 = begin_access [read] [static] [no_nested_conflict] %5 : $*Int // users: %13, %12 + %12 = load %11 : $*Int // user: %19 + end_access %11 : $*Int // id: %13 + set_deallocating %1 : $Foo // id: %14 + debug_value %1 : $Foo, let, name "self", argno 1 // id: %15 + debug_value %1 : $Foo, let, name "self", argno 1 // id: %16 + dealloc_ref %1 : $Foo // id: %17 + dealloc_ref [stack] %1 : $Foo // id: %18 + return %12 : $Int // id: %19 +} + +sil_vtable Foo { + +} diff --git a/test/SILOptimizer/object.swift b/test/SILOptimizer/object.swift new file mode 100644 index 0000000000000..dad1ee05a293e --- /dev/null +++ b/test/SILOptimizer/object.swift @@ -0,0 +1,88 @@ +// RUN: %target-swift-frontend %s -O -emit-sil | %FileCheck %s + +class Foo { + var x : Int = 0 +} + +func a( _ f : Foo) { + f.x += 1 +} + +func b( _ f : Foo) -> Int { + return f.x + 2 +} + +// CHECK-LABEL: sil @$s6object14simple_foo_useySiSbF +// CHECK-NOT: alloc_ref +// CHECK: [[I:%.*]] = integer_literal +// CHECK-NEXT: [[X:%.*]] = struct $Int ([[I]] +// CHECK-NEXT: [[OBJ:%.*]] = object $Foo ([[X]] : $Int) +// CHECK-NEXT: [[E:%.*]] = ref_element_addr [[OBJ]] : $Foo, #Foo.x +// CHECK-NEXT: begin_access +// CHECK-NEXT: [[VAL:%.*]] = load +// CHECK-NEXT: end_access +// CHECK-NEXT: return [[VAL]] +// CHECK-LABEL: end sil function '$s6object14simple_foo_useySiSbF' +public func simple_foo_use(_ check : Bool) -> Int { + let f = Foo() + return f.x +} + +// CHECK-LABEL: sil @$s6object3a_bySiSbF +// CHECK-NOT: alloc_ref +// CHECK: [[I:%.*]] = integer_literal +// CHECK-NEXT: [[X:%.*]] = struct $Int ([[I]] +// CHECK-NEXT: object $Foo (%3 : $Int) +// CHECK-LABEL: end sil function '$s6object3a_bySiSbF' +public func a_b(_ check : Bool) -> Int { + let f = Foo() + if check { + a(f) + } + return b(f) +} + +class Bar { + var x : [Int] + init (_ x: [Int]) { + self.x = x + } +} + +// CHECK-LABEL: sil @$s6object8arr_testSiyF +// CHECK-NOT: alloc_ref [stack] +// CHECK: object $Bar (%{{.*}} : $Array) +// CHECK-LABEL: end sil function '$s6object8arr_testSiyF' +public func arr_test() -> Int { + let arr = [0, 1, 2, 3, 4] + let f = Bar(arr) + return f.x.count +} + +class Gen { + var x : T + init (_ x: T) { + self.x = x + } +} + +// CHECK-LABEL: sil @$s6object12test_genericyyF +// CHECK-NOT: object +// CHECK: alloc_ref [stack] $Gen +// CHECK-LABEL: end sil function '$s6object12test_genericyyF' +public func test_generic() { + _ = Gen(0) +} + +class HasDeinit { + var x : Int = 0 + deinit { } +} + +// CHECK-LABEL: sil @$s6object11test_deinityyF +// CHECK-NOT: object +// CHECK: alloc_ref [stack] $HasDeinit +// CHECK-LABEL: end sil function '$s6object11test_deinityyF' +public func test_deinit() { + _ = HasDeinit() +}