Skip to content

Commit 803a6b0

Browse files
committed
Reflection: Emit metadata for fixed-layout SIL boxes
We were recovering metadata from generic boxes by reading the instantiated payload metadata from the box's metadata, but this approach doesn't work for fixed-size boxes, whose metadata does not store the payload metadata at all. Instead, emit a capture descriptor with no metadata sources and a single capture, using the lowered AST type appearing in the alloc_box instruction that emitted the box. Since box metadata is shared by all POD types of the same size, and all single-retainable pointer payloads, the AST type might not accurately reflect what is actually in the box. However, this type is *layout compatible* with the box payload, at least enough to know where the retainable pointers are, because after all IRGen uses this type to synthesize the destructor. Fixes <rdar://problem/26314060>.
1 parent b47b168 commit 803a6b0

File tree

9 files changed

+216
-18
lines changed

9 files changed

+216
-18
lines changed

Diff for: lib/IRGen/GenHeap.cpp

+13-8
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,7 @@ class BoxTypeInfo : public HeapTypeInfo<BoxTypeInfo> {
13861386

13871387
/// Allocate a box of the given type.
13881388
virtual OwnedAddress
1389-
allocate(IRGenFunction &IGF, SILType boxedType,
1389+
allocate(IRGenFunction &IGF, SILType boxedType, SILType boxedInterfaceType,
13901390
const llvm::Twine &name) const = 0;
13911391

13921392
/// Deallocate an uninitialized box.
@@ -1404,7 +1404,7 @@ class EmptyBoxTypeInfo final : public BoxTypeInfo {
14041404
EmptyBoxTypeInfo(IRGenModule &IGM) : BoxTypeInfo(IGM) {}
14051405

14061406
OwnedAddress
1407-
allocate(IRGenFunction &IGF, SILType boxedType,
1407+
allocate(IRGenFunction &IGF, SILType boxedType, SILType boxedInterfaceType,
14081408
const llvm::Twine &name) const override {
14091409
return OwnedAddress(IGF.getTypeInfo(boxedType).getUndefAddress(),
14101410
IGF.IGM.RefCountedNull);
@@ -1429,7 +1429,7 @@ class NonFixedBoxTypeInfo final : public BoxTypeInfo {
14291429
NonFixedBoxTypeInfo(IRGenModule &IGM) : BoxTypeInfo(IGM) {}
14301430

14311431
OwnedAddress
1432-
allocate(IRGenFunction &IGF, SILType boxedType,
1432+
allocate(IRGenFunction &IGF, SILType boxedType, SILType boxedInterfaceType,
14331433
const llvm::Twine &name) const override {
14341434
auto &ti = IGF.getTypeInfo(boxedType);
14351435
// Use the runtime to allocate a box of the appropriate size.
@@ -1470,14 +1470,15 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo {
14701470
{}
14711471

14721472
OwnedAddress
1473-
allocate(IRGenFunction &IGF, SILType boxedType, const llvm::Twine &name)
1473+
allocate(IRGenFunction &IGF, SILType boxedType, SILType boxedInterfaceType,
1474+
const llvm::Twine &name)
14741475
const override {
14751476
// Allocate a new object using the layout.
14761477

1477-
auto nullCaptureDescriptor
1478-
= llvm::ConstantPointerNull::get(IGF.IGM.CaptureDescriptorPtrTy);
1478+
auto boxDescriptor = IGF.IGM.getAddrOfBoxDescriptor(
1479+
boxedInterfaceType.getSwiftRValueType());
14791480
llvm::Value *allocation = IGF.emitUnmanagedAlloc(layout, name,
1480-
nullCaptureDescriptor);
1481+
boxDescriptor);
14811482
Address rawAddr = project(IGF, allocation, boxedType);
14821483
return {rawAddr, allocation};
14831484
}
@@ -1586,9 +1587,13 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
15861587

15871588
OwnedAddress
15881589
irgen::emitAllocateBox(IRGenFunction &IGF, CanSILBoxType boxType,
1590+
CanSILBoxType boxInterfaceType,
15891591
const llvm::Twine &name) {
15901592
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
1591-
return boxTI.allocate(IGF, boxType->getBoxedAddressType(), name);
1593+
return boxTI.allocate(IGF,
1594+
boxType->getBoxedAddressType(),
1595+
boxInterfaceType->getBoxedAddressType(),
1596+
name);
15921597
}
15931598

15941599
void irgen::emitDeallocateBox(IRGenFunction &IGF,

Diff for: lib/IRGen/GenHeap.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ void emitDeallocatePartialClassInstance(IRGenFunction &IGF,
116116
llvm::Value *alignMask);
117117

118118
/// Allocate a boxed value.
119+
///
120+
/// The interface type is required for emitting reflection metadata.
119121
OwnedAddress
120-
emitAllocateBox(IRGenFunction &IGF, CanSILBoxType boxType,
122+
emitAllocateBox(IRGenFunction &IGF,
123+
CanSILBoxType boxType,
124+
CanSILBoxType boxInterfaceType,
121125
const llvm::Twine &name);
122126

123127
/// Deallocate a box whose value is uninitialized.

Diff for: lib/IRGen/GenReflection.cpp

+72-4
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ class ReflectionMetadataBuilder : public ConstantBuilder<> {
195195
/// in the typeref reflection section.
196196
void addTypeRef(Module *ModuleContext, CanType type) {
197197
assert(type);
198+
199+
// Generic parameters should be written in terms of interface types
200+
// for the purposes of reflection metadata
201+
assert(!type->hasArchetype() && "Forgot to map typeref out of context");
202+
198203
Mangle::Mangler mangler(/*DWARFMangling*/false,
199204
/*usePunyCode*/ true,
200205
/*OptimizeProtocolNames*/ false);
@@ -515,6 +520,54 @@ class BuiltinTypeMetadataBuilder : public ReflectionMetadataBuilder {
515520
}
516521
};
517522

523+
/// Builds a constant LLVM struct describing the layout of a fixed-size
524+
/// SIL @box. These look like closure contexts, but without any necessary
525+
/// bindings or metadata sources, and only a single captured value.
526+
class BoxDescriptorBuilder : public ReflectionMetadataBuilder {
527+
CanType BoxedType;
528+
public:
529+
BoxDescriptorBuilder(IRGenModule &IGM,
530+
llvm::SetVector<CanType> &BuiltinTypes,
531+
CanType BoxedType)
532+
: ReflectionMetadataBuilder(IGM, BuiltinTypes),
533+
BoxedType(BoxedType) {}
534+
535+
void layout() {
536+
addConstantInt32(1);
537+
addConstantInt32(0); // Number of sources
538+
addConstantInt32(0); // Number of generic bindings
539+
540+
addTypeRef(IGM.getSILModule().getSwiftModule(), BoxedType);
541+
addBuiltinTypeRefs(BoxedType);
542+
}
543+
544+
llvm::GlobalVariable *emit() {
545+
auto tempBase = std::unique_ptr<llvm::GlobalVariable>(
546+
new llvm::GlobalVariable(IGM.Int8Ty, /*isConstant*/ true,
547+
llvm::GlobalValue::PrivateLinkage));
548+
setRelativeAddressBase(tempBase.get());
549+
550+
layout();
551+
auto init = getInit();
552+
553+
if (!init)
554+
return nullptr;
555+
556+
auto var = new llvm::GlobalVariable(*IGM.getModule(), init->getType(),
557+
/*isConstant*/ true,
558+
llvm::GlobalValue::PrivateLinkage,
559+
init,
560+
"\x01l__swift3_box_descriptor");
561+
var->setSection(IGM.getCaptureDescriptorMetadataSectionName());
562+
var->setAlignment(4);
563+
564+
auto replacer = llvm::ConstantExpr::getBitCast(var, IGM.Int8PtrTy);
565+
tempBase->replaceAllUsesWith(replacer);
566+
567+
return var;
568+
}
569+
};
570+
518571
/// Builds a constant LLVM struct describing the layout of a heap closure,
519572
/// the types of its captures, and the sources of metadata if any of the
520573
/// captures are generic.
@@ -524,15 +577,15 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder {
524577
CanSILFunctionType OrigCalleeType;
525578
CanSILFunctionType SubstCalleeType;
526579
ArrayRef<Substitution> Subs;
527-
HeapLayout &Layout;
580+
const HeapLayout &Layout;
528581
public:
529582
CaptureDescriptorBuilder(IRGenModule &IGM,
530583
llvm::SetVector<CanType> &BuiltinTypes,
531584
SILFunction &Caller,
532585
CanSILFunctionType OrigCalleeType,
533586
CanSILFunctionType SubstCalleeType,
534587
ArrayRef<Substitution> Subs,
535-
HeapLayout &Layout)
588+
const HeapLayout &Layout)
536589
: ReflectionMetadataBuilder(IGM, BuiltinTypes),
537590
Caller(Caller), OrigCalleeType(OrigCalleeType),
538591
SubstCalleeType(SubstCalleeType), Subs(Subs),
@@ -650,7 +703,7 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder {
650703

651704
// Now add typerefs of all of the captures.
652705
for (auto CaptureType : CaptureTypes) {
653-
addTypeRef(Caller.getModule().getSwiftModule(), CaptureType);
706+
addTypeRef(IGM.getSILModule().getSwiftModule(), CaptureType);
654707
addBuiltinTypeRefs(CaptureType);
655708
}
656709

@@ -756,12 +809,27 @@ llvm::Constant *IRGenModule::getAddrOfStringForTypeRef(StringRef Str) {
756809
return entry.second;
757810
}
758811

812+
llvm::Constant *
813+
IRGenModule::getAddrOfBoxDescriptor(CanType BoxedType) {
814+
if (!IRGen.Opts.EnableReflectionMetadata)
815+
return llvm::Constant::getNullValue(CaptureDescriptorPtrTy);
816+
817+
llvm::SetVector<CanType> BuiltinTypes;
818+
BoxDescriptorBuilder builder(*this, BuiltinTypes, BoxedType);
819+
820+
auto var = builder.emit();
821+
if (var)
822+
addUsedGlobal(var);
823+
824+
return llvm::ConstantExpr::getBitCast(var, CaptureDescriptorPtrTy);
825+
}
826+
759827
llvm::Constant *
760828
IRGenModule::getAddrOfCaptureDescriptor(SILFunction &Caller,
761829
CanSILFunctionType OrigCalleeType,
762830
CanSILFunctionType SubstCalleeType,
763831
ArrayRef<Substitution> Subs,
764-
HeapLayout &Layout) {
832+
const HeapLayout &Layout) {
765833
if (!IRGen.Opts.EnableReflectionMetadata)
766834
return llvm::Constant::getNullValue(CaptureDescriptorPtrTy);
767835

Diff for: lib/IRGen/IRGenModule.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,8 @@ class IRGenModule {
618618
llvm::Function *fn);
619619
llvm::Constant *emitProtocolConformances();
620620
llvm::Constant *emitTypeMetadataRecords();
621+
622+
// Remote reflection metadata
621623
void emitReflectionMetadata(const NominalTypeDecl *Decl);
622624
void emitAssociatedTypeMetadataRecord(const NominalTypeDecl *Decl);
623625
void emitAssociatedTypeMetadataRecord(const ExtensionDecl *Ext);
@@ -629,7 +631,8 @@ class IRGenModule {
629631
CanSILFunctionType origCalleeType,
630632
CanSILFunctionType substCalleeType,
631633
ArrayRef<Substitution> subs,
632-
HeapLayout &layout);
634+
const HeapLayout &layout);
635+
llvm::Constant *getAddrOfBoxDescriptor(CanType boxedType);
633636
std::string getBuiltinTypeMetadataSectionName();
634637
std::string getFieldTypeMetadataSectionName();
635638
std::string getAssociatedTypeMetadataSectionName();

Diff for: lib/IRGen/IRGenSIL.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -3589,7 +3589,11 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) {
35893589
# endif
35903590

35913591
auto boxTy = i->getType().castTo<SILBoxType>();
3592-
OwnedAddress boxWithAddr = emitAllocateBox(*this, boxTy, DbgName);
3592+
auto boxInterfaceTy = cast<SILBoxType>(
3593+
CurSILFn->mapTypeOutOfContext(boxTy)
3594+
->getCanonicalType());
3595+
OwnedAddress boxWithAddr = emitAllocateBox(*this, boxTy, boxInterfaceTy,
3596+
DbgName);
35933597
setLoweredBox(i, boxWithAddr);
35943598

35953599
if (IGM.DebugInfo && Decl) {

Diff for: test/Reflection/box_descriptors.sil

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: %target-build-swift %s -emit-module -emit-library -module-name capture_descriptors -o %t/capture_descriptors.%target-dylib-extension
3+
// RUN: %target-swift-reflection-dump -binary-filename %t/capture_descriptors.%target-dylib-extension | FileCheck %s
4+
5+
sil_stage canonical
6+
7+
import Builtin
8+
import Swift
9+
import SwiftShims
10+
11+
// CHECK: CAPTURE DESCRIPTORS:
12+
// CHECK-NEXT: ====================
13+
14+
class C<T> {}
15+
16+
sil_vtable C {}
17+
18+
sil @make_some_boxes : $@convention(thin) <T> () -> () {
19+
%a = alloc_box $Int
20+
%b = alloc_box $(Int, Int)
21+
%c = alloc_box $C<T>
22+
%result = tuple ()
23+
return %result : $()
24+
}
25+
26+
// CHECK: - Capture types:
27+
// CHECK-NEXT: (struct Swift.Int)
28+
// CHECK-NEXT: - Metadata sources:
29+
30+
// CHECK: - Capture types:
31+
// CHECK-NEXT: (tuple
32+
// CHECK-NEXT: (struct Swift.Int)
33+
// CHECK-NEXT: (struct Swift.Int))
34+
// CHECK-NEXT: - Metadata sources:
35+
36+
// CHECK: - Capture types:
37+
// CHECK-NEXT: (bound_generic_class capture_descriptors.C
38+
// CHECK-NEXT: (generic_type_parameter depth=0 index=0))
39+
// CHECK-NEXT: - Metadata sources:

Diff for: test/Reflection/capture_descriptors.sil

+12
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ bb0(%i: $Int, %p: $@thick P.Type):
3030
return %c : $@callee_owned () -> ()
3131
}
3232

33+
// This is the descriptor for '@box Int' above
34+
35+
// CHECK: - Capture types:
36+
// CHECK-NEXT: (struct Swift.Int)
37+
// CHECK-NEXT: - Metadata sources:
38+
3339
// CHECK: - Capture types:
3440
// CHECK-NEXT: (struct Swift.Int)
3541
// CHECK-NEXT: (sil_box
@@ -60,6 +66,12 @@ bb0:
6066
return %c : $@callee_owned () -> ()
6167
}
6268

69+
// This is the descriptor for '@box String' above
70+
71+
// CHECK: - Capture types:
72+
// CHECK-NEXT: (struct Swift.String)
73+
// CHECK-NEXT: - Metadata sources:
74+
6375
// CHECK: - Capture types:
6476
// CHECK-NEXT: (struct Swift.Int)
6577
// CHECK-NEXT: (sil_box

Diff for: validation-test/Reflection/functions.swift

+23-3
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,36 @@ func concrete(x: Int, y: Any) {
3333
// CHECK-64-NEXT: (field name=_value offset=0
3434
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0)))))
3535

36-
// FIXME: Need @box reflection -- here the context is a single boxed value
36+
// Here the context is a single boxed value
3737
reflect(function: {print(y)})
3838
// CHECK: Type reference:
3939
// CHECK-NEXT: (builtin Builtin.NativeObject)
4040

4141
// CHECK-32: Type info:
42-
// CHECK-32-NEXT: <null type info>
42+
// CHECK-32-NEXT: (closure_context size=28 alignment=4 stride=28 num_extra_inhabitants=0
43+
// CHECK-32-NEXT: (field offset=12
44+
// CHECK-32-NEXT: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=0
45+
// CHECK-32-NEXT: (field name=value offset=0
46+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1))
47+
// CHECK-32-NEXT: (field name=value offset=4
48+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1))
49+
// CHECK-32-NEXT: (field name=value offset=8
50+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1))
51+
// CHECK-32-NEXT: (field name=metadata offset=12
52+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1)))))
4353

4454
// CHECK-64: Type info:
45-
// CHECK-64-NEXT: <null type info>
55+
// CHECK-64-NEXT: (closure_context size=48 alignment=8 stride=48 num_extra_inhabitants=0
56+
// CHECK-64-NEXT: (field offset=16
57+
// CHECK-64-NEXT: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=0
58+
// CHECK-64-NEXT: (field name=value offset=0
59+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1))
60+
// CHECK-64-NEXT: (field name=value offset=8
61+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1))
62+
// CHECK-64-NEXT: (field name=value offset=16
63+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1))
64+
// CHECK-64-NEXT: (field name=metadata offset=24
65+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1)))))
4666
}
4767

4868
concrete(x: 10, y: true)

Diff for: validation-test/Reflection/functions_objc.swift

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: %target-build-swift -lswiftSwiftReflectionTest %s -o %t/functions
3+
// RUN: %target-run %target-swift-reflection-test %t/functions | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
4+
// REQUIRES: objc_interop
5+
6+
import SwiftReflectionTest
7+
import Foundation
8+
9+
func capturesImportedClass(x: Int, n: NSURLX, r: NSRect) {
10+
reflect(function: {print(x); print(n); print(r)})
11+
12+
// CHECK-32: Type reference:
13+
// CHECK-32-NEXT: (builtin Builtin.NativeObject)
14+
15+
// CHECK-32: Type info:
16+
// CHECK-32-NEXT: (closure_context size=56 alignment=8 stride=56 num_extra_inhabitants=0
17+
// CHECK-32-NEXT: (field offset=12
18+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
19+
// CHECK-32-NEXT: (field name=_value offset=0
20+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0))))
21+
// CHECK-32-NEXT: (field offset=16
22+
// CHECK-32-NEXT: (reference kind=strong refcounting=unknown))
23+
// CHECK-32-NEXT: (field offset=24
24+
// CHECK-32-NEXT: (builtin size=32 alignment=8 stride=32 num_extra_inhabitants=0)))
25+
26+
// CHECK-64: Type reference:
27+
// CHECK-64-NEXT: (builtin Builtin.NativeObject)
28+
29+
// CHECK-64: Type info:
30+
// CHECK-64-NEXT: (closure_context size=64 alignment=8 stride=64 num_extra_inhabitants=0
31+
// CHECK-64-NEXT: (field offset=16
32+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
33+
// CHECK-64-NEXT: (field name=_value offset=0
34+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0))))
35+
// CHECK-64-NEXT: (field offset=24
36+
// CHECK-64-NEXT: (reference kind=strong refcounting=unknown))
37+
// CHECK-64-NEXT: (field offset=32
38+
// CHECK-64-NEXT: (builtin size=32 alignment=8 stride=32 num_extra_inhabitants=0)))
39+
}
40+
41+
capturesImportedClass(x: 10, n: NSURL(), r: NSRect(x: 1, y: 2, width: 3, height: 4))
42+
43+
doneReflecting()

0 commit comments

Comments
 (0)