Skip to content

Commit

Permalink
IRGen: Preliminary resilient struct support
Browse files Browse the repository at this point in the history
Add a new ResilientStructTypeInfo. This is a singleton since
all resilient structs have opaque payloads and are accessed
through value witness tables.

With this in place, flesh out IRGenModule::isResilient() and
use the new singleton to convert resilient structs.

Note that the old isResilient() was hard-coded to report that
all Clang-imported classes are "resilient". Now that this has
been unified with NominalTypeDecl::hasFixedLayout(), we will
report Clang-imported classes are "resilient" at the SIL level.
This should not introduce any semantic differences at this
point.

Unlike SIL, where currently resilient types are always resilient
even when used from the same module, IRGen is able to perform
direct manipulation of resilient structs from the current
module, since IRGen's type lowering has a resilience scope
plumbed through.

Note that we do not yet support laying out structs and classes
containing resilient fields -- this will come in a future patch.
  • Loading branch information
slavapestov committed Nov 13, 2015
1 parent 02ea8b8 commit 19458ce
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 8 deletions.
15 changes: 13 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1835,16 +1835,27 @@ Type TypeDecl::getDeclaredInterfaceType() const {

bool NominalTypeDecl::hasFixedLayout() const {
// Private and internal types always have a fixed layout.
// TODO: internal types with availability information need to be
// resilient, since they can be used from @_transparent functions.
if (getFormalAccess() != Accessibility::Public)
return true;

// Check for an explicit @_fixed_layout attribute.
if (getAttrs().hasAttribute<FixedLayoutAttr>())
return true;

// Types imported from C always have a fixed layout.
if (hasClangNode())
if (hasClangNode()) {
// Classes imported from Objective-C *never* have a fixed layout.
// IRGen needs to use dynamic ivar layout to ensure that subclasses
// of Objective-C classes are resilient against base class size
// changes.
if (isa<ClassDecl>(this))
return false;

// Structs and enums imported from C *always* have a fixed layout.
// We know their size, and pass them as values in SIL and IRGen.
return true;
}

// Otherwise, access via indirect "resilient" interfaces.
return false;
Expand Down
18 changes: 13 additions & 5 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2589,13 +2589,21 @@ StringRef IRGenModule::mangleType(CanType type, SmallVectorImpl<char> &buffer) {
}

/// Is the given declaration resilient?
bool IRGenModule::isResilient(Decl *theDecl, ResilienceScope scope) {
// Classes defined by Clang are resilient.
if (auto theClass = dyn_cast<ClassDecl>(theDecl)) {
return theClass->hasClangNode();
bool IRGenModule::isResilient(Decl *D, ResilienceScope scope) {
auto NTD = dyn_cast<NominalTypeDecl>(D);
if (!NTD)
return false;

switch (scope) {
case ResilienceScope::Local:
case ResilienceScope::Component:
return !NTD->hasFixedLayout(SILMod->getSwiftModule());
case ResilienceScope::Program:
case ResilienceScope::Universal:
return !NTD->hasFixedLayout();
}

return false;
llvm_unreachable("Bad resilience scope");
}

/// Fetch the witness table access function for a protocol conformance.
Expand Down
23 changes: 23 additions & 0 deletions lib/IRGen/GenStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "Linking.h"
#include "IndirectTypeInfo.h"
#include "NonFixedTypeInfo.h"
#include "ResilientTypeInfo.h"
#include "StructMetadataLayout.h"

#pragma clang diagnostic ignored "-Winconsistent-missing-override"
Expand Down Expand Up @@ -743,9 +744,31 @@ void IRGenModule::emitStructDecl(StructDecl *st) {
emitNestedTypeDecls(st->getMembers());
}

namespace {
/// A type implementation for resilient struct types. This is not a
/// StructTypeInfoBase at all, since we don't know anything about
/// the struct's fields.
class ResilientStructTypeInfo
: public ResilientTypeInfo<ResilientStructTypeInfo>
{
public:
ResilientStructTypeInfo(llvm::Type *T)
: ResilientTypeInfo(T) { }
};
}

const TypeInfo *TypeConverter::convertResilientStruct() {
llvm::Type *storageType = IGM.OpaquePtrTy->getElementType();
return new ResilientStructTypeInfo(storageType);
}

const TypeInfo *TypeConverter::convertStructType(TypeBase *key, CanType type,
StructDecl *D) {
// All resilient structs have the same opaque lowering, since they are
// indistinguishable as values.
if (IGM.isResilient(D, ResilienceScope::Local))
return &getResilientStructTypeInfo();

// Create the struct type.
auto ty = IGM.createNominalType(D);

Expand Down
8 changes: 8 additions & 0 deletions lib/IRGen/GenType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,14 @@ const LoadableTypeInfo &TypeConverter::getEmptyTypeInfo() {
return *EmptyTI;
}

const TypeInfo &TypeConverter::getResilientStructTypeInfo() {
if (ResilientStructTI) return *ResilientStructTI;
ResilientStructTI = convertResilientStruct();
ResilientStructTI->NextConverted = FirstType;
FirstType = ResilientStructTI;
return *ResilientStructTI;
}

/// Get the fragile type information for the given type, which may not
/// have yet undergone SIL type lowering. The type can serve as its own
/// abstraction pattern.
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/GenType.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class TypeConverter {
const LoadableTypeInfo *TypeMetadataPtrTI = nullptr;
const LoadableTypeInfo *ObjCClassPtrTI = nullptr;
const LoadableTypeInfo *EmptyTI = nullptr;

const TypeInfo *ResilientStructTI = nullptr;

llvm::DenseMap<std::pair<unsigned, unsigned>, const LoadableTypeInfo *>
OpaqueStorageTypes;
Expand Down Expand Up @@ -130,6 +132,7 @@ class TypeConverter {
const LoadableTypeInfo *convertBuiltinNativeObject();
const LoadableTypeInfo *convertBuiltinUnknownObject();
const LoadableTypeInfo *convertBuiltinBridgeObject();
const TypeInfo *convertResilientStruct();
const TypeInfo *convertUnmanagedStorageType(UnmanagedStorageType *T);
const TypeInfo *convertUnownedStorageType(UnownedStorageType *T);
const TypeInfo *convertWeakStorageType(WeakStorageType *T);
Expand All @@ -149,6 +152,7 @@ class TypeConverter {
const LoadableTypeInfo &getObjCClassPtrTypeInfo();
const LoadableTypeInfo &getWitnessTablePtrTypeInfo();
const LoadableTypeInfo &getEmptyTypeInfo();
const TypeInfo &getResilientStructTypeInfo();
const ProtocolInfo &getProtocolInfo(ProtocolDecl *P);
const LoadableTypeInfo &getOpaqueStorageTypeInfo(Size storageSize,
Alignment storageAlign);
Expand Down
57 changes: 57 additions & 0 deletions test/IRGen/struct_resilience.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// RUN: %target-swift-frontend -I %S/../Inputs -enable-source-import -emit-ir -enable-resilience %s | FileCheck %s

import resilient_struct

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

// CHECK-LABEL: define void @_TF17struct_resilience26functionWithResilientTypesFTV16resilient_struct4Size1fFS1_S1__S1_(%swift.opaque* noalias nocapture sret, %swift.opaque* noalias nocapture, i8*, %swift.refcounted*)

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: [[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: [[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: [[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: ret void

return f(s)
}

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

public struct MySize {
public let w: Int
public let h: Int
}

// CHECK-LABEL: define void @_TF17struct_resilience28functionWithMyResilientTypesFTVS_6MySize1fFS0_S0__S0_(%V17struct_resilience6MySize* {{.*}}, %V17struct_resilience6MySize* {{.*}}, i8*, %swift.refcounted*)
public func functionWithMyResilientTypes(s: MySize, f: MySize -> MySize) -> MySize {

// CHECK: [[TEMP:%.*]] = alloca %V17struct_resilience6MySize, align 8
// CHECK: [[COPY:%.*]] = bitcast %V17struct_resilience6MySize* %4 to i8*
// CHECK: [[ARG:%.*]] = bitcast %V17struct_resilience6MySize* %1 to i8*
// CHECK: call void @llvm.memcpy{{.*}}(i8* [[COPY]], i8* [[ARG]], {{i32|i64}} 16, i32 {{.*}}, i1 false)
// CHECK: [[FN:%.*]] = bitcast i8* %2
// CHECK: call void [[FN]](%V17struct_resilience6MySize* {{.*}} %0, {{.*}} [[TEMP]], %swift.refcounted* %3)
// CHECK: ret void

return f(s)
}
File renamed without changes.
2 changes: 1 addition & 1 deletion test/SILGen/struct_resilience.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -I %S/Inputs -enable-source-import -emit-silgen -enable-resilience %s | FileCheck %s
// RUN: %target-swift-frontend -I %S/../Inputs -enable-source-import -emit-silgen -enable-resilience %s | FileCheck %s

import resilient_struct

Expand Down

0 comments on commit 19458ce

Please sign in to comment.