Skip to content

Commit fef7c77

Browse files
committed
[IRGen] Use memcpy for BitwiseCopyable archetypes.
Besides being an optimization, using memcpy directly rather than a value witness is required in order to interact with unaligned instances of such types: the value witness functions expect their arguments to be aligned. rdar://96919870
1 parent 9562312 commit fef7c77

File tree

3 files changed

+164
-0
lines changed

3 files changed

+164
-0
lines changed

lib/IRGen/GenArchetype.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/Decl.h"
2121
#include "swift/AST/GenericEnvironment.h"
2222
#include "swift/AST/IRGenOptions.h"
23+
#include "swift/AST/KnownProtocols.h"
2324
#include "swift/AST/Types.h"
2425
#include "swift/IRGen/Linking.h"
2526
#include "swift/SIL/SILValue.h"
@@ -172,6 +173,76 @@ class FixedSizeArchetypeTypeInfo
172173
}
173174
};
174175

176+
class BitwiseCopyableArchetypeTypeInfo
177+
: public WitnessSizedTypeInfo<BitwiseCopyableArchetypeTypeInfo> {
178+
using Self = BitwiseCopyableArchetypeTypeInfo;
179+
using Super = WitnessSizedTypeInfo<Self>;
180+
BitwiseCopyableArchetypeTypeInfo(llvm::Type *type,
181+
IsABIAccessible_t abiAccessible)
182+
: Super(type, Alignment(1), IsTriviallyDestroyable, IsBitwiseTakable,
183+
IsCopyable, abiAccessible) {}
184+
185+
public:
186+
static const BitwiseCopyableArchetypeTypeInfo *
187+
create(llvm::Type *type, IsABIAccessible_t abiAccessible) {
188+
return new Self(type, abiAccessible);
189+
}
190+
191+
void bitwiseCopy(IRGenFunction &IGF, Address destAddr, Address srcAddr,
192+
SILType T, bool isOutlined) const {
193+
IGF.Builder.CreateMemCpy(destAddr, srcAddr, getSize(IGF, T));
194+
}
195+
196+
void initializeWithTake(IRGenFunction &IGF, Address destAddr, Address srcAddr,
197+
SILType T, bool isOutlined) const override {
198+
bitwiseCopy(IGF, destAddr, srcAddr, T, isOutlined);
199+
}
200+
201+
void initializeWithCopy(IRGenFunction &IGF, Address destAddr, Address srcAddr,
202+
SILType T, bool isOutlined) const override {
203+
bitwiseCopy(IGF, destAddr, srcAddr, T, isOutlined);
204+
}
205+
206+
void assignWithCopy(IRGenFunction &IGF, Address destAddr, Address srcAddr,
207+
SILType T, bool isOutlined) const override {
208+
bitwiseCopy(IGF, destAddr, srcAddr, T, isOutlined);
209+
}
210+
211+
void assignWithTake(IRGenFunction &IGF, Address destAddr, Address srcAddr,
212+
SILType T, bool isOutlined) const override {
213+
bitwiseCopy(IGF, destAddr, srcAddr, T, isOutlined);
214+
}
215+
216+
void destroy(IRGenFunction &IGF, Address address, SILType T,
217+
bool isOutlined) const override {
218+
// BitwiseCopyable types are trivial, so destroy is a no-op.
219+
}
220+
221+
llvm::Value *getEnumTagSinglePayload(IRGenFunction &IGF,
222+
llvm::Value *numEmptyCases,
223+
Address enumAddr, SILType T,
224+
bool isOutlined) const override {
225+
return emitGetEnumTagSinglePayloadCall(IGF, T, numEmptyCases, enumAddr);
226+
}
227+
228+
void storeEnumTagSinglePayload(IRGenFunction &IGF, llvm::Value *whichCase,
229+
llvm::Value *numEmptyCases, Address enumAddr,
230+
SILType T, bool isOutlined) const override {
231+
emitStoreEnumTagSinglePayloadCall(IGF, T, whichCase, numEmptyCases,
232+
enumAddr);
233+
}
234+
235+
void collectMetadataForOutlining(OutliningMetadataCollector &collector,
236+
SILType T) const override {
237+
// We'll need formal type metadata for this archetype.
238+
collector.collectTypeMetadataForLayout(T);
239+
}
240+
241+
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM, SILType T,
242+
bool useStructLayouts) const override {
243+
return IGM.typeLayoutCache.getOrCreateArchetypeEntry(T.getObjectType());
244+
}
245+
};
175246
} // end anonymous namespace
176247

177248
/// Emit a single protocol witness table reference.
@@ -361,6 +432,19 @@ const TypeInfo *TypeConverter::convertArchetypeType(ArchetypeType *archetype) {
361432
? IsABIAccessible
362433
: IsNotABIAccessible;
363434
}
435+
436+
auto &ASTContext = IGM.getSwiftModule()->getASTContext();
437+
if (ASTContext.LangOpts.hasFeature(Feature::BitwiseCopyable)) {
438+
// TODO: Should this conformance imply isAddressOnlyTrivial is true?
439+
auto *proto = ASTContext.getProtocol(KnownProtocolKind::BitwiseCopyable);
440+
// It's possible for the protocol not to exist if the stdlib is built
441+
// no_asserts.
442+
if (proto && IGM.getSwiftModule()->lookupConformance(archetype, proto)) {
443+
return BitwiseCopyableArchetypeTypeInfo::create(storageType,
444+
abiAccessible);
445+
}
446+
}
447+
364448
return OpaqueArchetypeTypeInfo::create(storageType, abiAccessible);
365449
}
366450

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all -enable-experimental-feature BuiltinModule -enable-experimental-feature BitwiseCopyable) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
5+
// Execute an unaligned load of SIMD16<UInt8> which conforms to a protocol derived from BitwiseCopyable.
6+
7+
public protocol MyBitwiseCopyable : _BitwiseCopyable {}
8+
9+
extension SIMD16 : MyBitwiseCopyable {}
10+
11+
func doit() {
12+
let bytes: [UInt8] = Array(repeating: 0, count: 64)
13+
bytes.withUnsafeBufferPointer { bytes in
14+
let rawBytes = UnsafeRawPointer(bytes.baseAddress!) + 1
15+
let vector = rawBytes.myLoadUnaligned(as: SIMD16<UInt8>.self)
16+
//CHECK: SIMD16<UInt8>(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
17+
blackhole(vector)
18+
}
19+
}
20+
21+
import Builtin
22+
23+
extension UnsafeRawPointer {
24+
@inlinable
25+
@_alwaysEmitIntoClient
26+
public func myLoadUnaligned<T : MyBitwiseCopyable>(
27+
fromByteOffset offset: Int = 0,
28+
as type: T.Type
29+
) -> T {
30+
return Builtin.loadRaw((self + offset)._rawValue)
31+
}
32+
}
33+
34+
doit()
35+
36+
@_silgen_name("blackhole")
37+
@inline(never)
38+
@_semantics("optimize.sil.specialize.generic.never")
39+
func blackhole<T>(_ t: T) {
40+
print(t)
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all -enable-experimental-feature BuiltinModule -enable-experimental-feature BitwiseCopyable -Xfrontend -disable-availability-checking) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
5+
// Execute an unaligned load of SIMD16<UInt8> which retroactively conforms directly to BitwiseCopyable.
6+
7+
extension SIMD16 : @retroactive _BitwiseCopyable {}
8+
9+
func doit() {
10+
let bytes: [UInt8] = Array(repeating: 0, count: 64)
11+
bytes.withUnsafeBufferPointer { bytes in
12+
let rawBytes = UnsafeRawPointer(bytes.baseAddress!) + 1
13+
let vector = rawBytes.myLoadUnaligned(as: SIMD16<UInt8>.self)
14+
//CHECK: SIMD16<UInt8>(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
15+
blackhole(vector)
16+
}
17+
}
18+
19+
import Builtin
20+
21+
extension UnsafeRawPointer {
22+
@inlinable
23+
@_alwaysEmitIntoClient
24+
public func myLoadUnaligned<T : _BitwiseCopyable>(
25+
fromByteOffset offset: Int = 0,
26+
as type: T.Type
27+
) -> T {
28+
return Builtin.loadRaw((self + offset)._rawValue)
29+
}
30+
}
31+
32+
doit()
33+
34+
@_silgen_name("blackhole")
35+
@inline(never)
36+
@_semantics("optimize.sil.specialize.generic.never")
37+
func blackhole<T>(_ t: T) {
38+
print(t)
39+
}

0 commit comments

Comments
 (0)