Skip to content

Commit 3309809

Browse files
committed
SILGen: improve code generation for array literals
It used to be done with a library intrinsic which returns the array and an element address pointer. A `mark_dependence` was used to "connect" the two results. But this resulted in completely wrong OSSA after inlining. It just didn't get caught be the verifier because the SIL was obfuscated by address-pointer conversions. Now we don't use the second result (= the element address) of the intrinsic but generate a correct borrow scope from the first (= array) result. Within that borrow scope we project out the element address with a `ref_tail_addr` instruction. This relies on knowledge of the internal Array data structures. However those data structures are baked into the ABI since a long time and will not change.
1 parent 1803d3e commit 3309809

14 files changed

+115
-68
lines changed

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.

test/IRGen/unmanaged_objc_throw_func.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import Foundation
1515
func returnUnmanagedCFArray() throws -> Unmanaged<CFArray> {
1616
// CHECK: %[[T0:.+]] = call swiftcc { ptr, ptr } @"$ss27_allocateUninitializedArrayySayxG_BptBwlF"(i{{32|64}} 1, ptr @"$sSiN")
1717
// CHECK-NEXT: %[[T1:.+]] = extractvalue { ptr, ptr } %[[T0]], 0
18-
// CHECK-NEXT: %[[T2:.+]] = extractvalue { ptr, ptr } %[[T0]], 1
18+
// CHECK-NEXT: = extractvalue { ptr, ptr } %[[T0]], 1
19+
// CHECK-NEXT: %[[T2:.+]] = getelementptr inbounds i8, ptr %[[T1]]
1920
// CHECK-NEXT: %._value = getelementptr inbounds{{.*}} %TSi, ptr %[[T2]], i32 0, i32 0
2021
// CHECK: %[[T7:.+]] = call swiftcc ptr @"$ss27_finalizeUninitializedArrayySayxGABnlF"(ptr %[[T1]], ptr @"$sSiN")
2122
// CHECK: %[[T4:.+]] = call swiftcc ptr @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(ptr %[[T7]], ptr @"$sSiN")

test/Interop/Cxx/foreign-reference/reference-counted-irgen.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,6 @@ public func getArrayOfLocalCount() -> [NS.LocalCount] {
7474
// CHECK-NEXT: %0 = call swiftcc %swift.metadata_response @"$sSo2NSO10LocalCountVMa"(i{{.*}} 0)
7575
// CHECK-NEXT: %1 = extractvalue %swift.metadata_response %0, 0
7676
// CHECK-NEXT: %2 = call swiftcc { ptr, ptr } @"$ss27_allocateUninitializedArrayySayxG_BptBwlF"(i{{.*}} 1, ptr %1)
77-
// CHECK: %5 = call ptr @{{_ZN2NS10LocalCount6createEv|"\?create\@LocalCount\@NS\@\@SAPEAU12\@XZ"}}()
78-
// CHECK-NEXT: call void @{{_Z8LCRetainPN2NS10LocalCountE|"\?LCRetain\@\@YAXPEAULocalCount\@NS\@\@\@Z"}}(ptr %5)
77+
// CHECK: %6 = call ptr @{{_ZN2NS10LocalCount6createEv|"\?create\@LocalCount\@NS\@\@SAPEAU12\@XZ"}}()
78+
// CHECK-NEXT: call void @{{_Z8LCRetainPN2NS10LocalCountE|"\?LCRetain\@\@YAXPEAULocalCount\@NS\@\@\@Z"}}(ptr %6)
7979
// CHECK: }

test/SILGen/arguments.swift

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@ struct UnicodeScalar {}
66

77
enum Never {}
88

9+
class ArrayBufer {}
10+
911
// Minimal implementation to support varargs.
10-
struct Array<T> { }
12+
struct Array<T> {
13+
let buffer: ArrayBufer
14+
}
1115

1216
func _allocateUninitializedArray<T>(_: Builtin.Word)
1317
-> (Array<T>, Builtin.RawPointer) {
1418
Builtin.int_trap()
1519
}
1620

17-
func _finalizeUninitializedArray<T>(_ a: Array<T>) -> Array<T> {
21+
func _finalizeUninitializedArray<T>(_ a: __owned Array<T>) -> Array<T> {
1822
return a
1923
}
2024

@@ -63,7 +67,7 @@ arg_default_tuple(x:i, y:f)
6367

6468
func variadic_arg_1(_ x: Int...) {}
6569
// CHECK-LABEL: sil hidden [ossa] @$ss14variadic_arg_1{{[_0-9a-zA-Z]*}}F
66-
// CHECK: bb0([[X:%[0-9]+]] : $Array<Int>):
70+
// CHECK: bb0([[X:%[0-9]+]] : @guaranteed $Array<Int>):
6771

6872
variadic_arg_1()
6973
variadic_arg_1(i)
@@ -72,23 +76,23 @@ variadic_arg_1(i, i, i)
7276

7377
func variadic_arg_2(_ x: Int, _ y: Float...) {}
7478
// CHECK-LABEL: sil hidden [ossa] @$ss14variadic_arg_2{{[_0-9a-zA-Z]*}}F
75-
// CHECK: bb0([[X:%[0-9]+]] : $Int, [[Y:%[0-9]+]] : $Array<Float>):
79+
// CHECK: bb0([[X:%[0-9]+]] : $Int, [[Y:%[0-9]+]] : @guaranteed $Array<Float>):
7680

7781
variadic_arg_2(i)
7882
variadic_arg_2(i, f)
7983
variadic_arg_2(i, f, f, f)
8084

8185
func variadic_arg_3(_ y: Float..., x: Int) {}
8286
// CHECK-LABEL: sil hidden [ossa] @$ss14variadic_arg_3{{[_0-9a-zA-Z]*}}F
83-
// CHECK: bb0([[Y:%[0-9]+]] : $Array<Float>, [[X:%[0-9]+]] : $Int):
87+
// CHECK: bb0([[Y:%[0-9]+]] : @guaranteed $Array<Float>, [[X:%[0-9]+]] : $Int):
8488

8589
func variadic_arg_4(_ y: Float..., x: Int...) {}
8690
// CHECK-LABEL: sil hidden [ossa] @$ss14variadic_arg_4{{[_0-9a-zA-Z]*}}F
87-
// CHECK: bb0([[Y:%[0-9]+]] : $Array<Float>, [[X:%[0-9]+]] : $Array<Int>):
91+
// CHECK: bb0([[Y:%[0-9]+]] : @guaranteed $Array<Float>, [[X:%[0-9]+]] : @guaranteed $Array<Int>):
8892

8993
func variadic_arg_5(a: Int, b: Float..., c: Int, d: Int...) {}
9094
// CHECK-LABEL: sil hidden [ossa] @$ss14variadic_arg_5{{[_0-9a-zA-Z]*}}F
91-
// CHECK: bb0([[A:%[0-9]+]] : $Int, [[B:%[0-9]+]] : $Array<Float>, [[C:%[0-9]+]] : $Int, [[D:%[0-9]+]] : $Array<Int>):
95+
// CHECK: bb0([[A:%[0-9]+]] : $Int, [[B:%[0-9]+]] : @guaranteed $Array<Float>, [[C:%[0-9]+]] : $Int, [[D:%[0-9]+]] : @guaranteed $Array<Int>):
9296

9397
variadic_arg_3(x: i)
9498
variadic_arg_3(f, x: i)

test/SILGen/array_literal_abstraction.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@
55
// <rdar://problem/16039286>
66

77
// CHECK-LABEL: sil hidden [ossa] @$s25array_literal_abstraction0A9_of_funcsSayyycGyF
8-
// CHECK: pointer_to_address {{.*}} $*@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>
8+
// CHECK: ref_tail_addr %{{[0-9]+}}, $@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>
99
func array_of_funcs() -> [(() -> ())] {
1010
return [{}, {}]
1111
}
1212

1313
// CHECK-LABEL: sil hidden [ossa] @$s25array_literal_abstraction13dict_of_funcsSDySiyycGyF
14-
// CHECK: pointer_to_address {{.*}} $*(Int, @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>)
14+
// CHECK: ref_tail_addr %{{[0-9]+}}, $(Int, @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>)
1515
func dict_of_funcs() -> Dictionary<Int, () -> ()> {
1616
return [0: {}, 1: {}]
1717
}
1818

1919
func vararg_funcs(_ fs: (() -> ())...) {}
2020

2121
// CHECK-LABEL: sil hidden [ossa] @$s25array_literal_abstraction17call_vararg_funcsyyF
22-
// CHECK: pointer_to_address {{.*}} $*@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>
22+
// CHECK: ref_tail_addr %{{[0-9]+}}, $@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>
2323
func call_vararg_funcs() {
2424
vararg_funcs({}, {})
2525
}

test/SILGen/errors.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -615,8 +615,9 @@ func test_variadic(_ cat: Cat) throws {
615615
// CHECK: [[T0:%.*]] = function_ref @$ss27_allocateUninitializedArray{{.*}}F
616616
// CHECK: [[T1:%.*]] = apply [[T0]]<Cat>([[N]])
617617
// CHECK: ([[ARRAY:%.*]], [[T2:%.*]]) = destructure_tuple [[T1]]
618-
// CHECK: [[MDI:%.*]] = mark_dependence [[T2]] : $Builtin.RawPointer on [[ARRAY]]
619-
// CHECK: [[ELT0:%.*]] = pointer_to_address [[MDI]] : $Builtin.RawPointer to [strict] $*Cat
618+
// CHECK: [[BB:%.*]] = begin_borrow [[ARRAY]]
619+
// CHECK: struct_extract [[BB]]
620+
// CHECK: [[ELT0:%.*]] = ref_tail_addr
620621
// Element 0.
621622
// CHECK: [[T0:%.*]] = function_ref @$s6errors10make_a_catAA3CatCyKF : $@convention(thin) () -> (@owned Cat, @error any Error)
622623
// CHECK: try_apply [[T0]]() : $@convention(thin) () -> (@owned Cat, @error any Error), normal [[NORM_0:bb[0-9]+]], error [[ERR_0:bb[0-9]+]]
@@ -654,7 +655,7 @@ func test_variadic(_ cat: Cat) throws {
654655
// CHECK-NEXT: return
655656
// Failure from element 0.
656657
// CHECK: [[ERR_0]]([[ERROR:%.*]] : @owned $any Error):
657-
// CHECK-NOT: end_borrow
658+
// CHECK: end_borrow [[BB]]
658659
// CHECK-NEXT: // function_ref
659660
// CHECK-NEXT: [[T0:%.*]] = function_ref @$ss29_deallocateUninitializedArray{{.*}}F
660661
// CHECK-NEXT: apply [[T0]]<Cat>([[ARRAY]])
@@ -663,6 +664,7 @@ func test_variadic(_ cat: Cat) throws {
663664
// CHECK: [[ERR_2]]([[ERROR:%.*]] : @owned $any Error):
664665
// CHECK-NEXT: destroy_addr [[ELT1]]
665666
// CHECK-NEXT: destroy_addr [[ELT0]]
667+
// CHECK: end_borrow [[BB]]
666668
// CHECK-NEXT: // function_ref
667669
// CHECK-NEXT: [[T0:%.*]] = function_ref @$ss29_deallocateUninitializedArray{{.*}}F
668670
// CHECK-NEXT: apply [[T0]]<Cat>([[ARRAY]])
@@ -672,6 +674,7 @@ func test_variadic(_ cat: Cat) throws {
672674
// CHECK-NEXT: destroy_addr [[ELT2]]
673675
// CHECK-NEXT: destroy_addr [[ELT1]]
674676
// CHECK-NEXT: destroy_addr [[ELT0]]
677+
// CHECK-NEXT: end_borrow [[BB]]
675678
// CHECK-NEXT: // function_ref
676679
// CHECK-NEXT: [[T0:%.*]] = function_ref @$ss29_deallocateUninitializedArray{{.*}}F
677680
// CHECK-NEXT: apply [[T0]]<Cat>([[ARRAY]])

0 commit comments

Comments
 (0)