Skip to content

Commit 3cf40ea

Browse files
drexinGwen Mittertreinergmittert
authored
[IRGen] Re-introduce TypeLayout strings (swiftlang#62059)
* Introduce TypeLayout Strings Layout strings encode the structure of a type into a byte string that can be interpreted by a runtime function to achieve a destroy or copy. Rather than generating ir for a destroy/assignWithCopy/etc, we instead generate a layout string which encodes enough information for a called runtime function to perform the operation for us. Value witness functions tend to be quite large, so this allows us to replace them with a single call instead. This gives us the option of making a codesize/runtime cost trade off. * Added Attribute @_GenerateLayoutBytecode This marks a type definition that should use generic bytecode based value witnesses rather than generating the standard suite of value witness functions. This should reduce the codesize of the binary for a runtime interpretation of the bytecode cost. * Statically link in implementation Summary: This creates a library to store the runtime functions in to deploy to runtimes that do not implement bytecode layouts. Right now, that is everything. Once these are added to the runtime itself, it can be used to deploy to old runtimes. * Implement Destroy at Runtime Using LayoutStrings If GenerateLayoutBytecode is enabled, Create a layout string and use it to call swift_generic_destroy * Add Resilient type and Archetype Support for BytecodeLayouts Add Resilient type and Archetype Support to Bytecode Layouts * Implement Bytecode assign/init with copy/take Implements swift_generic_initialize and swift_generic_assign to allow copying types using bytecode based witnesses. * Add EnumTag Support * Add IRGen Bytecode Layouts Test Added a test to ensure layouts are correct and getting generated * Implement BytecodeLayouts ObjC retain/release * Fix for Non static alignments in aligned groups * Disable MultiEnums MultiEnums currently have some correctness issues with non fixed multienum types. Disabling them for now then going to attempt a correct implementation in a follow up patch * Fixes after merge * More fixes * Possible fix for native unowned * Use TypeInfoeBasedTypeLayoutEntry for all scalars when ForceStructTypeLayouts is disabled * Remove @_GenerateBytecodeLayout attribute * Fix typelayout_based_value_witness.swift Co-authored-by: Gwen Mittertreiner <[email protected]> Co-authored-by: Gwen Mittertreiner <[email protected]>
1 parent d0b68a5 commit 3cf40ea

30 files changed

+3041
-227
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
##############
2+
Layout Strings
3+
##############
4+
5+
Introduction
6+
***********
7+
Layout strings encode the structure of a type into a bytestring that can be
8+
interpreted by a runtime function to achieve a destroy or copy. Rather than
9+
generating ir for a destroy/assignWithCopy/etc, we instead generate a layout
10+
string which encodes enough information for a called runtime function to
11+
perform the operation for us. Value witness functions tend to be quite large,
12+
so this allows us to replace them with a single call instead. This gives us
13+
the option of making a codesize/runtime cost trade off.
14+
15+
NB: Integers in this format are read and written as big endian. There's no
16+
strong reason for this. Just that it's slightly easier to read/debug the
17+
string when dumping it from memory.
18+
19+
The Runtime Functions
20+
********************
21+
There are 5 runtime functions which follow from the 5 similar value witness functions:
22+
23+
Destroy a given value at addr
24+
::
25+
swift_generic_destroy(void* addr, Metadata* metadata, const char* layoutStr);
26+
27+
Retain src if we are not taking, release dest, then copy a value from src to dest.
28+
::
29+
swift_generic_assign(void* dest, void* src, Metadata* metadata, const char* layoutStr, bool isTake);
30+
31+
Retain src if we are not taking, then copy a value from src to dest.
32+
::
33+
swift_generic_initialize(void* dest, void* src, Metadata* metadata, const char* layoutStr, bool isTake);
34+
35+
Each currently takes the layout string as an additional arugment. Ideally the
36+
layout string is stored in the metadata so that the extra arguments are not
37+
needed. This would additionally allow us to dynamically create strings at
38+
metadata initialization time to do things like runtime generic specialization
39+
for the layout string.
40+
41+
Value types
42+
*******
43+
44+
VALUE := SCALAR | ALIGNED_GROUP | SINGLE_ENUM | MULTI_ENUM | ARCHETYPE | RESILIENT_TYPE
45+
46+
Scalars
47+
******
48+
49+
SCALAR := 'c'|'s'|'l'|'L'|'C'|'r'|'N'|'n'|'W'|'u'|'w'|'b'|'B'|'o'|'f'|'x'
50+
51+
For our purposes, scalars are types that are not composed of other types. In
52+
swift, this is mostly POD types such as ints, and reference types.
53+
54+
We define POD types for types that just represent data. These do need to be
55+
copied, but do not need to be released or retained.
56+
::
57+
I8 = 'c',
58+
I16 = 's',
59+
I32 = 'l',
60+
I64 = 'L',
61+
62+
We also have reference types. While they are all 64bit sized, we need to
63+
differentiate between them because they have different ways of being
64+
released/retained.
65+
66+
::
67+
ErrorReference = 'r',
68+
NativeStrongReference = 'N',
69+
NativeUnownedReference = 'n',
70+
NativeWeakReference = 'W',
71+
UnknownUnownedReference = 'u',
72+
UnknownWeakReference = 'w',
73+
BlockReference = 'b',
74+
BridgeReference = 'B',
75+
ObjCReference = 'o',
76+
ExistentialReference = 'x',
77+
78+
Aligned Group
79+
*************
80+
Structs are expressed as a group of values that have required alignments.
81+
::
82+
ALIGNED_GROUP:= 'a' UINT32 (ALIGNMENT,UINT32,VALUE)+
83+
// ALIGNED_GROUP:= 'a' numFields (alignment,fieldLength,field)+
84+
ALIGNMENT := '0'|'1'|'2'|'3'
85+
86+
The Alignment attached to the structs indicates the field should be aligned on
87+
2^(ALIGNMENT) bytes
88+
89+
Enums
90+
*******
91+
92+
We distinguish between the less complex single enums, and the more complex
93+
multi payload enums. Note the no payload enums are lowered to a POD scalar
94+
rather than an enum.
95+
96+
Single Enums
97+
-------------
98+
::
99+
SIZE := uint32
100+
101+
// e numEmptyPayloads lengthOfPayload payload
102+
SINGLEENUM := 'e' SIZE SIZE VALUE
103+
104+
For single payload enums we need enough information to determine the overall
105+
size of the enum and how to release/retain it. For example, to release an
106+
single payload enum, we need to do the following:
107+
108+
::
109+
destroy SINGLEENUM:
110+
compute extra inhabitants of PAYLOAD
111+
determine if numEmptyPayloads fits in extra inhabitants
112+
if they don't fit, add extra tag bits
113+
check if any extra inhabitant bits or extra tag bits are set
114+
if not, we have a value:
115+
destroy value
116+
117+
Multi Enums
118+
-----------
119+
::
120+
// E numEmptyPayloads numPayloads lengthOfEachPayload payloads
121+
MULTIENUM := 'E' SIZE SIZE SIZE+ VALUE+
122+
123+
For multi payload enums we need enough information to determine the overall
124+
size of the enum from each paylaod and how to release/retain each payload. For
125+
example to release a multi enum, we need to do the following:
126+
127+
::
128+
destroy MULTIENUM:
129+
compute and merge the extra inhabitants of each possible payload
130+
compute the overall size of the enum (size of largest payload plus any extra tag bits)
131+
use the extra inhabitants and extra tag bits to get the encoded enum case
132+
if the case < numPayloads:
133+
destroy the indicated payload
134+
135+
Examples
136+
********
137+
138+
Struct
139+
------
140+
::
141+
struct {
142+
let a : Int8
143+
let b : Int16
144+
let c : Int16
145+
let d : SomeClass
146+
}
147+
148+
byte aligned int8
149+
2 byte aligned int16
150+
2 byte aligned int16
151+
8 byte aligned Native Pointer
152+
::
153+
'1c2s2s8N'
154+
155+
Single Enum
156+
----
157+
::
158+
enum {
159+
case a(c: SomeClasee)
160+
case b
161+
case c
162+
case d
163+
}
164+
165+
A single enum with 3 no payload cases, a payload length of 1, and a payload of
166+
a single Native Pointer
167+
::
168+
'e<0x0><0x0><0x0><0x3><0x0><0x0><0x0><0x1>N'
169+
170+
Multi Enum
171+
----
172+
::
173+
struct MyStruct {
174+
let a: SomeClass
175+
let b: SomeClass
176+
}
177+
enum {
178+
case a(c: SomeClass)
179+
case b(c: MyStruct)
180+
case c
181+
case d
182+
case e
183+
}
184+
185+
A Multi enum with 3 no payload cases, two payloads, one of a struct, the other of just a Native pointer
186+
::
187+
'E<0x0><0x0><0x0><0x3><0x0><0x0><0x0><0x2><0x0><0x0><0x0><0x4><0x0><0x0><0x0><0x1>N4N4N'
188+
^| Num no payloads | num payloads | strlength payload1 |strlen payload2 |^| MyStruct
189+
|----+--------Multi Enum Indicator |--SomeClass

include/swift/AST/IRGenOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ class IRGenOptions {
448448
Optional<llvm::VersionTuple> AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion;
449449
Optional<llvm::VersionTuple>
450450
AutolinkRuntimeCompatibilityConcurrencyLibraryVersion;
451+
bool AutolinkRuntimeCompatibilityBytecodeLayoutsLibrary;
451452

452453
JITDebugArtifact DumpJIT = JITDebugArtifact::None;
453454

include/swift/Option/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,12 @@ def disable_autolinking_runtime_compatibility_concurrency
13761376
HelpText<"Do not use autolinking for the concurrency runtime "
13771377
"compatibility library">;
13781378

1379+
def enable_autolinking_runtime_compatibility_bytecode_layouts
1380+
: Flag<[ "-" ], "enable-autolinking-runtime-compatibility-bytecode-layouts">,
1381+
Flags<[ FrontendOption ]>,
1382+
HelpText<"Enable autolinking for the bytecode layouts runtime "
1383+
"compatibility library">;
1384+
13791385
def emit_symbol_graph: Flag<["-"], "emit-symbol-graph">,
13801386
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>,
13811387
HelpText<"Emit a symbol graph">;

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,6 +2121,34 @@ FUNCTION(StoreMultiPayloadEnumTagSinglePayload,
21212121
ATTRS(NoUnwind),
21222122
EFFECT(NoEffect))
21232123

2124+
// void *swift_generic_destroy(opaque*, const Metadata* type, const char*);
2125+
FUNCTION(GenericDestroy,
2126+
swift_generic_destroy,
2127+
C_CC, AlwaysAvailable,
2128+
RETURNS(VoidTy),
2129+
ARGS(Int8PtrTy, TypeMetadataPtrTy, Int8PtrTy),
2130+
ATTRS(NoUnwind),
2131+
EFFECT(Deallocating))
2132+
2133+
2134+
// void *swift_generic_assign(opaque* dest, opaque* src, const Metadata* type, const char*, bool isTake);
2135+
FUNCTION(GenericAssign,
2136+
swift_generic_assign,
2137+
C_CC, AlwaysAvailable,
2138+
RETURNS(VoidTy),
2139+
ARGS(Int8PtrTy, Int8PtrTy, TypeMetadataPtrTy, Int8PtrTy, Int1Ty),
2140+
ATTRS(NoUnwind),
2141+
EFFECT(Refcounting, Deallocating))
2142+
2143+
// void *swift_generic_initialize(opaque* dest, opaque* src, const Metadata* type, const char*, bool isTake);
2144+
FUNCTION(GenericInitialize,
2145+
swift_generic_initialize,
2146+
C_CC, AlwaysAvailable,
2147+
RETURNS(VoidTy),
2148+
ARGS(Int8PtrTy, Int8PtrTy, TypeMetadataPtrTy, Int8PtrTy, Int1Ty),
2149+
ATTRS(NoUnwind),
2150+
EFFECT(Refcounting))
2151+
21242152
#undef RETURNS
21252153
#undef ARGS
21262154
#undef ATTRS

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2383,6 +2383,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
23832383
getRuntimeCompatVersion();
23842384
}
23852385

2386+
Opts.AutolinkRuntimeCompatibilityBytecodeLayoutsLibrary = Args.hasArg(
2387+
options::OPT_enable_autolinking_runtime_compatibility_bytecode_layouts);
2388+
23862389
if (const Arg *A = Args.getLastArg(OPT_num_threads)) {
23872390
if (StringRef(A->getValue()).getAsInteger(10, Opts.NumThreads)) {
23882391
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,

lib/IRGen/ClassTypeInfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class ClassTypeInfo : public HeapTypeInfo<ClassTypeInfo> {
4949
ClassTypeInfo(llvm::PointerType *irType, Size size, SpareBitVector spareBits,
5050
Alignment align, ClassDecl *theClass,
5151
ReferenceCounting refcount, llvm::StructType *classLayoutType)
52-
: HeapTypeInfo(irType, size, std::move(spareBits), align),
52+
: HeapTypeInfo(refcount, irType, size, std::move(spareBits), align),
5353
TheClass(theClass), classLayoutType(classLayoutType),
5454
Refcount(refcount) {}
5555

lib/IRGen/GenArchetype.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "ProtocolInfo.h"
5252
#include "ResilientTypeInfo.h"
5353
#include "TypeInfo.h"
54+
#include "TypeLayout.h"
5455

5556
using namespace swift;
5657
using namespace irgen;
@@ -133,7 +134,7 @@ class ClassArchetypeTypeInfo
133134
Size size, const SpareBitVector &spareBits,
134135
Alignment align,
135136
ReferenceCounting refCount)
136-
: HeapTypeInfo(storageType, size, spareBits, align),
137+
: HeapTypeInfo(refCount, storageType, size, spareBits, align),
137138
RefCount(refCount)
138139
{}
139140

lib/IRGen/GenConcurrency.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ class ExecutorTypeInfo :
5454

5555
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM,
5656
SILType T) const override {
57-
return IGM.typeLayoutCache.getOrCreateScalarEntry(*this, T);
57+
if (!IGM.getOptions().ForceStructTypeLayouts) {
58+
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
59+
}
60+
return IGM.typeLayoutCache.getOrCreateScalarEntry(*this, T,
61+
ScalarKind::POD);
5862
}
5963

6064
static Size getSecondElementOffset(IRGenModule &IGM) {

lib/IRGen/GenDecl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,11 @@ void IRGenModule::emitSourceFile(SourceFile &SF) {
530530
#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName) \
531531
addBackDeployLib(llvm::VersionTuple Version, LibraryName);
532532
#include "swift/Frontend/BackDeploymentLibs.def"
533+
534+
if (IRGen.Opts.AutolinkRuntimeCompatibilityBytecodeLayoutsLibrary)
535+
this->addLinkLibrary(LinkLibrary("swiftCompatibilityBytecodeLayouts",
536+
LibraryKind::Library,
537+
/*forceLoad*/ true));
533538
}
534539
}
535540

lib/IRGen/GenDiffFunc.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class DifferentiableFuncTypeInfo final
120120
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM,
121121
SILType T) const override {
122122
if (!IGM.getOptions().ForceStructTypeLayouts || !areFieldsABIAccessible()) {
123-
return IGM.typeLayoutCache.getOrCreateScalarEntry(*this, T);
123+
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
124124
}
125125

126126
if (getFields().empty()) {
@@ -292,7 +292,7 @@ class LinearFuncTypeInfo final
292292
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM,
293293
SILType T) const override {
294294
if (!IGM.getOptions().ForceStructTypeLayouts || !areFieldsABIAccessible()) {
295-
return IGM.typeLayoutCache.getOrCreateScalarEntry(*this, T);
295+
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
296296
}
297297

298298
if (getFields().empty()) {

0 commit comments

Comments
 (0)