Skip to content

Commit 48b4ca0

Browse files
authored
Merge pull request #69686 from gottesmm/rdar117880194
[region-isolation] When assigning RValues into memory, use tuple_addr_constructor instead of doing it in pieces.
2 parents ab6734a + b1f6903 commit 48b4ca0

40 files changed

+1300
-166
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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,35 @@ SILCloner<ImplClass>::visitTupleInst(TupleInst *Inst) {
21812181
: ValueOwnershipKind(OwnershipKind::None)));
21822182
}
21832183

2184+
template <typename ImplClass>
2185+
void SILCloner<ImplClass>::visitTupleAddrConstructorInst(
2186+
TupleAddrConstructorInst *Inst) {
2187+
SmallVector<SILValue, 8> Elements;
2188+
for (auto e : Inst->getElements()) {
2189+
SILValue mappedValue = getOpValue(e);
2190+
2191+
// Check if mappedValue only consists of empty tuple elements. If it does,
2192+
// then we do not add it to our result. This is because we know that the
2193+
// corresponding elements in getOpValue(Inst->getDest()) will also change
2194+
// into an empty exploded tuple. Since we only have leaf non-empty non-tuple
2195+
// elements as operands, these are not represented.
2196+
bool FoundNonTuple = false;
2197+
mappedValue->getType().getASTType().visit(
2198+
[&](CanType ty) { FoundNonTuple |= !ty->is<TupleType>(); });
2199+
if (FoundNonTuple)
2200+
Elements.push_back(mappedValue);
2201+
}
2202+
2203+
if (Elements.empty())
2204+
return;
2205+
2206+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
2207+
recordClonedInstruction(Inst, getBuilder().createTupleAddrConstructor(
2208+
getOpLocation(Inst->getLoc()),
2209+
getOpValue(Inst->getDest()), Elements,
2210+
Inst->isInitializationOfDest()));
2211+
}
2212+
21842213
template<typename ImplClass>
21852214
void
21862215
SILCloner<ImplClass>::visitEnumInst(EnumInst *Inst) {

include/swift/SIL/SILInstruction.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6339,6 +6339,80 @@ 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 &getDestOperand() { return getAllOperands().front(); }
6376+
const Operand &getDestOperand() const { return getAllOperands().front(); }
6377+
6378+
SILValue getDest() const { return getDestOperand().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 != &getDestOperand() && "Cannot pass in the destination");
6396+
return operand->getOperandNumber() + 1;
6397+
}
6398+
6399+
unsigned getNumElements() const { return getTupleType()->getNumElements(); }
6400+
6401+
TupleType *getTupleType() const {
6402+
// We use getASTType() since we want to look through a wrapped noncopyable
6403+
// type to get to the underlying tuple type.
6404+
return getDest()->getType().getASTType()->castTo<TupleType>();
6405+
}
6406+
6407+
IsInitialization_t isInitializationOfDest() const {
6408+
return IsInitialization_t(
6409+
sharedUInt8().TupleAddrConstructorInst.isInitializationOfDest);
6410+
}
6411+
void setIsInitializationOfDest(IsInitialization_t I) {
6412+
sharedUInt8().TupleAddrConstructorInst.isInitializationOfDest = (bool)I;
6413+
}
6414+
};
6415+
63426416
/// Represents a loadable enum constructed from one of its
63436417
/// elements.
63446418
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,

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ PASS(IRGenPrepare, "irgen-prepare",
369369
"Cleanup SIL in preparation for IRGen")
370370
PASS(TransferNonSendable, "transfer-non-sendable",
371371
"Checks calls that send non-sendable values between isolation domains")
372+
PASS(LowerTupleAddrConstructor, "lower-tuple-addr-constructor",
373+
"Lower tuple addr constructor to tuple_element_addr+copy_addr")
372374
PASS(SILGenCleanup, "silgen-cleanup",
373375
"Cleanup SIL in preparation for diagnostics")
374376
PASS(SILCombine, "sil-combine",

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
//===----------------------------------------------------------------------===//

lib/SIL/IR/SILInstructions.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,18 @@ TupleInst *TupleInst::create(SILDebugLocation Loc, SILType Ty,
14601460
return ::new (Buffer) TupleInst(Loc, Ty, Elements, forwardingOwnershipKind);
14611461
}
14621462

1463+
TupleAddrConstructorInst *TupleAddrConstructorInst::create(
1464+
SILDebugLocation Loc, SILValue DestAddr, ArrayRef<SILValue> Elements,
1465+
IsInitialization_t IsInitOfDest, SILModule &M) {
1466+
assert(DestAddr->getType().isAddress());
1467+
auto Size = totalSizeToAlloc<swift::Operand>(Elements.size() + 1);
1468+
auto Buffer = M.allocateInst(Size, alignof(TupleAddrConstructorInst));
1469+
llvm::SmallVector<SILValue, 16> Data;
1470+
Data.push_back(DestAddr);
1471+
copy(Elements, std::back_inserter(Data));
1472+
return ::new (Buffer) TupleAddrConstructorInst(Loc, Data, IsInitOfDest);
1473+
}
1474+
14631475
bool TupleExtractInst::isTrivialEltOfOneRCIDTuple() const {
14641476
auto *F = getFunction();
14651477

lib/SIL/IR/SILPrinter.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2215,7 +2215,25 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
22152215
*this << ')';
22162216
}
22172217
}
2218-
2218+
2219+
void visitTupleAddrConstructorInst(TupleAddrConstructorInst *TI) {
2220+
// First print out our dest.
2221+
if (TI->isInitializationOfDest()) {
2222+
*this << "[init] ";
2223+
} else {
2224+
*this << "[assign] ";
2225+
}
2226+
*this << getIDAndType(TI->getDest());
2227+
2228+
*this << " with (";
2229+
2230+
llvm::interleave(
2231+
TI->getElements(), [&](const SILValue &V) { *this << getIDAndType(V); },
2232+
[&] { *this << ", "; });
2233+
2234+
*this << ')';
2235+
}
2236+
22192237
void visitEnumInst(EnumInst *UI) {
22202238
*this << UI->getType() << ", "
22212239
<< SILDeclRef(UI->getElement(), SILDeclRef::Kind::EnumElement);

0 commit comments

Comments
 (0)