Skip to content

Commit 7c69bec

Browse files
authored
Merge pull request #73782 from eeckstein/static-arrays-in-embedded
IRGen: support read-only statically initialized arrays in embedded swift
2 parents 4a1662d + 1621393 commit 7c69bec

File tree

11 files changed

+182
-26
lines changed

11 files changed

+182
-26
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ extension Instruction {
370370
is VectorInst,
371371
is AllocVectorInst,
372372
is UncheckedRefCastInst,
373+
is UpcastInst,
373374
is ValueToBridgeObjectInst,
374375
is ConvertFunctionInst,
375376
is ThinToThickFunctionInst,

include/swift/IRGen/Linking.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ class LinkEntity {
127127
// This field appears in the TypeMetadata and ObjCResilientClassStub kinds.
128128
MetadataAddressShift = 8, MetadataAddressMask = 0x0300,
129129

130+
// This field appears in the TypeMetadata kind.
131+
ForceSharedShift = 12, ForceSharedMask = 0x1000,
132+
130133
// This field appears in associated type access functions.
131134
AssociatedTypeIndexShift = 8, AssociatedTypeIndexMask = ~KindMask,
132135

@@ -847,12 +850,14 @@ class LinkEntity {
847850
}
848851

849852
static LinkEntity forTypeMetadata(CanType concreteType,
850-
TypeMetadataAddress addr) {
853+
TypeMetadataAddress addr,
854+
bool forceShared = false) {
851855
assert(!isObjCImplementation(concreteType));
852856
assert(!isEmbedded(concreteType) || isMetadataAllowedInEmbedded(concreteType));
853857
LinkEntity entity;
854858
entity.setForType(Kind::TypeMetadata, concreteType);
855859
entity.Data |= LINKENTITY_SET_FIELD(MetadataAddress, unsigned(addr));
860+
entity.Data |= LINKENTITY_SET_FIELD(ForceShared, unsigned(forceShared));
856861
return entity;
857862
}
858863

@@ -1584,6 +1589,10 @@ class LinkEntity {
15841589
getKind() == Kind::ObjCResilientClassStub);
15851590
return (TypeMetadataAddress)LINKENTITY_GET_FIELD(Data, MetadataAddress);
15861591
}
1592+
bool isForcedShared() const {
1593+
assert(getKind() == Kind::TypeMetadata);
1594+
return (bool)LINKENTITY_GET_FIELD(Data, ForceShared);
1595+
}
15871596
bool isObjCClassRef() const {
15881597
return getKind() == Kind::ObjCClassRef;
15891598
}

include/swift/SIL/SILBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,10 @@ class SILBuilder {
12511251
UpcastInst *createUpcast(SILLocation Loc, SILValue Op, SILType Ty,
12521252
ValueOwnershipKind forwardingOwnershipKind) {
12531253
assert(Ty.isObject());
1254+
if (isInsertingIntoGlobal()) {
1255+
return insert(UpcastInst::create(getSILDebugLocation(Loc), Op, Ty,
1256+
getModule(), forwardingOwnershipKind));
1257+
}
12541258
return insert(UpcastInst::create(getSILDebugLocation(Loc), Op, Ty,
12551259
getFunction(), forwardingOwnershipKind));
12561260
}

include/swift/SIL/SILInstruction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5879,6 +5879,10 @@ class UpcastInst final : public UnaryInstructionWithTypeDependentOperandsBase<
58795879
forwardingOwnershipKind) {
58805880
}
58815881

5882+
static UpcastInst *create(SILDebugLocation DebugLoc, SILValue Operand,
5883+
SILType Ty, SILModule &Mod,
5884+
ValueOwnershipKind forwardingOwnershipKind);
5885+
58825886
static UpcastInst *create(SILDebugLocation DebugLoc, SILValue Operand,
58835887
SILType Ty, SILFunction &F,
58845888
ValueOwnershipKind forwardingOwnershipKind);

lib/IRGen/GenConstant.cpp

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,9 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand,
358358
} else if (auto *URCI = dyn_cast<UncheckedRefCastInst>(operand)) {
359359
return emitConstantValue(IGM, URCI->getOperand(), flatten);
360360

361+
} else if (auto *UCI = dyn_cast<UpcastInst>(operand)) {
362+
return emitConstantValue(IGM, UCI->getOperand(), flatten);
363+
361364
} else if (auto *T2TFI = dyn_cast<ThinToThickFunctionInst>(operand)) {
362365
SILType type = operand->getType();
363366
auto *sTy = cast<llvm::StructType>(IGM.getTypeInfo(type).getStorageType());
@@ -453,22 +456,37 @@ llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI,
453456

454457
if (IGM.canMakeStaticObjectReadOnly(OI->getType())) {
455458
if (!IGM.swiftImmortalRefCount) {
456-
auto *var = new llvm::GlobalVariable(IGM.Module, IGM.Int8Ty,
457-
/*constant*/ true, llvm::GlobalValue::ExternalLinkage,
458-
/*initializer*/ nullptr, "_swiftImmortalRefCount");
459-
IGM.swiftImmortalRefCount = var;
459+
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
460+
// = HeapObject.immortalRefCount
461+
IGM.swiftImmortalRefCount = llvm::ConstantInt::get(IGM.IntPtrTy, -1);
462+
} else {
463+
IGM.swiftImmortalRefCount = llvm::ConstantExpr::getPtrToInt(
464+
new llvm::GlobalVariable(IGM.Module, IGM.Int8Ty,
465+
/*constant*/ true, llvm::GlobalValue::ExternalLinkage,
466+
/*initializer*/ nullptr, "_swiftImmortalRefCount"),
467+
IGM.IntPtrTy);
468+
}
460469
}
461470
if (!IGM.swiftStaticArrayMetadata) {
462471
auto *classDecl = IGM.getStaticArrayStorageDecl();
463472
assert(classDecl && "no __StaticArrayStorage in stdlib");
464473
CanType classTy = CanType(ClassType::get(classDecl, Type(), IGM.Context));
465-
LinkEntity entity = LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::AddressPoint);
466-
auto *metatype = IGM.getAddrOfLLVMVariable(entity, NotForDefinition, DebugTypeInfo());
467-
IGM.swiftStaticArrayMetadata = cast<llvm::GlobalVariable>(metatype);
474+
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
475+
LinkEntity entity = LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::AddressPoint,
476+
/*forceShared=*/ true);
477+
// In embedded swift, the metadata for the array buffer class only needs to be very minimal:
478+
// No vtable needed, because the object is never destructed. It only contains the null super-
479+
// class pointer.
480+
llvm::Constant *superClass = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
481+
IGM.swiftStaticArrayMetadata = IGM.getAddrOfLLVMVariable(entity, superClass, DebugTypeInfo());
482+
} else {
483+
LinkEntity entity = LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::AddressPoint);
484+
IGM.swiftStaticArrayMetadata = IGM.getAddrOfLLVMVariable(entity, NotForDefinition, DebugTypeInfo());
485+
}
468486
}
469487
elements[0].add(llvm::ConstantStruct::get(ObjectHeaderTy, {
470488
IGM.swiftStaticArrayMetadata,
471-
llvm::ConstantExpr::getPtrToInt(IGM.swiftImmortalRefCount, IGM.IntPtrTy)}));
489+
IGM.swiftImmortalRefCount }));
472490
} else {
473491
elements[0].add(llvm::Constant::getNullValue(ObjectHeaderTy));
474492
}

lib/IRGen/IRGenModule.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,8 +2029,14 @@ void IRGenModule::error(SourceLoc loc, const Twine &message) {
20292029

20302030
bool IRGenModule::useDllStorage() { return ::useDllStorage(Triple); }
20312031

2032+
// In embedded swift features are available independent of deployment and
2033+
// runtime targets because the runtime library is always statically linked
2034+
// to the program.
2035+
20322036
#define FEATURE(N, V) \
20332037
bool IRGenModule::is##N##FeatureAvailable(const ASTContext &context) { \
2038+
if (Context.LangOpts.hasFeature(Feature::Embedded)) \
2039+
return true; \
20342040
auto deploymentAvailability \
20352041
= AvailabilityContext::forDeploymentTarget(context); \
20362042
auto runtimeAvailability \

lib/IRGen/IRGenModule.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -837,8 +837,8 @@ class IRGenModule {
837837

838838
llvm::GlobalVariable *TheTrivialPropertyDescriptor = nullptr;
839839

840-
llvm::GlobalVariable *swiftImmortalRefCount = nullptr;
841-
llvm::GlobalVariable *swiftStaticArrayMetadata = nullptr;
840+
llvm::Constant *swiftImmortalRefCount = nullptr;
841+
llvm::Constant *swiftStaticArrayMetadata = nullptr;
842842

843843
/// Used to create unique names for class layout types with tail allocated
844844
/// elements.

lib/IRGen/Linking.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,9 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
642642
return SILLinkage::Shared;
643643

644644
case Kind::TypeMetadata: {
645+
if (isForcedShared())
646+
return SILLinkage::Shared;
647+
645648
auto *nominal = getType().getAnyNominal();
646649
switch (getMetadataAddress()) {
647650
case TypeMetadataAddress::FullMetadata:

lib/SIL/IR/SILInstructions.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2714,6 +2714,15 @@ MetatypeInst *MetatypeInst::create(SILDebugLocation Loc, SILType Ty,
27142714
return ::new (Buffer) MetatypeInst(Loc, Ty, TypeDependentOperands);
27152715
}
27162716

2717+
UpcastInst *UpcastInst::create(SILDebugLocation DebugLoc, SILValue Operand,
2718+
SILType Ty, SILModule &Mod,
2719+
ValueOwnershipKind forwardingOwnershipKind) {
2720+
unsigned size = totalSizeToAlloc<swift::Operand>(1);
2721+
void *Buffer = Mod.allocateInst(size, alignof(UpcastInst));
2722+
return ::new (Buffer) UpcastInst(DebugLoc, Operand, {}, Ty,
2723+
forwardingOwnershipKind);
2724+
}
2725+
27172726
UpcastInst *UpcastInst::create(SILDebugLocation DebugLoc, SILValue Operand,
27182727
SILType Ty, SILFunction &F,
27192728
ValueOwnershipKind forwardingOwnershipKind) {

stdlib/public/core/EmbeddedRuntime.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public struct HeapObject {
4646
static let refcountMask = Int(bitPattern: 0x7fff_ffff)
4747
#endif
4848

49+
// Note: The immortalRefCount value of -1 is also hard-coded in IRGen in `irgen::emitConstantObject`.
4950
static let immortalRefCount = -1
5051
}
5152

test/embedded/static-object.swift

Lines changed: 116 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,128 @@
1-
// RUN: %target-swift-frontend -O -emit-irgen %s -module-name main -parse-as-library -enable-experimental-feature Embedded | %FileCheck %s --check-prefix CHECK-IR
2-
// RUN: %target-run-simple-swift(-O -enable-experimental-feature Embedded -parse-as-library -runtime-compatibility-version none -wmo -Xfrontend -disable-objc-interop) | %FileCheck %s
1+
// RUN: %target-swift-frontend -parse-as-library -enable-experimental-feature Embedded %s -O -wmo -sil-verify-all -module-name=test -emit-ir | %FileCheck %s
32

4-
// REQUIRES: swift_in_compiler
5-
// REQUIRES: executable_test
6-
// REQUIRES: optimized_stdlib
7-
// REQUIRES: OS=macosx || OS=linux-gnu
3+
// Also do an end-to-end test to check all components, including IRGen.
4+
// RUN: %empty-directory(%t)
5+
// RUN: %target-build-swift -parse-as-library -enable-experimental-feature Embedded -O -wmo -module-name=test %s -o %t/a.out
6+
// RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT
7+
8+
// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib
9+
10+
// Check if the optimizer is able to convert array literals to constant statically initialized arrays.
11+
12+
// CHECK-DAG: @"$s4test11arrayLookupyS2iFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} -1
13+
// CHECK-DAG: @"$s4test11returnArraySaySiGyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} -1
14+
// CHECK-DAG: @"$s4test9passArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} -1
15+
// CHECK-DAG: @"$s4test9passArrayyyFTv0_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} -1
16+
// CHECK-DAG: @"$s4test10storeArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} -1
17+
// CHECK-DAG: @"$s4test3StrV9staticLet_WZTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} -1
18+
// CHECK-DAG: @"$s4test3StrV9staticVar_WZTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} -1
19+
// CHECK-DAG: @"$s4test3StrV9staticVarSaySiGvpZ" = global {{.*}} ptr @"$s4test3StrV9staticVar_WZTv_r"
20+
// CHECK-DAG: @"$s4test3StrV14twoDimensionalSaySaySiGGvpZ" = global {{.*}} ptr @"$s4test3StrV14twoDimensional_WZTv{{[0-9]*}}_r"
21+
22+
// Currently, constant static arrays only work on Darwin platforms.
23+
// REQUIRES: VENDOR=apple
24+
25+
26+
public struct Str {
27+
public static let staticLet = [ 200, 201, 202 ]
28+
public static var staticVar = [ 300, 301, 302 ]
29+
public static var twoDimensional = [[1, 2], [3, 4], [5, 6]]
30+
}
31+
32+
@inline(never)
33+
public func arrayLookup(_ i: Int) -> Int {
34+
let lookupTable = [10, 11, 12]
35+
return lookupTable[i]
36+
}
37+
38+
@inline(never)
39+
public func returnArray() -> [Int] {
40+
return [20, 21]
41+
}
42+
43+
@inline(never)
44+
public func modifyArray() -> [Int] {
45+
var a = returnArray()
46+
a[1] = 27
47+
return a
48+
}
49+
50+
public var gg: [Int]?
51+
52+
@inline(never)
53+
public func receiveArray(_ a: [Int]) {
54+
gg = a
55+
}
56+
57+
@inline(never)
58+
public func passArray() {
59+
receiveArray([27, 28])
60+
receiveArray([29])
61+
}
62+
63+
@inline(never)
64+
public func storeArray() {
65+
gg = [227, 228]
66+
}
867

968
public func stringArray() -> [StaticString] {
1069
return ["a", "b", "c", "d"]
1170
}
12-
// CHECK-IR: define {{.*}}@"$s4main11stringArraySays12StaticStringVGyF"
13-
// CHECK-IR-NEXT: entry:
14-
// CHECK-IR-NEXT: call {{.*}}@swift_initStaticObject
1571

16-
@main
17-
struct Main {
72+
@main struct Main {
1873
static func main() {
74+
75+
// CHECK-OUTPUT: [200, 201, 202]
76+
printArray(Str.staticLet)
77+
78+
// CHECK-OUTPUT: [300, 301, 302]
79+
printArray(Str.staticVar)
80+
81+
// CHECK-OUTPUT: [1, 2]
82+
// CHECK-OUTPUT-NEXT: [3, 4]
83+
// CHECK-OUTPUT-NEXT: [5, 6]
84+
for x in Str.twoDimensional {
85+
printArray(x)
86+
}
87+
88+
// CHECK-OUTPUT-NEXT: 11
89+
print(arrayLookup(1))
90+
91+
// CHECK-OUTPUT-NEXT: [20, 21]
92+
printArray(returnArray())
93+
94+
// CHECK-OUTPUT-NEXT: [20, 27]
95+
// CHECK-OUTPUT-NEXT: [20, 27]
96+
// CHECK-OUTPUT-NEXT: [20, 27]
97+
// CHECK-OUTPUT-NEXT: [20, 27]
98+
// CHECK-OUTPUT-NEXT: [20, 27]
99+
for _ in 0..<5 {
100+
printArray(modifyArray())
101+
}
102+
103+
passArray()
104+
// CHECK-OUTPUT-NEXT: [29]
105+
printArray(gg!)
106+
107+
storeArray()
108+
// CHECK-OUTPUT-NEXT: [227, 228]
109+
printArray(gg!)
110+
19111
for c in stringArray() {
112+
// CHECK-OUTPUT-NEXT: a
113+
// CHECK-OUTPUT-NEXT: b
114+
// CHECK-OUTPUT-NEXT: c
115+
// CHECK-OUTPUT-NEXT: d
20116
print(c)
21-
// CHECK: a
22-
// CHECK: b
23-
// CHECK: c
24-
// CHECK: d
25117
}
26118
}
27119
}
120+
121+
func printArray(_ a: [Int]) {
122+
print("[", terminator: "")
123+
for (i, x) in a.enumerated() {
124+
print(x, terminator: i == a.count - 1 ? "" : ", ")
125+
}
126+
print("]")
127+
}
128+

0 commit comments

Comments
 (0)