Skip to content

Commit 291467c

Browse files
authored
Merge branch 'main' into mandatory-copyprop
2 parents 0a02081 + f81aba7 commit 291467c

File tree

206 files changed

+6232
-3489
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

206 files changed

+6232
-3489
lines changed

docs/DebuggingTheCompiler.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,10 @@ A short (non-exhaustive) list of SIL printing options:
233233
`-sil-print-function` and/or `-sil-print-functions`.
234234

235235
* `-Xllvm -sil-print-before=$PASS_NAME`: Like `-sil-print-around`, but prints
236-
the SIL only _before_ the specfied pass runs.
236+
the SIL only _before_ the specified pass runs.
237237

238238
* `-Xllvm -sil-print-after=$PASS_NAME`: Like `-sil-print-around`, but prints
239-
the SIL only _after_ the specfied pass did run.
239+
the SIL only _after_ the specified pass did run.
240240

241241
NOTE: This may emit a lot of text to stderr, so be sure to pipe the
242242
output to a file.

docs/SIL.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3547,6 +3547,27 @@ by an `end_borrow`_ instruction. All `load_borrow`_ instructions must be
35473547
paired with exactly one `end_borrow`_ instruction along any path through the
35483548
program. Until `end_borrow`_, it is illegal to invalidate or store to ``%0``.
35493549

3550+
store_borrow
3551+
````````````
3552+
3553+
::
3554+
3555+
sil-instruction ::= 'store_borrow' sil-value 'to' sil-operand
3556+
3557+
store_borrow %0 to %1 : $*T
3558+
// $T must be a loadable type
3559+
// %1 must be an alloc_stack $T
3560+
3561+
Stores the value ``%0`` to a stack location ``%1``, which must be an
3562+
``alloc_stack $T``.
3563+
The stored value is alive until the ``dealloc_stack`` or until another
3564+
``store_borrow`` overwrites the value. During the its lifetime, the stored
3565+
value must not be modified or destroyed.
3566+
The source value ``%0`` is borrowed (i.e. not copied) and it's borrow scope
3567+
must outlive the lifetime of the stored value.
3568+
3569+
Note: This is the current implementation and the design is not final.
3570+
35503571
begin_borrow
35513572
````````````
35523573

include/swift/ABI/MetadataValues.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ enum {
4646
/// The number of words (in addition to the heap-object header)
4747
/// in a default actor.
4848
NumWords_DefaultActor = 10,
49+
50+
/// The number of words in a task group.
51+
NumWords_TaskGroup = 32,
4952
};
5053

5154
struct InProcess;
@@ -121,6 +124,9 @@ const size_t MaximumAlignment = 16;
121124
/// The alignment of a DefaultActor.
122125
const size_t Alignment_DefaultActor = MaximumAlignment;
123126

127+
/// The alignment of a TaskGroup.
128+
const size_t Alignment_TaskGroup = MaximumAlignment;
129+
124130
/// Flags stored in the value-witness table.
125131
template <typename int_type>
126132
class TargetValueWitnessFlags {

include/swift/ABI/Task.h

Lines changed: 18 additions & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@
1717
#ifndef SWIFT_ABI_TASK_H
1818
#define SWIFT_ABI_TASK_H
1919

20+
#include "swift/ABI/TaskLocal.h"
2021
#include "swift/ABI/Executor.h"
2122
#include "swift/ABI/HeapObject.h"
2223
#include "swift/ABI/Metadata.h"
2324
#include "swift/ABI/MetadataValues.h"
2425
#include "swift/Runtime/Config.h"
2526
#include "swift/Basic/STLExtras.h"
2627
#include "bitset"
27-
#include "string"
28-
#include "queue"
28+
#include "queue" // TODO: remove and replace with our own mpsc
2929

3030
namespace swift {
3131
class AsyncTask;
@@ -147,7 +147,6 @@ class ActiveTaskStatus {
147147
/// An AsyncTask may have the following fragments:
148148
///
149149
/// +--------------------------+
150-
/// | taskLocalValuesFragment |
151150
/// | childFragment? |
152151
/// | groupChildFragment? |
153152
/// | futureFragment? |*
@@ -173,12 +172,16 @@ class AsyncTask : public HeapObject, public Job {
173172
/// Reserved for the use of the task-local stack allocator.
174173
void *AllocatorPrivate[4];
175174

175+
/// Task local values storage container.
176+
TaskLocal::Storage Local;
177+
176178
AsyncTask(const HeapMetadata *metadata, JobFlags flags,
177179
TaskContinuationFunction *run,
178180
AsyncContext *initialContext)
179181
: HeapObject(metadata), Job(flags, run),
180182
ResumeContext(initialContext),
181-
Status(ActiveTaskStatus()) {
183+
Status(ActiveTaskStatus()),
184+
Local(TaskLocal::Storage()) {
182185
assert(flags.isAsyncTask());
183186
}
184187

@@ -196,237 +199,20 @@ class AsyncTask : public HeapObject, public Job {
196199
return Status.load(std::memory_order_relaxed).isCancelled();
197200
}
198201

199-
// ==== Task Locals Values ---------------------------------------------------
200-
201-
/// Storage fragment for task local values.
202-
class TaskLocalValuesFragment {
203-
public:
204-
/// Type of the pointed at `next` task local item.
205-
enum class NextLinkType : uintptr_t {
206-
/// This task is known to be a "terminal" node in the lookup of task locals.
207-
/// In other words, even if it had a parent, the parent (and its parents)
208-
/// are known to not contain any any more task locals, and thus any further
209-
/// search beyond this task.
210-
IsTerminal = 0b00,
211-
/// The storage pointer points at the next TaskLocalChainItem in this task.
212-
IsNext = 0b01,
213-
/// The storage pointer points at a parent AsyncTask, in which we should
214-
/// continue the lookup.
215-
///
216-
/// Note that this may not necessarily be the same as the task's parent
217-
/// task -- we may point to a super-parent if we know / that the parent
218-
/// does not "contribute" any task local values. This is to speed up
219-
/// lookups by skipping empty parent tasks during get(), and explained
220-
/// in depth in `createParentLink`.
221-
IsParent = 0b11
222-
};
223-
224-
/// Values must match `TaskLocalInheritance` declared in `TaskLocal.swift`.
225-
enum class TaskLocalInheritance : uint8_t {
226-
Default = 0,
227-
Never = 1
228-
};
229-
230-
class TaskLocalItem {
231-
private:
232-
/// Mask used for the low status bits in a task local chain item.
233-
static const uintptr_t statusMask = 0x03;
234-
235-
/// Pointer to the next task local item; be it in this task or in a parent.
236-
/// Low bits encode `NextLinkType`.
237-
/// TaskLocalItem *next = nullptr;
238-
uintptr_t next;
239-
240-
public:
241-
/// The type of the key with which this value is associated.
242-
const Metadata *keyType;
243-
/// The type of the value stored by this item.
244-
const Metadata *valueType;
245-
246-
// Trailing storage for the value itself. The storage will be
247-
// uninitialized or contain an instance of \c valueType.
248-
249-
private:
250-
explicit TaskLocalItem(const Metadata *keyType, const Metadata *valueType)
251-
: next(0),
252-
keyType(keyType),
253-
valueType(valueType) { }
254-
255-
public:
256-
/// TaskLocalItem which does not by itself store any value, but only points
257-
/// to the nearest task-local-value containing parent's first task item.
258-
///
259-
/// This item type is used to link to the appropriate parent task's item,
260-
/// when the current task itself does not have any task local values itself.
261-
///
262-
/// When a task actually has its own task locals, it should rather point
263-
/// to the parent's *first* task-local item in its *last* item, extending
264-
/// the TaskLocalItem linked list into the appropriate parent.
265-
static TaskLocalItem* createParentLink(AsyncTask *task, AsyncTask *parent) {
266-
assert(parent);
267-
size_t amountToAllocate = TaskLocalItem::itemSize(/*valueType*/nullptr);
268-
// assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
269-
void *allocation = malloc(amountToAllocate); // TODO: use task-local allocator
270-
271-
TaskLocalItem *item =
272-
new(allocation) TaskLocalItem(nullptr, nullptr);
273-
274-
auto parentHead = parent->localValuesFragment()->head;
275-
if (parentHead) {
276-
if (parentHead->isEmpty()) {
277-
switch (parentHead->getNextLinkType()) {
278-
case NextLinkType::IsParent:
279-
// it has no values, and just points to its parent,
280-
// therefore skip also skip pointing to that parent and point
281-
// to whichever parent it was pointing to as well, it may be its
282-
// immediate parent, or some super-parent.
283-
item->next = reinterpret_cast<uintptr_t>(parentHead->getNext()) |
284-
static_cast<uintptr_t>(NextLinkType::IsParent);
285-
break;
286-
case NextLinkType::IsNext:
287-
assert(false && "empty taskValue head in parent task, yet parent's 'head' is `IsNext`, "
288-
"this should not happen, as it implies the parent must have stored some value.");
289-
break;
290-
case NextLinkType::IsTerminal:
291-
item->next = reinterpret_cast<uintptr_t>(parentHead->getNext()) |
292-
static_cast<uintptr_t>(NextLinkType::IsTerminal);
293-
break;
294-
}
295-
} else {
296-
item->next = reinterpret_cast<uintptr_t>(parentHead) |
297-
static_cast<uintptr_t>(NextLinkType::IsParent);
298-
}
299-
} else {
300-
item->next = reinterpret_cast<uintptr_t>(parentHead) |
301-
static_cast<uintptr_t>(NextLinkType::IsTerminal);
302-
}
303-
304-
return item;
305-
}
306-
307-
static TaskLocalItem* createLink(AsyncTask *task,
308-
const Metadata *keyType,
309-
const Metadata *valueType) {
310-
assert(task);
311-
size_t amountToAllocate = TaskLocalItem::itemSize(valueType);
312-
// assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
313-
void *allocation = malloc(amountToAllocate); // TODO: use task-local allocator rdar://74218679
314-
TaskLocalItem *item =
315-
new(allocation) TaskLocalItem(keyType, valueType);
316-
317-
auto next = task->localValuesFragment()->head;
318-
auto nextLinkType = next ? NextLinkType::IsNext : NextLinkType::IsTerminal;
319-
item->next = reinterpret_cast<uintptr_t>(next) |
320-
static_cast<uintptr_t>(nextLinkType);
321-
322-
return item;
323-
}
324-
325-
void destroy() {
326-
if (valueType) {
327-
valueType->vw_destroy(getStoragePtr());
328-
}
329-
}
330-
331-
TaskLocalItem *getNext() {
332-
return reinterpret_cast<TaskLocalItem *>(next & ~statusMask);
333-
}
334-
335-
NextLinkType getNextLinkType() {
336-
return static_cast<NextLinkType>(next & statusMask);
337-
}
338-
339-
/// Item does not contain any actual value, and is only used to point at
340-
/// a specific parent item.
341-
bool isEmpty() {
342-
return !valueType;
343-
}
344-
345-
/// Retrieve a pointer to the storage of the value.
346-
OpaqueValue *getStoragePtr() {
347-
return reinterpret_cast<OpaqueValue *>(
348-
reinterpret_cast<char *>(this) + storageOffset(valueType));
349-
}
350-
351-
/// Compute the offset of the storage from the base of the item.
352-
static size_t storageOffset(const Metadata *valueType) {
353-
size_t offset = sizeof(TaskLocalItem);
354-
if (valueType) {
355-
size_t alignment = valueType->vw_alignment();
356-
return (offset + alignment - 1) & ~(alignment - 1);
357-
} else {
358-
return offset;
359-
}
360-
}
202+
// ==== Task Local Values ----------------------------------------------------
361203

362-
/// Determine the size of the item given a particular value type.
363-
static size_t itemSize(const Metadata *valueType) {
364-
size_t offset = storageOffset(valueType);
365-
if (valueType) {
366-
offset += valueType->vw_size();
367-
}
368-
return offset;
369-
}
370-
};
371-
372-
private:
373-
/// A stack (single-linked list) of task local values.
374-
///
375-
/// Once task local values within this task are traversed, the list continues
376-
/// to the "next parent that contributes task local values," or if no such
377-
/// parent exists it terminates with null.
378-
///
379-
/// If the TaskLocalValuesFragment was allocated, it is expected that this
380-
/// value should be NOT null; it either has own values, or at least one
381-
/// parent that has values. If this task does not have any values, the head
382-
/// pointer MAY immediately point at this task's parent task which has values.
383-
///
384-
/// ### Concurrency
385-
/// Access to the head is only performed from the task itself, when it
386-
/// creates child tasks, the child during creation will inspect its parent's
387-
/// task local value stack head, and point to it. This is done on the calling
388-
/// task, and thus needs not to be synchronized. Subsequent traversal is
389-
/// performed by child tasks concurrently, however they use their own
390-
/// pointers/stack and can never mutate the parent's stack.
391-
///
392-
/// The stack is only pushed/popped by the owning task, at the beginning and
393-
/// end a `body` block of `withLocal(_:boundTo:body:)` respectively.
394-
///
395-
/// Correctness of the stack strongly relies on the guarantee that tasks
396-
/// never outline a scope in which they are created. Thanks to this, if
397-
/// tasks are created inside the `body` of `withLocal(_:,boundTo:body:)`
398-
/// all tasks created inside the `withLocal` body must complete before it
399-
/// returns, as such, any child tasks potentially accessing the value stack
400-
/// are guaranteed to be completed by the time we pop values off the stack
401-
/// (after the body has completed).
402-
TaskLocalItem *head = nullptr;
403-
404-
public:
405-
TaskLocalValuesFragment() {}
406-
407-
void destroy();
408-
409-
/// If the parent task has task local values defined, point to in
410-
/// the task local values chain.
411-
void initializeLinkParent(AsyncTask* task, AsyncTask* parent);
412-
413-
void pushValue(AsyncTask *task, const Metadata *keyType,
414-
/* +1 */ OpaqueValue *value, const Metadata *valueType);
415-
416-
void popValue(AsyncTask *task);
417-
418-
OpaqueValue* get(const Metadata *keType, TaskLocalInheritance inheritance);
419-
};
420-
421-
TaskLocalValuesFragment *localValuesFragment() {
422-
auto offset = reinterpret_cast<char*>(this);
423-
offset += sizeof(AsyncTask);
424-
return reinterpret_cast<TaskLocalValuesFragment*>(offset);
204+
void localValuePush(const Metadata *keyType,
205+
/* +1 */ OpaqueValue *value, const Metadata *valueType) {
206+
Local.pushValue(this, keyType, value, valueType);
425207
}
426208

427209
OpaqueValue* localValueGet(const Metadata *keyType,
428-
TaskLocalValuesFragment::TaskLocalInheritance inheritance) {
429-
return localValuesFragment()->get(keyType, inheritance);
210+
TaskLocal::TaskLocalInheritance inherit) {
211+
return Local.getValue(this, keyType, inherit);
212+
}
213+
214+
void localValuePop() {
215+
Local.popValue(this);
430216
}
431217

432218
// ==== Child Fragment -------------------------------------------------------
@@ -476,7 +262,6 @@ class AsyncTask : public HeapObject, public Job {
476262

477263
auto offset = reinterpret_cast<char*>(this);
478264
offset += sizeof(AsyncTask);
479-
offset += sizeof(TaskLocalValuesFragment);
480265

481266
return reinterpret_cast<ChildFragment*>(offset);
482267
}
@@ -516,7 +301,6 @@ class AsyncTask : public HeapObject, public Job {
516301

517302
auto offset = reinterpret_cast<char*>(this);
518303
offset += sizeof(AsyncTask);
519-
offset += sizeof(TaskLocalValuesFragment);
520304
if (hasChildFragment())
521305
offset += sizeof(ChildFragment);
522306

@@ -625,7 +409,6 @@ class AsyncTask : public HeapObject, public Job {
625409
assert(isFuture());
626410
auto offset = reinterpret_cast<char*>(this);
627411
offset += sizeof(AsyncTask);
628-
offset += sizeof(TaskLocalValuesFragment);
629412
if (hasChildFragment())
630413
offset += sizeof(ChildFragment);
631414
if (hasGroupChildFragment())
@@ -665,7 +448,7 @@ class AsyncTask : public HeapObject, public Job {
665448
};
666449

667450
// The compiler will eventually assume these.
668-
static_assert(sizeof(AsyncTask) == 12 * sizeof(void*),
451+
static_assert(sizeof(AsyncTask) == 14 * sizeof(void*),
669452
"AsyncTask size is wrong");
670453
static_assert(alignof(AsyncTask) == 2 * alignof(void*),
671454
"AsyncTask alignment is wrong");

0 commit comments

Comments
 (0)