Skip to content

Commit 89e5e3d

Browse files
committed
[DefaultOverrides] SIL representation.
See the comment in SILDefaultOverrideTable.h for details.
1 parent 5eacbfd commit 89e5e3d

File tree

5 files changed

+493
-0
lines changed

5 files changed

+493
-0
lines changed
Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
//===--- SILDefaultOverrideTable.h ------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// TLDR: class : protocol :: SILDefaultOverrideTable : SILDefaultWitnessTable
14+
//
15+
// This file defines the SILDefaultOverrideTable class, which is used to
16+
// provide default override implementations of class routines which have been
17+
// come to implement the same semantic class member that was previously
18+
// implemented by a different routine. As with SILDefaultWitnessTable, this
19+
// type enables IRGen to generate metadata which in turn allows the runtime to
20+
// instantiate vtables which contain these default overrides when they are
21+
// needed: in the vtables of subclasses which were emitted prior to the
22+
// replacement of the routine that implements the semantic member AND which
23+
// already provided an override of the routine that previously implemented the
24+
// semantic member.
25+
//
26+
// +--Example--------------------------------------------------------------{{ -+
27+
//
28+
// ResilientFramework v1:
29+
// open class C {
30+
// open var x: X {
31+
// _read {
32+
// ...
33+
// }
34+
// }
35+
// }
36+
//
37+
// func useCOrDerived(_ c: C) {
38+
// // calls C.x._read
39+
// let x = C.x
40+
// }
41+
//
42+
// ResilientFramework v2:
43+
// open class C {
44+
// open var: x: X {
45+
// read {
46+
// ...
47+
// }
48+
// // compiler generated thunk
49+
// _read {
50+
// yield x.read() // this isn't actually expressible in Swift
51+
// }
52+
// }
53+
// }
54+
//
55+
// func useCOrDerived(_ c: C) {
56+
// // calls C.x.read (!!!)
57+
// let x = C.x
58+
// pass(x)
59+
// }
60+
//
61+
// The "semantic class member" here is the "reader" for C.x. In 1, this member
62+
// was implemented by C.x._read. In 2.0, this was implemented by C.x.read. In
63+
// other words, when C.x is read from some instance `Instance` of C or a
64+
// subclass (e.g. in useCOrDerived), the C.x.read routine of `Instance`'s class
65+
// is used. More concretely, the routine stored in the C.x.read slot from
66+
// class(`Instance`)'s vtable is dispatched to.
67+
//
68+
// Without the solution provided SILDefaultOverrideTable, that slot could
69+
// contain the wrong routine!, namely C's implementation of C.x.read when the
70+
// subclass overrode C.x._read, as illustrated below.
71+
//
72+
// ClientBinary, built against version 1:
73+
// class D : C {
74+
// override var x: X {
75+
// _read {
76+
// ...
77+
// }
78+
// }
79+
// }
80+
//
81+
// When C.x._read is called on an instance of D, D's override of that routine is
82+
// called. When running ClientBinary against ResilientFramework v1, this is
83+
// sufficient: useCOrDerived's reading of x dispatches to the routine in the
84+
// x._read slot of the vtable, which D's vtable contains an override of.
85+
//
86+
// In detail, here's what happens at runtime: {{ // Runtime situation with v1
87+
//
88+
// // Pseudocode describing dispatch in ResilientFramework.useCOrDerived.
89+
// func useCOrDerived(_ c: C) {
90+
// let vtable = typeof(c).VTable
91+
// let reader = vtable[#slot(x._read)]
92+
// let x = reader(c)
93+
// pass(x)
94+
// }
95+
//
96+
// +- C.VTable ----------+
97+
// | slot | impl |
98+
// +---------+-----------+
99+
// | x._read | C.x._read |
100+
// +---------+-----------+
101+
//
102+
// +- D.VTable ----------+
103+
// | slot | impl |
104+
// +---------+-----------+
105+
// | x._read | D.x._read |
106+
// +---------+-----------+
107+
//
108+
// When an instance d of D is passed, execution will proceed thus:
109+
//
110+
// func useCOrDerived(_ c: C = d: D) {
111+
// let vtable = typeof(c).VTable = typeof(d).VTable = D.VTable
112+
// let reader = vtable[#slot(x._read)] = D.VTable[#slot(x._read)] = D.x._read
113+
// let x = reader(c) = D.x._read(d)
114+
// pass (x)
115+
// }
116+
//
117+
// In other words, D's override of x._read will be looked up in D's vtable and
118+
// invoked.
119+
//
120+
// }} // Runtime situation with v1
121+
//
122+
// Now, suppose no additional machinery were added to the runtime, and consider
123+
// the same situation when running against ResilientFramework v2. When running
124+
// ClientBinary against ResilientFramework v2, useCOrDerived's reading of x
125+
// dispatches to the routine in the x.read slot of the vtable (NOT the x._read
126+
// slot). And D's vtable contains no override of that routine!
127+
//
128+
// Here's the v2 runtime situation WITHOUT additional machinery: {{ // BAD runtime situation with v2
129+
//
130+
// // Pseudocode describing dispatch in ResilientFramework.useCOrDerived.
131+
// func useCOrDerived(_ c: C) {
132+
// let vtable = typeof(c).VTable
133+
// let reader = vtable[#slot(x.read)] // NOTE! This is now x.read NOT x._read!
134+
// let x = reader(c)
135+
// pass(x)
136+
// }
137+
//
138+
// +- C.VTable ----------+
139+
// | slot | impl |
140+
// +---------+-----------+
141+
// | x._read | C.x._read |
142+
// | x.read | C.x.read |
143+
// +---------+-----------+
144+
//
145+
// +- D.VTable ----------+
146+
// | slot | impl |
147+
// +---------+-----------+
148+
// | x._read | D.x._read |
149+
// | x.read | C.x.read | <- The bad entry!
150+
// +---------+-----------+
151+
//
152+
// When an instance d of D is passed, execution will proceed thus:
153+
//
154+
// func useCOrDerived(_ c: C = d: D) {
155+
// let vtable = typeof(c).VTable = typeof(d).VTable = D.VTable
156+
// // The wrong implementation is looked up!
157+
// let reader = vtable[#slot(x.read)] = D.VTable[#slot(x.read)] = C.x.read
158+
// // The wrong implementation is called!
159+
// let x = reader(c) = C.x._read(d)
160+
// pass (x)
161+
// }
162+
//
163+
// In other words, D's override of x._read is ignored, and C's implementation is
164+
// used.
165+
//
166+
// }} // BAD runtime situation with v2
167+
//
168+
// As described so far, this problem has a few solutions. Those not taken are
169+
// mentioned now with their reason for rejection.
170+
//
171+
// Rejected solutions:
172+
//
173+
// (a) Don't change the slot dispatched to by useCOrDerived.
174+
//
175+
// Rejection rationale: The new routine C.x.read has an improved ABI.
176+
//
177+
// (b) Reuse D.x._read in the x.read slot.
178+
//
179+
// Rejection rationale: D.x._read has the ABI appropriate for x._read and
180+
// not the ABI appropriate for x.read.
181+
//
182+
// Accepted solution:
183+
//
184+
// Provide an implementation of x.read for use by D. The implementation is as
185+
// follows, in pseudo-code that can't be written in Swift:
186+
//
187+
// ResilientFrameworkv2:
188+
// func C_x_read_default_override(self: C) yields_once -> X {
189+
// yield self.x._read // vtable dispatch
190+
// }
191+
//
192+
// At runtime, when the VTable for D is assembled (which occurs at runtime
193+
// because D's is a subclass of C which is defined in a resilient framework),
194+
// this default override is slotted into D's VTable for x.read:
195+
//
196+
// +- D.VTable --------------------------+
197+
// | slot | impl |
198+
// +---------+---------------------------+
199+
// | x._read | D.x._read |
200+
// | x.read | C_x_read_default_override |
201+
// +---------+---------------------------+
202+
//
203+
// +-}} Example ---------------------------------------------------------------+
204+
//
205+
//===----------------------------------------------------------------------===//
206+
207+
#ifndef SWIFT_SIL_SILDEFAULTOVERRIDETABLE_H
208+
#define SWIFT_SIL_SILDEFAULTOVERRIDETABLE_H
209+
210+
#include "swift/SIL/SILAllocated.h"
211+
#include "swift/SIL/SILDeclRef.h"
212+
#include "llvm/ADT/ilist.h"
213+
214+
namespace swift {
215+
216+
struct PrintOptions;
217+
218+
/// Map the pair of overridable member and semantically equivalent overridable
219+
/// member to the SIL-level entity used to override the former for subclasses
220+
/// which do override the latter.
221+
///
222+
/// In the example above, an entry in the table would be
223+
///
224+
/// (C.x.read, C.x._read) -> C_x_read_default_override.
225+
class SILDefaultOverrideTable
226+
: public llvm::ilist_node<SILDefaultOverrideTable>,
227+
public SILAllocated<SILDefaultOverrideTable> {
228+
public:
229+
struct Entry {
230+
/// The method for which a default implementation may be needed by
231+
/// subclasses in old clients.
232+
SILDeclRef method;
233+
/// The method the presence of whose override in subclasses in old clients
234+
/// necessitates the provision of the default override.
235+
SILDeclRef original;
236+
/// The default override of `method` to be provided when `original` is
237+
/// present.
238+
SILFunction *impl;
239+
};
240+
241+
private:
242+
friend class SILModule;
243+
244+
/// The containing module.
245+
SILModule &module;
246+
247+
/// The linkage of the override table.
248+
SILLinkage linkage;
249+
250+
/// The ClassDecl this table pertains to.
251+
const ClassDecl *decl;
252+
253+
/// The contents of the table.
254+
MutableArrayRef<Entry> entries;
255+
256+
enum class State {
257+
Declared,
258+
Defined,
259+
};
260+
/// The table's current state. This must eventually become ::Defined.
261+
State state;
262+
263+
/// Private constructor for making SILDefaultOverrideTable declarations.
264+
SILDefaultOverrideTable(SILModule &M, SILLinkage Linkage,
265+
const ClassDecl *decl);
266+
267+
void registerWithModule();
268+
269+
public:
270+
/// Declare a new table.
271+
static SILDefaultOverrideTable *declare(SILModule &module, SILLinkage linkage,
272+
const ClassDecl *decl);
273+
274+
/// Define a new table.
275+
static SILDefaultOverrideTable *define(SILModule &module, SILLinkage linkage,
276+
const ClassDecl *decl,
277+
ArrayRef<Entry> entries);
278+
279+
/// A name unique among override tables but not symbols.
280+
std::string getUniqueName() const;
281+
282+
/// Get the linkage of the default override table.
283+
SILLinkage getLinkage() const { return linkage; }
284+
285+
/// Set the linkage of the default override table.
286+
void setLinkage(SILLinkage linkage) { this->linkage = linkage; }
287+
288+
/// Promote the current table from a declaration to a definition consisting of
289+
/// the specified entries.
290+
void define(ArrayRef<Entry> entries);
291+
292+
~SILDefaultOverrideTable();
293+
294+
SILModule &getModule() const { return module; }
295+
296+
/// Return true if this is a declaration with no body.
297+
bool isDeclaration() const { return state == State::Declared; }
298+
299+
/// Return the ClassDecl this table pertains to.
300+
const ClassDecl *getClass() const { return decl; }
301+
302+
/// Return all of the default override table entries.
303+
ArrayRef<Entry> getEntries() const { return entries; }
304+
305+
/// Verify that the default override table is well-formed.
306+
void verify(const SILModule &M) const;
307+
};
308+
309+
} // namespace swift
310+
311+
//===----------------------------------------------------------------------===//
312+
// ilist_traits for SILDefaultOverrideTable
313+
//===----------------------------------------------------------------------===//
314+
315+
namespace llvm {
316+
317+
template <>
318+
struct ilist_traits<::swift::SILDefaultOverrideTable>
319+
: public ilist_node_traits<::swift::SILDefaultOverrideTable> {
320+
using SILDefaultOverrideTable = ::swift::SILDefaultOverrideTable;
321+
322+
public:
323+
static void deleteNode(SILDefaultOverrideTable *WT) {
324+
WT->~SILDefaultOverrideTable();
325+
}
326+
327+
private:
328+
void createNode(const SILDefaultOverrideTable &);
329+
};
330+
331+
} // namespace llvm
332+
333+
#endif

0 commit comments

Comments
 (0)