Skip to content

Commit 4d4c80b

Browse files
committed
[IRGen] Distributed: Always invoke decodeNextArgument through witness thunk
`decodeNextArgument` is now a requirement, so thunks should be always calling that instead of trying to dispatch directly.
1 parent 91b6bda commit 4d4c80b

File tree

3 files changed

+55
-135
lines changed

3 files changed

+55
-135
lines changed

lib/IRGen/GenDistributed.cpp

Lines changed: 16 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,6 @@ struct ArgumentDecoderInfo {
8181
/// The type of `decodeNextArgument` method.
8282
CanSILFunctionType MethodType;
8383

84-
/// Protocol requirements associated with the generic
85-
/// parameter `Argument` of this decode method.
86-
GenericSignature::RequiredProtocols ProtocolRequirements;
87-
8884
// Witness metadata for conformance to DistributedTargetInvocationDecoder
8985
// protocol.
9086
WitnessMetadata Witness;
@@ -94,31 +90,19 @@ struct ArgumentDecoderInfo {
9490
FunctionPointer decodeNextArgumentPtr,
9591
CanSILFunctionType decodeNextArgumentTy)
9692
: Decoder(decoder), MethodPtr(decodeNextArgumentPtr),
97-
MethodType(decodeNextArgumentTy),
98-
ProtocolRequirements(findProtocolRequirements(decodeNextArgumentTy)) {
93+
MethodType(decodeNextArgumentTy) {
9994
Witness.SelfMetadata = decoderType;
10095
Witness.SelfWitnessTable = decoderWitnessTable;
10196
}
10297

10398
CanSILFunctionType getMethodType() const { return MethodType; }
10499

105-
ArrayRef<ProtocolDecl *> getProtocolRequirements() const {
106-
return ProtocolRequirements;
100+
WitnessMetadata *getWitnessMetadata() const {
101+
return const_cast<WitnessMetadata *>(&Witness);
107102
}
108103

109104
/// Form a callee to a decode method - `decodeNextArgument`.
110105
Callee getCallee() const;
111-
112-
private:
113-
static GenericSignature::RequiredProtocols
114-
findProtocolRequirements(CanSILFunctionType decodeMethodTy) {
115-
auto signature = decodeMethodTy->getInvocationGenericSignature();
116-
auto genericParams = signature.getGenericParams();
117-
118-
// func decodeNextArgument<Arg : #SerializationRequirement#>() throws -> Arg
119-
assert(genericParams.size() == 1);
120-
return signature->getRequiredProtocols(genericParams.front());
121-
}
122106
};
123107

124108
class DistributedAccessor {
@@ -156,10 +140,6 @@ class DistributedAccessor {
156140
llvm::Value *argumentType, const SILParameterInfo &param,
157141
Explosion &arguments);
158142

159-
void lookupWitnessTables(llvm::Value *value,
160-
ArrayRef<ProtocolDecl *> protocols,
161-
Explosion &witnessTables);
162-
163143
/// Load witness table addresses (if any) from the given buffer
164144
/// into the given argument explosion.
165145
///
@@ -417,17 +397,13 @@ void DistributedAccessor::decodeArgument(unsigned argumentIdx,
417397
// substitution Argument -> <argument metadata>
418398
decodeArgs.add(argumentType);
419399

420-
// Lookup witness tables for the requirement on the argument type.
421-
lookupWitnessTables(argumentType, decoder.getProtocolRequirements(),
422-
decodeArgs);
423-
424400
Address calleeErrorSlot;
425401
llvm::Value *decodeError = nullptr;
426402

427403
emission->begin();
428404
{
429405
emission->setArgs(decodeArgs, /*isOutlined=*/false,
430-
/*witnessMetadata=*/nullptr);
406+
/*witnessMetadata=*/decoder.getWitnessMetadata());
431407

432408
Explosion result;
433409
emission->emitToExplosion(result, /*isOutlined=*/false);
@@ -528,37 +504,6 @@ void DistributedAccessor::decodeArgument(unsigned argumentIdx,
528504
}
529505
}
530506

531-
void DistributedAccessor::lookupWitnessTables(
532-
llvm::Value *value, ArrayRef<ProtocolDecl *> protocols,
533-
Explosion &witnessTables) {
534-
auto conformsToProtocol = IGM.getConformsToProtocolFunctionPointer();
535-
536-
for (auto *protocol : protocols) {
537-
auto *protocolDescriptor = IGM.getAddrOfProtocolDescriptor(protocol);
538-
auto *witnessTable =
539-
IGF.Builder.CreateCall(conformsToProtocol, {value, protocolDescriptor});
540-
541-
auto failBB = IGF.createBasicBlock("missing-witness");
542-
auto contBB = IGF.createBasicBlock("");
543-
544-
auto isNull = IGF.Builder.CreateICmpEQ(
545-
witnessTable, llvm::ConstantPointerNull::get(IGM.WitnessTablePtrTy));
546-
IGF.Builder.CreateCondBr(isNull, failBB, contBB);
547-
548-
// This operation shouldn't fail because runtime should have checked that
549-
// a particular argument type conforms to `SerializationRequirement`
550-
// of the distributed actor the decoder is used for. If it does fail
551-
// then accessor should trap.
552-
{
553-
IGF.Builder.emitBlock(failBB);
554-
IGF.emitTrap("missing witness table", /*EmitUnreachable=*/true);
555-
}
556-
557-
IGF.Builder.emitBlock(contBB);
558-
witnessTables.add(witnessTable);
559-
}
560-
}
561-
562507
void DistributedAccessor::emitLoadOfWitnessTables(llvm::Value *witnessTables,
563508
llvm::Value *numTables,
564509
unsigned expectedWitnessTables,
@@ -803,70 +748,22 @@ DistributedAccessor::getCalleeForDistributedTarget(llvm::Value *self) const {
803748

804749
ArgumentDecoderInfo DistributedAccessor::findArgumentDecoder(
805750
llvm::Value *decoder, llvm::Value *decoderTy, llvm::Value *witnessTable) {
806-
auto *actor = getDistributedActorOf(Target);
807-
auto expansionContext = IGM.getMaximalTypeExpansionContext();
808-
809-
auto *decodeFn = IGM.Context.getDistributedActorArgumentDecodingMethod(actor);
810-
assert(decodeFn && "no suitable decoder?");
811-
812-
auto methodTy = IGM.getSILTypes().getConstantFunctionType(
813-
expansionContext, SILDeclRef(decodeFn));
814-
815-
auto fpKind = FunctionPointerKind::defaultAsync();
816-
auto signature = IGM.getSignature(methodTy, fpKind);
817-
818-
// If the decoder class is `final`, let's emit a direct reference.
819-
auto *decoderDecl = decodeFn->getDeclContext()->getSelfNominalTypeDecl();
820-
821-
// If decoder is a class, need to load it first because generic parameter
822-
// is passed indirectly. This is good for structs and enums because
823-
// `decodeNextArgument` is a mutating method, but not for classes because
824-
// in that case heap object is mutated directly.
825-
bool usesDispatchThunk = false;
751+
auto &C = IGM.Context;
826752

827-
if (auto classDecl = dyn_cast<ClassDecl>(decoderDecl)) {
828-
auto selfTy = methodTy->getSelfParameter().getSILStorageType(
829-
IGM.getSILModule(), methodTy, expansionContext);
753+
auto decoderProtocol = C.getDistributedTargetInvocationDecoderDecl();
754+
SILDeclRef decodeNextArgumentRef(
755+
decoderProtocol->getSingleRequirement(C.Id_decodeNextArgument));
830756

831-
auto &classTI = IGM.getTypeInfo(selfTy).as<ClassTypeInfo>();
832-
auto &classLayout = classTI.getClassLayout(IGM, selfTy,
833-
/*forBackwardDeployment=*/false);
757+
llvm::Constant *fnPtr =
758+
IGM.getAddrOfDispatchThunk(decodeNextArgumentRef, NotForDefinition);
834759

835-
llvm::Value *typedDecoderPtr = IGF.Builder.CreateBitCast(
836-
decoder, classLayout.getType()->getPointerTo()->getPointerTo());
837-
838-
Explosion instance;
839-
840-
classTI.loadAsTake(IGF,
841-
{typedDecoderPtr, classTI.getStorageType(),
842-
classTI.getBestKnownAlignment()},
843-
instance);
844-
845-
decoder = instance.claimNext();
846-
847-
/// When using library evolution functions have another "dispatch thunk"
848-
/// so we must use this instead of the decodeFn directly.
849-
usesDispatchThunk =
850-
getMethodDispatch(decodeFn) == swift::MethodDispatch::Class &&
851-
classDecl->hasResilientMetadata();
852-
}
853-
854-
FunctionPointer methodPtr;
855-
856-
if (usesDispatchThunk) {
857-
auto fnPtr = IGM.getAddrOfDispatchThunk(SILDeclRef(decodeFn), NotForDefinition);
858-
methodPtr = FunctionPointer::createUnsigned(
859-
methodTy, fnPtr, signature, /*useSignature=*/true);
860-
} else {
861-
SILFunction *decodeSILFn = IGM.getSILModule().lookUpFunction(SILDeclRef(decodeFn));
862-
auto fnPtr = IGM.getAddrOfSILFunction(decodeSILFn, NotForDefinition,
863-
/*isDynamicallyReplaceable=*/false);
864-
methodPtr = FunctionPointer::forDirect(
865-
classifyFunctionPointerKind(decodeSILFn), fnPtr,
866-
/*secondaryValue=*/nullptr, signature);
867-
}
760+
auto fnType = IGM.getSILTypes().getConstantFunctionType(
761+
IGM.getMaximalTypeExpansionContext(), decodeNextArgumentRef);
868762

869-
return {decoder, decoderTy, witnessTable, methodPtr, methodTy};
763+
auto sig = IGM.getSignature(fnType);
764+
auto fn = FunctionPointer::forDirect(fnType, fnPtr,
765+
/*secondaryValue=*/nullptr, sig, true);
766+
return {decoder, decoderTy, witnessTable, fn, fnType};
870767
}
871768

872769
SILType DistributedAccessor::getResultType() const {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -module-name main -Xfrontend -enable-experimental-distributed -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out | %FileCheck %s --color
5+
6+
// REQUIRES: executable_test
7+
// REQUIRES: concurrency
8+
// REQUIRES: distributed
9+
10+
// rdar://76038845
11+
// UNSUPPORTED: use_os_stdlib
12+
// UNSUPPORTED: back_deployment_runtime
13+
14+
// rdar://90373022
15+
// UNSUPPORTED: OS=watchos
16+
17+
import Distributed
18+
19+
distributed actor Worker<ActorSystem> where ActorSystem: DistributedActorSystem<any Codable>, ActorSystem.ActorID: Codable {
20+
distributed func hi(name: String) {
21+
print("Hi, \(name)!")
22+
}
23+
24+
nonisolated var description: Swift.String {
25+
"Worker(\(id))"
26+
}
27+
}
28+
29+
// ==== Execute ----------------------------------------------------------------
30+
@main struct Main {
31+
static func main() async throws {
32+
let system = LocalTestingDistributedActorSystem()
33+
34+
let actor = Worker(actorSystem: system)
35+
try await actor.hi(name: "P") // local calls should still just work
36+
// CHECK: Hi, P!
37+
}
38+
}

test/Distributed/distributed_actor_accessor_thunks_64bit.swift

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,25 +98,10 @@ public distributed actor MyOtherActor {
9898

9999
/// Read the current offset and cast an element to `Int`
100100

101-
// CHECK: [[DECODER:%.*]] = load ptr, ptr %1
102-
// CHECK-NEXT: [[DECODE_NEXT_ARG_REF:%.*]] = getelementptr inbounds ptr, ptr %2, i64
103-
104101
// CHECK: [[ARG_0_SIZE_ADJ:%.*]] = add i64 %size, 15
105102
// CHECK-NEXT: [[ARG_0_SIZE:%.*]] = and i64 [[ARG_0_SIZE_ADJ]], -16
106103
// CHECK-NEXT: [[ARG_0_VALUE_BUF:%.*]] = call swiftcc ptr @swift_task_alloc(i64 [[ARG_0_SIZE]])
107-
// CHECK-NEXT: [[ENCODABLE_WITNESS:%.*]] = call ptr @swift_conformsToProtocol{{(2)?}}(ptr %arg_type, ptr @"$sSeMp")
108-
// CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq ptr [[ENCODABLE_WITNESS]], null
109-
// CHECK-NEXT: br i1 [[IS_NULL]], label %missing-witness, label [[CONT:%.*]]
110-
// CHECK: missing-witness:
111-
// CHECK-NEXT: call void @llvm.trap()
112-
// CHECK-NEXT: unreachable
113-
// CHECK: [[DECODABLE_WITNESS:%.*]] = call ptr @swift_conformsToProtocol{{(2)?}}(ptr %arg_type, ptr @"$sSEMp")
114-
// CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq ptr [[DECODABLE_WITNESS]], null
115-
// CHECK-NEXT: br i1 [[IS_NULL]], label %missing-witness1, label [[CONT:%.*]]
116-
// CHECK: missing-witness1:
117-
// CHECK-NEXT: call void @llvm.trap()
118-
// CHECK-NEXT: unreachable
119-
// CHECK: call swiftcc void @"$s27FakeDistributedActorSystems0A17InvocationDecoderC18decodeNextArgumentxyKSeRzSERzlF"(ptr noalias sret(%swift.opaque) [[ARG_0_VALUE_BUF]], ptr %arg_type, ptr [[ENCODABLE_WITNESS]], ptr [[DECODABLE_WITNESS]], ptr swiftself [[DECODER]], ptr noalias nocapture swifterror dereferenceable(8) %swifterror)
104+
// CHECK-NEXT: call swiftcc void @"$s11Distributed0A23TargetInvocationDecoderP18decodeNextArgumentqd__yKlFTj"(ptr noalias sret(%swift.opaque) [[ARG_0_VALUE_BUF]], ptr %arg_type, ptr swiftself %1, ptr noalias nocapture swifterror dereferenceable(8) %swifterror, ptr [[DECODER_TYPE]], ptr [[DECODER_PROTOCOL_WITNESS]])
120105

121106
// CHECK: store ptr null, ptr %swifterror
122107
// CHECK-NEXT: %._value = getelementptr inbounds %TSi, ptr [[ARG_0_VALUE_BUF]], i32 0, i32 0

0 commit comments

Comments
 (0)