Skip to content

[sil] add instruction copy_to_ref #30787

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
~~~~~

Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,15 @@ SILCloner<ImplClass>::visitCopyAddrInst(CopyAddrInst *Inst) {
Inst->isInitializationOfDest()));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitCopyToRefInst(CopyToRefInst *inst) {
getBuilder().setCurrentDebugScope(getOpScope(inst->getDebugScope()));
recordClonedInstruction(
inst, getBuilder().createCopyToRef(getOpLocation(inst->getLoc()),
getOpValue(inst->getSrc()),
getOpValue(inst->getDest())));
}

template<typename ImplClass>
void
SILCloner<ImplClass>::visitBindMemoryInst(BindMemoryInst *Inst) {
Expand Down
34 changes: 34 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -4210,6 +4210,40 @@ class CopyAddrInst
MutableArrayRef<Operand> 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<SILInstructionKind::CopyToRefInst,
NonValueInstruction> {
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<Operand> getAllOperands() const { return Operands.asArray(); }
MutableArrayRef<Operand> 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
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SIL/SILNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
28 changes: 28 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 *) {}
Expand Down
30 changes: 30 additions & 0 deletions lib/ParseSIL/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,11 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
*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 ";
Expand Down
53 changes: 53 additions & 0 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2265,6 +2265,59 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"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<TupleType>();
if (!srcType) {
srcType = copy->getSrc()->getType().getAs<StructType>();
}
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<TupleType>()) {
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<TupleType>()) {
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");
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/Utils/SILInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
9 changes: 9 additions & 0 deletions lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions lib/Serialization/SerializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading