Skip to content

Commit c59c793

Browse files
authored
Merge pull request #68928 from kubamracek/embedded-concurrency
[embedded] Initial Swift Concurrency for embedded Swift
2 parents f26d596 + df58797 commit c59c793

25 files changed

+539
-129
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ extension BuiltinInst : OnoneSimplifyable {
4242
.AssignCopyArrayBackToFront,
4343
.AssignTakeArray,
4444
.IsPOD:
45-
optimizeFirstArgumentToThinMetatype(context)
45+
optimizeArgumentToThinMetatype(argument: 0, context)
46+
case .CreateAsyncTask:
47+
// In embedded Swift, CreateAsyncTask needs a thin metatype
48+
if context.options.enableEmbeddedSwift {
49+
optimizeArgumentToThinMetatype(argument: 1, context)
50+
}
4651
default:
4752
if let literal = constantFold(context) {
4853
uses.replaceAll(with: literal, context)
@@ -174,16 +179,25 @@ private extension BuiltinInst {
174179
context.erase(instruction: self)
175180
}
176181

177-
func optimizeFirstArgumentToThinMetatype(_ context: SimplifyContext) {
178-
guard let metatypeInst = operands[0].value as? MetatypeInst,
179-
metatypeInst.type.representationOfMetatype(in: parentFunction) == .Thick else {
182+
func optimizeArgumentToThinMetatype(argument: Int, _ context: SimplifyContext) {
183+
let type: Type
184+
185+
if let metatypeInst = operands[argument].value as? MetatypeInst {
186+
type = metatypeInst.type
187+
} else if let initExistentialInst = operands[argument].value as? InitExistentialMetatypeInst {
188+
type = initExistentialInst.metatype.type
189+
} else {
190+
return
191+
}
192+
193+
guard type.representationOfMetatype(in: parentFunction) == .Thick else {
180194
return
181195
}
182196

183-
let instanceType = metatypeInst.type.instanceTypeOfMetatype(in: parentFunction)
197+
let instanceType = type.instanceTypeOfMetatype(in: parentFunction)
184198
let builder = Builder(before: self, context)
185-
let metatype = builder.createMetatype(of: instanceType, representation: .Thin)
186-
operands[0].set(to: metatype, context)
199+
let newMetatype = builder.createMetatype(of: instanceType, representation: .Thin)
200+
operands[argument].set(to: newMetatype, context)
187201
}
188202
}
189203

include/swift/ABI/MetadataValues.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,6 +2478,8 @@ enum class TaskOptionRecordKind : uint8_t {
24782478
AsyncLet = 2,
24792479
/// Request a child task for an 'async let'.
24802480
AsyncLetWithBuffer = 3,
2481+
/// Information about the result type of the task, used in embedded Swift.
2482+
ResultTypeInfo = 4,
24812483
/// Request a child task for swift_task_run_inline.
24822484
RunInline = UINT8_MAX,
24832485
};

include/swift/ABI/Task.h

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,67 @@ class NullaryContinuationJob : public Job {
185185
}
186186
};
187187

188+
/// Descibes type information and offers value methods for an arbitrary concrete
189+
/// type in a way that's compatible with regular Swift and embedded Swift. In
190+
/// regular Swift, just holds a Metadata pointer and dispatches to the value
191+
/// witness table. In embedded Swift, because we do not have any value witness
192+
/// tables present at runtime, the witnesses are stored and referenced directly.
193+
///
194+
/// This structure is created from swift_task_create, where in regular Swift, the
195+
/// compiler provides the Metadata pointer, and in embedded Swift, a
196+
/// TaskOptionRecord is used to provide the witnesses.
197+
struct ResultTypeInfo {
198+
#if !SWIFT_CONCURRENCY_EMBEDDED
199+
const Metadata *metadata = nullptr;
200+
bool isNull() {
201+
return metadata == nullptr;
202+
}
203+
size_t vw_size() {
204+
return metadata->vw_size();
205+
}
206+
size_t vw_alignment() {
207+
return metadata->vw_alignment();
208+
}
209+
void vw_initializeWithCopy(OpaqueValue *result, OpaqueValue *src) {
210+
metadata->vw_initializeWithCopy(result, src);
211+
}
212+
void vw_storeEnumTagSinglePayload(OpaqueValue *v, unsigned whichCase,
213+
unsigned emptyCases) {
214+
metadata->vw_storeEnumTagSinglePayload(v, whichCase, emptyCases);
215+
}
216+
void vw_destroy(OpaqueValue *v) {
217+
metadata->vw_destroy(v);
218+
}
219+
#else
220+
size_t size = 0;
221+
size_t alignMask = 0;
222+
void (*initializeWithCopy)(OpaqueValue *result, OpaqueValue *src) = nullptr;
223+
void (*storeEnumTagSinglePayload)(OpaqueValue *v, unsigned whichCase,
224+
unsigned emptyCases) = nullptr;
225+
void (*destroy)(OpaqueValue *) = nullptr;
226+
227+
bool isNull() {
228+
return initializeWithCopy == nullptr;
229+
}
230+
size_t vw_size() {
231+
return size;
232+
}
233+
size_t vw_alignment() {
234+
return alignMask + 1;
235+
}
236+
void vw_initializeWithCopy(OpaqueValue *result, OpaqueValue *src) {
237+
initializeWithCopy(result, src);
238+
}
239+
void vw_storeEnumTagSinglePayload(OpaqueValue *v, unsigned whichCase,
240+
unsigned emptyCases) {
241+
storeEnumTagSinglePayload(v, whichCase, emptyCases);
242+
}
243+
void vw_destroy(OpaqueValue *v) {
244+
destroy(v);
245+
}
246+
#endif
247+
};
248+
188249
/// An asynchronous task. Tasks are the analogue of threads for
189250
/// asynchronous functions: that is, they are a persistent identity
190251
/// for the overall async computation.
@@ -503,7 +564,7 @@ class AsyncTask : public Job {
503564
std::atomic<WaitQueueItem> waitQueue;
504565

505566
/// The type of the result that will be produced by the future.
506-
const Metadata *resultType;
567+
ResultTypeInfo resultType;
507568

508569
SwiftError *error = nullptr;
509570

@@ -513,14 +574,14 @@ class AsyncTask : public Job {
513574
friend class AsyncTask;
514575

515576
public:
516-
explicit FutureFragment(const Metadata *resultType)
577+
explicit FutureFragment(ResultTypeInfo resultType)
517578
: waitQueue(WaitQueueItem::get(Status::Executing, nullptr)),
518579
resultType(resultType) { }
519580

520581
/// Destroy the storage associated with the future.
521582
void destroy();
522583

523-
const Metadata *getResultType() const {
584+
ResultTypeInfo getResultType() const {
524585
return resultType;
525586
}
526587

@@ -534,7 +595,7 @@ class AsyncTask : public Job {
534595
// `this` must have the same value modulo that alignment as
535596
// `fragmentOffset` has in that function.
536597
char *fragmentAddr = reinterpret_cast<char *>(this);
537-
uintptr_t alignment = resultType->vw_alignment();
598+
uintptr_t alignment = resultType.vw_alignment();
538599
char *resultAddr = fragmentAddr + sizeof(FutureFragment);
539600
uintptr_t unalignedResultAddrInt =
540601
reinterpret_cast<uintptr_t>(resultAddr);
@@ -553,12 +614,12 @@ class AsyncTask : public Job {
553614
/// Determine the size of the future fragment given the result type
554615
/// of the future.
555616
static size_t fragmentSize(size_t fragmentOffset,
556-
const Metadata *resultType) {
617+
ResultTypeInfo resultType) {
557618
assert((fragmentOffset & (alignof(FutureFragment) - 1)) == 0);
558-
size_t alignment = resultType->vw_alignment();
619+
size_t alignment = resultType.vw_alignment();
559620
size_t resultOffset = fragmentOffset + sizeof(FutureFragment);
560621
resultOffset = (resultOffset + alignment - 1) & ~(alignment - 1);
561-
size_t endOffset = resultOffset + resultType->vw_size();
622+
size_t endOffset = resultOffset + resultType.vw_size();
562623
return (endOffset - fragmentOffset);
563624
}
564625
};

include/swift/ABI/TaskOptions.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,21 @@ class AsyncLetWithBufferTaskOptionRecord : public TaskOptionRecord {
141141
}
142142
};
143143

144+
#if SWIFT_CONCURRENCY_EMBEDDED
145+
class ResultTypeInfoTaskOptionRecord : public TaskOptionRecord {
146+
public:
147+
size_t size;
148+
size_t alignMask;
149+
void (*initializeWithCopy)(OpaqueValue *, OpaqueValue *);
150+
void (*storeEnumTagSinglePayload)(OpaqueValue *, unsigned, unsigned);
151+
void (*destroy)(OpaqueValue *);
152+
153+
static bool classof(const TaskOptionRecord *record) {
154+
return record->getKind() == TaskOptionRecordKind::ResultTypeInfo;
155+
}
156+
};
157+
#endif
158+
144159
class RunInlineTaskOptionRecord : public TaskOptionRecord {
145160
void *allocation;
146161
size_t allocationBytes;

include/swift/IRGen/Linking.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,8 @@ class LinkEntity {
10121012
}
10131013

10141014
static LinkEntity forValueWitness(CanType concreteType, ValueWitness witness) {
1015-
assert(!isEmbedded(concreteType));
1015+
// Explicitly allowed in embedded Swift because we generate value witnesses
1016+
// (but not witness tables) for Swift Concurrency usage.
10161017
LinkEntity entity;
10171018
entity.Pointer = concreteType.getPointer();
10181019
entity.Data = LINKENTITY_SET_FIELD(Kind, unsigned(Kind::ValueWitness))

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3138,6 +3138,8 @@ bool CompilerInvocation::parseArgs(
31383138
IRGenOpts.DisableLegacyTypeInfo = true;
31393139
IRGenOpts.ReflectionMetadata = ReflectionMetadataMode::None;
31403140
IRGenOpts.EnableReflectionNames = false;
3141+
TypeCheckerOpts.SkipFunctionBodies = FunctionBodySkipping::None;
3142+
SILOpts.SkipFunctionBodies = FunctionBodySkipping::None;
31413143
SILOpts.CMOMode = CrossModuleOptimizationMode::Everything;
31423144
SILOpts.EmbeddedSwift = true;
31433145
}

lib/IRGen/GenBuiltin.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,15 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
326326
(Builtin.ID == BuiltinValueKind::CreateAsyncTaskInGroup)
327327
? args.claimNext()
328328
: nullptr;
329-
auto futureResultType = args.claimNext();
329+
330+
// In embedded Swift, futureResultType is a thin metatype, not backed by any
331+
// actual value.
332+
llvm::Value *futureResultType =
333+
llvm::ConstantPointerNull::get(IGF.IGM.Int8PtrTy);
334+
if (!IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
335+
futureResultType = args.claimNext();
336+
}
337+
330338
auto taskFunction = args.claimNext();
331339
auto taskContext = args.claimNext();
332340

lib/IRGen/GenCall.cpp

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4470,18 +4470,17 @@ void irgen::emitTaskCancel(IRGenFunction &IGF, llvm::Value *task) {
44704470
call->setCallingConv(IGF.IGM.SwiftCC);
44714471
}
44724472

4473-
llvm::Value *irgen::emitTaskCreate(
4474-
IRGenFunction &IGF,
4475-
llvm::Value *flags,
4476-
llvm::Value *taskGroup,
4477-
llvm::Value *futureResultType,
4478-
llvm::Value *taskFunction,
4479-
llvm::Value *localContextInfo,
4480-
SubstitutionMap subs) {
4481-
// If there is a task group, emit a task group option structure to contain
4482-
// it.
4483-
llvm::Value *taskOptions = llvm::ConstantInt::get(
4484-
IGF.IGM.SwiftTaskOptionRecordPtrTy, 0);
4473+
llvm::Value *irgen::emitTaskCreate(IRGenFunction &IGF, llvm::Value *flags,
4474+
llvm::Value *taskGroup,
4475+
llvm::Value *futureResultType,
4476+
llvm::Value *taskFunction,
4477+
llvm::Value *localContextInfo,
4478+
SubstitutionMap subs) {
4479+
// Start with empty task options.
4480+
llvm::Value *taskOptions =
4481+
llvm::ConstantInt::get(IGF.IGM.SwiftTaskOptionRecordPtrTy, 0);
4482+
4483+
// If there is a task group, emit a task group option structure to contain it.
44854484
if (taskGroup) {
44864485
TaskOptionRecordFlags optionsFlags(TaskOptionRecordKind::TaskGroup);
44874486
llvm::Value *optionsFlagsVal = llvm::ConstantInt::get(
@@ -4492,14 +4491,73 @@ llvm::Value *irgen::emitTaskCreate(
44924491
"task_group_options");
44934492
auto optionsBaseRecord = IGF.Builder.CreateStructGEP(
44944493
optionsRecord, 0, Size());
4494+
4495+
// Flags
44954496
IGF.Builder.CreateStore(
44964497
optionsFlagsVal,
44974498
IGF.Builder.CreateStructGEP(optionsBaseRecord, 0, Size()));
4499+
// Parent
44984500
IGF.Builder.CreateStore(
44994501
taskOptions, IGF.Builder.CreateStructGEP(optionsBaseRecord, 1, Size()));
4500-
4502+
// TaskGroup
45014503
IGF.Builder.CreateStore(
45024504
taskGroup, IGF.Builder.CreateStructGEP(optionsRecord, 1, Size()));
4505+
4506+
taskOptions = IGF.Builder.CreateBitOrPointerCast(
4507+
optionsRecord.getAddress(), IGF.IGM.SwiftTaskOptionRecordPtrTy);
4508+
}
4509+
4510+
// In embedded Swift, create and pass result type info.
4511+
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
4512+
auto optionsRecord =
4513+
IGF.createAlloca(IGF.IGM.SwiftResultTypeInfoTaskOptionRecordTy,
4514+
Alignment(), "result_type_info");
4515+
auto optionsBaseRecord =
4516+
IGF.Builder.CreateStructGEP(optionsRecord, 0, Size());
4517+
4518+
TaskOptionRecordFlags optionsFlags(TaskOptionRecordKind::ResultTypeInfo);
4519+
llvm::Value *optionsFlagsVal = llvm::ConstantInt::get(
4520+
IGF.IGM.SizeTy, optionsFlags.getOpaqueValue());
4521+
4522+
// Flags
4523+
IGF.Builder.CreateStore(
4524+
optionsFlagsVal,
4525+
IGF.Builder.CreateStructGEP(optionsBaseRecord, 0, Size()));
4526+
// Parent
4527+
IGF.Builder.CreateStore(
4528+
taskOptions, IGF.Builder.CreateStructGEP(optionsBaseRecord, 1, Size()));
4529+
4530+
Type unloweredType = subs.getReplacementTypes()[0];
4531+
SILType lowered = IGF.IGM.getLoweredType(unloweredType);
4532+
const TypeInfo &TI = IGF.IGM.getTypeInfo(lowered);
4533+
CanType canType = lowered.getASTType();
4534+
FixedPacking packing = TI.getFixedPacking(IGF.IGM);
4535+
4536+
// Size
4537+
IGF.Builder.CreateStore(
4538+
TI.getStaticSize(IGF.IGM),
4539+
IGF.Builder.CreateStructGEP(optionsRecord, 1, Size()));
4540+
// Align mask
4541+
IGF.Builder.CreateStore(
4542+
TI.getStaticAlignmentMask(IGF.IGM),
4543+
IGF.Builder.CreateStructGEP(optionsRecord, 2, Size()));
4544+
// initializeWithCopy witness
4545+
IGF.Builder.CreateStore(
4546+
IGF.IGM.getOrCreateValueWitnessFunction(
4547+
ValueWitness::InitializeWithCopy, packing, canType, lowered, TI),
4548+
IGF.Builder.CreateStructGEP(optionsRecord, 3, Size()));
4549+
// storeEnumTagSinglePayload witness
4550+
IGF.Builder.CreateStore(
4551+
IGF.IGM.getOrCreateValueWitnessFunction(
4552+
ValueWitness::StoreEnumTagSinglePayload, packing, canType, lowered,
4553+
TI),
4554+
IGF.Builder.CreateStructGEP(optionsRecord, 4, Size()));
4555+
// destroy witness
4556+
IGF.Builder.CreateStore(
4557+
IGF.IGM.getOrCreateValueWitnessFunction(ValueWitness::Destroy, packing,
4558+
canType, lowered, TI),
4559+
IGF.Builder.CreateStructGEP(optionsRecord, 5, Size()));
4560+
45034561
taskOptions = IGF.Builder.CreateBitOrPointerCast(
45044562
optionsRecord.getAddress(), IGF.IGM.SwiftTaskOptionRecordPtrTy);
45054563
}

lib/IRGen/GenValueWitness.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ static Address getArgAsBuffer(IRGenFunction &IGF,
389389
/// Don't add new callers of this, it doesn't make any sense.
390390
static CanType getFormalTypeInPrimaryContext(CanType abstractType) {
391391
auto *nominal = abstractType.getAnyNominal();
392-
if (abstractType->isEqual(nominal->getDeclaredType())) {
392+
if (nominal && abstractType->isEqual(nominal->getDeclaredType())) {
393393
return nominal->mapTypeIntoContext(nominal->getDeclaredInterfaceType())
394394
->getCanonicalType();
395395
}
@@ -1260,12 +1260,20 @@ addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B, ValueWitness index,
12601260
llvm_unreachable("bad value witness kind");
12611261

12621262
standard:
1263+
llvm::Function *fn = IGM.getOrCreateValueWitnessFunction(
1264+
index, packing, abstractType, concreteType, concreteTI);
1265+
addFunction(fn);
1266+
}
1267+
1268+
llvm::Function *IRGenModule::getOrCreateValueWitnessFunction(
1269+
ValueWitness index, FixedPacking packing, CanType abstractType,
1270+
SILType concreteType, const TypeInfo &type) {
12631271
llvm::Function *fn =
1264-
IGM.getAddrOfValueWitness(abstractType, index, ForDefinition);
1272+
getAddrOfValueWitness(abstractType, index, ForDefinition);
12651273
if (fn->empty())
1266-
buildValueWitnessFunction(IGM, fn, index, packing, abstractType,
1267-
concreteType, concreteTI);
1268-
addFunction(fn);
1274+
buildValueWitnessFunction(*this, fn, index, packing, abstractType,
1275+
concreteType, type);
1276+
return fn;
12691277
}
12701278

12711279
static bool shouldAddEnumWitnesses(CanType abstractType) {

lib/IRGen/IRGenModule.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,15 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
671671
SwiftTaskOptionRecordTy, // Base option record
672672
SwiftTaskGroupPtrTy, // Task group
673673
});
674+
SwiftResultTypeInfoTaskOptionRecordTy = createStructType(
675+
*this, "swift.result_type_info_task_option", {
676+
SwiftTaskOptionRecordTy, // Base option record
677+
SizeTy,
678+
SizeTy,
679+
Int8PtrTy,
680+
Int8PtrTy,
681+
Int8PtrTy,
682+
});
674683
ExecutorFirstTy = SizeTy;
675684
ExecutorSecondTy = SizeTy;
676685
SwiftExecutorTy = createStructType(*this, "swift.executor", {

0 commit comments

Comments
 (0)