Skip to content

Commit 7b159d3

Browse files
committed
[Distributed] IRGen: Switch accessor to accept argument decoder instance instead of argument buffer
This obsoletes `HeterogeneousBuffer` and instead emits calls to `decodeNextArgument` directly in distributed accessor.
1 parent 3a754cb commit 7b159d3

File tree

1 file changed

+77
-187
lines changed

1 file changed

+77
-187
lines changed

lib/IRGen/GenDistributed.cpp

Lines changed: 77 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,6 @@ llvm::Value *irgen::emitDistributedActorInitializeRemote(
6666

6767
namespace {
6868

69-
struct AllocationInfo {
70-
unsigned ArgumentIdx;
71-
StackAddress Addr;
72-
};
73-
7469
struct ArgumentDecoderInfo {
7570
CanSILFunctionType Type;
7671
FunctionPointer Fn;
@@ -97,7 +92,7 @@ class DistributedAccessor {
9792
ArgumentDecoderInfo ArgumentDecoder;
9893

9994
/// The list of all arguments that were allocated on the stack.
100-
SmallVector<AllocationInfo, 4> AllocatedArguments;
95+
SmallVector<StackAddress, 4> AllocatedArguments;
10196

10297
public:
10398
DistributedAccessor(IRGenFunction &IGF, SILFunction *target,
@@ -106,33 +101,17 @@ class DistributedAccessor {
106101
void emit();
107102

108103
private:
109-
void computeArguments(llvm::Value *argumentBuffer,
110-
llvm::Value *argumentTypes,
111-
Explosion &arguments);
104+
void decodeArguments(llvm::Value *decoder, llvm::Value *argumentTypes,
105+
Explosion &arguments);
112106

113-
/// Load an argument value from the given buffer \c argumentSlot
114-
/// to the given explosion \c arguments. Type of the argument is
115-
/// the same as parameter type.
116-
///
117-
/// Returns a pair of aligned offset and value size.
118-
std::pair<llvm::Value *, llvm::Value *>
119-
loadArgument(unsigned argumentIdx,
120-
llvm::Value *argumentSlot,
121-
SILType paramType,
122-
ParameterConvention convention,
123-
Explosion &arguments);
124-
125-
/// Load an argument value from the given buffer \c argumentSlot
107+
/// Load an argument value from the given decoder \c decoder
126108
/// to the given explosion \c arguments. Information describing
127109
/// the type of argument comes from runtime metadata.
128110
///
129111
/// Returns a pair of aligned offset and value size.
130-
std::pair<llvm::Value *, llvm::Value *>
131-
loadArgument(unsigned argumentIdx,
132-
llvm::Value *argumentSlot,
133-
llvm::Value *argumentType,
134-
const SILParameterInfo &param,
135-
Explosion &arguments);
112+
void decodeArgument(unsigned argumentIdx, llvm::Value *decoder,
113+
llvm::Value *argumentType, const SILParameterInfo &param,
114+
Explosion &arguments);
136115

137116
/// Load witness table addresses (if any) from the given buffer
138117
/// into the given argument explosion.
@@ -261,9 +240,9 @@ DistributedAccessor::DistributedAccessor(IRGenFunction &IGF,
261240
FunctionPointer::BasicKind::AsyncFunctionPointer))),
262241
ArgumentDecoder(findArgumentDecoder(IGM, target)) {}
263242

264-
void DistributedAccessor::computeArguments(llvm::Value *argumentBuffer,
265-
llvm::Value *argumentTypes,
266-
Explosion &arguments) {
243+
void DistributedAccessor::decodeArguments(llvm::Value *decoder,
244+
llvm::Value *argumentTypes,
245+
Explosion &arguments) {
267246
auto fnType = Target->getLoweredFunctionType();
268247

269248
// Cover all of the arguments except to `self` of the actor.
@@ -273,13 +252,6 @@ void DistributedAccessor::computeArguments(llvm::Value *argumentBuffer,
273252
if (parameters.empty())
274253
return;
275254

276-
auto offset =
277-
IGF.createAlloca(IGM.Int8PtrTy, IGM.getPointerAlignment(), "offset");
278-
IGF.Builder.CreateLifetimeStart(offset, IGM.getPointerSize());
279-
280-
// Initialize "offset" with the address of the base of the argument buffer.
281-
IGF.Builder.CreateStore(argumentBuffer, offset);
282-
283255
// Cast type buffer to `swift.type**`
284256
argumentTypes =
285257
IGF.Builder.CreateBitCast(argumentTypes, IGM.TypeMetadataPtrPtrTy);
@@ -288,75 +260,78 @@ void DistributedAccessor::computeArguments(llvm::Value *argumentBuffer,
288260
const auto &param = parameters[i];
289261
auto paramTy = param.getSILStorageInterfaceType();
290262

291-
// The size of the loaded argument value
292-
llvm::Value *valueSize;
263+
// Check whether the native representation is empty e.g.
264+
// this happens for empty enums, and if so - continue to
265+
// the next argument.
266+
if (paramTy.isObject()) {
267+
auto &typeInfo = IGM.getTypeInfo(paramTy);
268+
auto &nativeSchema = typeInfo.nativeParameterValueSchema(IGM);
293269

294-
// Load current offset
295-
llvm::Value *currentOffset = IGF.Builder.CreateLoad(offset, "elt_offset");
296-
297-
if (paramTy.hasTypeParameter()) {
298-
auto offset =
299-
Size(i * IGM.DataLayout.getTypeAllocSize(IGM.TypeMetadataPtrTy));
300-
auto alignment =
301-
IGM.DataLayout.getABITypeAlignment(IGM.TypeMetadataPtrTy);
270+
if (nativeSchema.empty())
271+
continue;
272+
}
302273

303-
// Load metadata describing argument value from argument types buffer.
304-
auto typeLoc = IGF.emitAddressAtOffset(
305-
argumentTypes, Offset(offset), IGM.TypeMetadataPtrTy,
306-
Alignment(alignment), "arg_type_loc");
274+
auto offset =
275+
Size(i * IGM.DataLayout.getTypeAllocSize(IGM.TypeMetadataPtrTy));
276+
auto alignment = IGM.DataLayout.getABITypeAlignment(IGM.TypeMetadataPtrTy);
307277

308-
auto *argumentTy = IGF.Builder.CreateLoad(typeLoc, "arg_type");
278+
// Load metadata describing argument value from argument types buffer.
279+
auto typeLoc = IGF.emitAddressAtOffset(
280+
argumentTypes, Offset(offset), IGM.TypeMetadataPtrTy,
281+
Alignment(alignment), "arg_type_loc");
309282

310-
// Load argument value based using loaded type metadata.
311-
std::tie(currentOffset, valueSize) =
312-
loadArgument(i, currentOffset, argumentTy, param, arguments);
313-
} else {
314-
std::tie(currentOffset, valueSize) = loadArgument(
315-
i, currentOffset, paramTy, param.getConvention(), arguments);
316-
}
283+
auto *argumentTy = IGF.Builder.CreateLoad(typeLoc, "arg_type");
317284

318-
// Move the offset to the beginning of the next element, unless
319-
// this is the last element
320-
if (param != parameters.back()) {
321-
llvm::Value *addr = IGF.Builder.CreatePtrToInt(currentOffset, IGM.IntPtrTy);
322-
llvm::Value *nextOffset = IGF.Builder.CreateIntToPtr(
323-
IGF.Builder.CreateAdd(addr, valueSize), IGM.Int8PtrTy);
324-
IGF.Builder.CreateStore(nextOffset, offset);
325-
}
285+
// Decode and load argument value using loaded type metadata.
286+
decodeArgument(i, decoder, argumentTy, param, arguments);
326287
}
327-
328-
IGF.Builder.CreateLifetimeEnd(offset, IGM.getPointerSize());
329288
}
330289

331-
std::pair<llvm::Value *, llvm::Value *>
332-
DistributedAccessor::loadArgument(unsigned argumentIdx,
333-
llvm::Value *argumentSlot,
334-
llvm::Value *argumentType,
335-
const SILParameterInfo &param,
336-
Explosion &arguments) {
290+
void DistributedAccessor::decodeArgument(unsigned argumentIdx,
291+
llvm::Value *decoder,
292+
llvm::Value *argumentType,
293+
const SILParameterInfo &param,
294+
Explosion &arguments) {
295+
auto &paramInfo = IGM.getTypeInfo(param.getSILStorageInterfaceType());
337296
// TODO: `emitLoad*` would actually load value witness table every
338297
// time it's called, which is sub-optimal but all of the APIs that
339298
// deal with value witness tables are currently hidden in GenOpaque.cpp
340299
llvm::Value *valueSize = emitLoadOfSize(IGF, argumentType);
341300

342-
llvm::Value *isInline, *flags;
343-
std::tie(isInline, flags) = emitLoadOfIsInline(IGF, argumentType);
301+
Callee callee = ArgumentDecoder.getCallee(decoder);
302+
303+
std::unique_ptr<CallEmission> emission =
304+
getCallEmission(IGF, callee.getSwiftContext(), std::move(callee));
344305

345-
llvm::Value *alignmentMask = emitAlignMaskFromFlags(IGF, flags);
306+
StackAddress resultValue = IGF.emitDynamicAlloca(
307+
IGM.Int8Ty, valueSize, paramInfo.getBestKnownAlignment());
346308

347-
// Align argument value offset as required by its type metadata.
348-
llvm::Value *alignedSlot =
349-
IGF.Builder.CreatePtrToInt(argumentSlot, IGM.IntPtrTy);
309+
llvm::Value *resultAddr = resultValue.getAddress().getAddress();
310+
311+
resultAddr = IGF.Builder.CreateBitCast(resultAddr, IGM.OpaquePtrTy);
312+
313+
Explosion decodeArgs;
314+
// indirect result buffer as `swift.opaque*`
315+
decodeArgs.add(resultAddr);
316+
// substitution Argument -> <argument metadata>
317+
decodeArgs.add(argumentType);
318+
319+
emission->begin();
350320
{
351-
alignedSlot = IGF.Builder.CreateNUWAdd(alignedSlot, alignmentMask);
321+
emission->setArgs(decodeArgs, /*isOutlined=*/false,
322+
/*witnessMetadata=*/nullptr);
352323

353-
llvm::Value *invertedMask = IGF.Builder.CreateNot(alignmentMask);
354-
alignedSlot = IGF.Builder.CreateAnd(alignedSlot, invertedMask);
355-
alignedSlot =
356-
IGF.Builder.CreateIntToPtr(alignedSlot, IGM.OpaquePtrTy);
324+
Explosion result;
325+
emission->emitToExplosion(result, /*isOutlined=*/false);
326+
assert(result.empty());
327+
328+
// TODO: Add error handling a new block that uses `emitAsyncReturn`
329+
// if error slot is non-null.
357330
}
331+
emission->end();
358332

359-
Address argumentAddr(alignedSlot, IGM.getPointerAlignment());
333+
// Remember to deallocate later.
334+
AllocatedArguments.push_back(resultValue);
360335

361336
switch (param.getConvention()) {
362337
case ParameterConvention::Indirect_In:
@@ -367,22 +342,19 @@ DistributedAccessor::loadArgument(unsigned argumentIdx,
367342

368343
auto stackAddr =
369344
IGF.emitDynamicAlloca(IGM.Int8Ty, valueSize, Alignment(16));
370-
auto argPtr = stackAddr.getAddress().getAddress();
371345

372346
emitInitializeWithCopyCall(IGF, argumentType, stackAddr.getAddress(),
373-
argumentAddr);
374-
375-
arguments.add(argPtr);
347+
resultValue.getAddress());
376348

377-
// Remember to deallocate later.
378-
AllocatedArguments.push_back({argumentIdx, stackAddr});
349+
// Remember to deallocate a copy.
350+
AllocatedArguments.push_back(stackAddr);
379351
break;
380352
}
381353

382354
case ParameterConvention::Indirect_In_Guaranteed: {
383355
// The argument is +0, so we can use the address of the param in
384356
// the context directly.
385-
arguments.add(alignedSlot);
357+
arguments.add(resultAddr);
386358
break;
387359
}
388360

@@ -393,95 +365,20 @@ DistributedAccessor::loadArgument(unsigned argumentIdx,
393365
case ParameterConvention::Direct_Guaranteed:
394366
case ParameterConvention::Direct_Unowned: {
395367
auto paramTy = param.getSILStorageInterfaceType();
396-
auto &typeInfo = IGM.getTypeInfo(paramTy);
397368
Address eltPtr = IGF.Builder.CreateBitCast(
398-
argumentAddr, IGM.getStoragePointerType(paramTy));
369+
resultValue.getAddress(), IGM.getStoragePointerType(paramTy));
399370

400-
cast<LoadableTypeInfo>(typeInfo).loadAsTake(IGF, eltPtr, arguments);
371+
cast<LoadableTypeInfo>(paramInfo).loadAsTake(IGF, eltPtr, arguments);
401372
break;
402373
}
403374

404375
case ParameterConvention::Direct_Owned: {
405-
auto &typeInfo = IGM.getTypeInfo(param.getSILStorageInterfaceType());
406376
// Copy the value out at +1.
407-
cast<LoadableTypeInfo>(typeInfo).loadAsCopy(IGF, argumentAddr, arguments);
377+
cast<LoadableTypeInfo>(paramInfo).loadAsCopy(IGF, resultValue.getAddress(),
378+
arguments);
408379
break;
409380
}
410381
}
411-
412-
return {alignedSlot, valueSize};
413-
}
414-
415-
std::pair<llvm::Value *, llvm::Value *>
416-
DistributedAccessor::loadArgument(unsigned argumentIdx,
417-
llvm::Value *argumentSlot,
418-
SILType paramTy,
419-
ParameterConvention convention,
420-
Explosion &arguments) {
421-
const TypeInfo &typeInfo = IGF.getTypeInfo(paramTy);
422-
423-
// 1. Check whether the native representation is empty e.g.
424-
// this happens for empty enums.
425-
if (paramTy.isObject()) {
426-
auto &nativeSchema = typeInfo.nativeParameterValueSchema(IGM);
427-
// If schema is empty, skip to the next argument.
428-
if (nativeSchema.empty())
429-
return {argumentSlot, typeInfo.getSize(IGF, paramTy)};
430-
}
431-
432-
// 2. Cast the pointer to the type of the element.
433-
Address eltPtr = IGF.Builder.CreateBitCast(
434-
Address(argumentSlot, IGM.getPointerAlignment()),
435-
IGM.getStoragePointerType(paramTy));
436-
437-
// 3. Adjust typed pointer to the alignment of the type.
438-
auto alignedOffset = typeInfo.roundUpToTypeAlignment(IGF, eltPtr, paramTy);
439-
440-
// 4. Create an exploded version of the type to pass as an
441-
// argument to distributed method.
442-
443-
switch (convention) {
444-
case ParameterConvention::Indirect_In:
445-
case ParameterConvention::Indirect_In_Constant: {
446-
// The +1 argument is passed indirectly, so we need to copy it into
447-
// a temporary.
448-
449-
auto stackAddr = typeInfo.allocateStack(IGF, paramTy, "arg.temp");
450-
auto argPtr = stackAddr.getAddress().getAddress();
451-
452-
typeInfo.initializeWithCopy(IGF, stackAddr.getAddress(), alignedOffset,
453-
paramTy, /*isOutlined=*/false);
454-
arguments.add(argPtr);
455-
456-
// Remember to deallocate later.
457-
AllocatedArguments.push_back({argumentIdx, stackAddr});
458-
break;
459-
}
460-
461-
case ParameterConvention::Indirect_In_Guaranteed: {
462-
// The argument is +0, so we can use the address of the param in
463-
// the context directly.
464-
arguments.add(alignedOffset.getAddress());
465-
break;
466-
}
467-
468-
case ParameterConvention::Indirect_Inout:
469-
case ParameterConvention::Indirect_InoutAliasable:
470-
llvm_unreachable("indirect 'inout' parameters are not supported");
471-
472-
case ParameterConvention::Direct_Guaranteed:
473-
case ParameterConvention::Direct_Unowned: {
474-
cast<LoadableTypeInfo>(typeInfo).loadAsTake(IGF, alignedOffset, arguments);
475-
break;
476-
}
477-
478-
case ParameterConvention::Direct_Owned:
479-
// Copy the value out at +1.
480-
cast<LoadableTypeInfo>(typeInfo).loadAsCopy(IGF, alignedOffset, arguments);
481-
break;
482-
}
483-
484-
return {alignedOffset.getAddress(), typeInfo.getSize(IGF, paramTy)};
485382
}
486383

487384
void DistributedAccessor::emitLoadOfWitnessTables(llvm::Value *witnessTables,
@@ -575,7 +472,7 @@ void DistributedAccessor::emit() {
575472

576473
// Step one is to load all of the data from argument buffer,
577474
// so it could be forwarded to the distributed method.
578-
computeArguments(argBuffer, argTypes, arguments);
475+
decodeArguments(argDecoder, argTypes, arguments);
579476

580477
// Add all of the substitutions to the explosion
581478
if (auto *genericEnvironment = Target->getGenericEnvironment()) {
@@ -647,19 +544,12 @@ void DistributedAccessor::emit() {
647544

648545
emission->end();
649546

650-
// Deallocate all of the copied arguments.
547+
// Deallocate all of the copied arguments. Since allocations happened
548+
// on stack they have to be deallocated in reverse order.
651549
{
652-
auto targetType = Target->getLoweredFunctionType();
653-
for (auto &alloca : AllocatedArguments) {
654-
const auto &param = targetType->getParameters()[alloca.ArgumentIdx];
655-
auto paramTy = param.getSILStorageInterfaceType();
656-
657-
if (paramTy.hasTypeParameter()) {
658-
IGF.emitDeallocateDynamicAlloca(alloca.Addr);
659-
} else {
660-
auto &typeInfo = IGF.getTypeInfo(paramTy);
661-
typeInfo.deallocateStack(IGF, alloca.Addr, paramTy);
662-
}
550+
while (!AllocatedArguments.empty()) {
551+
auto argument = AllocatedArguments.pop_back_val();
552+
IGF.emitDeallocateDynamicAlloca(argument);
663553
}
664554
}
665555

0 commit comments

Comments
 (0)