From 2e01b0edeb949892ca7f71de51ec53a034edd763 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 4 Apr 2019 11:24:30 -0700 Subject: [PATCH] SIL: add assign_by_delegate instruction Used for property delegates. --- docs/SIL.rst | 26 ++++++++++ include/swift/SIL/SILBuilder.h | 10 ++++ include/swift/SIL/SILCloner.h | 12 +++++ include/swift/SIL/SILInstruction.h | 59 ++++++++++++++++++---- include/swift/SIL/SILNode.h | 4 ++ include/swift/SIL/SILNodes.def | 2 + include/swift/Serialization/ModuleFormat.h | 2 +- lib/IRGen/IRGenSIL.cpp | 3 ++ lib/ParseSIL/ParseSIL.cpp | 28 ++++++++++ lib/SIL/MemAccessUtils.cpp | 1 + lib/SIL/OperandOwnership.cpp | 10 ++++ lib/SIL/SILInstructions.cpp | 15 +++++- lib/SIL/SILPrinter.cpp | 8 +++ lib/SIL/SILVerifier.cpp | 47 +++++++++++++++++ lib/SILOptimizer/Utils/SILInliner.cpp | 1 + lib/Serialization/DeserializeSIL.cpp | 2 + lib/Serialization/SerializeSIL.cpp | 2 + 17 files changed, 218 insertions(+), 14 deletions(-) diff --git a/docs/SIL.rst b/docs/SIL.rst index 3bb31eb6714..893b4db6d88 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -2333,6 +2333,32 @@ with a sequence that also correctly destroys the current value. This instruction is only valid in Raw SIL and is rewritten as appropriate by the definitive initialization pass. +assign_by_delegate +`````````````````` +:: + + sil-instruction ::= 'assign_by_delegate' sil-operand 'to' sil-operand ',' 'init' sil-operand ',' 'set' sil-operand + + assign_by_delegate %0 : $S to %1 : $*T, init %2 : $F, set %3 : $G + // $S can be a value or address type + // $T must be the type of a property delegate. + // $F must be a function type, taking $S as a single argument and returning $T + // $G must be a function type, taking $S as a single argument and with not return value + +Similar to the ``assign`` instruction, but the assignment is done via a +delegate. + +In case of an initialization, the function ``%2`` is called with ``%0`` as +argument. The result is stored to ``%1``. In case ``%2`` is an address type, +it is simply passed as a first out-argument to ``%2``. + +In case of a re-assignment, the function ``%3`` is called with ``%0`` as +argument. As ``%3`` is a setter (e.g. for the property in the containing +nominal type), the destination address ``%1`` is not used in this case. + +This instruction is only valid in Raw SIL and is rewritten as appropriate +by the definitive initialization pass. + mark_uninitialized `````````````````` :: diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index a2790eabd60..a8448a8dada 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -842,6 +842,16 @@ public: Qualifier)); } + AssignByDelegateInst *createAssignByDelegate(SILLocation Loc, + SILValue Src, SILValue Dest, + SILValue Initializer, + SILValue Setter, + AssignOwnershipQualifier Qualifier) { + return insert(new (getModule()) + AssignByDelegateInst(getSILDebugLocation(Loc), Src, Dest, + Initializer, Setter, Qualifier)); + } + StoreBorrowInst *createStoreBorrow(SILLocation Loc, SILValue Src, SILValue DestAddr) { return insert(new (getModule()) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 6c6eafa76d9..2d61ebe4e66 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1204,6 +1204,18 @@ void SILCloner::visitAssignInst(AssignInst *Inst) { Inst->getOwnershipQualifier())); } +template +void SILCloner::visitAssignByDelegateInst(AssignByDelegateInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction( + Inst, getBuilder().createAssignByDelegate(getOpLocation(Inst->getLoc()), + getOpValue(Inst->getSrc()), + getOpValue(Inst->getDest()), + getOpValue(Inst->getInitializer()), + getOpValue(Inst->getSetter()), + Inst->getOwnershipQualifier())); +} + template void SILCloner::visitMarkUninitializedInst(MarkUninitializedInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5b4c7cee240..f417d491f65 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3684,19 +3684,17 @@ enum class AssignOwnershipQualifier { }; static_assert(2 == SILNode::NumAssignOwnershipQualifierBits, "Size mismatch"); -/// AssignInst - Represents an abstract assignment to a memory location, which -/// may either be an initialization or a store sequence. This is only valid in -/// Raw SIL. -class AssignInst - : public InstructionBase { - friend SILBuilder; +template +class AssignInstBase + : public InstructionBase { - FixedOperandList<2> Operands; +protected: + FixedOperandList Operands; - AssignInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, - AssignOwnershipQualifier Qualifier = - AssignOwnershipQualifier::Unknown); + template + AssignInstBase(SILDebugLocation DebugLoc, T&&...args) : + InstructionBase(DebugLoc), + Operands(this, std::forward(args)...) { } public: enum { @@ -3711,7 +3709,20 @@ public: ArrayRef getAllOperands() const { return Operands.asArray(); } MutableArrayRef getAllOperands() { return Operands.asArray(); } +}; +/// AssignInst - Represents an abstract assignment to a memory location, which +/// may either be an initialization or a store sequence. This is only valid in +/// Raw SIL. +class AssignInst + : public AssignInstBase { + friend SILBuilder; + + AssignInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, + AssignOwnershipQualifier Qualifier = + AssignOwnershipQualifier::Unknown); + +public: AssignOwnershipQualifier getOwnershipQualifier() const { return AssignOwnershipQualifier( SILInstruction::Bits.AssignInst.OwnershipQualifier); @@ -3721,6 +3732,32 @@ public: } }; +/// AssignByDelegateInst - Represents an abstract assignment via a delegate, +/// which may either be an initialization or a store sequence. This is only +/// valid in Raw SIL. +class AssignByDelegateInst + : public AssignInstBase { + friend SILBuilder; + + AssignByDelegateInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, + SILValue Initializer, SILValue Setter, + AssignOwnershipQualifier Qualifier = + AssignOwnershipQualifier::Unknown); + +public: + + SILValue getInitializer() { return Operands[2].get(); } + SILValue getSetter() { return Operands[3].get(); } + + AssignOwnershipQualifier getOwnershipQualifier() const { + return AssignOwnershipQualifier( + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier); + } + void setOwnershipQualifier(AssignOwnershipQualifier qualifier) { + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier = unsigned(qualifier); + } +}; + /// Indicates that a memory location is uninitialized at this point and needs to /// be initialized by the end of the function and before any escape point for /// this instruction. This is only valid in Raw SIL. diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index d582c4b3901..8a6f546f5b6 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -285,6 +285,10 @@ protected: NumAssignOwnershipQualifierBits, OwnershipQualifier : NumAssignOwnershipQualifierBits ); + SWIFT_INLINE_BITFIELD(AssignByDelegateInst, NonValueInstruction, + NumAssignOwnershipQualifierBits, + OwnershipQualifier : NumAssignOwnershipQualifierBits + ); SWIFT_INLINE_BITFIELD(UncheckedOwnershipConversionInst,SingleValueInstruction, NumVOKindBits, diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index d90c2d1e85c..78323539009 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -805,6 +805,8 @@ NON_VALUE_INST(StoreInst, store, SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(AssignInst, assign, SILInstruction, MayWrite, DoesNotRelease) +NON_VALUE_INST(AssignByDelegateInst, assign_by_delegate, + SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(MarkFunctionEscapeInst, mark_function_escape, SILInstruction, None, DoesNotRelease) NON_VALUE_INST(DebugValueInst, debug_value, diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index e64af939471..095446581bb 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/Serialization/ModuleFormat.h @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 487; // Last change: Opaque result types generic params +const uint16_t SWIFTMODULE_VERSION_MINOR = 488; // assign_by_delegate using DeclIDField = BCFixed<31>; diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index a87e7939c3d..2708f36b0f7 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -933,6 +933,9 @@ public: void visitAssignInst(AssignInst *i) { llvm_unreachable("assign is not valid in canonical SIL"); } + void visitAssignByDelegateInst(AssignByDelegateInst *i) { + llvm_unreachable("assign_by_delegate is not valid in canonical SIL"); + } void visitMarkUninitializedInst(MarkUninitializedInst *i) { llvm_unreachable("mark_uninitialized is not valid in canonical SIL"); } diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index c5226b31b29..48d92862721 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -3540,6 +3540,34 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } + case SILInstructionKind::AssignByDelegateInst: { + SILValue Src, DestAddr, InitFn, SetFn; + SourceLoc DestLoc; + AssignOwnershipQualifier AssignQualifier; + if (parseTypedValueRef(Src, B) || + parseVerbatim("to") || + parseAssignOwnershipQualifier(AssignQualifier, *this) || + parseTypedValueRef(DestAddr, DestLoc, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("init") || + parseTypedValueRef(InitFn, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("set") || + parseTypedValueRef(SetFn, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + if (!DestAddr->getType().isAddress()) { + P.diagnose(DestLoc, diag::sil_operand_not_address, "destination", + OpcodeName); + return true; + } + + ResultVal = B.createAssignByDelegate(InstLoc, Src, DestAddr, InitFn, SetFn, + AssignQualifier); + break; + } + case SILInstructionKind::BeginAccessInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::EndAccessInst: diff --git a/lib/SIL/MemAccessUtils.cpp b/lib/SIL/MemAccessUtils.cpp index 7fdb51295cc..28c540fc981 100644 --- a/lib/SIL/MemAccessUtils.cpp +++ b/lib/SIL/MemAccessUtils.cpp @@ -678,6 +678,7 @@ void swift::visitAccessedAddress(SILInstruction *I, llvm_unreachable("unexpected memory access."); case SILInstructionKind::AssignInst: + case SILInstructionKind::AssignByDelegateInst: visitor(&I->getAllOperands()[AssignInst::Dest]); return; diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index b0ac2139ca1..e6694dbd3ac 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -840,6 +840,16 @@ OperandOwnershipKindClassifier::visitAssignInst(AssignInst *i) { UseLifetimeConstraint::MustBeInvalidated); } +OperandOwnershipKindMap +OperandOwnershipKindClassifier::visitAssignByDelegateInst(AssignByDelegateInst *i) { + if (getValue() != i->getSrc()) { + return Map::allLive(); + } + + return Map::compatibilityMap(ValueOwnershipKind::Owned, + UseLifetimeConstraint::MustBeInvalidated); +} + OperandOwnershipKindMap OperandOwnershipKindClassifier::visitStoreInst(StoreInst *i) { if (getValue() != i->getSrc()) { diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index 8df99bb2b67..cda5c617f7a 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -813,11 +813,22 @@ StringRef swift::getSILAccessEnforcementName(SILAccessEnforcement enforcement) { } AssignInst::AssignInst(SILDebugLocation Loc, SILValue Src, SILValue Dest, - AssignOwnershipQualifier Qualifier) - : InstructionBase(Loc), Operands(this, Src, Dest) { + AssignOwnershipQualifier Qualifier) : + AssignInstBase(Loc, Src, Dest) { SILInstruction::Bits.AssignInst.OwnershipQualifier = unsigned(Qualifier); } +AssignByDelegateInst::AssignByDelegateInst(SILDebugLocation Loc, + SILValue Src, SILValue Dest, + SILValue Initializer, + SILValue Setter, + AssignOwnershipQualifier Qualifier) : + AssignInstBase(Loc, Src, Dest, Initializer, Setter) { + assert(Initializer->getType().is()); + SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier = + unsigned(Qualifier); +} + MarkFunctionEscapeInst * MarkFunctionEscapeInst::create(SILDebugLocation Loc, ArrayRef Elements, SILFunction &F) { diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index c353d48f44f..85c84369a1d 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -1309,6 +1309,14 @@ public: *this << getIDAndType(AI->getDest()); } + void visitAssignByDelegateInst(AssignByDelegateInst *AI) { + *this << getIDAndType(AI->getSrc()) << " to "; + printAssignOwnershipQualifier(AI->getOwnershipQualifier()); + *this << getIDAndType(AI->getDest()) + << ", init " << getIDAndType(AI->getInitializer()) + << ", set " << getIDAndType(AI->getSetter()); + } + void visitMarkUninitializedInst(MarkUninitializedInst *MU) { switch (MU->getKind()) { case MarkUninitializedInst::Var: *this << "[var] "; break; diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index b2baa10fc12..80175772128 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -1761,6 +1761,53 @@ public: "Store operand type and dest type mismatch"); } + void checkAssignByDelegateInst(AssignByDelegateInst *AI) { + SILValue Src = AI->getSrc(), Dest = AI->getDest(); + require(AI->getModule().getStage() == SILStage::Raw, + "assign instruction can only exist in raw SIL"); + require(Dest->getType().isAddress(), "Must store to an address dest"); + + unsigned indirectInitResults = Src->getType().isAddress() ? 1 : 0; + + SILValue initFn = AI->getInitializer(); + CanSILFunctionType initTy = initFn->getType().castTo(); + SILFunctionConventions initConv(initTy, AI->getModule()); + require(initConv.getNumIndirectSILResults() == indirectInitResults, + "init function has wrong number of indirect results"); + unsigned firstArgIdx = initConv.getSILArgIndexOfFirstParam(); + require(initConv.getNumSILArguments() == firstArgIdx + 1, + "init function has wrong number of arguments"); + require(Src->getType() == initConv.getSILArgumentType(firstArgIdx), + "wrong argument type of init function"); + switch (initConv.getNumIndirectSILResults()) { + case 0: + require(initConv.getNumDirectSILResults() == 1, + "wrong number of init function results"); + require(Dest->getType().getObjectType() == + initConv.getDirectSILResultTypes().front(), + "wrong init function result type"); + break; + case 1: + require(initConv.getNumDirectSILResults() == 0, + "wrong number of init function results"); + require(Dest->getType() == initConv.getIndirectSILResultTypes().front(), + "wrong indirect init function result type"); + break; + default: + require(false, "wrong number of indirect init function results"); + } + + SILValue setterFn = AI->getSetter(); + CanSILFunctionType setterTy = setterFn->getType().castTo(); + SILFunctionConventions setterConv(setterTy, AI->getModule()); + require(setterConv.getNumIndirectSILResults() == 0, + "set function has indirect results"); + require(setterConv.getNumSILArguments() == 1, + "init function has wrong number of arguments"); + require(Src->getType() == setterConv.getSILArgumentType(0), + "wrong argument type of init function"); + } + #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ void checkLoad##Name##Inst(Load##Name##Inst *LWI) { \ require(LWI->getType().isObject(), "Result of load must be an object"); \ diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index a91ac5a74fe..bc618895bd9 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -731,6 +731,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::WitnessMethodInst: case SILInstructionKind::AssignInst: + case SILInstructionKind::AssignByDelegateInst: case SILInstructionKind::BranchInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::CheckedCastValueBranchInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index d0147d0ee54..097a29ad66b 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1852,6 +1852,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, qualifier); break; } + case SILInstructionKind::AssignByDelegateInst: + llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { assert(RecordKind == SIL_ONE_TYPE_VALUES && "Layout should be OneTypeValues."); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 4a596519037..125b5f0711c 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1705,6 +1705,8 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { addValueRef(operand)); break; } + case SILInstructionKind::AssignByDelegateInst: + llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { auto *BI = cast(&SI); SILValue baseOperand = BI->getBase();