From f4a49dd00f5198a22fa37e78b40e4c03351504a4 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 2 Apr 2020 11:48:51 -0700 Subject: [PATCH 1/2] Add copy_to_ref sil instruction This sil instruction can be used to copy an address to a class reference. It does this by memcpying the address to the first element in the class reference (the element after the reference count object). The source must contain all stored properties of the class reference and must be an address. See SIL.rst for more details. --- docs/SIL.rst | 34 ++++++ include/swift/SIL/SILBuilder.h | 8 ++ include/swift/SIL/SILCloner.h | 9 ++ include/swift/SIL/SILInstruction.h | 34 ++++++ include/swift/SIL/SILNodes.def | 2 + lib/IRGen/IRGenSIL.cpp | 28 +++++ lib/ParseSIL/ParseSIL.cpp | 30 +++++ lib/SIL/IR/OperandOwnership.cpp | 1 + lib/SIL/IR/SILPrinter.cpp | 5 + lib/SIL/Verifier/SILVerifier.cpp | 53 +++++++++ .../UtilityPasses/SerializeSILPass.cpp | 1 + lib/SILOptimizer/Utils/SILInliner.cpp | 1 + lib/Serialization/DeserializeSIL.cpp | 9 ++ lib/Serialization/SerializeSIL.cpp | 1 + test/IRGen/copy_to_ref.sil | 107 ++++++++++++++++++ 15 files changed, 323 insertions(+) create mode 100644 test/IRGen/copy_to_ref.sil diff --git a/docs/SIL.rst b/docs/SIL.rst index ccee30459cf98..3fcf54802d69a 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -4200,6 +4200,40 @@ object which is created by an ``alloc_ref`` with ``tail_elems``. It is undefined behavior if the class instance does not have tail-allocated arrays or if the element-types do not match. +copy_to_ref +``````````` +:: + + sil-instruction ::= 'copy_to_ref' sil-value 'to' sil-operand + + copy_to_ref %0 to %1 : $C + // %0 must be a pointer. + // %1 must be class type. + +Given an instance of a tuple or struct, bitcasts the "from" value to a byte +pointer and emits a memcopy to the "to" operand which is a reference. The "from" value must have all stored properties of the "to" operand. I.e. the following would be allowed: + + class Foo { + var x: Int + var y: Int + var z: Int + var cond: Bool + } + + %f = alloc_ref $Foo + %sa = alloc_stack $(Int, Int, Int, Bool) + %t = tuple (%0 : $Int, %1 : Int, %2 : Int, %3 : Bool) + store %t to %sa : $*(Int, Int, Int, Bool) + + copy_to_ref %sa to %f : $Foo + +And the following is not allowed: + + // ... + %t = tuple (%0 : $Int, %1 : Int, %2 : Int) + store %t to %sa : $*(Int, Int, Int) + copy_to_ref %sa to %f : $Foo // Error! + Enums ~~~~~ diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 61174385d77a9..da7ee0b389096 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -967,6 +967,14 @@ class SILBuilder { getSILDebugLocation(Loc), srcAddr, destAddr, isTake, isInitialize)); } + CopyToRefInst *createCopyToRef(SILLocation Loc, SILValue srcAddr, + SILValue destRef) { + assert(srcAddr->getType().isAddress()); + assert(destRef->getType().isAnyClassReferenceType()); + return insert(new (getModule()) CopyToRefInst(getSILDebugLocation(Loc), + srcAddr, destRef)); + } + BindMemoryInst *createBindMemory(SILLocation Loc, SILValue base, SILValue index, SILType boundType) { return insert(BindMemoryInst::create(getSILDebugLocation(Loc), base, index, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index cf85fdec14f5d..c30ae0c7cd9c0 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1389,6 +1389,15 @@ SILCloner::visitCopyAddrInst(CopyAddrInst *Inst) { Inst->isInitializationOfDest())); } +template +void SILCloner::visitCopyToRefInst(CopyToRefInst *inst) { + getBuilder().setCurrentDebugScope(getOpScope(inst->getDebugScope())); + recordClonedInstruction( + inst, getBuilder().createCopyToRef(getOpLocation(inst->getLoc()), + getOpValue(inst->getSrc()), + getOpValue(inst->getDest()))); +} + template void SILCloner::visitBindMemoryInst(BindMemoryInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 4634d8c60aa87..df2b1fa1b4b09 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -4210,6 +4210,40 @@ class CopyAddrInst MutableArrayRef getAllOperands() { return Operands.asArray(); } }; +/// CopyToRef - Represents a copy from one memory location to a reference. This +/// is similar to: +/// copy_addr %0 to %1 : $Foo +/// but is valid for reference types. +class CopyToRefInst : public InstructionBase { + friend SILBuilder; + +public: + enum { + /// The lvalue being loaded from. + src, + + /// The lvalue being stored to. + dest + }; + +private: + FixedOperandList<2> Operands; + + CopyToRefInst(SILDebugLocation debugLog, SILValue src, SILValue dest) + : InstructionBase(debugLog), Operands(this, src, dest) {} + +public: + SILValue getSrc() const { return Operands[src].get(); } + SILValue getDest() const { return Operands[dest].get(); } + + void setSrc(SILValue val) { Operands[src].set(val); } + void setDest(SILValue val) { Operands[dest].set(val); } + + ArrayRef getAllOperands() const { return Operands.asArray(); } + MutableArrayRef getAllOperands() { return Operands.asArray(); } +}; + /// BindMemoryInst - /// "bind_memory %0 : $Builtin.RawPointer, %1 : $Builtin.Word to $T" /// Binds memory at the raw pointer %0 to type $T with enough capacity diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index d85bdb0bac175..74a8e8b2eaad9 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -646,6 +646,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SingleValueInstruction, None, DoesNotRelease) SINGLE_VALUE_INST(RefTailAddrInst, ref_tail_addr, SingleValueInstruction, None, DoesNotRelease) + NON_VALUE_INST(CopyToRefInst, copy_to_ref, + SILInstruction, MayHaveSideEffects, DoesNotRelease) // Enums SINGLE_VALUE_INST(EnumInst, enum, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index aa06d47d78b0c..1136b437c021f 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -982,6 +982,7 @@ class IRGenSILFunction : void visitDeallocPartialRefInst(DeallocPartialRefInst *i); void visitCopyAddrInst(CopyAddrInst *i); + void visitCopyToRefInst(CopyToRefInst *i); void visitDestroyAddrInst(DestroyAddrInst *i); void visitBindMemoryInst(BindMemoryInst *i); @@ -5587,6 +5588,33 @@ void IRGenSILFunction::visitCopyAddrInst(swift::CopyAddrInst *i) { } } +void IRGenSILFunction::visitCopyToRefInst(swift::CopyToRefInst *i) { + // If the class is empty we don't have to do anything. + if (i->getDest() + ->getType() + .getClassOrBoundGenericClass() + ->getStoredProperties() + .empty()) + return; + // Otherwise, get the soruce address and the destination reference. + Address src = getLoweredAddress(i->getSrc()); + Explosion classRefExplosion = getLoweredExplosion(i->getDest()); + llvm::Value *classRef = classRefExplosion.claimNext(); + // Get the size of the source object for memcpy. + const auto &size = + getTypeInfo(i->getSrc()->getType().getAddressType()) + .getSize(*this, i->getSrc()->getType().getAddressType()); + // Find where we want to copy our source into. This is the first element of + // the struct, the one directly after the refcounted object. + auto start = Builder.CreateGEP(classRef, {IGM.getInt32(0), IGM.getInt32(1)}); + // Bitcast both to a byte pointer for memcpy. + auto bytePtr = Builder.CreateBitCast(src.getAddress(), IGM.Int8PtrTy); + start = Builder.CreateBitCast(start, IGM.Int8PtrTy); + // Copy the whole source object into the start of the stored properties in the + // reference class. + emitMemCpy(start, bytePtr, size, src.getAlignment()); +} + // This is a no-op because we do not lower Swift TBAA info to LLVM IR, and it // does not produce any values. void IRGenSILFunction::visitBindMemoryInst(swift::BindMemoryInst *) {} diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 049cd14000227..dc525bd5ff050 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -4352,6 +4352,36 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, IsInitialization_t(IsInit)); break; } + case SILInstructionKind::CopyToRefInst: { + SILValue srcVal; + SILValue destVal; + SourceLoc toLoc, srcLoc, destLoc; + Identifier toToken; + if (parseTypedValueRef(srcVal, srcLoc, B) || + parseSILIdentifier(toToken, toLoc, diag::expected_tok_in_sil_instr, + "to") || + parseTypedValueRef(destVal, destLoc, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + if (toToken.str() != "to") { + P.diagnose(toLoc, diag::expected_tok_in_sil_instr, "to"); + return true; + } + + if (!destVal->getType().isAnyClassReferenceType()) { + P.diagnose(destLoc, diag::sil_invalid_instr_operands); + return true; + } + + if (!srcVal->getType().isAddress()) { + P.diagnose(destLoc, diag::sil_invalid_instr_operands); + return true; + } + + ResultVal = B.createCopyToRef(InstLoc, srcVal, destVal); + break; + } case SILInstructionKind::BindMemoryInst: { SILValue IndexVal; Identifier ToToken; diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index dc5ba790c88b3..72438738e2c40 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -179,6 +179,7 @@ CONSTANT_OWNERSHIP_INST(None, MustBeLive, BindMemory) CONSTANT_OWNERSHIP_INST(None, MustBeLive, CheckedCastAddrBranch) CONSTANT_OWNERSHIP_INST(None, MustBeLive, CondFail) CONSTANT_OWNERSHIP_INST(None, MustBeLive, CopyAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, CopyToRef) CONSTANT_OWNERSHIP_INST(None, MustBeLive, DeallocStack) CONSTANT_OWNERSHIP_INST(None, MustBeLive, DebugValueAddr) CONSTANT_OWNERSHIP_INST(None, MustBeLive, DeinitExistentialAddr) diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 871bb1222cb2f..27ceb686b55bb 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -1465,6 +1465,11 @@ class SILPrinter : public SILInstructionVisitor { *this << getIDAndType(CI->getDest()); } + void visitCopyToRefInst(CopyToRefInst *copy) { + *this << Ctx.getID(copy->getSrc()) << " to " + << getIDAndType(copy->getDest()); + } + void visitBindMemoryInst(BindMemoryInst *BI) { *this << getIDAndType(BI->getBase()) << ", "; *this << getIDAndType(BI->getIndex()) << " to "; diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index a901adf9539d3..c0aa4af297e5d 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -2265,6 +2265,59 @@ class SILVerifier : public SILVerifierBase { "cannot directly copy type with inaccessible ABI"); } + void checkCopyToRefInst(CopyToRefInst *copy) { + require(copy->getSrc()->getType().isAddress(), + "Src value should be an address"); + require(copy->getDest()->getType().isAnyClassReferenceType(), + "Dest value should be a class reference type"); + + auto classDecl = copy->getDest()->getType().getClassOrBoundGenericClass(); + if (classDecl->getStoredProperties().size() == 1) { + require( + classDecl->getStoredProperties().front()->getType().getPointer() == + copy->getSrc()->getType().getASTType().getPointer(), + "The source type must be the same as the class's only property type"); + return; + } + + Type srcType = copy->getSrc()->getType().getAs(); + if (!srcType) { + srcType = copy->getSrc()->getType().getAs(); + } + require(!srcType.isNull(), + "The source value must be either a tuple or struct if there is " + "not one stored property in the class"); + + unsigned i = 0; + for (auto *prop : classDecl->getStoredProperties()) { + Type srcPropType; + if (auto tupleType = srcType->getAs()) { + srcPropType = tupleType->getElement(i++).getType(); + } else { + srcPropType = srcType->getStructOrBoundGenericStruct() + ->getStoredProperties()[i++] + ->getType(); + } + require( + srcPropType.getPointer() == prop->getType().getPointer(), + "All stored properties must exist as operands of the source value"); + } + + unsigned numElements; + if (auto tupleType = srcType->getAs()) { + numElements = tupleType->getNumElements(); + } else { + numElements = srcType->getStructOrBoundGenericStruct() + ->getStoredProperties() + .size(); + } + require(i == numElements, "Source operands must only be stored properties " + "of the destination type"); + + require(F.isTypeABIAccessible(copy->getDest()->getType()), + "cannot directly copy type with inaccessible ABI"); + } + void checkRetainValueInst(RetainValueInst *I) { require(I->getOperand()->getType().isObject(), "Source value should be an object value"); diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index ed80be81e0981..e59f4adfa5443 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -314,6 +314,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::Store##Name##Inst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::CopyToRefInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::InjectEnumAddrInst: diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index 29df76e4b416f..8db3170c9e467 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -806,6 +806,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::CopyBlockInst: case SILInstructionKind::CopyBlockWithoutEscapingInst: case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::CopyToRefInst: case SILInstructionKind::RetainValueInst: case SILInstructionKind::RetainValueAddrInst: case SILInstructionKind::UnmanagedRetainValueInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 7cb208358bd1f..179bb72088ea1 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1968,6 +1968,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, IsInitialization_t(isInit)); break; } + case SILInstructionKind::CopyToRefInst: { + SILType addrType = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + SILType referenceType = + getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory, Fn); + ResultVal = Builder.createCopyToRef(Loc, getLocalValue(ValID, addrType), + getLocalValue(ValID2, referenceType)); + break; + } case SILInstructionKind::AssignInst: { auto Ty = MF->getType(TyID); SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 86535115c605f..7a8c46160ec46 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1718,6 +1718,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::AssignInst: case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::CopyToRefInst: case SILInstructionKind::StoreInst: case SILInstructionKind::StoreBorrowInst: { SILValue operand, value; diff --git a/test/IRGen/copy_to_ref.sil b/test/IRGen/copy_to_ref.sil new file mode 100644 index 0000000000000..10b06f63496f5 --- /dev/null +++ b/test/IRGen/copy_to_ref.sil @@ -0,0 +1,107 @@ +// RUN: %swift run %s -emit-ir -o - | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift + +class SingleInt { + var x : Int +} + +sil @test_int : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int64, 1 + %1 = struct $Int (%0 : $Builtin.Int64) + + %tsa = alloc_stack $Int + %f = alloc_ref [stack] $SingleInt + + store %1 to %tsa : $*Int + + copy_to_ref %tsa : $*Int to %f : $SingleInt + + dealloc_ref [stack] %f : $SingleInt + dealloc_stack %tsa : $*Int + + %999 = tuple () + return %999 : $() +} + +class XYZCheck { + var x : Int + var y : Int + var z : Int + var check : Bool +} + +sil @test_xyzcheck_tuple : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int64, 1 + %1 = struct $Int (%0 : $Builtin.Int64) + %2 = integer_literal $Builtin.Int64, 2 + %3 = struct $Int (%2 : $Builtin.Int64) + %4 = tuple (%1 : $Int, %3 : $Int, %3 : $Int) + + %tsa = alloc_stack $(Int, Int, Int) + %f = alloc_ref [stack] $XYZCheck + + store %4 to %tsa : $*(Int, Int, Int) + + copy_to_ref %tsa : $*(Int, Int, Int) to %f : $XYZCheck + + dealloc_ref [stack] %f : $XYZCheck + dealloc_stack %tsa : $*(Int, Int, Int) + + %999 = tuple () + return %999 : $() +} + +struct XYZB { + var x : Int + var y : Int + var z : Int + var check : Bool +} + +sil @test_xyzcheck_struct : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int64, 1 + %1 = struct $Int (%0 : $Builtin.Int64) + %2 = integer_literal $Builtin.Int64, 2 + %3 = struct $Int (%2 : $Builtin.Int64) + %4 = struct $XYZB (%1 : $Int, %3 : $Int, %3 : $Int) + + %tsa = alloc_stack $XYZB + %f = alloc_ref [stack] $XYZCheck + + store %4 to %tsa : $*XYZB + + copy_to_ref %tsa : $*XYZB to %f : $XYZCheck + + dealloc_ref [stack] %f : $XYZCheck + dealloc_stack %tsa : $*XYZB + + %999 = tuple () + return %999 : $() +} + +class Empty { } + +sil @test_xyzcheck : $@convention(thin) () -> () { +bb0: + %empty_tuple = tuple () + + %tsa = alloc_stack $() + %f = alloc_ref [stack] $Empty + + store %empty_tuple to %tsa : $*() + + copy_to_ref %tsa : $*() to %f : $Empty + + dealloc_ref [stack] %f : $Empty + dealloc_stack %tsa : $*() + + %999 = tuple () + return %999 : $() +} From 1dde9c3f4e59e44b77743db78923d8755327ec8a Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 2 Apr 2020 16:26:45 -0700 Subject: [PATCH 2/2] Add IRGen test for copy_to_ref to test both IRGen and llvm optimization --- test/IRGen/copy_to_ref.sil | 72 +++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/test/IRGen/copy_to_ref.sil b/test/IRGen/copy_to_ref.sil index 10b06f63496f5..2906e1150c1c1 100644 --- a/test/IRGen/copy_to_ref.sil +++ b/test/IRGen/copy_to_ref.sil @@ -1,4 +1,4 @@ -// RUN: %swift run %s -emit-ir -o - | %FileCheck %s +// RUN: %swift %s -emit-ir -O -module-name run | %FileCheck %s sil_stage canonical @@ -9,6 +9,16 @@ class SingleInt { var x : Int } +// CHECK-LABEL: define swiftcc void @test_int +// CHECK: [[ALLOC:%.*]] = alloca %T3run9SingleIntC +// CHECK-NEXT: [[META:%.*]] = tail call swiftcc %swift.metadata_response @"$s3run9SingleIntCMa" +// CHECK-NEXT: [[META_E:%.*]] = extractvalue %swift.metadata_response [[META]] +// CHECK-NEXT: [[REF_COUNT:%.*]] = getelementptr inbounds %T3run9SingleIntC, %T3run9SingleIntC* [[ALLOC]] +// CHECK-NEXT: [[OBJ_REF:%.*]] = call %swift.refcounted* @swift_initStackObject(%swift.type* [[META_E]], %swift.refcounted* nonnull [[REF_COUNT]]) +// CHECK-NEXT: [[SROA_IDX:%.*]] = getelementptr inbounds %swift.refcounted, %swift.refcounted* [[OBJ_REF]], [[IT:i32|i64]] 1 +// CHECK-NEXT: [[X_PTR:%.*]] = bitcast %swift.refcounted* [[SROA_IDX]] to [[IT]]* +// CHECK-NEXT: store [[IT]] 1, [[IT]]* [[X_PTR]] +// CHECK: ret void sil @test_int : $@convention(thin) () -> () { bb0: %0 = integer_literal $Builtin.Int64, 1 @@ -28,13 +38,28 @@ bb0: return %999 : $() } -class XYZCheck { +class XYZClass { var x : Int var y : Int var z : Int - var check : Bool } +// Check both tuple and struct (tuple at the bottom of the file): + +// CHECK-LABEL: define swiftcc void @test_xyzcheck_struct +// CHECK: [[ALLOC:%.*]] = alloca %T3run8XYZClassC +// CHECK-NEXT: [[META:%.*]] = tail call swiftcc %swift.metadata_response @"$s3run8XYZClassCMa" +// CHECK-NEXT: [[META_E:%.*]] = extractvalue %swift.metadata_response [[META]] +// CHECK-NEXT: [[REF_COUNT:%.*]] = getelementptr inbounds %T3run8XYZClassC, %T3run8XYZClassC* [[ALLOC]] +// CHECK-NEXT: [[OBJ_REF:%.*]] = call %swift.refcounted* @swift_initStackObject(%swift.type* [[META_E]], %swift.refcounted* nonnull [[REF_COUNT]]) +// CHECK-NEXT: [[SROA_IDX:%.*]] = getelementptr inbounds %swift.refcounted, %swift.refcounted* [[OBJ_REF]], [[IT:i32|i64]] 1 +// CHECK-NEXT: [[XY_PTR:%.*]] = bitcast %swift.refcounted* [[SROA_IDX]] to <2 x [[IT]]>* +// CHECK-NEXT: store <2 x [[IT]]> <[[IT]] 1, [[IT]] 2>, <2 x [[IT]]>* [[XY_PTR]] +// CHECK-NEXT: [[Z_PTR:%.*]] = getelementptr inbounds %swift.refcounted, %swift.refcounted* [[OBJ_REF]], i64 2 +// CHECK-NEXT: [[Z_IPTR:%.*]] = bitcast %swift.refcounted* [[Z_PTR]] to [[IT]]* +// CHECK-NEXT: store [[IT]] 2, [[IT]]* [[Z_IPTR]] +// CHECK: ret void + sil @test_xyzcheck_tuple : $@convention(thin) () -> () { bb0: %0 = integer_literal $Builtin.Int64, 1 @@ -44,24 +69,23 @@ bb0: %4 = tuple (%1 : $Int, %3 : $Int, %3 : $Int) %tsa = alloc_stack $(Int, Int, Int) - %f = alloc_ref [stack] $XYZCheck + %f = alloc_ref [stack] $XYZClass store %4 to %tsa : $*(Int, Int, Int) - copy_to_ref %tsa : $*(Int, Int, Int) to %f : $XYZCheck + copy_to_ref %tsa : $*(Int, Int, Int) to %f : $XYZClass - dealloc_ref [stack] %f : $XYZCheck + dealloc_ref [stack] %f : $XYZClass dealloc_stack %tsa : $*(Int, Int, Int) %999 = tuple () return %999 : $() } -struct XYZB { +struct XYZStruct { var x : Int var y : Int var z : Int - var check : Bool } sil @test_xyzcheck_struct : $@convention(thin) () -> () { @@ -70,17 +94,17 @@ bb0: %1 = struct $Int (%0 : $Builtin.Int64) %2 = integer_literal $Builtin.Int64, 2 %3 = struct $Int (%2 : $Builtin.Int64) - %4 = struct $XYZB (%1 : $Int, %3 : $Int, %3 : $Int) + %4 = struct $XYZStruct (%1 : $Int, %3 : $Int, %3 : $Int) - %tsa = alloc_stack $XYZB - %f = alloc_ref [stack] $XYZCheck + %tsa = alloc_stack $XYZStruct + %f = alloc_ref [stack] $XYZClass - store %4 to %tsa : $*XYZB + store %4 to %tsa : $*XYZStruct - copy_to_ref %tsa : $*XYZB to %f : $XYZCheck + copy_to_ref %tsa : $*XYZStruct to %f : $XYZClass - dealloc_ref [stack] %f : $XYZCheck - dealloc_stack %tsa : $*XYZB + dealloc_ref [stack] %f : $XYZClass + dealloc_stack %tsa : $*XYZStruct %999 = tuple () return %999 : $() @@ -88,7 +112,15 @@ bb0: class Empty { } -sil @test_xyzcheck : $@convention(thin) () -> () { +// CHECK-LABEL: define swiftcc void @test_empty +// CHECK: [[ALLOC:%.*]] = alloca %swift.refcounted +// CHECK-NEXT: [[META:%.*]] = tail call swiftcc %swift.metadata_response @"$s3run5EmptyCMa" +// CHECK-NEXT: [[META_E:%.*]] = extractvalue %swift.metadata_response [[META]] +// CHECK-NEXT: [[OBJ_REF:%.*]] = call %swift.refcounted* @swift_initStackObject(%swift.type* [[META_E]], %swift.refcounted* nonnull [[ALLOC]]) +// CHECK-NEXT: bitcast +// CHECK-NEXT: llvm.lifetime.end +// CHECK: ret void +sil @test_empty : $@convention(thin) () -> () { bb0: %empty_tuple = tuple () @@ -105,3 +137,11 @@ bb0: %999 = tuple () return %999 : $() } + +sil_vtable SingleInt { } +sil_vtable XYZClass { } +sil_vtable Empty { } + +// CHECK-LABEL: define swiftcc void @test_xyzcheck_tuple +// CHECK: tail call swiftcc void @test_xyzcheck_struct() +// CHECK-NEXT: ret void