Skip to content

Commit 6a65c78

Browse files
committed
[sil] Add tuple_addr_constructor an instruction that can be used to initial a tuple in memory from individual address and object components.
This commit just introduces the instruction. In a subsequent commit, I am going to add support to SILGen to emit this. This ensures that when we assign into a tuple var we initialize it with one instruction instead of doing it in pieces. The problem with doing it in pieces is that when one is emitting diagnostics it looks semantically like SILGen actually is emitting code for initializing in pieces which could be an error.
1 parent c47e943 commit 6a65c78

23 files changed

+643
-102
lines changed

docs/SIL.rst

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4799,6 +4799,71 @@ eliminated. However, a memory location ``%a`` must not be accessed
47994799
after ``destroy_addr %a`` (which has not yet been eliminated)
48004800
regardless of its type.
48014801

4802+
tuple_addr_constructor
4803+
``````````````````````
4804+
4805+
::
4806+
4807+
sil-instruction ::= 'tuple_addr_constructor' sil-tuple-addr-constructor-init sil-operand 'with' sil-tuple-addr-constructor-elements
4808+
sil-tuple-addr-constructor-init ::= init|assign
4809+
sil-tuple-addr-constructor-elements ::= '(' (sil-operand (',' sil-operand)*)? ')'
4810+
4811+
// %destAddr has the type $*(Type1, Type2, Type3). Note how we convert all of the types
4812+
// to their address form.
4813+
%1 = tuple_addr_constructor [init] %destAddr : $*(Type1, Type2, Type3) with (%a : $Type1, %b : $*Type2, %c : $Type3)
4814+
4815+
Creates a new tuple in memory from an exploded list of object and address
4816+
values. The SSA values form the leaf elements of the exploded tuple. So for a
4817+
simple tuple that only has top level tuple elements, then the instruction lowers
4818+
as follows::
4819+
4820+
%1 = tuple_addr_constructor [init] %destAddr : $*(Type1, Type2, Type3) with (%a : $Type1, %b : $*Type2, %c : $Type3)
4821+
4822+
-->
4823+
4824+
%0 = tuple_element_addr %destAddr : $*(Type1, Type2, Type3), 0
4825+
store %a to [init] %0 : $*Type1
4826+
%1 = tuple_element_addr %destAddr : $*(Type1, Type2, Type3), 1
4827+
copy_addr %b to [init] %1 : $*Type2
4828+
%2 = tuple_element_addr %destAddr : $*(Type1, Type2, Type3), 2
4829+
store %2 to [init] %2 : $*Type3
4830+
4831+
A ``tuple_addr_constructor`` is lowered similarly with each store/copy_addr
4832+
being changed to their dest assign form.
4833+
4834+
In contrast, if we have a more complicated form of tuple with sub-tuples, then
4835+
we read one element from the list as we process the tuple recursively from left
4836+
to right. So for instance we would lower as follows a more complicated tuple::
4837+
4838+
%1 = tuple_addr_constructor [init] %destAddr : $*((), (Type1, ((), Type2)), Type3) with (%a : $Type1, %b : $*Type2, %c : $Type3)
4839+
4840+
->
4841+
4842+
%0 = tuple_element_addr %destAddr : $*((), (Type1, ((), Type2)), Type3), 1
4843+
%1 = tuple_element_addr %0 : $*(Type1, ((), Type2)), 0
4844+
store %a to [init] %1 : $*Type1
4845+
%2 = tuple_element_addr %0 : $*(Type1, ((), Type2)), 1
4846+
%3 = tuple_element_addr %2 : $*((), Type2), 1
4847+
copy_addr %b to [init] %3 : $*Type2
4848+
%4 = tuple_element_addr %destAddr : $*((), (Type1, ((), Type2)), Type3), 2
4849+
store %c to [init] %4 : $*Type3
4850+
4851+
This instruction exists to enable for SILGen to init and assign RValues into
4852+
tuples with a single instruction. Since an RValue is a potentially exploded
4853+
tuple, we are forced to use our representation here. If SILGen instead just uses
4854+
separate address projections and stores when it sees such an aggregate,
4855+
diagnostic SIL passes can not tell the difference semantically in between
4856+
initializing a tuple in parts or at once::
4857+
4858+
var arg = (Type1(), Type2())
4859+
4860+
// This looks the same at the SIL level...
4861+
arg = (a, b)
4862+
4863+
// to assigning in pieces even though we have formed a new tuple.
4864+
arg.0 = a
4865+
arg.1 = a
4866+
48024867
index_addr
48034868
``````````
48044869
::

include/swift/SIL/AddressWalker.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) && {
199199
isa<UncheckedRefCastAddrInst>(user) || isa<KeyPathInst>(user) ||
200200
isa<RetainValueAddrInst>(user) || isa<ReleaseValueAddrInst>(user) ||
201201
isa<PackElementSetInst>(user) || isa<PackElementGetInst>(user) ||
202-
isa<DeinitExistentialAddrInst>(user) || isa<LoadBorrowInst>(user)) {
202+
isa<DeinitExistentialAddrInst>(user) || isa<LoadBorrowInst>(user) ||
203+
isa<TupleAddrConstructorInst>(user)) {
203204
callVisitUse(op);
204205
continue;
205206
}

include/swift/SIL/InstructionUtils.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,23 @@ struct PolymorphicBuiltinSpecializedOverloadInfo {
205205
/// polymorphic builtin or does not have any available overload for these types,
206206
/// return SILValue().
207207
SILValue getStaticOverloadForSpecializedPolymorphicBuiltin(BuiltinInst *bi);
208+
209+
/// Visit the exploded leaf elements of a tuple type that contains potentially
210+
/// a tree of tuples.
211+
///
212+
/// If visitor returns false, we stop processing early. We return true if we
213+
/// visited all of the tuple elements without the visitor returing false.
214+
bool visitExplodedTupleType(SILType type,
215+
llvm::function_ref<bool(SILType)> callback);
216+
217+
/// Visit the exploded leaf elements of a tuple type that contains potentially
218+
/// a tree of tuples.
219+
///
220+
/// If visitor returns false, we stop processing early. We return true if we
221+
/// visited all of the tuple elements without the visitor returing false.
222+
bool visitExplodedTupleValue(SILValue value,
223+
llvm::function_ref<SILValue(SILValue, std::optional<unsigned>)> callback);
224+
208225
} // end namespace swift
209226

210227
#endif

include/swift/SIL/SILBuilder.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,15 @@ class SILBuilder {
16441644

16451645
TupleInst *createTuple(SILLocation loc, ArrayRef<SILValue> elts);
16461646

1647+
TupleAddrConstructorInst *
1648+
createTupleAddrConstructor(SILLocation Loc, SILValue DestAddr,
1649+
ArrayRef<SILValue> Elements,
1650+
IsInitialization_t IsInitOfDest) {
1651+
return insert(TupleAddrConstructorInst::create(getSILDebugLocation(Loc),
1652+
DestAddr, Elements,
1653+
IsInitOfDest, getModule()));
1654+
}
1655+
16471656
EnumInst *createEnum(SILLocation Loc, SILValue Operand,
16481657
EnumElementDecl *Element, SILType Ty) {
16491658
return createEnum(Loc, Operand, Element, Ty,

include/swift/SIL/SILCloner.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,17 @@ SILCloner<ImplClass>::visitTupleInst(TupleInst *Inst) {
21812181
: ValueOwnershipKind(OwnershipKind::None)));
21822182
}
21832183

2184+
template <typename ImplClass>
2185+
void SILCloner<ImplClass>::visitTupleAddrConstructorInst(
2186+
TupleAddrConstructorInst *Inst) {
2187+
auto Elements = getOpValueArray<8>(Inst->getElements());
2188+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
2189+
recordClonedInstruction(Inst, getBuilder().createTupleAddrConstructor(
2190+
getOpLocation(Inst->getLoc()),
2191+
getOpValue(Inst->getDestValue()), Elements,
2192+
Inst->isInitializationOfDest()));
2193+
}
2194+
21842195
template<typename ImplClass>
21852196
void
21862197
SILCloner<ImplClass>::visitEnumInst(EnumInst *Inst) {

include/swift/SIL/SILInstruction.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6339,6 +6339,76 @@ class TupleInst final : public InstructionBaseWithTrailingOperands<
63396339
}
63406340
};
63416341

6342+
/// TupleAddrConstructorInst - a constructor for address tuples. Can take
6343+
/// objects and addresses. Intended only to be used with diagnostics and be
6344+
/// lowered after diagnostics run. Once we have opaque values this will not be
6345+
/// necessary.
6346+
///
6347+
/// tuple_addr_constructor [init] dest with (operands)
6348+
///
6349+
/// This always consumes its operands but will either init or assign into dest.
6350+
class TupleAddrConstructorInst final
6351+
: public InstructionBaseWithTrailingOperands<
6352+
SILInstructionKind::TupleAddrConstructorInst,
6353+
TupleAddrConstructorInst, NonValueInstruction> {
6354+
friend SILBuilder;
6355+
USE_SHARED_UINT8;
6356+
6357+
TupleAddrConstructorInst(SILDebugLocation DebugLoc, ArrayRef<SILValue> Elts,
6358+
IsInitialization_t IsInitOfDest)
6359+
: InstructionBaseWithTrailingOperands(Elts, DebugLoc) {
6360+
sharedUInt8().TupleAddrConstructorInst.isInitializationOfDest =
6361+
bool(IsInitOfDest);
6362+
}
6363+
6364+
static TupleAddrConstructorInst *create(SILDebugLocation DebugLoc,
6365+
SILValue DestAddr,
6366+
ArrayRef<SILValue> Elements,
6367+
IsInitialization_t IsInitOfDest,
6368+
SILModule &Mod);
6369+
6370+
public:
6371+
enum {
6372+
Dest = 0,
6373+
};
6374+
6375+
Operand &getDest() { return getAllOperands().front(); }
6376+
const Operand &getDest() const { return getAllOperands().front(); }
6377+
6378+
SILValue getDestValue() const { return getDest().get(); }
6379+
6380+
/// The elements referenced by this TupleInst.
6381+
MutableArrayRef<Operand> getElementOperands() {
6382+
return getAllOperands().drop_front();
6383+
}
6384+
6385+
/// The elements referenced by this TupleInst.
6386+
OperandValueArrayRef getElements() const {
6387+
return OperandValueArrayRef(getAllOperands().drop_front());
6388+
}
6389+
6390+
/// Return the i'th value referenced by this TupleInst.
6391+
SILValue getElement(unsigned i) const { return getElements()[i]; }
6392+
6393+
unsigned getElementIndex(Operand *operand) {
6394+
assert(operand->getUser() == this);
6395+
assert(operand != &getDest() && "Cannot pass in the destination");
6396+
return operand->getOperandNumber() + 1;
6397+
}
6398+
6399+
TupleType *getTupleType() const {
6400+
return getDest().get()->getType().getRawASTType()->castTo<TupleType>();
6401+
}
6402+
6403+
IsInitialization_t isInitializationOfDest() const {
6404+
return IsInitialization_t(
6405+
sharedUInt8().TupleAddrConstructorInst.isInitializationOfDest);
6406+
}
6407+
void setIsInitializationOfDest(IsInitialization_t I) {
6408+
sharedUInt8().TupleAddrConstructorInst.isInitializationOfDest = (bool)I;
6409+
}
6410+
};
6411+
63426412
/// Represents a loadable enum constructed from one of its
63436413
/// elements.
63446414
class EnumInst

include/swift/SIL/SILNode.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ class alignas(8) SILNode :
251251
isTakeOfSrc : 1,
252252
isInitializationOfDest : 1);
253253

254+
SHARED_FIELD(TupleAddrConstructorInst, uint8_t
255+
isInitializationOfDest : 1);
256+
254257
SHARED_FIELD(PointerToAddressInst, uint8_t
255258
isStrict : 1,
256259
isInvariant : 1);

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,8 @@ NON_VALUE_INST(CopyAddrInst, copy_addr,
848848
SILInstruction, MayHaveSideEffects, MayRelease)
849849
NON_VALUE_INST(ExplicitCopyAddrInst, explicit_copy_addr,
850850
SILInstruction, MayHaveSideEffects, MayRelease)
851+
NON_VALUE_INST(TupleAddrConstructorInst, tuple_addr_constructor,
852+
SILInstruction, MayHaveSideEffects, MayRelease)
851853
NON_VALUE_INST(DestroyAddrInst, destroy_addr,
852854
SILInstruction, MayHaveSideEffects, MayRelease)
853855
NON_VALUE_INST(EndLifetimeInst, end_lifetime,

lib/IRGen/IRGenSIL.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,9 @@ class IRGenSILFunction :
14171417
void visitMarkUnresolvedMoveAddrInst(MarkUnresolvedMoveAddrInst *mai) {
14181418
llvm_unreachable("Valid only when ownership is enabled");
14191419
}
1420+
void visitTupleAddrConstructorInst(TupleAddrConstructorInst *i) {
1421+
llvm_unreachable("Valid only in raw SIL");
1422+
}
14201423
void visitDestroyAddrInst(DestroyAddrInst *i);
14211424

14221425
void visitBindMemoryInst(BindMemoryInst *i);

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,15 @@ OperandOwnership OperandOwnershipClassifier::visitKeyPathInst(KeyPathInst *I) {
676676
return OperandOwnership::ForwardingConsume;
677677
}
678678

679+
OperandOwnership OperandOwnershipClassifier::visitTupleAddrConstructorInst(
680+
TupleAddrConstructorInst *inst) {
681+
// If we have an object, then we have an instantaneous use...
682+
if (getValue()->getType().isObject())
683+
return OperandOwnership::DestroyingConsume;
684+
// Otherwise, we have a trivial use since we have an address.
685+
return OperandOwnership::TrivialUse;
686+
}
687+
679688
//===----------------------------------------------------------------------===//
680689
// Builtin Use Checker
681690
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)