Skip to content

[WIP] Support or property delegates in DI #1

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
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
26 changes: 26 additions & 0 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
``````````````````
::
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SIL/InstructionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ bool isSanitizerInstrumentation(SILInstruction *Instruction);
/// argument of the partial apply if it is.
SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI);

/// Returns true if \p PAI is only used by an assign_by_delegate instruction as
/// init or set function.
bool onlyUsedByAssignByDelegate(PartialApplyInst *PAI);

/// If V is a function closure, return the reaching set of partial_apply's.
void findClosuresForFunctionValue(SILValue V,
TinyPtrVector<PartialApplyInst *> &results);
Expand Down
10 changes: 10 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,16 @@ class SILBuilder {
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())
Expand Down
12 changes: 12 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,18 @@ void SILCloner<ImplClass>::visitAssignInst(AssignInst *Inst) {
Inst->getOwnershipQualifier()));
}

template <typename ImplClass>
void SILCloner<ImplClass>::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<typename ImplClass>
void
SILCloner<ImplClass>::visitMarkUninitializedInst(MarkUninitializedInst *Inst) {
Expand Down
59 changes: 48 additions & 11 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3708,19 +3708,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<SILInstructionKind::AssignInst,
NonValueInstruction> {
friend SILBuilder;
template <SILInstructionKind Kind, int NumOps>
class AssignInstBase
: public InstructionBase<Kind, NonValueInstruction> {

FixedOperandList<2> Operands;
protected:
FixedOperandList<NumOps> Operands;

AssignInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest,
AssignOwnershipQualifier Qualifier =
AssignOwnershipQualifier::Unknown);
template <class... T>
AssignInstBase(SILDebugLocation DebugLoc, T&&...args) :
InstructionBase<Kind, NonValueInstruction>(DebugLoc),
Operands(this, std::forward<T>(args)...) { }

public:
enum {
Expand All @@ -3735,7 +3733,20 @@ class AssignInst

ArrayRef<Operand> getAllOperands() const { return Operands.asArray(); }
MutableArrayRef<Operand> 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<SILInstructionKind::AssignInst, 2> {
friend SILBuilder;

AssignInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest,
AssignOwnershipQualifier Qualifier =
AssignOwnershipQualifier::Unknown);

public:
AssignOwnershipQualifier getOwnershipQualifier() const {
return AssignOwnershipQualifier(
SILInstruction::Bits.AssignInst.OwnershipQualifier);
Expand All @@ -3745,6 +3756,32 @@ class AssignInst
}
};

/// 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<SILInstructionKind::AssignByDelegateInst, 4> {
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.
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SIL/SILNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ class alignas(8) SILNode {
NumAssignOwnershipQualifierBits,
OwnershipQualifier : NumAssignOwnershipQualifierBits
);
SWIFT_INLINE_BITFIELD(AssignByDelegateInst, NonValueInstruction,
NumAssignOwnershipQualifierBits,
OwnershipQualifier : NumAssignOwnershipQualifierBits
);

SWIFT_INLINE_BITFIELD(UncheckedOwnershipConversionInst,SingleValueInstruction,
NumVOKindBits,
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 @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 480; // property delegates
const uint16_t SWIFTMODULE_VERSION_MINOR = 481; // assign_by_delegate

using DeclIDField = BCFixed<31>;

Expand Down
3 changes: 3 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,9 @@ class IRGenSILFunction :
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");
}
Expand Down
28 changes: 28 additions & 0 deletions lib/ParseSIL/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
15 changes: 15 additions & 0 deletions lib/SIL/InstructionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,21 @@ SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) {
return Arg;
}

bool swift::onlyUsedByAssignByDelegate(PartialApplyInst *PAI) {
bool usedByAssignByDelegate = false;
for (Operand *Op : PAI->getUses()) {
SILInstruction *User = Op->getUser();
if (isa<AssignByDelegateInst>(User) && Op->getOperandNumber() >= 2) {
usedByAssignByDelegate = true;
continue;
}
if (isa<DestroyValueInst>(User))
continue;
return false;
}
return usedByAssignByDelegate;
}

/// Given a block used as a noescape function argument, attempt to find all
/// Swift closures that invoking the block will call. The StoredClosures may not
/// actually be partial_apply instructions. They may be copied, block arguments,
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/MemAccessUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
10 changes: 10 additions & 0 deletions lib/SIL/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
15 changes: 13 additions & 2 deletions lib/SIL/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SILFunctionType>());
SILInstruction::Bits.AssignByDelegateInst.OwnershipQualifier =
unsigned(Qualifier);
}

MarkFunctionEscapeInst *
MarkFunctionEscapeInst::create(SILDebugLocation Loc,
ArrayRef<SILValue> Elements, SILFunction &F) {
Expand Down
8 changes: 8 additions & 0 deletions lib/SIL/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,14 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
*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;
Expand Down
47 changes: 47 additions & 0 deletions lib/SIL/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1757,6 +1757,53 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"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<SILFunctionType>();
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<SILFunctionType>();
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"); \
Expand Down
Loading