Skip to content

Commit ee6d801

Browse files
authored
Merge pull request #84795 from eeckstein/array-initialization
SILGen: improve code generation for array literals
2 parents d47627f + 60efd32 commit ee6d801

33 files changed

+638
-441
lines changed

benchmark/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ set(SWIFT_BENCH_MODULES
181181
single-source/SortLargeExistentials
182182
single-source/SortLettersInPlace
183183
single-source/SortStrings
184-
single-source/StackPromo
185184
single-source/StaticArray
186185
single-source/StrComplexWalk
187186
single-source/StrToInt

benchmark/single-source/StackPromo.swift

Lines changed: 0 additions & 65 deletions
This file was deleted.

benchmark/utils/main.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ import SortIntPyramids
186186
import SortLargeExistentials
187187
import SortLettersInPlace
188188
import SortStrings
189-
import StackPromo
190189
import StaticArray
191190
import StrComplexWalk
192191
import StrToInt
@@ -388,7 +387,6 @@ register(SortIntPyramids.benchmarks)
388387
register(SortLargeExistentials.benchmarks)
389388
register(SortLettersInPlace.benchmarks)
390389
register(SortStrings.benchmarks)
391-
register(StackPromo.benchmarks)
392390
register(StaticArray.benchmarks)
393391
register(StrComplexWalk.benchmarks)
394392
register(StrToInt.benchmarks)

lib/SILGen/SILGenApply.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7058,7 +7058,7 @@ RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) {
70587058
/// Allocate an uninitialized array of a given size, returning the array
70597059
/// and a pointer to its uninitialized contents, which must be initialized
70607060
/// before the array is valid.
7061-
std::pair<ManagedValue, SILValue>
7061+
ManagedValue
70627062
SILGenFunction::emitUninitializedArrayAllocation(Type ArrayTy,
70637063
SILValue Length,
70647064
SILLocation Loc) {
@@ -7075,11 +7075,13 @@ SILGenFunction::emitUninitializedArrayAllocation(Type ArrayTy,
70757075
SmallVector<ManagedValue, 2> resultElts;
70767076
std::move(result).getAll(resultElts);
70777077

7078-
// Add a mark_dependence between the interior pointer and the array value
7079-
auto dependentValue = B.createMarkDependence(Loc, resultElts[1].getValue(),
7080-
resultElts[0].getValue(),
7081-
MarkDependenceKind::Escaping);
7082-
return {resultElts[0], dependentValue};
7078+
// The second result, which is the base element address, is not used. We extract
7079+
// it from the array (= the first result) directly to create a correct borrow scope.
7080+
// TODO: Consider adding a new intrinsic which only returns the array.
7081+
// Although the current intrinsic is inlined and the code for returning the
7082+
// second result is optimized away. So it doesn't make a performance difference.
7083+
7084+
return resultElts[0];
70837085
}
70847086

70857087
/// Deallocate an uninitialized array.

lib/SILGen/SILGenExpr.cpp

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2664,6 +2664,29 @@ RValue RValueEmitter::visitUnreachableExpr(UnreachableExpr *E, SGFContext C) {
26642664
return RValue(SGF, E, ManagedValue::forRValueWithoutOwnership(undef));
26652665
}
26662666

2667+
static SILValue getArrayBuffer(SILValue array, SILGenFunction &SGF, SILLocation loc) {
2668+
SILValue v = array;
2669+
SILType storageType;
2670+
while (auto *sd = v->getType().getStructOrBoundGenericStruct()) {
2671+
ASSERT(sd->getStoredProperties().size() == 1 &&
2672+
"Array or its internal structs should have exactly one stored property");
2673+
auto *se = SGF.getBuilder().createStructExtract(loc, v, v->getType().getFieldDecl(0));
2674+
if (se->getType() == SILType::getBridgeObjectType(SGF.getASTContext())) {
2675+
auto bridgeObjTy = cast<BoundGenericStructType>(v->getType().getASTType());
2676+
CanType ct = CanType(bridgeObjTy->getGenericArgs()[0]);
2677+
storageType = SILType::getPrimitiveObjectType(ct);
2678+
}
2679+
v = se;
2680+
}
2681+
2682+
if (storageType) {
2683+
v = SGF.getBuilder().createUncheckedRefCast(loc, v, storageType);
2684+
}
2685+
ASSERT(v->getType().isReferenceCounted(&SGF.F) &&
2686+
"expected a reference-counted buffer in the Array data type");
2687+
return v;
2688+
}
2689+
26672690
VarargsInfo Lowering::emitBeginVarargs(SILGenFunction &SGF, SILLocation loc,
26682691
CanType baseTy, CanType arrayTy,
26692692
unsigned numElements) {
@@ -2675,12 +2698,7 @@ VarargsInfo Lowering::emitBeginVarargs(SILGenFunction &SGF, SILLocation loc,
26752698
SILValue numEltsVal = SGF.B.createIntegerLiteral(loc,
26762699
SILType::getBuiltinWordType(SGF.getASTContext()),
26772700
numElements);
2678-
// The first result is the array value.
2679-
ManagedValue array;
2680-
// The second result is a RawPointer to the base address of the array.
2681-
SILValue basePtr;
2682-
std::tie(array, basePtr)
2683-
= SGF.emitUninitializedArrayAllocation(arrayTy, numEltsVal, loc);
2701+
ManagedValue array = SGF.emitUninitializedArrayAllocation(arrayTy, numEltsVal, loc);
26842702

26852703
// Temporarily deactivate the main array cleanup.
26862704
if (array.hasCleanup())
@@ -2690,13 +2708,15 @@ VarargsInfo Lowering::emitBeginVarargs(SILGenFunction &SGF, SILLocation loc,
26902708
auto abortCleanup =
26912709
SGF.enterDeallocateUninitializedArrayCleanup(array.getValue());
26922710

2693-
// Turn the pointer into an address.
2694-
basePtr = SGF.B.createPointerToAddress(
2695-
loc, basePtr, baseTL.getLoweredType().getAddressType(),
2696-
/*isStrict*/ true,
2697-
/*isInvariant*/ false);
2711+
auto borrowedArray = array.borrow(SGF, loc);
2712+
auto borrowCleanup = SGF.Cleanups.getTopCleanup();
2713+
2714+
SILValue buffer = getArrayBuffer(borrowedArray.getValue(), SGF, loc);
26982715

2699-
return VarargsInfo(array, abortCleanup, basePtr, baseTL, baseAbstraction);
2716+
SILType elementAddrTy = baseTL.getLoweredType().getAddressType();
2717+
SILValue baseAddr = SGF.getBuilder().createRefTailAddr(loc, buffer, elementAddrTy);
2718+
2719+
return VarargsInfo(array, borrowCleanup, abortCleanup, baseAddr, baseTL, baseAbstraction);
27002720
}
27012721

27022722
ManagedValue Lowering::emitEndVarargs(SILGenFunction &SGF, SILLocation loc,
@@ -2710,6 +2730,8 @@ ManagedValue Lowering::emitEndVarargs(SILGenFunction &SGF, SILLocation loc,
27102730
if (array.hasCleanup())
27112731
SGF.Cleanups.setCleanupState(array.getCleanup(), CleanupState::Active);
27122732

2733+
SGF.Cleanups.popAndEmitCleanup(varargs.getBorrowCleanup(), CleanupLocation(loc), NotForUnwind);
2734+
27132735
// Array literals only need to be finalized, if the array is really allocated.
27142736
// In case of zero elements, no allocation is done, but the empty-array
27152737
// singleton is used. "Finalization" means to emit an end_cow_mutation

lib/SILGen/SILGenFunction.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,11 +1899,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
18991899
ManagedValue emitUndef(Type type);
19001900
ManagedValue emitUndef(SILType type);
19011901
RValue emitUndefRValue(SILLocation loc, Type type);
1902-
1903-
std::pair<ManagedValue, SILValue>
1904-
emitUninitializedArrayAllocation(Type ArrayTy,
1905-
SILValue Length,
1906-
SILLocation Loc);
1902+
1903+
ManagedValue emitUninitializedArrayAllocation(Type ArrayTy,
1904+
SILValue Length,
1905+
SILLocation Loc);
19071906

19081907
CleanupHandle enterDeallocateUninitializedArrayCleanup(SILValue array);
19091908
void emitUninitializedArrayDeallocation(SILLocation loc, SILValue array);

lib/SILGen/Varargs.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,26 @@ class TypeLowering;
3131
/// Information about a varargs emission.
3232
class VarargsInfo {
3333
ManagedValue Array;
34+
CleanupHandle borrowCleanup;
3435
CleanupHandle AbortCleanup;
3536
SILValue BaseAddress;
3637
AbstractionPattern BasePattern;
3738
const TypeLowering &BaseTL;
3839
public:
39-
VarargsInfo(ManagedValue array, CleanupHandle abortCleanup,
40+
VarargsInfo(ManagedValue array, CleanupHandle borrowCleanup, CleanupHandle abortCleanup,
4041
SILValue baseAddress, const TypeLowering &baseTL,
4142
AbstractionPattern basePattern)
42-
: Array(array), AbortCleanup(abortCleanup),
43+
: Array(array), borrowCleanup(borrowCleanup), AbortCleanup(abortCleanup),
4344
BaseAddress(baseAddress), BasePattern(basePattern), BaseTL(baseTL) {}
4445

4546
/// Return the array value. emitEndVarargs() is really the only
4647
/// function that should be accessing this directly.
4748
ManagedValue getArray() const {
4849
return Array;
4950
}
51+
52+
CleanupHandle getBorrowCleanup() const { return borrowCleanup; }
53+
5054
CleanupHandle getAbortCleanup() const { return AbortCleanup; }
5155

5256
/// An address of the lowered type.

lib/SILOptimizer/Analysis/ArraySemantic.cpp

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/Basic/Assertions.h"
1515
#include "swift/SIL/DebugUtils.h"
1616
#include "swift/SIL/InstructionUtils.h"
17+
#include "swift/SIL/NodeDatastructures.h"
1718
#include "swift/SIL/SILArgument.h"
1819
#include "swift/SIL/SILBuilder.h"
1920
#include "swift/SIL/SILFunction.h"
@@ -732,14 +733,10 @@ SILValue swift::ArraySemanticsCall::getArrayElementStoragePointer() const {
732733
return getArrayUninitializedInitResult(*this, 1);
733734
}
734735

735-
bool swift::ArraySemanticsCall::mapInitializationStores(
736-
llvm::DenseMap<uint64_t, StoreInst *> &ElementValueMap) {
737-
if (getKind() != ArrayCallKind::kArrayUninitialized &&
738-
getKind() != ArrayCallKind::kArrayUninitializedIntrinsic)
739-
return false;
740-
SILValue ElementBuffer = getArrayElementStoragePointer();
736+
static SILValue getElementBaseAddress(ArraySemanticsCall initArray) {
737+
SILValue ElementBuffer = initArray.getArrayElementStoragePointer();
741738
if (!ElementBuffer)
742-
return false;
739+
return SILValue();
743740

744741
// Match initialization stores into ElementBuffer. E.g.
745742
// %82 = struct_extract %element_buffer : $UnsafeMutablePointer<Int>
@@ -756,9 +753,29 @@ bool swift::ArraySemanticsCall::mapInitializationStores(
756753
// mark_dependence can be an operand of the struct_extract or its user.
757754

758755
SILValue UnsafeMutablePointerExtract;
759-
if (getKind() == ArrayCallKind::kArrayUninitializedIntrinsic) {
756+
if (initArray.getKind() == ArrayCallKind::kArrayUninitializedIntrinsic) {
760757
UnsafeMutablePointerExtract = dyn_cast_or_null<MarkDependenceInst>(
761758
getSingleNonDebugUser(ElementBuffer));
759+
if (!UnsafeMutablePointerExtract) {
760+
SILValue array = initArray.getArrayValue();
761+
ValueWorklist worklist(array);
762+
while (SILValue v = worklist.pop()) {
763+
for (auto use : v->getUses()) {
764+
switch (use->getUser()->getKind()) {
765+
case SILInstructionKind::UncheckedRefCastInst:
766+
case SILInstructionKind::StructExtractInst:
767+
case SILInstructionKind::BeginBorrowInst:
768+
worklist.pushIfNotVisited(cast<SingleValueInstruction>(use->getUser()));
769+
break;
770+
case SILInstructionKind::RefTailAddrInst:
771+
return cast<RefTailAddrInst>(use->getUser());
772+
default:
773+
break;
774+
}
775+
}
776+
}
777+
return SILValue();
778+
}
762779
} else {
763780
auto user = getSingleNonDebugUser(ElementBuffer);
764781
// Match mark_dependence (struct_extract or
@@ -774,21 +791,33 @@ bool swift::ArraySemanticsCall::mapInitializationStores(
774791
}
775792
}
776793
if (!UnsafeMutablePointerExtract)
777-
return false;
794+
return SILValue();
778795

779796
auto *PointerToAddress = dyn_cast_or_null<PointerToAddressInst>(
780797
getSingleNonDebugUser(UnsafeMutablePointerExtract));
781798
if (!PointerToAddress)
799+
return SILValue();
800+
return PointerToAddress;
801+
}
802+
803+
bool swift::ArraySemanticsCall::mapInitializationStores(
804+
llvm::DenseMap<uint64_t, StoreInst *> &ElementValueMap) {
805+
if (getKind() != ArrayCallKind::kArrayUninitialized &&
806+
getKind() != ArrayCallKind::kArrayUninitializedIntrinsic)
807+
return false;
808+
809+
SILValue elementAddr = getElementBaseAddress(*this);
810+
if (!elementAddr)
782811
return false;
783812

784813
// Match the stores. We can have either a store directly to the address or
785814
// to an index_addr projection.
786-
for (auto *Op : PointerToAddress->getUses()) {
815+
for (auto *Op : elementAddr->getUses()) {
787816
auto *Inst = Op->getUser();
788817

789818
// Store to the base.
790819
auto *SI = dyn_cast<StoreInst>(Inst);
791-
if (SI && SI->getDest() == PointerToAddress) {
820+
if (SI && SI->getDest() == elementAddr) {
792821
// We have already seen an entry for this index bail.
793822
if (ElementValueMap.count(0))
794823
return false;

0 commit comments

Comments
 (0)