Skip to content

Commit

Permalink
IRGen: Use metadata accessors for types with resilient layout
Browse files Browse the repository at this point in the history
If a struct has fixed layout but contains fields which are opaque,
or if the struct itself is opaque, use a metadata accessor function
instead of loading the metadata directly.

Let's say that A and B are two structs defined in the same module,
and B has a fixed size. This patch adds support for these two cases:

1) Fixed-layout struct A contains resilient struct B
2) Resilient struct A contains resilient struct B

In case 1),

a) Inside X: A is fixed-size and has constant metadata
 i) Direct metadata access can be performed on A and B
 ii) Direct field access can be performed on A and B
d) Outside X: B has an opaque layout
 i) Metadata accessor must be called for A and B
 ii) Fields of A do not have constant offsets, instead the offsets
     must be loaded from type metadata

Case 2) is the same as above except ii) does not apply, since fields
of resilient structs are manipulated via accessors.

Eventually, we will use metadata accessor functions for all public
struct and enum types.
  • Loading branch information
slavapestov committed Nov 16, 2015
1 parent 72fa928 commit a05f6c8
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 35 deletions.
3 changes: 2 additions & 1 deletion lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,8 @@ SILLinkage LinkEntity::getLinkage(IRGenModule &IGM,

case Kind::TypeMetadataAccessFunction:
case Kind::TypeMetadataLazyCacheVariable:
switch (getTypeMetadataAccessStrategy(getType())) {
switch (getTypeMetadataAccessStrategy(IGM, getType(),
/*preferDirectAccess=*/false)) {
case MetadataAccessStrategy::PublicUniqueAccessor:
return getSILLinkage(FormalLinkage::PublicUnique, forDefinition);
case MetadataAccessStrategy::HiddenUniqueAccessor:
Expand Down
107 changes: 81 additions & 26 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,21 +395,37 @@ bool irgen::hasKnownVTableEntry(IRGenModule &IGM,
return hasKnownSwiftImplementation(IGM, theClass);
}

/// If we have a non-generic struct or enum whose size does not
/// depend on any opaque resilient types, we can access metadata
/// directly. Otherwise, call an accessor.
///
/// FIXME: Really, we want to use accessors for any nominal type
/// defined in a different module, too.
static bool isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) {
if (isa<StructType>(type) || isa<EnumType>(type))
if (IGM.getTypeInfoForLowered(type).isFixedSize())
return true;

return false;
}

/// Return the standard access strategy for getting a non-dependent
/// type metadata object.
MetadataAccessStrategy irgen::getTypeMetadataAccessStrategy(CanType type) {
MetadataAccessStrategy
irgen::getTypeMetadataAccessStrategy(IRGenModule &IGM, CanType type,
bool preferDirectAccess) {
assert(!type->hasArchetype());

// Non-generic structs, enums, and classes are special cases.
auto nominal = dyn_cast<NominalType>(type);
if (nominal && !isa<ProtocolType>(nominal) &&
!nominal->getDecl()->isGenericContext()) {
if (nominal && !isa<ProtocolType>(nominal)) {
assert(!nominal->getDecl()->isGenericContext());

// Struct and enum metadata can be accessed directly.
if (!isa<ClassType>(nominal))
if (preferDirectAccess &&
isTypeMetadataAccessTrivial(IGM, type))
return MetadataAccessStrategy::Direct;

// Classes require accessors.
// Everything else requires accessors.
switch (getDeclLinkage(nominal->getDecl())) {
case FormalLinkage::PublicUnique:
return MetadataAccessStrategy::PublicUniqueAccessor;
Expand Down Expand Up @@ -996,32 +1012,42 @@ static void emitDirectTypeMetadataInitialization(IRGenFunction &IGF,
}

/// Emit the body of a lazy cache accessor.
///
/// If cacheVariable is null, we perform the direct access every time.
/// This is used for metadata accessors that come about due to resilience,
/// where the direct access is completely trivial.
void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM,
llvm::Function *accessor,
llvm::GlobalVariable *cacheVariable,
const llvm::function_ref<llvm::Value*(IRGenFunction &IGF)> &getValue) {
llvm::Constant *null =
llvm::ConstantPointerNull::get(
cast<llvm::PointerType>(cacheVariable->getValueType()));

accessor->setDoesNotThrow();

// This function is logically 'readnone': the caller does not need
// to reason about any side effects or stores it might perform.
accessor->setDoesNotAccessMemory();

// Set up the cache variable.
cacheVariable->setInitializer(null);
cacheVariable->setAlignment(IGM.getPointerAlignment().getValue());
Address cache(cacheVariable, IGM.getPointerAlignment());

IRGenFunction IGF(IGM, accessor);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, IGF.CurFn);

if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, accessor);

// If there's no cache variable, just perform the direct access.
if (cacheVariable == nullptr) {
IGF.Builder.CreateRet(getValue(IGF));
return;
}

// Set up the cache variable.
llvm::Constant *null =
llvm::ConstantPointerNull::get(
cast<llvm::PointerType>(cacheVariable->getValueType()));

cacheVariable->setInitializer(null);
cacheVariable->setAlignment(IGM.getPointerAlignment().getValue());
Address cache(cacheVariable, IGM.getPointerAlignment());

// Okay, first thing, check the cache variable.
//
// Conceptually, this needs to establish memory ordering with the
Expand Down Expand Up @@ -1086,8 +1112,15 @@ static llvm::Function *getTypeMetadataAccessFunction(IRGenModule &IGM,
return accessor;

// Okay, define the accessor.
auto cacheVariable = cast<llvm::GlobalVariable>(
IGM.getAddrOfTypeMetadataLazyCacheVariable(type, ForDefinition));
llvm::GlobalVariable *cacheVariable = nullptr;

// If our preferred access method is to go via an accessor, it means
// there is some non-trivial computation that needs to be cached.
if (!isTypeMetadataAccessTrivial(IGM, type)) {
cacheVariable = cast<llvm::GlobalVariable>(
IGM.getAddrOfTypeMetadataLazyCacheVariable(type, ForDefinition));
}

emitLazyCacheAccessFunction(IGM, accessor, cacheVariable,
[&](IRGenFunction &IGF) -> llvm::Value* {
emitDirectTypeMetadataInitialization(IGF, type);
Expand All @@ -1097,6 +1130,22 @@ static llvm::Function *getTypeMetadataAccessFunction(IRGenModule &IGM,
return accessor;
}

/// Force a public metadata access function into existence if necessary
/// for the given type.
static void maybeEmitTypeMetadataAccessFunction(IRGenModule &IGM,
NominalTypeDecl *theDecl) {
CanType declaredType = theDecl->getDeclaredType()->getCanonicalType();

// FIXME: Also do this for generic structs.
// FIXME: Internal types with availability from another module can be
// referenced from @_transparent functions.
if (!theDecl->isGenericContext() &&
(isa<ClassDecl>(theDecl) ||
theDecl->getFormalAccess() == Accessibility::Public ||
!IGM.getTypeInfoForLowered(declaredType).isFixedSize()))
(void) getTypeMetadataAccessFunction(IGM, declaredType, ForDefinition);
}

/// Emit a call to the type metadata accessor for the given function.
static llvm::Value *emitCallToTypeMetadataAccessFunction(IRGenFunction &IGF,
CanType type,
Expand All @@ -1121,7 +1170,8 @@ static llvm::Value *emitCallToTypeMetadataAccessFunction(IRGenFunction &IGF,
/// Produce the type metadata pointer for the given type.
llvm::Value *IRGenFunction::emitTypeMetadataRef(CanType type) {
if (!type->hasArchetype()) {
switch (getTypeMetadataAccessStrategy(type)) {
switch (getTypeMetadataAccessStrategy(IGM, type,
/*preferDirectAccess=*/true)) {
case MetadataAccessStrategy::Direct:
return emitDirectTypeMetadataRef(*this, type);
case MetadataAccessStrategy::PublicUniqueAccessor:
Expand Down Expand Up @@ -3548,8 +3598,6 @@ void irgen::emitClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
const StructLayout &layout) {
assert(!classDecl->isForeign());

CanType declaredType = classDecl->getDeclaredType()->getCanonicalType();

// TODO: classes nested within generic types
llvm::Constant *init;
bool isPattern;
Expand All @@ -3566,11 +3614,12 @@ void irgen::emitClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
init = builder.getInit();
isPattern = false;
hasRuntimeBase = builder.hasRuntimeBase();

// Force the type metadata access function into existence.
(void) getTypeMetadataAccessFunction(IGM, declaredType, ForDefinition);
}

maybeEmitTypeMetadataAccessFunction(IGM, classDecl);

CanType declaredType = classDecl->getDeclaredType()->getCanonicalType();

// For now, all type metadata is directly stored.
bool isIndirect = false;

Expand Down Expand Up @@ -4410,10 +4459,13 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) {
isPattern = false;
}

maybeEmitTypeMetadataAccessFunction(IGM, structDecl);

CanType declaredType = structDecl->getDeclaredType()->getCanonicalType();

// For now, all type metadata is directly stored.
bool isIndirect = false;

CanType declaredType = structDecl->getDeclaredType()->getCanonicalType();
IGM.defineTypeMetadata(declaredType, isIndirect, isPattern,
/*isConstant*/!isPattern, init);
}
Expand Down Expand Up @@ -4541,11 +4593,14 @@ void irgen::emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum) {
init = builder.getInit();
isPattern = false;
}


maybeEmitTypeMetadataAccessFunction(IGM, theEnum);

CanType declaredType = theEnum->getDeclaredType()->getCanonicalType();

// For now, all type metadata is directly stored.
bool isIndirect = false;

CanType declaredType = theEnum->getDeclaredType()->getCanonicalType();
IGM.defineTypeMetadata(declaredType, isIndirect, isPattern,
/*isConstant*/!isPattern, init);
}
Expand Down
4 changes: 3 additions & 1 deletion lib/IRGen/GenMeta.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,9 @@ namespace irgen {
};

/// Determine how the given type metadata should be accessed.
MetadataAccessStrategy getTypeMetadataAccessStrategy(CanType type);
MetadataAccessStrategy getTypeMetadataAccessStrategy(IRGenModule &IGM,
CanType type,
bool preferDirectAccess);

/// Get the runtime identifier for a special protocol, if any.
SpecialProtocol getSpecialProtocolID(ProtocolDecl *P);
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/GenStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ enum class StructTypeInfoKind {
};

static StructTypeInfoKind getStructTypeInfoKind(const TypeInfo &type) {
// FIXME: check that this is not a resilient struct type
return (StructTypeInfoKind) type.getSubclassKind();
}

Expand Down
51 changes: 46 additions & 5 deletions test/IRGen/struct_resilience.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// RUN: %target-swift-frontend -I %S/../Inputs -enable-source-import -emit-ir -enable-resilience %s | FileCheck %s
// RUN: %target-swift-frontend -I %S/../Inputs -enable-source-import -emit-ir -enable-resilience -O %s

import resilient_struct

// CHECK: %Si = type <{ [[INT:i32|i64]] }>

// Resilient structs from outside our resilience domain are manipulated via
// value witnesses

Expand All @@ -10,32 +13,65 @@ import resilient_struct
public func functionWithResilientTypes(s: Size, f: Size -> Size) -> Size {

// CHECK: [[RESULT:%.*]] = alloca [[BUFFER_TYPE:\[.* x i8\]]]
// CHECK: [[VWT:%.*]] = load i8**, i8*** getelementptr inbounds (i8**, i8*** bitcast (%swift.type* @_TMV16resilient_struct4Size to i8***), {{i32|i64}} -1)

// CHECK: [[METADATA:%.*]] = call %swift.type* @_TMaV16resilient_struct4Size()
// CHECK: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i8***
// CHECK: [[VWT_ADDR:%.*]] = getelementptr inbounds i8**, i8*** [[METADATA_ADDR]], [[INT]] -1
// CHECK: [[VWT:%.*]] = load i8**, i8*** [[VWT_ADDR]]

// CHECK: [[WITNESS_PTR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 5
// CHECK: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_PTR]]
// CHECK: [[initializeBufferWithCopy:%.*]] = bitcast i8* [[WITNESS]]
// CHECK: [[BUFFER:%.*]] = call %swift.opaque* [[initializeBufferWithCopy]]([[BUFFER_TYPE]]* [[RESULT]], %swift.opaque* %1, %swift.type* @_TMV16resilient_struct4Size)
// CHECK: [[BUFFER:%.*]] = call %swift.opaque* [[initializeBufferWithCopy]]([[BUFFER_TYPE]]* [[RESULT]], %swift.opaque* %1, %swift.type* [[METADATA]])

// CHECK: [[FN:%.*]] = bitcast i8* %2 to void (%swift.opaque*, %swift.opaque*, %swift.refcounted*)*
// CHECK: call void [[FN]](%swift.opaque* noalias nocapture sret %0, %swift.opaque* noalias nocapture [[BUFFER]], %swift.refcounted* %3)

// CHECK: [[WITNESS_PTR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 3
// CHECK: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_PTR]]
// CHECK: [[deallocateBuffer:%.*]] = bitcast i8* [[WITNESS]]
// CHECK: call void [[deallocateBuffer]]([[BUFFER_TYPE]]* [[RESULT]], %swift.type* @_TMV16resilient_struct4Size)
// CHECK: call void [[deallocateBuffer]]([[BUFFER_TYPE]]* [[RESULT]], %swift.type* [[METADATA]])

// CHECK: [[WITNESS_PTR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4
// CHECK: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_PTR]]
// CHECK: [[destroy:%.*]] = bitcast i8* [[WITNESS]] to void (%swift.opaque*, %swift.type*)*
// CHECK: call void [[destroy]](%swift.opaque* %1, %swift.type* @_TMV16resilient_struct4Size)
// CHECK: call void [[destroy]](%swift.opaque* %1, %swift.type* [[METADATA]])
// CHECK: ret void

return f(s)
}

// CHECK-LABEL: declare %swift.type* @_TMaV16resilient_struct4Size()

// Rectangle has fixed layout inside its resilience domain, and dynamic
// layout on the outside.
//
// Make sure we use a type metadata accessor function, and load indirect
// field offsets from it.

// CHECK-LABEL: define void @_TF17struct_resilience26functionWithResilientTypesFV16resilient_struct9RectangleT_(%V16resilient_struct9Rectangle* noalias nocapture)
public func functionWithResilientTypes(r: Rectangle) {

// CHECK: [[METADATA:%.*]] = call %swift.type* @_TMaV16resilient_struct9Rectangle()
// CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to [[INT]]*
// CHECK-NEXT: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], i32 3
// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[FIELD_OFFSET_VECTOR]], i32 2
// CHECK-NEXT: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]]
// CHECK-NEXT: [[STRUCT_ADDR:%.*]] = bitcast %V16resilient_struct9Rectangle* %0 to i8*
// CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[STRUCT_ADDR]], [[INT]] [[FIELD_OFFSET]]
// CHECK-NEXT: [[FIELD_PTR:%.*]] = bitcast i8* [[FIELD_ADDR]] to %Si*
// CHECK-NEXT: [[FIELD_PAYLOAD_PTR:%.*]] = getelementptr inbounds %Si, %Si* [[FIELD_PTR]], i32 0, i32 0
// CHECK-NEXT: [[FIELD_PAYLOAD:%.*]] = load [[INT]], [[INT]]* [[FIELD_PAYLOAD_PTR]]

_ = r.color

// CHECK: ret void

}


// Resilient structs from inside our resilience domain are manipulated
// directly
// directly.

public struct MySize {
public let w: Int
Expand All @@ -55,3 +91,8 @@ public func functionWithMyResilientTypes(s: MySize, f: MySize -> MySize) -> MySi

return f(s)
}

// Public metadata accessor for our resilient struct

// CHECK-LABEL: define %swift.type* @_TMaV17struct_resilience6MySize()
// CHECK: ret %swift.type* bitcast (i64* getelementptr inbounds {{.*}} @_TMfV17struct_resilience6MySize, i32 0, i32 1) to %swift.type*)
45 changes: 43 additions & 2 deletions test/Inputs/resilient_struct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,54 @@ public struct Size {
public mutating func mutantMethod() {}
}

// Fixed-layout struct with resilient-layout members
// Fixed-layout struct with resilient members
@_fixed_layout public struct Rectangle {
public let p: Point
public let s: Size
public let color: Int

public init(p: Point, s: Size) {
public init(p: Point, s: Size, color: Int) {
self.p = p
self.s = s
self.color = color
}
}

// More complicated resilient structs for runtime tests
public struct ResilientBool {
public let b: Bool

public init(b: Bool) {
self.b = b
}
}

public struct ResilientInt {
public let i: Int

public init(i: Int) {
self.i = i
}
}

public struct ResilientDouble {
public let d: Double

public init(d: Double) {
self.d = d
}
}

@_fixed_layout public struct ResilientLayoutRuntimeTest {
public let b1: ResilientBool
public let i: ResilientInt
public let b2: ResilientBool
public let d: ResilientDouble

public init(b1: ResilientBool, i: ResilientInt, b2: ResilientBool, d: ResilientDouble) {
self.b1 = b1
self.i = i
self.b2 = b2
self.d = d
}
}
Loading

0 comments on commit a05f6c8

Please sign in to comment.