Skip to content

Commit cbe4b0d

Browse files
committed
Teach IRGen how to work with archetypes of fixed sizes.
This allows IRGen to use direct loads and stores instead of calling value witness tables.
1 parent 6f76726 commit cbe4b0d

File tree

3 files changed

+108
-2
lines changed

3 files changed

+108
-2
lines changed

lib/IRGen/GenArchetype.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,14 +280,39 @@ class ClassArchetypeTypeInfo
280280
}
281281
};
282282

283+
class FixedSizeArchetypeTypeInfo
284+
: public PODSingleScalarTypeInfo<FixedSizeArchetypeTypeInfo, LoadableTypeInfo>,
285+
public ArchetypeTypeInfoBase
286+
{
287+
FixedSizeArchetypeTypeInfo(llvm::Type *type, Size size, Alignment align,
288+
const SpareBitVector &spareBits,
289+
ArrayRef<ProtocolEntry> protocols)
290+
: PODSingleScalarTypeInfo(type, size, spareBits, align),
291+
ArchetypeTypeInfoBase(this + 1, protocols) {}
292+
293+
public:
294+
static const FixedSizeArchetypeTypeInfo *
295+
create(llvm::Type *type, Size size, Alignment align,
296+
const SpareBitVector &spareBits, ArrayRef<ProtocolEntry> protocols) {
297+
void *buffer = operator new(sizeof(FixedSizeArchetypeTypeInfo) +
298+
protocols.size() * sizeof(ProtocolEntry));
299+
return ::new (buffer)
300+
FixedSizeArchetypeTypeInfo(type, size, align, spareBits, protocols);
301+
}
302+
};
303+
283304
} // end anonymous namespace
284305

285306
/// Return the ArchetypeTypeInfoBase information from the TypeInfo for any
286307
/// archetype.
287308
static const ArchetypeTypeInfoBase &
288309
getArchetypeInfo(IRGenFunction &IGF, CanArchetypeType t, const TypeInfo &ti) {
289-
if (t->requiresClass())
310+
LayoutConstraint LayoutInfo = t->getLayoutConstraint();
311+
if (t->requiresClass() || (LayoutInfo && LayoutInfo->isRefCountedObject()))
290312
return ti.as<ClassArchetypeTypeInfo>();
313+
if (LayoutInfo && LayoutInfo->isFixedSizeTrivial())
314+
return ti.as<FixedSizeArchetypeTypeInfo>();
315+
// TODO: Handle LayoutConstraintInfo::Trivial
291316
return ti.as<OpaqueArchetypeTypeInfo>();
292317
}
293318

@@ -374,9 +399,12 @@ const TypeInfo *TypeConverter::convertArchetypeType(ArchetypeType *archetype) {
374399
protocols.push_back(ProtocolEntry(protocol, impl));
375400
}
376401

402+
LayoutConstraint LayoutInfo = archetype->getLayoutConstraint();
403+
377404
// If the archetype is class-constrained, use a class pointer
378405
// representation.
379-
if (archetype->requiresClass()) {
406+
if (archetype->requiresClass() ||
407+
(LayoutInfo && LayoutInfo->isRefCountedObject())) {
380408
ReferenceCounting refcount;
381409
llvm::PointerType *reprTy;
382410

@@ -412,6 +440,28 @@ const TypeInfo *TypeConverter::convertArchetypeType(ArchetypeType *archetype) {
412440
protocols, refcount);
413441
}
414442

443+
// If the archetype is trivial fixed-size layout-constrained, use a fixed size
444+
// representation.
445+
if (LayoutInfo && LayoutInfo->isFixedSizeTrivial()) {
446+
Size size(LayoutInfo->getTrivialSizeInBytes());
447+
Alignment align(LayoutInfo->getTrivialSizeInBytes());
448+
auto spareBits =
449+
SpareBitVector::getConstant(size.getValueInBits(), false);
450+
// Get an integer type of the required size.
451+
auto ProperlySizedIntTy = SILType::getBuiltinIntegerType(
452+
size.getValueInBits(), IGM.getSwiftModule()->getASTContext());
453+
auto storageType = IGM.getStorageType(ProperlySizedIntTy);
454+
return FixedSizeArchetypeTypeInfo::create(storageType, size, align,
455+
spareBits, protocols);
456+
}
457+
458+
// If the archetype is a trivial layout-constrained, use a POD
459+
// representation. This type is not loadable, but it is known
460+
// to be a POD.
461+
if (LayoutInfo && LayoutInfo->isAddressOnlyTrivial()) {
462+
// TODO: Create NonFixedSizeArchetypeTypeInfo and return it.
463+
}
464+
415465
// Otherwise, for now, always use an opaque indirect type.
416466
llvm::Type *storageType = IGM.OpaquePtrTy->getElementType();
417467
return OpaqueArchetypeTypeInfo::create(storageType, protocols);

lib/IRGen/GenOpaque.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,9 @@ void irgen::emitAssignWithTakeCall(IRGenFunction &IGF,
669669
void irgen::emitDestroyCall(IRGenFunction &IGF,
670670
SILType T,
671671
Address object) {
672+
// If T is a trivial/POD type, nothing needs to be done.
673+
if (T.getObjectType().isTrivial(IGF.getSILModule()))
674+
return;
672675
auto metadata = IGF.emitTypeMetadataRefForLayout(T);
673676
llvm::Value *fn = IGF.emitValueWitnessForLayout(T,
674677
ValueWitness::Destroy);
@@ -683,6 +686,9 @@ void irgen::emitDestroyArrayCall(IRGenFunction &IGF,
683686
SILType T,
684687
Address object,
685688
llvm::Value *count) {
689+
// If T is a trivial/POD type, nothing needs to be done.
690+
if (T.getObjectType().isTrivial(IGF.getSILModule()))
691+
return;
686692
auto metadata = IGF.emitTypeMetadataRefForLayout(T);
687693
llvm::Value *fn = IGF.emitValueWitnessForLayout(T,
688694
ValueWitness::DestroyArray);

test/SILOptimizer/eager_specialize.sil

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -eager-specializer -new-mangling-for-tests %s | %FileCheck %s
22
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -eager-specializer -sil-deadfuncelim -new-mangling-for-tests %s | %FileCheck --check-prefix=CHECK-DEADFUNCELIM %s
3+
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -eager-specializer -new-mangling-for-tests %s -o %t.sil && %target-swift-frontend -assume-parsing-unqualified-ownership-sil -module-name=eager_specialize -emit-ir %t.sil | %FileCheck --check-prefix=CHECK-IRGEN %s
34

45
sil_stage canonical
56

@@ -641,3 +642,52 @@ bb0(%0 : $*T, %1 : $*S):
641642
// CHECK: %19 = unchecked_trivial_bit_cast %18 : $() to $()
642643
// CHECK: br bb2
643644
// CHECK: } // end sil function '_T016eager_specialize34checkExplicitPartialSpecializationyx_q_tr0_lF'
645+
646+
////////////////////////////////////////////////////////////////////
647+
// Check that IRGen generates efficient code for fixed-size Trivial
648+
// constraints.
649+
////////////////////////////////////////////////////////////////////
650+
651+
// Check that a specialization for _Trivial(32) uses direct loads and stores
652+
// instead of value witness functions to load and store the value of a generic type.
653+
// CHECK-IRGEN-LABEL: define linkonce_odr hidden void @_T016eager_specialize18copyValueAndReturnxx_xz1stlFxxxRlze31_lItilr_Tp5(i32* noalias nocapture sret, i32* noalias nocapture dereferenceable(4), i32* nocapture dereferenceable(4), %swift.type* %"\CF\84_0_0")
654+
// CHECK-IRGEN: entry:
655+
// CHECK-IRGEN-NEXT: %3 = load i32, i32* %2, align 4
656+
// CHECK-IRGEN-NEXT: store i32 %3, i32* %0, align 4
657+
// CHECK-IRGEN-NEXT: ret void
658+
// CHECK-IRGEN-NEXT:}
659+
660+
// Check that a specialization for _Trivial(64) uses direct loads and stores
661+
// instead of value witness functions to load and store the value of a generic type.
662+
// CHECK-IRGEN-LABEL: define linkonce_odr hidden void @_T016eager_specialize18copyValueAndReturnxx_xz1stlFxxxRlze63_lItilr_Tp5(i64* noalias nocapture sret, i64* noalias nocapture dereferenceable(8), i64* nocapture dereferenceable(8), %swift.type* %"\CF\84_0_0")
663+
// CHECK-IRGEN: entry:
664+
// CHECK-IRGEN-NEXT: %3 = load i64, i64* %2, align 8
665+
// CHECK-IRGEN-NEXT: store i64 %3, i64* %0, align 8
666+
// CHECK-IRGEN-NEXT: ret void
667+
// CHECK-IRGEN-NEXT: }
668+
669+
// Check that a specialization for _Trivial does not call the 'destroy' value witness,
670+
// because it is known that the object is Trivial, i.e. contains no references.
671+
// CHECK-IRGEN-LABEL: define linkonce_odr hidden void @_T016eager_specialize19copyValueAndReturn2xx_xz1stlFxxxRlzTlItilr_Tp5(%swift.opaque* noalias nocapture sret, %swift.opaque* noalias nocapture, %swift.opaque* nocapture, %swift.type* %"\CF\84_0_0")
672+
// CHECK-IRGEN-NEXT: entry:
673+
// CHECK-IRGEN-NEXT: %3 = bitcast %swift.type* %"\CF\84_0_0" to i8***
674+
// CHECK-IRGEN-NEXT: %4 = getelementptr inbounds i8**, i8*** %3, i64 -1
675+
// CHECK-IRGEN-NEXT: %"\CF\84_0_0.valueWitnesses" = load i8**, i8*** %4, align 8, !invariant.load !11, !dereferenceable !12
676+
// CHECK-IRGEN-NEXT: %5 = getelementptr inbounds i8*, i8** %"\CF\84_0_0.valueWitnesses", i32 6
677+
// CHECK-IRGEN-NEXT: %6 = load i8*, i8** %5, align 8, !invariant.load !11
678+
// CHECK-IRGEN-NEXT: %initializeWithCopy = bitcast i8* %6 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
679+
// CHECK-IRGEN-NEXT: %7 = call %swift.opaque* %initializeWithCopy(%swift.opaque* %0, %swift.opaque* %2, %swift.type* %"\CF\84_0_0") #3
680+
// CHECK-IRGEN-NEXT: ret void
681+
// CHECK-IRGEN-NEXT: }
682+
683+
// Check that a specialization for _RefCountedObject just copies the fixed-size reference,
684+
// and call retain/release directly, instead of calling the value witness functions.
685+
// CHECK-IRGEN-LABEL: define linkonce_odr hidden void @_T016eager_specialize19copyValueAndReturn3xx_xz1stlFxxxRlzRlItilr_Tp5(%objc_object** noalias nocapture sret, %objc_object** noalias nocapture dereferenceable(8), %objc_object** nocapture dereferenceable(8), %swift.type* %"\CF\84_0_0")
686+
// CHECK-IRGEN: entry:
687+
// CHECK-IRGEN-NEXT: %3 = load %objc_object*, %objc_object** %2, align 8
688+
// CHECK-IRGEN-NEXT: call void @swift_unknownRetain(%objc_object* %3) #3
689+
// CHECK-IRGEN-NEXT: store %objc_object* %3, %objc_object** %0, align 8
690+
// CHECK-IRGEN-NEXT: %toDestroy = load %objc_object*, %objc_object** %1, align 8
691+
// CHECK-IRGEN-NEXT: call void @swift_unknownRelease(%objc_object* %toDestroy) #3
692+
// CHECK-IRGEN-NEXT: ret void
693+
// CHECK-IRGEN-NEXT: }

0 commit comments

Comments
 (0)